prediction progress

This commit is contained in:
jacob 2025-02-20 13:55:39 -06:00
parent b8119c9ef9
commit 044fc1db9d
5 changed files with 503 additions and 309 deletions

337
src/sim.c
View File

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

101
src/sim.h
View File

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

View File

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

View File

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

View File

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