From 044fc1db9d6a6134ca6e532023c1df5c20dbe6be Mon Sep 17 00:00:00 2001 From: jacob Date: Thu, 20 Feb 2025 13:55:39 -0600 Subject: [PATCH] prediction progress --- src/sim.c | 337 ++++++++++++++++++-------------------------- src/sim.h | 101 +------------- src/sim_snapshot.c | 1 + src/sim_snapshot.h | 34 +++-- src/user.c | 339 ++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 503 insertions(+), 309 deletions(-) diff --git a/src/sim.c b/src/sim.c index da7119e1..36dba40d 100644 --- a/src/sim.c +++ b/src/sim.c @@ -372,8 +372,7 @@ INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, collision_data_array, wo * Update * ========================== */ -/* Simulates and generates a snapshot at tick `prev_snapshot` + 1 for duration `real_dt_ns`. */ -struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct sim_snapshot *prev_snapshot, struct sim_cmd_frame_list input_frames, i64 real_dt_ns) +void sim_step(struct sim_snapshot *world, struct sim_snapshot_list cmd_snapshots, i64 real_dt_ns) { __prof; struct temp_arena scratch = scratch_begin_no_conflict(); @@ -382,8 +381,6 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct * Begin frame * ========================== */ - struct sim_snapshot *world = sim_snapshot_alloc(snapshot_store, prev_snapshot, prev_snapshot->tick + 1); - @@ -422,19 +419,6 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct //sys_sleep_precise(rng_rand_f32(0, 0.050)); //sys_sleep_precise(0.050); - - - - - - world->phys_iteration = prev_snapshot->phys_iteration; - - - - - - - world->real_dt_ns = max_i64(0, real_dt_ns); world->real_time_ns += world->real_dt_ns; @@ -455,216 +439,159 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct struct sim_ent *root = sim_ent_from_handle(world, SIM_ENT_ROOT_HANDLE); - /* ========================== * - * Spawn test entities - * ========================== */ + if (world->is_master) { + /* ========================== * + * Spawn test entities + * ========================== */ - /* TODO: remove this (testing) */ - /* Initialize entities */ - { - static b32 run = 0; - if (!run) { - run = 1; - spawn_test_entities(world, V2(0, 0)); - } - } - - /* ========================== * - * Release entities - * ========================== */ - - release_entities_with_prop(world, SIM_ENT_PROP_RELEASE_NEXT_TICK); - - /* ========================== * - * Activate entities - * ========================== */ - - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!ent->valid) continue; - - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_ACTIVE)) { - u64 atick = ent->activation_tick; - if (atick != 0 || world->tick >= atick) { - sim_ent_activate(ent, world->tick); + /* TODO: remove this (testing) */ + /* Initialize entities */ + { + static b32 run = 0; + if (!run) { + run = 1; + spawn_test_entities(world, V2(0, 0)); } } - } - /* ========================== * - * Reset triggered entities - * ========================== */ + /* ========================== * + * Release entities + * ========================== */ - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; + release_entities_with_prop(world, SIM_ENT_PROP_RELEASE_NEXT_TICK); - if (sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGER_NEXT_TICK)) { - sim_ent_disable_prop(ent, SIM_ENT_PROP_TRIGGER_NEXT_TICK); - sim_ent_enable_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK); - } else if (sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK)) { - sim_ent_disable_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK); - } - } + /* ========================== * + * Activate entities + * ========================== */ - /* ========================== * - * Create user client if it doesn't exist - * ========================== */ + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!ent->valid) continue; - - /* ========================== * - * Sort incoming frames by client - * ========================== */ - - struct sim_cmd_frame **client_frames; - { - /* Create connecting clients */ - if (world->is_master) { - /* Create local client if it doesn't exist */ - struct sim_client *local_client = sim_client_from_handle(world, world->local_client); - if (!local_client->valid) { - local_client = sim_client_alloc(world, HOST_CHANNEL_ID_NIL, SIM_CLIENT_KIND_SIM_MASTER); - world->local_client = local_client->handle; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_ACTIVE)) { + u64 atick = ent->activation_tick; + if (atick != 0 || world->tick >= atick) { + sim_ent_activate(ent, world->tick); + } } - /* Create slave sim clients */ - for (struct sim_cmd_frame *frame = input_frames.first; frame; frame = frame->next) { - struct sim_client *client; - if (!frame->sender_is_local) { - 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_SIM_SLAVE); - break; - } + } + + /* ========================== * + * Reset triggered entities + * ========================== */ + + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + + if (sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGER_NEXT_TICK)) { + sim_ent_disable_prop(ent, SIM_ENT_PROP_TRIGGER_NEXT_TICK); + sim_ent_enable_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK); + } else if (sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK)) { + sim_ent_disable_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK); + } + } + + /* ========================== * + * Create / update client ents + * ========================== */ + + for (struct sim_snapshot_list_node *n = cmd_snapshots.first; n; n = n->next) { + struct sim_snapshot *cmd_snapshot = n->ss; + struct sim_client_handle client_handle = cmd_snapshot->producer_client; + struct sim_ent *client_ent = sim_ent_from_client_handle(world, client_handle); + + /* Create client ent if it doesn't exist */ + if (!client_ent->valid) { + client_ent = sim_ent_alloc(root); + client_ent->client_handle = client_handle; + sim_ent_enable_prop(client_ent, SIM_ENT_PROP_CLIENT); + if (cmd_snapshot->producer_client_is_local) { + sim_ent_enable_prop(client_ent, SIM_ENT_PROP_LOCAL_CLIENT); + } + sim_ent_enable_prop(client_ent, SIM_ENT_PROP_ACTIVE); + } + + client_ent->client_control = cmd_snapshot->control; + } + + /* ========================== * + * Process client cmds + * ========================== */ + + for (u64 i = 0; i < world->num_ents_reserved; ++i) { + struct sim_ent *ent = &world->ents[i]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (sim_ent_has_prop(ent, SIM_ENT_PROP_CLIENT)) { + ent->client_dbg_drag_start = false; + ent->client_dbg_drag_stop = false; + + struct sim_control old_control = ent->client_control; + struct sim_control *control = &ent->client_control; + *control = cmd->control; + 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 control_ent_handle = ent->control_ent; + struct sim_ent *control_ent = sim_ent_from_handle(world, control_ent_handle); + if (control_ent->valid || sim_ent_handle_eq(control_ent_handle, SIM_ENT_NIL_HANDLE)) { + /* Only update cursor pos if focus ent is valid (or nil) */ + ent->client_cursor_pos = v2_add(sim_ent_get_xform(control_ent).og, ent->client_control.focus); + } + } + + u32 flags = control->flags; + if (flags & SIM_CONTROL_FLAG_DRAGGING) { + if (!(old_control.flags & SIM_CONTROL_FLAG_DRAGGING)) { + ent->client_dbg_drag_start = true; + } + } else { + if (old_control.flags & SIM_CONTROL_FLAG_DRAGGING) { + 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)); } } } } } - /* 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; - 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) { - client_frames[client->handle.idx] = frame; - } - } - } - - if (world->is_master) { - /* ========================== * - * Process client sim cmds - * ========================== */ - - /* Process client cmds */ - u64 oldest_client_ack = world->tick; - for (u64 i = 0; i < world->num_clients_reserved; ++i) { - struct sim_client *client = &world->clients[i]; - struct sim_cmd_frame *frame = client_frames[client->handle.idx]; - if (client->valid && frame) { - struct sim_control *control = &client->control; - - client->dbg_drag_start = false; - client->dbg_drag_stop = false; - - if (frame->ack > client->ack) { - client->ack = frame->ack; - } - - for (struct sim_cmd *cmd = frame->first; cmd; cmd = cmd->next) { - enum sim_cmd_kind kind = cmd->kind; - switch (kind) { - /* TODO: Combine movement from multiple inputs? E.ctx-> a sudden - * start and immediate stop cmd should still move the player a - * tad. */ - case SIM_CMD_KIND_CLIENT_CONTROL: - { - struct sim_control old_control = client->control; - *control = cmd->control; - 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 control_ent_handle = client->control_ent; - struct sim_ent *control_ent = sim_ent_from_handle(world, control_ent_handle); - if (control_ent->valid || sim_ent_handle_eq(control_ent_handle, SIM_ENT_NIL_HANDLE)) { - /* Only update cursor pos if focus ent is valid (or nil) */ - client->cursor_pos = v2_add(sim_ent_get_xform(control_ent).og, client->control.focus); - } - } - - u32 flags = control->flags; - if (flags & SIM_CONTROL_FLAG_DRAGGING) { - if (!(old_control.flags & SIM_CONTROL_FLAG_DRAGGING)) { - client->dbg_drag_start = true; - } - } else { - if (old_control.flags & SIM_CONTROL_FLAG_DRAGGING) { - 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)); - } - } - } - } break; - - /* Disconnect client */ - case SIM_CMD_KIND_SIM_CLIENT_DISCONNECT: - { - sim_client_release(client); - } break; - - default: break; - }; - } - - if (client->ack < oldest_client_ack || oldest_client_ack == 0) { - oldest_client_ack = client->ack; - } - } - } /* ========================== * - * Create client ents + * Create client player ents * ========================== */ - for (u64 i = 0; i < world->num_clients_reserved; ++i) { - struct sim_client *client = &world->clients[i]; - if (client->valid) { + for (u64 i = 0; i < world->num_ents_reserved; ++i) { + struct sim_ent *ent = &world->ents[i]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (sim_ent_has_prop(ent, SIM_ENT_PROP_CLIENT)) { /* FIXME: Ents never released when client disconnects */ - struct sim_ent *player_ent = sim_ent_from_handle(world, client->control_ent); + struct sim_ent *player_ent = sim_ent_from_handle(world, ent->client_player_ent); if (!player_ent->valid) { player_ent = spawn_test_player(world); sim_ent_enable_prop(player_ent, SIM_ENT_PROP_CONTROLLED); - client->control_ent = player_ent->handle; - player_ent->controlling_client = client->handle; + ent->client_player_ent = player_ent->handle; + player_ent->controlling_client = ent->handle; } - struct sim_ent *camera_ent = sim_ent_from_handle(world, client->camera_ent); + struct sim_ent *camera_ent = sim_ent_from_handle(world, ent->client_camera_ent); if (!camera_ent->valid) { camera_ent = spawn_test_player_camera(world, player_ent); - client->camera_ent = camera_ent->handle; + ent->client_camera_ent = camera_ent->handle; } } } @@ -678,9 +605,9 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct if (!sim_ent_is_valid_and_active(ent)) continue; if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTROLLED)) { - struct sim_client *client = sim_client_from_handle(world, ent->controlling_client); - if (client->valid) { - ent->control = client->control; + struct sim_ent *client_ent = sim_client_from_handle(world, ent->controlling_client); + if (client_ent->valid) { + ent->control = client_ent->client_control; /* TODO: Move this */ if (ent->control.flags & SIM_CONTROL_FLAG_FIRING) { sim_ent_enable_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED); diff --git a/src/sim.h b/src/sim.h index d5812604..569fa43b 100644 --- a/src/sim.h +++ b/src/sim.h @@ -9,34 +9,9 @@ struct host_startup_receipt; struct sim_snapshot_startup_receipt; /* ========================== * - * Cmd + * Control * ========================== */ -enum sim_cmd_state { - SIM_CMD_STATE_STOP = -1, - SIM_CMD_STATE_NO_CHANGE = 0, - SIM_CMD_STATE_START = 1 -}; - -enum sim_cmd_kind { - SIM_CMD_KIND_NONE, - - SIM_CMD_KIND_CLIENT_CONTROL, - - SIM_CMD_KIND_SIM_CLIENT_CONNECT, - SIM_CMD_KIND_SIM_CLIENT_DISCONNECT, - - SIM_CMD_KIND_CONNECT, - SIM_CMD_KIND_DISCONNECT, - SIM_CMD_KIND_SNAPSHOT, - - //SIM_CMD_KIND_ENTITY_UPDATE, - //SIM_CMD_KIND_ENTITY_CREATE, - //SIM_CMD_KIND_ENTITY_DESTROY - - SIM_CMD_KIND_COUNT -}; - enum sim_control_flag { SIM_CONTROL_FLAG_NONE = 0, SIM_CONTROL_FLAG_FIRING = 1 << 0, @@ -56,84 +31,22 @@ struct sim_control { u32 flags; }; -struct sim_cmd { - struct sim_cmd *next; - - /* Cmd metadata */ - enum sim_cmd_kind kind; - - /* ====================================================================== */ - /* SIM_CMD_KIND_CLIENT_CONTROL */ - - struct sim_control control; - -#if RTC - u32 collider_gjk_steps; -#endif - - /* ====================================================================== */ - /* SIM_CMD_KIND_CLIENT_DISCONNECT */ - - struct string disconnect_reason; - - /* =========================================================== */ - /* SIM_CMD_KIND_SNAPSHOT */ - - u64 snapshot_tick_start; - u64 snapshot_tick_end; - - /* Delta encoded snapshot containing changes between start & end ticks */ - struct string snapshot_encoded; -}; - -/* Represents all cmds generated by a user/sim for a particular channel in a single tick. */ -struct sim_cmd_frame { - 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; - - struct sim_cmd_frame *next; -}; - -struct sim_cmd_frame_list { - struct sim_cmd_frame *first; - struct sim_cmd_frame *last; -}; - /* ========================== * * Layers * ========================== */ /* Absolute layers */ -#define SIM_LAYER_FLOOR_DECALS -300 -#define SIM_LAYER_BULLETS -200 -#define SIM_LAYER_TRACERS -100 -#define SIM_LAYER_SHOULDERS 0 +#define SIM_LAYER_FLOOR_DECALS (-300) +#define SIM_LAYER_BULLETS (-200) +#define SIM_LAYER_TRACERS (-100) +#define SIM_LAYER_SHOULDERS (0) /* Relative layers */ -#define SIM_LAYER_RELATIVE_DEFAULT 0 -#define SIM_LAYER_RELATIVE_WEAPON 1 +#define SIM_LAYER_RELATIVE_DEFAULT (0) +#define SIM_LAYER_RELATIVE_WEAPON (1) struct sim_snapshot; struct sim_snapshot_store; struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct sim_snapshot *prev_snapshot, struct sim_cmd_frame_list input_frames, i64 real_dt_ns); -/* ========================== * - * Cmd frame encode / decode - * ========================== */ - -/* TODO: Move this */ - -#include "host.h" - -void sim_cmd_frames_encode(struct bitbuff_writer *bw, struct sim_cmd_frame_list frames); -void sim_cmd_frames_decode(struct arena *arena, struct host_event_array host_events, struct sim_cmd_frame_list *frames_out); - - - #endif diff --git a/src/sim_snapshot.c b/src/sim_snapshot.c index 296e3b00..17abb1a4 100644 --- a/src/sim_snapshot.c +++ b/src/sim_snapshot.c @@ -202,6 +202,7 @@ struct sim_snapshot *sim_snapshot_alloc(struct sim_snapshot_store *store, struct ss->world_time_ns = src->world_time_ns; ss->continuity_gen = src->continuity_gen; ss->local_client = src->local_client; + ss->phys_iteration = src->phys_iteration; /* Copy client lookup buckets */ if (src->num_client_lookup_buckets > 0) { diff --git a/src/sim_snapshot.h b/src/sim_snapshot.h index 46234cad..f1e219e1 100644 --- a/src/sim_snapshot.h +++ b/src/sim_snapshot.h @@ -4,15 +4,17 @@ #include "sim_ent.h" #include "sim_client.h" +#if 0 +enum sim_snapshot_flag { + SIM_SNAPSHOT_FLAG_NONE = 0, + /* The snapshot contains client control data */ + SIM_SNAPSHOT_FLAG_CONTROL = (1 << 0), -/* TODO: Remove this */ -struct space; - - - - - + /* The snapshot contains entity state data */ + SIM_SNAPSHOT_FLAG_STATE = (1 << 1) +}; +#endif struct sim_snapshot_store { b32 valid; @@ -33,6 +35,7 @@ struct sim_snapshot_store { struct sim_snapshot { b32 valid; u64 tick; + u64 ack; struct sim_snapshot_store *store; struct sim_snapshot *next_free; struct sim_snapshot *next_in_bucket; @@ -42,6 +45,14 @@ struct sim_snapshot { struct arena arena; + /* ====================================================================== */ + /* SIM_SNAPSHOT_FLAG_CONTROL */ + + struct sim_control control; + + /* ====================================================================== */ + /* SIM_SNAPSHOT_FLAG_STATE */ + /* Was this snapshot created by the master sim */ b32 is_master; @@ -86,6 +97,14 @@ struct sim_snapshot { u64 num_ents_reserved; }; +#if 0 +struct sim_snapshot_encoded { + u64 base_tick; + u64 tick; + struct string data; /* Contains snapshot deltas for `tick` from `base_tick` */ +}; +#endif + /* ========================== * * Startup * ========================== */ @@ -138,5 +157,4 @@ struct sim_snapshot *sim_snapshot_alloc_from_lerp(struct sim_snapshot_store *sto void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_client *receiver, struct sim_snapshot *ss0, struct sim_snapshot *ss1); void sim_snapshot_decode(struct bitbuff_reader *br, struct sim_snapshot *ss); - #endif diff --git a/src/user.c b/src/user.c index 1b2bf789..cd944a16 100644 --- a/src/user.c +++ b/src/user.c @@ -88,7 +88,6 @@ GLOBAL struct { /* 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 last_user_sim_cmd_gen; u64 user_sim_cmd_gen; u64 user_sim_cmd_ack; @@ -1704,6 +1703,9 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_thread_entry_point, arg) * Local sim thread * ========================== */ +#if 1 + + INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) { #if 0 @@ -1731,6 +1733,337 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) + + + + + + + + + + + + + + + + struct bitbuff msg_writer_bb = bitbuff_alloc(GIGABYTE(64)); + struct bitbuff snapshot_writer_bb = bitbuff_alloc(GIGABYTE(64)); + + + + + + + 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_client = sim_client_alloc(store, SIM_CLIENT_KIND_USER); + struct sim_client *master_client = sim_client_nil(); + + u64 step_tick = 0; + i64 last_publish_ns = 0; + i64 last_tick_ns = 0; + i64 step_dt_ns = NS_FROM_SECONDS(1) / SIM_TICKS_PER_SECOND; + i64 compute_dt_ns = step_dt_ns; + 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(); + } + ++step_tick; + + /* Create user cmd snapshot */ + { + 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->ack = G.user_sim_cmd_ack; + user_cmd_ss->control = G.user_sim_cmd_control; + ++G.user_sim_cmd_gen; + sys_mutex_unlock(&lock); + } + + /* Read net snapshots */ + { + host_update(host); + struct host_event_array host_events = host_pop_events(scratch.arena, host); + for (u64 i = 0; i < host_events.count; ++i) { + struct host_event *event = &host_events.events[i]; + struct host_channel_id channel_id = event->channel_id; + struct sim_client *client = sim_client_from_channel_id(store, channel_id); + switch (event->kind) { + case HOST_EVENT_KIND_CHANNEL_OPENED: + { + /* Create sim client */ + if (!client->valid) { + /* TODO: Master challenge */ + if (is_master) { + client = sim_client_alloc(store, SIM_CLIENT_KIND_SLAVE_SIM); + } else { + client = sim_client_alloc(store, SIM_CLIENT_KIND_MASTER_SIM); + master_client = client; + } + sim_client_set_channel_id(client, channel_id); + } + } break; + + case HOST_EVENT_KIND_CHANNEL_MSG: + { + if (client->valid) { + struct bitbuff msg_bb = bitbuff_from_string(event->msg); + struct bitbuff_reader msg_br = br_from_bitbuff(&msg_bb); + + u64 ack = br_read_uv(&msg_br); + u64 reverse_ack = br_read_uv(&msg_br); + if (ack > client->ack) { + client->ack = ack; + } + if (reverse_ack > client->reverse_ack) { + client->reverse_ack = reverse_ack; + } + + /* Read snapshots */ + struct string encoded_tmp = ZI; + 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; + } + while (encoded_tmp.len > 0) { + struct bitbuff decoder_bb = bitbuff_from_string(encoded_tmp); + struct bitbuff_reader decoder_br = br_from_bitbuff(&decoder_bb); + u64 base_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) { + 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); + } else { + /* We do not have the base tick that the incoming tick is delta encoded from */ + ASSERT(false); + } + } + } else { + /* TODO: Limit range of incoming cmd ticks */ + if (tick == client->last_tick + 1) { + 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); + } 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; + } + } + } + + /* Release unneeded snapshots */ + u64 oldest_client_ack = 0; + 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) { + u64 ack = client->ack; + u64 reverse_ack = client->reverse_ack; + if (reverse_ack > 1) { + sim_client_release_snapshot_ticks_in_range(client, 0, reverse_ack - 1); + } + if (ack < oldest_client_ack || oldest_client_ack == 0) { + oldest_client_ack = ack; + } + } + } + if (oldest_client_ack > 1) { + sim_client_release_snapshot_ticks_in_range(local_client, 0, oldest_client_ack - 1); + } + + /* 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); + } else { + struct sim_snapshot *newest_master_ss = sim_snapshot_from_tick(master_client, master_client->last_tick); + step_ss = sim_snapshot_alloc(local_client, newest_master_ss, step_tick); + } + + /* Step */ + sim_step(step_ss, client_step_cmds, step_dt_ns); + + /* Publish snapshot to clients */ + for (u64 i = 0; i < store->num_clients_reserved; ++i) { + struct sim_client *client = &store->clients[i]; + 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(client, client->ack); + if (base_ss->tick == client->ack) { + /* Encode single master state sanpshot snapshot */ + 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); + + u64 ack = client->last_tick; + 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); + } else { + /* We do not have the client's ack tick to delta encode from */ + ASSERT(false); + } + } break; + + case SIM_CLIENT_KIND_MASTER_SIM: + { + u64 ack = 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(&bw, ack); + bw_write_uv(&bw, reverse_ack); + /* Write all user cmd snapshots since last client ack */ + struct sim_snapshot *send_ss = sim_snapshot_from_tick(user_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); + bw_write_uv(&msg_bw, encoded_tmp.len); + bw_write_bytes(&msg_bw, encoded_tmp); + } + bw_write_uv(&msg_bw, 0); + } else { + /* We do not have the client's ack tick to delta encode from */ + ASSERT(false); + } + } break; + + case SIM_CLIENT_KIND_USER: + { + /* TODO: Double buffer */ + struct sys_lock lock = sys_mutex_lock_e(&G.local_to_user_snapshot_mutex); + struct sim_snapshot *pub_ss = sim_snapshot_alloc(G.local_to_user_snapshot_client, step_ss, step_ss->tick); + i64 publish_ns = sys_time_ns(); + pub_ss->publish_dt_ns = publish_ns - last_publish_ns; + pub_ss->publish_time_ns = publish_ns; + last_publish_ns = publish_ns; + sys_mutex_unlock(&lock); + } break; + + default: break; + } + } + } + host_update(host); + __profframe("Local sim"); + + scratch_end(scratch); + } + + + + + + + + + + + sim_snapshot_store_release(store); + bitbuff_release(&snapshot_writer_bb); + bitbuff_release(&msg_writer_bb); + host_release(host); +} + +#else + + +INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) +{ +#if 0 + struct host_listen_address local_listen_addr = host_listen_address_from_local_name(LIT("LOCAL_SIM")); + struct host_listen_address net_listen_addr = host_listen_address_from_net_port(12345); + //struct host *host = host_alloc(); + /* TODO: Host system should allocate & copy string stored in local_listen_addr */ + //host_listen(host, local_listen_addr); + //host_listen(host, net_listen_addr); +#else + struct host *host = host_alloc(12345); +#endif + (UNUSED)arg; + + b32 is_master = false; + if (G.connect_address_str.len > 0) { + struct sock_address addr = sock_address_from_string(G.connect_address_str); + host_queue_connect_to_address(host, addr); + } else { + is_master = true; + } + + + + + + + + + + + + + + + + + + + + struct bitbuff encoder_bitbuff = bitbuff_alloc(GIGABYTE(64)); @@ -1768,7 +2101,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) struct sim_cmd_frame_list input_cmds = ZI; struct sim_cmd_frame *user_cmd_frame; { - /* Grab cmds from host */ + /* Grab snapshots from host */ { host_update(host); struct host_event_array host_events = host_pop_events(scratch.arena, host); @@ -1983,3 +2316,5 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) bitbuff_release(&encoder_bitbuff); host_release(host); } + +#endif