From 8e5b2bd7737ab621286a6f77f615cae367da3a68 Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 19 Feb 2025 08:22:04 -0600 Subject: [PATCH] sim ctx decoupling progress --- build.c | 4 +- src/config.h | 2 +- src/phys.c | 12 +- src/phys.h | 6 + src/sim.c | 174 ++++++++++++++----- src/sim.h | 10 +- src/sim_client.c | 62 ++++--- src/sim_client.h | 9 +- src/sim_ent.c | 2 +- src/sim_ent.h | 2 +- src/sim_snapshot.h | 8 - src/user.c | 420 ++++++++++++++++++++------------------------- 12 files changed, 382 insertions(+), 329 deletions(-) diff --git a/build.c b/build.c index 5c216a1b..175c506c 100644 --- a/build.c +++ b/build.c @@ -581,12 +581,14 @@ void OnBuild(StringList cli_args) OS_Exit(1); } StringListAppend(&perm, &compile_args, Lit("-DPROFILING=1")); + /* Tracy flags */ StringListAppend(&perm, &compile_args, Lit("-DTRACY_ENABLE=1")); if (!arg_profiler_sampling) { StringListAppend(&perm, &compile_args, Lit("-DTRACY_NO_SAMPLING -DTRACY_NO_SYSTEM_TRACING -DTRACY_NO_CALLSTACK")); } - /* Disable compile_warnings when compiling tracy client */ + + /* Disable compiler warnings when compiling tracy client */ compile_warnings = (StringList) { 0 }; link_warnings = (StringList) { 0 }; StringListAppend(&perm, &compile_warnings, Lit("-Wno-everything")); diff --git a/src/config.h b/src/config.h index 1c0de12a..c0ef98f4 100644 --- a/src/config.h +++ b/src/config.h @@ -33,7 +33,7 @@ #define SPACE_CELL_BUCKETS_SQRT (256) #define SPACE_CELL_SIZE 1.0f -#define SIM_TICKS_PER_SECOND 100.0 +#define SIM_TICKS_PER_SECOND 50.0 #define SIM_TIMESCALE 1 #define SIM_PHYSICS_SUBSTEPS 4 diff --git a/src/phys.c b/src/phys.c index 7de51621..eb04200c 100644 --- a/src/phys.c +++ b/src/phys.c @@ -45,10 +45,10 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a struct phys_collision_data_array res = ZI; res.a = arena_dry_push(arena, struct phys_collision_data); struct sim_snapshot *ss = ctx->ss; - struct sim_ent_lookup *contact_lookup = &ss->contact_lookup; - struct space *space = ss->space; + struct space *space = ctx->space; + struct sim_ent_lookup *contact_lookup = ctx->contact_lookup; #if COLLIDER_DEBUG - struct sim_ent_lookup *debug_lookup = &ss->collision_debug_lookup; + struct sim_ent_lookup *debug_lookup = ctx->collision_debug_lookup; #endif struct sim_ent *root = sim_ent_from_handle(ss, SIM_ENT_ROOT_HANDLE); @@ -284,7 +284,7 @@ void phys_prepare_contacts(struct phys_ctx *ctx, u64 phys_iteration) { __prof; struct sim_snapshot *ss = ctx->ss; - struct sim_ent_lookup *contact_lookup = &ss->contact_lookup; + struct sim_ent_lookup *contact_lookup = ctx->contact_lookup; for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) { struct sim_ent *constraint_ent = &ss->ents[sim_ent_index]; @@ -1003,7 +1003,7 @@ f32 phys_determine_earliest_toi_for_bullets(struct phys_ctx *ctx, f32 step_dt, f { __prof; struct sim_snapshot *ss = ctx->ss; - struct space *space = ss->space; + struct space *space = ctx->space; f32 smallest_t = 1; for (u64 e0_index = 0; e0_index < ss->num_ents_reserved; ++e0_index) { @@ -1053,7 +1053,7 @@ f32 phys_determine_earliest_toi_for_bullets(struct phys_ctx *ctx, f32 step_dt, f void phys_update_aabbs(struct phys_ctx *ctx) { struct sim_snapshot *ss = ctx->ss; - struct space *space = ss->space; + struct space *space = ctx->space; 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]; if (!sim_ent_is_valid_and_active(ent)) continue; diff --git a/src/phys.h b/src/phys.h index 2b6d8014..8d4b28b2 100644 --- a/src/phys.h +++ b/src/phys.h @@ -33,6 +33,12 @@ struct phys_ctx { struct sim_snapshot *ss; phys_collision_callback_func *pre_solve_callback; phys_collision_callback_func *post_solve_callback; + + struct space *space; + struct sim_ent_lookup *contact_lookup; +#if COLLIDER_DEBUG + struct sim_ent_lookup *collision_debug_lookup; +#endif }; /* ========================== * diff --git a/src/sim.c b/src/sim.c index 25f54623..463a5138 100644 --- a/src/sim.c +++ b/src/sim.c @@ -52,7 +52,7 @@ struct sim_ctx *sim_ctx_alloc(struct sprite_startup_receipt *sprite_sr, /* Create snapshot store */ snapshot_store = sim_snapshot_store_alloc(); - world = sim_snapshot_nil(); + ss_blended = sim_snapshot_nil(); return ctx; } @@ -277,15 +277,16 @@ INTERNAL void test_clear_level(struct sim_snapshot *world) * Release entities * ========================== */ -INTERNAL void release_entities_with_prop(struct sim_snapshot *world, enum sim_ent_prop prop) +#if 0 +INTERNAL void release_entities_with_prop(struct sim_snapshot *ss_blended, enum sim_ent_prop prop) { struct temp_arena scratch = scratch_begin_no_conflict(); - struct space *space = world->space; + struct space *space = ss_blended->space; struct sim_ent **ents_to_release = arena_dry_push(scratch.arena, struct sim_ent *); u64 ents_to_release_count = 0; - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; + for (u64 ent_index = 0; ent_index < ss_blended->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &ss_blended->ents[ent_index]; if (ent->valid && sim_ent_has_prop(ent, prop)) { *arena_push(scratch.arena, struct sim_ent *) = ent; ++ents_to_release_count; @@ -316,6 +317,34 @@ INTERNAL void release_entities_with_prop(struct sim_snapshot *world, enum sim_en scratch_end(scratch); } +#else +INTERNAL void release_entities_with_prop(struct sim_snapshot *world, enum sim_ent_prop prop) +{ + struct temp_arena scratch = scratch_begin_no_conflict(); + + struct sim_ent **ents_to_release = arena_dry_push(scratch.arena, struct sim_ent *); + u64 ents_to_release_count = 0; + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (ent->valid && sim_ent_has_prop(ent, prop)) { + *arena_push(scratch.arena, struct sim_ent *) = ent; + ++ents_to_release_count; + } + } + + /* Release from snapshot */ + /* TODO: Breadth first iteration to only release parent entities (since + * child entities will be released along with parent anyway) */ + for (u64 i = 0; i < ents_to_release_count; ++i) { + struct sim_ent *ent = ents_to_release[i]; + if (ent->is_top && !ent->is_root) { + sim_ent_release(ent); + } + } + + scratch_end(scratch); +} +#endif /* ========================== * * Respond to physics collisions @@ -423,7 +452,7 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct /* Release old snapshots */ { /* TODO: Something better */ - i64 release_tick = (i64)world->tick - 25; /* Arbitrary tick offset */ + i64 release_tick = (i64)ss_blended->tick - 25; /* Arbitrary tick offset */ if (release_tick > 0) { struct sim_snapshot *old = sim_snapshot_from_tick(snapshot_store, release_tick); if (old->valid) { @@ -438,14 +467,36 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct /* TODO: Remove this */ - world->space = prev_snapshot->space; - world->contact_lookup = prev_snapshot->contact_lookup; - if (!world->space) { - world->space = space_alloc(1, 256); + + /* Acceleration structures */ + struct space *space = space_alloc(1, 256); + for (u64 sim_ent_index = 0; sim_ent_index < world->num_ents_reserved; ++sim_ent_index) { + struct sim_ent *ent = &world->ents[sim_ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + + MEMZERO_STRUCT(&ent->space_handle); } - if (!world->contact_lookup.arena.base) { - world->contact_lookup = sim_ent_lookup_alloc(4096); + +#if COLLIDER_DEBUG + struct sim_ent_lookup collision_debug_lookup = sim_ent_lookup_alloc(4096); +#endif + struct sim_ent_lookup contact_lookup = sim_ent_lookup_alloc(4096); + for (u64 sim_ent_index = 0; sim_ent_index < world->num_ents_reserved; ++sim_ent_index) { + struct sim_ent *constraint_ent = &world->ents[sim_ent_index]; + if (!sim_ent_is_valid_and_active(constraint_ent)) continue; + if (!sim_ent_has_prop(constraint_ent, SIM_ENT_PROP_CONTACT_CONSTRAINT)) continue; + + struct sim_ent_lookup_key key = sim_ent_lookup_key_from_two_handles(constraint_ent->contact_constraint_data.e0, constraint_ent->contact_constraint_data.e1); + sim_ent_lookup_set(&contact_lookup, key, constraint_ent->handle); } + + + + + + + + world->phys_iteration = prev_snapshot->phys_iteration; @@ -539,14 +590,22 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct { /* Create connecting clients */ for (struct sim_cmd_frame *frame = input_frames.first; frame; frame = frame->next) { - struct host_channel_id channel_id = frame->channel; - struct sim_client *client = sim_client_from_channel_id(world, channel_id); - if (!client->valid) { - for (struct sim_cmd *cmd = frame->first; cmd; cmd = cmd->next) { - enum sim_cmd_kind kind = cmd->kind; - if (kind == SIM_CMD_KIND_SIM_CLIENT_CONNECT && !host_channel_id_is_nil(channel_id)) { - client = sim_client_alloc(world, channel_id); - break; + struct sim_client *client; + if (frame->sender_is_local) { + client = sim_client_from_handle(world, world->local_client); + if (!client->valid) { + client = sim_client_alloc(world, HOST_CHANNEL_ID_NIL, SIM_CLIENT_KIND_LOCAL); + world->local_client = client->handle; + } + } else { + client = sim_client_from_channel_id(world, frame->sender_channel); + if (!client->valid) { + for (struct sim_cmd *cmd = frame->first; cmd; cmd = cmd->next) { + enum sim_cmd_kind kind = cmd->kind; + if (kind == SIM_CMD_KIND_SIM_CLIENT_CONNECT && !host_channel_id_is_nil(frame->sender_channel)) { + client = sim_client_alloc(world, frame->sender_channel, SIM_CLIENT_KIND_NETSIM); + break; + } } } } @@ -554,7 +613,12 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct /* Sort cmd frames by client */ client_frames = arena_push_array_zero(scratch.arena, struct sim_cmd_frame *, world->num_clients_reserved); for (struct sim_cmd_frame *frame = input_frames.first; frame; frame = frame->next) { - struct sim_client *client = sim_client_from_channel_id(world, frame->channel); + struct sim_client *client; + if (frame->sender_is_local) { + client = sim_client_from_handle(world, world->local_client); + } else { + client = sim_client_from_channel_id(world, frame->sender_channel); + } if (client->valid && frame->tick == world->tick) { client_frames[client->handle.idx] = frame; } @@ -694,25 +758,30 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct /* Update animation */ { struct sprite_sheet_span span = sprite_sheet_get_span(sheet, ent->sprite_span_name); + if (ent->animation_last_frame_change_time_ns == 0) { + ent->animation_last_frame_change_time_ns = world_time; + } - f64 time_in_frame = ent->animation_time_in_frame + world_dt; + f64 time_in_frame = SECONDS_FROM_NS(world->world_time_ns - ent->animation_last_frame_change_time_ns); u64 frame_index = ent->animation_frame; if (frame_index < span.start || frame_index > span.end) { frame_index = span.start; } - struct sprite_sheet_frame frame = sprite_sheet_get_frame(sheet, frame_index); - while (time_in_frame > frame.duration) { - time_in_frame -= frame.duration; - ++frame_index; - if (frame_index > span.end) { - /* Loop animation */ - frame_index = span.start; + if (span.end > span.start + 1) { + struct sprite_sheet_frame frame = sprite_sheet_get_frame(sheet, frame_index); + while (time_in_frame > frame.duration) { + time_in_frame -= frame.duration; + ++frame_index; + if (frame_index > span.end) { + /* Loop animation */ + frame_index = span.start; + } + frame = sprite_sheet_get_frame(sheet, frame_index); + ent->animation_last_frame_change_time_ns = world->world_time_ns; } - frame = sprite_sheet_get_frame(sheet, frame_index); } - ent->animation_time_in_frame = time_in_frame; ent->animation_frame = frame_index; } @@ -805,8 +874,8 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct * ========================== */ #if 0 - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; + for (u64 ent_index = 0; ent_index < ss_blended->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &ss_blended->ents[ent_index]; if (!sim_ent_is_valid_and_active(ent)) continue; if (!sim_ent_has_prop(ent, SIM_ENT_PROP_TEST)) continue; @@ -1162,6 +1231,11 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct struct phys_ctx phys = ZI; phys.ss = world; phys.pre_solve_callback = on_collision; + phys.space = space; + phys.contact_lookup = &contact_lookup; +#if COLLIDER_DEBUG + phys.collision_debug_lookup = collision_debug_lookup; +#endif /* Step */ phys_step(&phys, world_dt); @@ -1212,7 +1286,7 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct /* Add shooter velocity to bullet */ { /* TODO: Add angular velocity as well? */ - struct sim_ent *top = sim_ent_from_handle(world, src->top); + struct sim_ent *top = sim_ent_from_handle(ss_blended, src->top); impulse = v2_add(impulse, v2_mul(top->linear_velocity, dt)); } #endif @@ -1358,19 +1432,19 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct #if 0 struct sim_cmd_queue_list output_cmds = ZI; - for (u64 i = 0; i < world->num_clients_reserved; ++i) { - struct sim_client *client = &world->clients[i]; + for (u64 i = 0; i < ss_blended->num_clients_reserved; ++i) { + struct sim_client *client = &ss_blended->clients[i]; if (client->valid) { struct temp_arena temp = arena_temp_begin(scratch.arena); struct sim_snapshot *ss0 = sim_snapshot_from_tick(snapshot_store, client->ack); - struct sim_snapshot *ss1 = world; + struct sim_snapshot *ss1 = ss_blended; /* Create & encode snapshot cmd */ { struct sim_cmd *cmd = arena_push(arena, struct sim_cmd); cmd->kind = SIM_CMD_KIND_SNAPSHOT; - cmd->tick = world->tick; + cmd->tick = ss_blended->tick; cmd->snapshot_tick_start = ss0->tick; cmd->snapshot_tick_end = ss1->tick; @@ -1404,11 +1478,27 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct #endif /* ========================== * - * End frame cache scopes + * End frame * ========================== */ + + + + /* TODO: Remove this */ +#if COLLIDER_DEBUG + sim_ent_lookup_release(&collision_debug_lookup); +#endif + sim_ent_lookup_release(&contact_lookup); + space_release(space); + + + + + + sprite_scope_end(sprite_frame_scope); + scratch_end(scratch); return world; } @@ -1472,7 +1562,7 @@ void sim_cmd_frames_decode(struct arena *arena, struct host_event_array host_eve { /* Create a stand-alone cmd frame for connecting */ struct sim_cmd_frame *frame = arena_push_zero(arena, struct sim_cmd_frame); - frame->channel = host_event.channel_id; + frame->sender_channel = host_event.channel_id; struct sim_cmd *cmd = arena_push_zero(arena, struct sim_cmd); cmd->kind = SIM_CMD_KIND_SIM_CLIENT_CONNECT; @@ -1492,7 +1582,7 @@ void sim_cmd_frames_decode(struct arena *arena, struct host_event_array host_eve { /* Create a stand-alone cmd frame for disconnecting */ struct sim_cmd_frame *frame = arena_push_zero(arena, struct sim_cmd_frame); - frame->channel = host_event.channel_id; + frame->sender_channel = host_event.channel_id; struct sim_cmd *cmd = arena_push_zero(arena, struct sim_cmd); cmd->kind = SIM_CMD_KIND_SIM_CLIENT_DISCONNECT; @@ -1515,7 +1605,7 @@ void sim_cmd_frames_decode(struct arena *arena, struct host_event_array host_eve struct bitbuff_reader br = br_from_bitbuff(&bb); struct sim_cmd_frame *frame = arena_push_zero(arena, struct sim_cmd_frame); - frame->channel = host_event.channel_id; + frame->sender_channel = host_event.channel_id; frame->tick = br_read_uv(&br); frame->ack = br_read_uv(&br); diff --git a/src/sim.h b/src/sim.h index e87eea21..40c617bb 100644 --- a/src/sim.h +++ b/src/sim.h @@ -89,9 +89,11 @@ struct sim_cmd { /* Represents all cmds generated by a user/sim for a particular channel in a single tick. */ struct sim_cmd_frame { - struct host_channel_id channel; /* Sender's channel id (if this cmd frame was received by a host) */ - u64 tick; /* Sender's tick (this will always be 0 for user -> local sim cmds) */ - u64 ack; /* Sender's last received cmd frame tick from dst channel */ + struct host_channel_id sender_channel; /* Sender's channel ID will be nil if sender_is_local = true */ + b32 sender_is_local; + + u64 tick; /* The tick that this cmd frame is meant to execute on */ + u64 ack; /* Sender's last received cmd frame tick */ struct sim_cmd *first; struct sim_cmd *last; @@ -152,7 +154,7 @@ struct sim_ctx { /* Snapshot */ struct sim_snapshot_store *snapshot_store; - struct sim_snapshot *world; + struct sim_snapshot *ss_blended; }; /* TODO: Get rid of startup receipts */ diff --git a/src/sim_client.c b/src/sim_client.c index a175cb8d..04688aab 100644 --- a/src/sim_client.c +++ b/src/sim_client.c @@ -41,7 +41,7 @@ struct sim_client *sim_client_from_channel_id(struct sim_snapshot *ss, struct ho return res; } -struct sim_client *sim_client_alloc(struct sim_snapshot *ss, struct host_channel_id channel_id) +struct sim_client *sim_client_alloc(struct sim_snapshot *ss, struct host_channel_id channel_id, enum sim_client_kind kind) { struct sim_client_handle handle = ZI; struct sim_client *client = sim_client_from_handle(ss, ss->first_free_client); @@ -58,24 +58,28 @@ struct sim_client *sim_client_alloc(struct sim_snapshot *ss, struct host_channel ++ss->num_clients_allocated; *client = *sim_client_nil(); client->valid = true; + client->kind = kind; client->handle = handle; - u64 channel_hash = hash_from_channel_id(channel_id); - client->channel_id = channel_id; - client->channel_hash = channel_hash; + if (kind == SIM_CLIENT_KIND_NETSIM) { + ASSERT(!host_channel_id_is_nil(channel_id)); + u64 channel_hash = hash_from_channel_id(channel_id); + client->channel_id = channel_id; + client->channel_hash = channel_hash; - /* Insert into channel lookup */ - u64 bucket_index = channel_hash % ss->num_client_lookup_buckets; - struct sim_client_lookup_bucket *bucket = &ss->client_lookup_buckets[bucket_index]; - { - struct sim_client *prev_in_bucket = sim_client_from_handle(ss, bucket->last); - if (prev_in_bucket->valid) { - prev_in_bucket->next_in_bucket = client->handle; - client->prev_in_bucket = prev_in_bucket->handle; - } else { - bucket->first = client->handle; + /* Insert into channel lookup */ + u64 bucket_index = channel_hash % ss->num_client_lookup_buckets; + struct sim_client_lookup_bucket *bucket = &ss->client_lookup_buckets[bucket_index]; + { + struct sim_client *prev_in_bucket = sim_client_from_handle(ss, bucket->last); + if (prev_in_bucket->valid) { + prev_in_bucket->next_in_bucket = client->handle; + client->prev_in_bucket = prev_in_bucket->handle; + } else { + bucket->first = client->handle; + } + bucket->last = client->handle; } - bucket->last = client->handle; } return client; @@ -91,19 +95,21 @@ void sim_client_release(struct sim_client *client) --ss->num_clients_allocated; /* Remove from channel lookup */ - u64 bucket_index = client->channel_hash % ss->num_client_lookup_buckets; - struct sim_client_lookup_bucket *bucket = &ss->client_lookup_buckets[bucket_index]; - struct sim_client *prev = sim_client_from_handle(ss, client->prev_in_bucket); - struct sim_client *next = sim_client_from_handle(ss, client->next_in_bucket); - if (prev->valid) { - prev->next_in_bucket = next->handle; - } else { - bucket->first = next->handle; - } - if (next->valid) { - next->prev_in_bucket = prev->handle; - } else { - bucket->last = prev->handle; + if (client->kind == SIM_CLIENT_KIND_NETSIM) { + u64 bucket_index = client->channel_hash % ss->num_client_lookup_buckets; + struct sim_client_lookup_bucket *bucket = &ss->client_lookup_buckets[bucket_index]; + struct sim_client *prev = sim_client_from_handle(ss, client->prev_in_bucket); + struct sim_client *next = sim_client_from_handle(ss, client->next_in_bucket); + if (prev->valid) { + prev->next_in_bucket = next->handle; + } else { + bucket->first = next->handle; + } + if (next->valid) { + next->prev_in_bucket = prev->handle; + } else { + bucket->last = prev->handle; + } } } diff --git a/src/sim_client.h b/src/sim_client.h index 047b0bfe..e5484a91 100644 --- a/src/sim_client.h +++ b/src/sim_client.h @@ -8,6 +8,12 @@ struct sim_client_channel_lookup_bucket; struct sim_snapshot; +enum sim_client_kind { + SIM_CLIENT_KIND_INVALID, + SIM_CLIENT_KIND_LOCAL, + SIM_CLIENT_KIND_NETSIM +}; + struct sim_client_lookup_bucket { struct sim_client_handle first; struct sim_client_handle last; @@ -15,6 +21,7 @@ struct sim_client_lookup_bucket { struct sim_client { b32 valid; + enum sim_client_kind kind; struct sim_client_handle handle; struct sim_snapshot *ss; @@ -51,7 +58,7 @@ INLINE struct sim_client *sim_client_nil(void) struct sim_client *sim_client_from_handle(struct sim_snapshot *ss, struct sim_client_handle handle); struct sim_client *sim_client_from_channel_id(struct sim_snapshot *ss, struct host_channel_id channel_id); -struct sim_client *sim_client_alloc(struct sim_snapshot *ss, struct host_channel_id channel_id); +struct sim_client *sim_client_alloc(struct sim_snapshot *ss, struct host_channel_id channel_id, enum sim_client_kind kind); void sim_client_release(struct sim_client *client); /* ========================== * diff --git a/src/sim_ent.c b/src/sim_ent.c index 897351be..483575f8 100644 --- a/src/sim_ent.c +++ b/src/sim_ent.c @@ -464,7 +464,7 @@ void sim_ent_lerp(struct sim_ent *e, struct sim_ent *e0, struct sim_ent *e1, f64 e->control.focus = v2_lerp(e0->control.focus, e1->control.focus, blend); e->sprite_local_xform = xform_lerp(e0->sprite_local_xform, e1->sprite_local_xform, blend); - e->animation_time_in_frame = math_lerp_f64(e0->animation_time_in_frame, e1->animation_time_in_frame, (f64)blend); + e->animation_last_frame_change_time_ns = math_lerp_i64(e0->animation_last_frame_change_time_ns, e1->animation_last_frame_change_time_ns, (f64)blend); e->animation_frame = (u32)math_round_to_int(math_lerp_f32(e0->animation_frame, e1->animation_frame, blend)); e->camera_quad_xform = xform_lerp(e0->camera_quad_xform, e1->camera_quad_xform, blend); diff --git a/src/sim_ent.h b/src/sim_ent.h index fdfa4374..a26dddd6 100644 --- a/src/sim_ent.h +++ b/src/sim_ent.h @@ -212,7 +212,7 @@ struct sim_ent { /* Animation */ /* SIM_ENT_PROP_ANIMATING */ - f64 animation_time_in_frame; + i64 animation_last_frame_change_time_ns; u32 animation_frame; /* ====================================================================== */ diff --git a/src/sim_snapshot.h b/src/sim_snapshot.h index 6ce3f576..a6d888c8 100644 --- a/src/sim_snapshot.h +++ b/src/sim_snapshot.h @@ -47,15 +47,7 @@ struct sim_snapshot { - /* FIXME: Remove this */ - struct space *space; - /* Bookkeeping structures */ - /* TODO: Store in snapshot for determinism */ - struct sim_ent_lookup contact_lookup; -#if COLLIDER_DEBUG - struct sim_ent_lookup collision_debug_lookup; -#endif diff --git a/src/user.c b/src/user.c index a16a4414..44a11db4 100644 --- a/src/user.c +++ b/src/user.c @@ -49,9 +49,9 @@ GLOBAL struct { struct host *host; struct string connect_address_str; - struct sim_snapshot_store *sim_snapshot_store; /* Contains buffered snapshots from sim */ - struct sim_snapshot_store *world_snapshot_store; /* Contains single world snapshot from result of blending sim snapshots */ - struct sim_snapshot *world; + struct sim_snapshot_store *unblended_snapshot_store; /* Contains buffered snapshots from sim */ + struct sim_snapshot_store *blended_snapshot_store; /* Contains single world snapshot from result of blending sim snapshots */ + struct sim_snapshot *ss_blended; /* Dynamic bitbuff used by encoders */ struct bitbuff encoder_bitbuff; @@ -82,15 +82,26 @@ GLOBAL struct { b32 debug_draw; - /* User thread input */ + /* Window -> user */ struct sys_mutex sys_events_mutex; struct arena sys_events_arena; + /* User -> local sim */ + struct sys_mutex user_sim_cmd_mutex; + struct sim_control user_sim_cmd_control; + struct v2 user_sim_cmd_control_cursor_pos; + u64 user_sim_cmd_ack; + + /* Local sim -> user */ + struct sys_mutex local_sim_ss_mutex; + struct sim_snapshot_store *local_sim_ss_store; + i64 real_dt_ns; i64 real_time_ns; u64 local_sim_last_known_tick; i64 local_sim_last_known_time_ns; + i64 last_snapshot_received_at_ns; /* Calculated from */ i64 local_sim_predicted_time_ns; @@ -180,13 +191,22 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr, (UNUSED)sim_snapshot_sr; G.arena = arena_alloc(GIGABYTE(64)); + + /* Snapshot store */ + G.unblended_snapshot_store = sim_snapshot_store_alloc(); + G.blended_snapshot_store = sim_snapshot_store_alloc(); + G.ss_blended = sim_snapshot_nil(); + + /* Sys events */ G.sys_events_mutex = sys_mutex_alloc(); G.sys_events_arena = arena_alloc(GIGABYTE(64)); - /* Snapshot store */ - G.sim_snapshot_store = sim_snapshot_store_alloc(); - G.world_snapshot_store = sim_snapshot_store_alloc(); - G.world = sim_snapshot_nil(); + /* User sim control */ + G.user_sim_cmd_mutex = sys_mutex_alloc(); + + /* Local sim snapshot store */ + G.local_sim_ss_mutex = sys_mutex_alloc(); + G.local_sim_ss_store = sim_snapshot_store_alloc(); //struct sock_address bind_addr = sock_address_from_any_local_interface_with_dynamic_port(); G.host = host_alloc(0); @@ -245,10 +265,8 @@ INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(user_shutdown) sim_ctx_release(G.local_sim_ctx); } #else - if (G.local_sim_ctx) { - atomic_i32_eval_exchange(&G.local_sim_thread_shutdown, true); - sys_thread_wait_release(&G.local_sim_thread); - } + atomic_i32_eval_exchange(&G.local_sim_thread_shutdown, true); + sys_thread_wait_release(&G.local_sim_thread); #endif } @@ -364,47 +382,6 @@ INTERNAL SORT_COMPARE_FUNC_DEF(ent_draw_order_cmp, arg_a, arg_b, udata) /* ========================== * * Update * ========================== */ - - - - - - - - - - -INTERNAL void queue_sim_cmd(struct arena *arena, struct sim_cmd_frame *frame, struct sim_cmd src) -{ - struct sim_cmd *cmd = arena_push(arena, struct sim_cmd); - *cmd = src; - if (frame->last) { - frame->last->next = cmd; - } else { - frame->first = cmd; - } - frame->last = cmd; -} - - - - - - - - - - - - - - - - - - - - INTERNAL void user_update(void) { @@ -419,8 +396,8 @@ INTERNAL void user_update(void) G.screen_size = sys_window_get_size(G.window); struct sprite_scope *sprite_frame_scope = sprite_scope_begin(); - struct sim_cmd_frame cmd_frame = ZI; +#if 0 /* ========================== * * Process host events into sim cmds * ========================== */ @@ -444,6 +421,7 @@ INTERNAL void user_update(void) * Process sim cmd frame * ========================== */ +#if 0 { static f64 last_try_connect = 0; f64 now = SECONDS_FROM_NS(sys_time_ns()); @@ -470,7 +448,7 @@ INTERNAL void user_update(void) case SIM_CMD_KIND_SNAPSHOT: { /* TODO: Only read newest tick cmd */ - if (cmd->snapshot_tick_end > G.world->tick) { + if (cmd->snapshot_tick_end > G.ss_blended->tick) { u64 ss0_tick = cmd->snapshot_tick_start; u64 ss1_tick = cmd->snapshot_tick_end; struct sim_snapshot *ss0 = sim_snapshot_from_tick(G.sim_snapshot_store, ss0_tick); @@ -495,13 +473,64 @@ INTERNAL void user_update(void) } } } +#else + { + for (struct sim_cmd *cmd = incoming_cmd_frame.first; cmd; cmd = cmd->next) { + enum sim_cmd_kind kind = cmd->kind; + switch (kind) { + case SIM_CMD_KIND_SNAPSHOT: + { + /* TODO: Only read newest tick cmd */ + if (cmd->snapshot_tick_end > G.ss_blended->tick) { + u64 ss0_tick = cmd->snapshot_tick_start; + u64 ss1_tick = cmd->snapshot_tick_end; + struct sim_snapshot *ss0 = sim_snapshot_from_tick(G.sim_snapshot_store, ss0_tick); + struct sim_snapshot *ss1 = sim_snapshot_from_tick(G.sim_snapshot_store, ss1_tick); + if (ss0->tick == ss0_tick) { + if (!ss1->valid) { + ss1 = sim_snapshot_alloc(G.sim_snapshot_store, ss0, ss1_tick); + ss1->received_at_ns = G.real_time_ns; + + struct bitbuff bb = bitbuff_from_string(cmd->snapshot_encoded); + struct bitbuff_reader br = br_from_bitbuff(&bb); + sim_snapshot_decode(&br, ss1); + } + } else { + /* User should always have src tick present */ + ASSERT(false); + } + } + } break; + + default: break; + } + } + } +#endif +#endif + + /* ========================== * + * Pull latest snapshot + * ========================== */ + + { + struct sys_lock lock = sys_mutex_lock_e(&G.local_sim_ss_mutex); + u64 old_last_tick = G.unblended_snapshot_store->last_tick; + u64 last_tick = G.local_sim_ss_store->last_tick; + if (last_tick > old_last_tick) { + struct sim_snapshot *src = sim_snapshot_from_tick(G.local_sim_ss_store, last_tick); + sim_snapshot_alloc(G.unblended_snapshot_store, src, src->tick); + G.last_snapshot_received_at_ns = G.real_time_ns; + } + sys_mutex_unlock(&lock); + } /* ========================== * * Create user world from blended snapshots * ========================== */ { - struct sim_snapshot *newest_snapshot = sim_snapshot_from_tick(G.sim_snapshot_store, G.sim_snapshot_store->last_tick); + struct sim_snapshot *newest_snapshot = sim_snapshot_from_tick(G.unblended_snapshot_store, G.unblended_snapshot_store->last_tick); G.local_sim_last_known_time_ns = newest_snapshot->real_time_ns; G.local_sim_last_known_tick = newest_snapshot->tick; @@ -515,11 +544,11 @@ INTERNAL void user_update(void) * to variance in snapshot receive time. */ /* TODO: Use a value that indicates desired dt to next frame, rather than real dt from last frame? */ f64 sim_time_smoothed_correction_rate = SECONDS_FROM_NS(G.real_dt_ns) / 0.05; - i64 time_since_newest_tick_ns = G.real_time_ns - newest_snapshot->received_at_ns; + i64 time_since_newest_tick_ns = G.real_time_ns - G.last_snapshot_received_at_ns; G.local_sim_predicted_time_ns = newest_snapshot->real_time_ns + time_since_newest_tick_ns; G.local_sim_predicted_time_smoothed_ns += G.real_dt_ns; - /* FIXME: Signed overflow check */ G.local_sim_predicted_time_smoothed_ns += (G.local_sim_predicted_time_ns - G.local_sim_predicted_time_smoothed_ns) * sim_time_smoothed_correction_rate; + /* FIXME: Signed overflow check */ #if USER_INTERP_ENABLED i64 render_time_ns = G.local_sim_predicted_time_smoothed_ns - (USER_INTERP_RATIO * newest_snapshot->real_dt_ns); @@ -528,7 +557,7 @@ INTERNAL void user_update(void) struct sim_snapshot *left_snapshot = sim_snapshot_nil(); struct sim_snapshot *right_snapshot = newest_snapshot; { - struct sim_snapshot *ss = sim_snapshot_from_tick(G.sim_snapshot_store, G.sim_snapshot_store->first_tick); + struct sim_snapshot *ss = sim_snapshot_from_tick(G.unblended_snapshot_store, G.unblended_snapshot_store->first_tick); while (ss->valid) { u64 next_tick = ss->next_tick; i64 ss_time_ns = ss->real_time_ns; @@ -542,29 +571,29 @@ INTERNAL void user_update(void) if (ss_time_ns > render_time_ns && ss_time_ns < right_snapshot->real_time_ns) { right_snapshot = ss; } - ss = sim_snapshot_from_tick(G.sim_snapshot_store, next_tick); + ss = sim_snapshot_from_tick(G.unblended_snapshot_store, next_tick); } } /* Create world from blended snapshots */ if (left_snapshot->valid && right_snapshot->valid) { f64 blend = (f64)(render_time_ns - left_snapshot->real_time_ns) / (f64)(right_snapshot->real_time_ns - left_snapshot->real_time_ns); - G.world = sim_snapshot_alloc_from_lerp(G.world_snapshot_store, left_snapshot, right_snapshot, blend); + G.ss_blended = sim_snapshot_alloc_from_lerp(G.blended_snapshot_store, left_snapshot, right_snapshot, blend); } else if (left_snapshot->valid) { - G.world = sim_snapshot_alloc(G.world_snapshot_store, left_snapshot, left_snapshot->tick); + G.ss_blended = sim_snapshot_alloc(G.blended_snapshot_store, left_snapshot, left_snapshot->tick); } else if (right_snapshot->valid) { - G.world = sim_snapshot_alloc(G.world_snapshot_store, right_snapshot, right_snapshot->tick); + G.ss_blended = sim_snapshot_alloc(G.blended_snapshot_store, right_snapshot, right_snapshot->tick); } /* Release all other render snapshots */ { - struct sim_snapshot *ss = sim_snapshot_from_tick(G.world_snapshot_store, G.world_snapshot_store->first_tick); + struct sim_snapshot *ss = sim_snapshot_from_tick(G.blended_snapshot_store, G.blended_snapshot_store->first_tick); while (ss->valid) { u64 next_tick = ss->next_tick; - if (ss != G.world) { + if (ss != G.ss_blended) { sim_snapshot_release(ss); } - ss = sim_snapshot_from_tick(G.world_snapshot_store, next_tick); + ss = sim_snapshot_from_tick(G.blended_snapshot_store, next_tick); } } @@ -582,11 +611,11 @@ INTERNAL void user_update(void) } } - if (G.world->tick != newest_snapshot->tick) { - if (G.world->valid) { - sim_snapshot_release(G.world); + if (G.ss_blended->tick != newest_snapshot->tick) { + if (G.ss_blended->valid) { + sim_snapshot_release(G.ss_blended); } - G.world = sim_snapshot_alloc(G.world_snapshot_store, newest_snapshot, newest_snapshot->tick); + G.ss_blended = sim_snapshot_alloc(G.blended_snapshot_store, newest_snapshot, newest_snapshot->tick); } #endif } @@ -684,16 +713,16 @@ INTERNAL void user_update(void) * Find local entities * ========================== */ - struct sim_client *local_client = sim_client_from_handle(G.world, G.world->local_client); - struct sim_ent *local_player = sim_ent_from_handle(G.world, local_client->control_ent); - struct sim_ent *local_camera = sim_ent_from_handle(G.world, local_client->camera_ent); + struct sim_client *local_client = sim_client_from_handle(G.ss_blended, G.ss_blended->local_client); + struct sim_ent *local_player = sim_ent_from_handle(G.ss_blended, local_client->control_ent); + struct sim_ent *local_camera = sim_ent_from_handle(G.ss_blended, local_client->camera_ent); /* ========================== * * Apply shake * ========================== */ - for (u64 ent_index = 0; ent_index < G.world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &G.world->ents[ent_index]; + for (u64 ent_index = 0; ent_index < G.ss_blended->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &G.ss_blended->ents[ent_index]; if (!sim_ent_is_valid_and_active(ent)) continue; /* How much time between camera shakes */ @@ -701,7 +730,7 @@ INTERNAL void user_update(void) f32 shake = ent->shake; if (shake > 0) { u64 basis = hash_fnv64(HASH_FNV64_BASIS, STRING_FROM_STRUCT(&ent->handle)); - u64 angle_seed0 = basis + (u64)(G.world->world_time_ns / frequency_ns); + u64 angle_seed0 = basis + (u64)(G.ss_blended->world_time_ns / frequency_ns); u64 angle_seed1 = angle_seed0 + 1; f32 angle0 = rng_noise_f32(angle_seed0, 0, TAU); f32 angle1 = rng_noise_f32(angle_seed1, 0, TAU); @@ -711,7 +740,7 @@ INTERNAL void user_update(void) struct v2 vec1 = v2_with_len(v2_from_angle(angle1), shake); /* TODO: Cubic interp? */ - f32 blend = (f32)(G.world->world_time_ns % frequency_ns) / (f32)frequency_ns; + f32 blend = (f32)(G.ss_blended->world_time_ns % frequency_ns) / (f32)frequency_ns; struct v2 vec = v2_lerp(vec0, vec1, blend); struct xform xf = sim_ent_get_xform(ent); @@ -860,8 +889,8 @@ INTERNAL void user_update(void) /* Copy valid entities */ { __profscope(copy_sprites_for_sorting); - for (u64 ent_index = 0; ent_index < G.world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &G.world->ents[ent_index]; + for (u64 ent_index = 0; ent_index < G.ss_blended->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &G.ss_blended->ents[ent_index]; if (sim_ent_is_valid_and_active(ent)) { *arena_push(scratch.arena, struct sim_ent *) = ent; ++sorted_count; @@ -888,7 +917,7 @@ INTERNAL void user_update(void) struct sprite_tag sprite = ent->sprite; - struct sim_ent *parent = sim_ent_from_handle(G.world, ent->parent); + struct sim_ent *parent = sim_ent_from_handle(G.ss_blended, ent->parent); struct xform xf = sim_ent_get_xform(ent); struct xform parent_xf = sim_ent_get_xform(parent); @@ -1073,8 +1102,8 @@ INTERNAL void user_update(void) /* Draw contact constraint */ if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTACT_CONSTRAINT)) { struct phys_contact_constraint *data = &ent->contact_constraint_data; - struct sim_ent *e0 = sim_ent_from_handle(G.world, data->e0); - struct sim_ent *e1 = sim_ent_from_handle(G.world, data->e1); + struct sim_ent *e0 = sim_ent_from_handle(G.ss_blended, data->e0); + struct sim_ent *e1 = sim_ent_from_handle(G.ss_blended, data->e1); (UNUSED)e0; (UNUSED)e1; @@ -1147,8 +1176,8 @@ INTERNAL void user_update(void) if (sim_ent_has_prop(ent, SIM_ENT_PROP_COLLISION_DEBUG)) { struct phys_collision_debug *data = &ent->collision_debug_data; struct collider_collision_points_result collider_res = data->res; - struct sim_ent *e0 = sim_ent_from_handle(G.world, data->e0); - struct sim_ent *e1 = sim_ent_from_handle(G.world, data->e1); + struct sim_ent *e0 = sim_ent_from_handle(G.ss_blended, data->e0); + struct sim_ent *e1 = sim_ent_from_handle(G.ss_blended, data->e1); struct collider_shape e0_collider = e0->local_collider; struct collider_shape e1_collider = e1->local_collider; (UNUSED)e0_collider; @@ -1472,11 +1501,16 @@ INTERNAL void user_update(void) control.flags |= SIM_CONTROL_FLAG_SPAWN_TEST; } - queue_sim_cmd(scratch.arena, &cmd_frame, (struct sim_cmd) { - .kind = SIM_CMD_KIND_CLIENT_CONTROL, - .control = control, - .cursor_pos = G.world_cursor, - }); + /* Set user sim control */ + { + struct sys_lock lock = sys_mutex_lock_e(&G.user_sim_cmd_mutex); + u32 old_flags = G.user_sim_cmd_control.flags; + G.user_sim_cmd_control = control; + G.user_sim_cmd_control.flags |= old_flags; + G.user_sim_cmd_control_cursor_pos = G.world_cursor; + G.user_sim_cmd_ack = G.local_sim_last_known_tick; + sys_mutex_unlock(&lock); + } } #if COLLIDER_DEBUG @@ -1515,13 +1549,13 @@ INTERNAL void user_update(void) pos.y += spacing; pos.y += spacing; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("user entities: %F/%F"), FMT_UINT(G.world->num_ents_allocated), FMT_UINT(G.world->num_ents_reserved))); + draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("user entities: %F/%F"), FMT_UINT(G.ss_blended->num_ents_allocated), FMT_UINT(G.ss_blended->num_ents_reserved))); pos.y += spacing; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("user tick: %F"), FMT_UINT(G.world->tick))); + draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("user tick: %F"), FMT_UINT(G.ss_blended->tick))); pos.y += spacing; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("user time: %F"), FMT_FLOAT_P(SECONDS_FROM_NS(G.world->real_time_ns), 3))); + draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("user time: %F"), FMT_FLOAT_P(SECONDS_FROM_NS(G.ss_blended->real_time_ns), 3))); pos.y += spacing; pos.y += spacing; @@ -1602,6 +1636,7 @@ INTERNAL void user_update(void) } +#if 0 /* Publish sim cmds */ { struct sim_cmd_frame_list l = ZI; @@ -1615,6 +1650,7 @@ INTERNAL void user_update(void) } host_update(G.host); +#endif /* Update network usage stats */ G.client_bytes_read.last_second_end = G.host->bytes_received; @@ -1768,18 +1804,11 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) #else struct host *host = host_alloc(12345); #endif + (UNUSED)arg; struct bitbuff encoder_bitbuff = bitbuff_alloc(GIGABYTE(64)); struct sim_snapshot_store *snapshot_store = sim_snapshot_store_alloc(); - - - (UNUSED)arg; - - - -#if 1 - struct sim_snapshot *prev_ss = sim_snapshot_nil(); i64 last_tick_ns = 0; @@ -1810,109 +1839,72 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) } } - struct sim_cmd_frame_list raw_input_cmd_frames = ZI; + /* Retrieve cmds */ + struct sim_cmd_frame_list input_cmds = ZI; { - host_update(host); - struct host_event_array host_events = host_pop_events(scratch.arena, host); - sim_cmd_frames_decode(scratch.arena, host_events, &raw_input_cmd_frames); - } - - - - - b32 should_break = false; - - - - - - - /* Merge cmd frames from user thread */ - struct sim_cmd *user_control_cmd = NULL; - struct sim_cmd_frame user_cmd_frame = ZI; - struct sim_cmd_frame_list input_cmd_frames = ZI; - { - struct sim_cmd_frame *frame = raw_input_cmd_frames.first; - while (frame) { - struct sim_cmd_frame *next_frame = frame->next; - /* FIXME: Only do this for user cmds */ - if (frame->tick == 0) { - /* All cmd frames from the user thread need to be merged into a single cmd frame representing inputs for one sim tick */ - if (user_cmd_frame.tick == 0) { - user_cmd_frame = *frame; - user_cmd_frame.tick = prev_ss->tick + 1; - user_cmd_frame.next = NULL; - } - if (frame->ack > user_cmd_frame.ack) { - user_cmd_frame.ack = frame->ack; - } - struct sim_cmd *cmd = frame->first; - while (cmd) { - struct sim_cmd *next_cmd = cmd->next; - b32 should_insert = true; - if (cmd->kind == SIM_CMD_KIND_CLIENT_CONTROL) { - if (user_control_cmd) { - /* Merge with existing control cmd */ - - if (cmd->control.flags & SIM_CONTROL_FLAG_CLEAR_ALL) { - should_break = true; - DEBUGBREAKABLE; - } - - should_insert = false; - u32 flags = user_control_cmd->control.flags; - *user_control_cmd = *cmd; - user_control_cmd->control.flags |= flags; - } else { - user_control_cmd = cmd; - } - } - if (should_insert) { - if (user_cmd_frame.last) { - user_cmd_frame.last->next = cmd; - } else { - user_cmd_frame.first = cmd; - } - user_cmd_frame.last = cmd; - } - cmd->next = NULL; - cmd = next_cmd; - } - } else { - if (input_cmd_frames.last) { - input_cmd_frames.last = frame; - } else { - input_cmd_frames.first = frame; - } - input_cmd_frames.last = frame; - } - frame->next = NULL; - frame = next_frame; + /* Grab cmds from host */ + { + host_update(host); + struct host_event_array host_events = host_pop_events(scratch.arena, host); + sim_cmd_frames_decode(scratch.arena, host_events, &input_cmds); } - } - if (user_cmd_frame.tick != 0) { - if (input_cmd_frames.last) { - input_cmd_frames.last->next = &user_cmd_frame; + + /* Generate user sim cmd from user thread */ + struct sim_cmd_frame *user_cmd_frame; + { + struct sys_lock lock = sys_mutex_lock_e(&G.user_sim_cmd_mutex); + user_cmd_frame = arena_push_zero(scratch.arena, struct sim_cmd_frame); + user_cmd_frame->tick = prev_ss->tick + 1; + user_cmd_frame->ack = G.user_sim_cmd_ack; + user_cmd_frame->sender_is_local = true; + + struct sim_cmd *user_cmd = arena_push_zero(scratch.arena, struct sim_cmd); + user_cmd->kind = SIM_CMD_KIND_CLIENT_CONTROL; + user_cmd->control = G.user_sim_cmd_control; + user_cmd->cursor_pos = G.user_sim_cmd_control_cursor_pos; + user_cmd_frame->first = user_cmd; + user_cmd_frame->last = user_cmd; + + G.user_sim_cmd_control.flags = 0; + + sys_mutex_unlock(&lock); + } + if (input_cmds.last) { + input_cmds.last->next = user_cmd_frame; } else { - input_cmd_frames.first = &user_cmd_frame; + input_cmds.first = user_cmd_frame; } - input_cmd_frames.last = &user_cmd_frame; - } - - if (input_cmd_frames.first && input_cmd_frames.first->first && input_cmd_frames.first->first->control.flags & SIM_CONTROL_FLAG_CLEAR_ALL) { - DEBUGBREAKABLE; - } else if (should_break) { - DEBUGBREAKABLE; + input_cmds.last = user_cmd_frame; } /* Step */ - struct sim_snapshot *ss = sim_step(snapshot_store, prev_ss, input_cmd_frames, target_dt_ns); + struct sim_snapshot *ss = sim_step(snapshot_store, prev_ss, input_cmds, target_dt_ns); - /* Publish snapshot cmds */ + /* Publish snapshot to user */ + /* TODO: Double buffer */ + { + struct sys_lock lock = sys_mutex_lock_e(&G.local_sim_ss_mutex); + sim_snapshot_alloc(G.local_sim_ss_store, ss, ss->tick); + + struct sim_snapshot *remss = sim_snapshot_from_tick(G.local_sim_ss_store, G.local_sim_ss_store->first_tick); + while (remss) { + u64 next_tick = remss->next_tick; + if (remss->tick < ss->tick) { + sim_snapshot_release(remss); + } else { + break; + } + remss = sim_snapshot_from_tick(G.local_sim_ss_store, next_tick); + } + + sys_mutex_unlock(&lock); + } + + /* Publish snapshot cmds to networked clients */ u64 oldest_ack_tick = 0; for (u64 i = 0; i < ss->num_clients_reserved; ++i) { struct sim_client *client = &ss->clients[i]; - if (client->valid) { + if (client->valid && client->kind == SIM_CLIENT_KIND_NETSIM) { struct temp_arena temp = arena_temp_begin(scratch.arena); if (oldest_ack_tick == 0 || client->ack < oldest_ack_tick) { @@ -1967,50 +1959,6 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) prev_ss = ss; } -#else - - i64 last_tick_ns = 0; - i64 target_dt_ns = NS_FROM_SECONDS(1) / SIM_TICKS_PER_SECOND;; - while (!atomic_i32_eval(&G.local_sim_thread_shutdown)) { - struct temp_arena scratch = scratch_begin_no_conflict(); - { - __profscope(local_sim_sleep); - sleep_frame(last_tick_ns, target_dt_ns); - last_tick_ns = sys_time_ns(); - } - - struct sim_cmd_frame user_frame = ZI; - user_frame.tick = prev_ss->tick + 1; - user_frame.ack = prev_ss->tick; - - /* Read cmds from host */ - host_update(host); - struct host_event_array host_events = host_pop_events(scratch.arena, host); - sim_cmds_decode(scratch.arena, host_events, &sim_cmds); - - /* Step */ - struct sim_snapshot *ss = sim_step(ss_store, prev_ss, user_cmds, target_dt_ns); - - /* Encode & enqueue sim_cmd frames */ - for (struct sim_cmd_frame *f = output_cmds.first; f; f = f->next) { - struct host_channel_id channel_id = f->dst_channel; - struct string cmd_frame_msg = ZI; - struct bitbuff_writer bw = bw_from_bitbuff(&encoder_bitbuff); - sim_cmd_frame_encode(&bw, q); - cmd_frame_msg = bw_get_written(temp.arena, &bw); - host_queue_write(host, channel_id, cmd_frame_msg, 0); - } - - /* Send host messages */ - //host_update(host); - __profframe("Local sim"); - - scratch_end(scratch); - } -#endif - - - sim_snapshot_store_release(snapshot_store); bitbuff_release(&encoder_bitbuff); host_release(host);