prediction progress
This commit is contained in:
parent
27f49512d6
commit
4d8819d3ee
@ -62,7 +62,7 @@
|
||||
#define COLLIDER_DEBUG_DETAILED_DRAW_MENKOWSKI 0
|
||||
|
||||
/* If enabled, bitbuffs will insert/verify magic numbers & length for each read & write */
|
||||
#define BITBUFF_DEBUG 0
|
||||
#define BITBUFF_DEBUG RTC
|
||||
#define BITBUFF_TEST RTC
|
||||
|
||||
/* ========================== *
|
||||
|
||||
52
src/sim.c
52
src/sim.c
@ -125,7 +125,7 @@ void sim_client_store_release(struct sim_client_store *store)
|
||||
* Client alloc
|
||||
* ========================== */
|
||||
|
||||
struct sim_client *sim_client_alloc(struct sim_client_store *store, enum sim_client_kind kind)
|
||||
struct sim_client *sim_client_alloc(struct sim_client_store *store)
|
||||
{
|
||||
struct sim_client_handle handle = ZI;
|
||||
struct sim_client *client = sim_client_from_handle(store, store->first_free_client);
|
||||
@ -144,7 +144,6 @@ struct sim_client *sim_client_alloc(struct sim_client_store *store, enum sim_cli
|
||||
*client = *sim_client_nil();
|
||||
client->store = store;
|
||||
client->valid = true;
|
||||
client->kind = kind;
|
||||
client->handle = handle;
|
||||
|
||||
client->snapshots_arena = arena_alloc(GIGABYTE(8));
|
||||
@ -302,12 +301,8 @@ struct sim_snapshot *sim_snapshot_alloc(struct sim_client *client, struct sim_sn
|
||||
++client->num_ticks;
|
||||
|
||||
/* Copy src info */
|
||||
ss->is_master = src->is_master;
|
||||
ss->real_dt_ns = src->real_dt_ns;
|
||||
ss->real_time_ns = src->real_time_ns;
|
||||
ss->world_timescale = src->world_timescale;
|
||||
ss->world_dt_ns = src->world_dt_ns;
|
||||
ss->world_time_ns = src->world_time_ns;
|
||||
ss->sim_dt_ns = src->sim_dt_ns;
|
||||
ss->sim_time_ns = src->sim_time_ns;
|
||||
ss->continuity_gen = src->continuity_gen;
|
||||
ss->local_client = src->local_client;
|
||||
ss->phys_iteration = src->phys_iteration;
|
||||
@ -528,11 +523,8 @@ struct sim_snapshot *sim_snapshot_alloc_from_lerp(struct sim_client *client, str
|
||||
|
||||
if (should_blend) {
|
||||
/* Blend time */
|
||||
ss->real_dt_ns = math_lerp_i64(ss0->real_dt_ns, ss1->real_dt_ns, blend);
|
||||
ss->real_time_ns = math_lerp_i64(ss0->real_time_ns, ss1->real_time_ns, blend);
|
||||
ss->world_timescale = math_lerp_f64(ss0->world_timescale, ss1->world_timescale, blend);
|
||||
ss->world_dt_ns = math_lerp_i64(ss0->world_dt_ns, ss1->world_dt_ns, blend);
|
||||
ss->world_time_ns = math_lerp_i64(ss0->world_time_ns, ss1->world_time_ns, blend);
|
||||
ss->sim_dt_ns = math_lerp_i64(ss0->sim_dt_ns, ss1->sim_dt_ns, blend);
|
||||
ss->sim_time_ns = math_lerp_i64(ss0->sim_time_ns, ss1->sim_time_ns, blend);
|
||||
|
||||
/* Blend entities */
|
||||
{
|
||||
@ -559,15 +551,8 @@ void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_client *receiver,
|
||||
{
|
||||
__prof;
|
||||
|
||||
/* TODO: Don't encode this */
|
||||
bw_write_bit(bw, ss1->is_master);
|
||||
|
||||
bw_write_iv(bw, ss1->real_dt_ns);
|
||||
bw_write_iv(bw, ss1->real_time_ns);
|
||||
|
||||
bw_write_f64(bw, ss1->world_timescale);
|
||||
bw_write_iv(bw, ss1->world_dt_ns);
|
||||
bw_write_iv(bw, ss1->world_time_ns);
|
||||
bw_write_iv(bw, ss1->sim_dt_ns);
|
||||
bw_write_iv(bw, ss1->sim_time_ns);
|
||||
|
||||
bw_write_uv(bw, ss1->continuity_gen);
|
||||
bw_write_uv(bw, ss1->phys_iteration);
|
||||
@ -575,12 +560,6 @@ void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_client *receiver,
|
||||
bw_write_uv(bw, receiver->handle.gen);
|
||||
bw_write_uv(bw, receiver->handle.idx);
|
||||
|
||||
bw_write_f32(bw, ss1->control.move.x);
|
||||
bw_write_f32(bw, ss1->control.move.y);
|
||||
bw_write_f32(bw, ss1->control.focus.x);
|
||||
bw_write_f32(bw, ss1->control.focus.y);
|
||||
bw_write_ubits(bw, ss1->control.flags, 32);
|
||||
|
||||
/* Ents */
|
||||
if (ss1->num_ents_allocated == ss0->num_ents_allocated) {
|
||||
bw_write_bit(bw, 0);
|
||||
@ -612,15 +591,8 @@ void sim_snapshot_decode(struct bitbuff_reader *br, struct sim_snapshot *ss)
|
||||
{
|
||||
__prof;
|
||||
|
||||
/* TODO: Don't encode this */
|
||||
ss->is_master = br_read_bit(br);
|
||||
|
||||
ss->real_dt_ns = br_read_iv(br);
|
||||
ss->real_time_ns = br_read_iv(br);
|
||||
|
||||
ss->world_timescale = br_read_f64(br);
|
||||
ss->world_dt_ns = br_read_iv(br);
|
||||
ss->world_time_ns = br_read_iv(br);
|
||||
ss->sim_dt_ns = br_read_iv(br);
|
||||
ss->sim_time_ns = br_read_iv(br);
|
||||
|
||||
ss->continuity_gen = br_read_uv(br);
|
||||
ss->phys_iteration = br_read_uv(br);
|
||||
@ -628,12 +600,6 @@ void sim_snapshot_decode(struct bitbuff_reader *br, struct sim_snapshot *ss)
|
||||
ss->local_client.gen = br_read_uv(br);
|
||||
ss->local_client.idx = br_read_uv(br);
|
||||
|
||||
ss->control.move.x = br_read_f32(br);
|
||||
ss->control.move.y = br_read_f32(br);
|
||||
ss->control.focus.x = br_read_f32(br);
|
||||
ss->control.focus.y = br_read_f32(br);
|
||||
ss->control.flags = br_read_ubits(br, 32);
|
||||
|
||||
/* Ents */
|
||||
if (br_read_bit(br)) {
|
||||
ss->num_ents_allocated = br_read_uv(br);
|
||||
|
||||
77
src/sim.h
77
src/sim.h
@ -60,14 +60,6 @@ void sim_client_store_release(struct sim_client_store *store);
|
||||
|
||||
struct sim_snapshot;
|
||||
|
||||
enum sim_client_kind {
|
||||
SIM_CLIENT_KIND_INVALID,
|
||||
SIM_CLIENT_KIND_USER,
|
||||
SIM_CLIENT_KIND_LOCAL_SIM,
|
||||
SIM_CLIENT_KIND_SLAVE_SIM,
|
||||
SIM_CLIENT_KIND_MASTER_SIM,
|
||||
};
|
||||
|
||||
struct sim_snapshot_lookup_bucket {
|
||||
struct sim_snapshot *first;
|
||||
struct sim_snapshot *last;
|
||||
@ -75,7 +67,6 @@ struct sim_snapshot_lookup_bucket {
|
||||
|
||||
struct sim_client {
|
||||
b32 valid;
|
||||
enum sim_client_kind kind;
|
||||
struct sim_client_handle handle;
|
||||
struct sim_client_store *store;
|
||||
|
||||
@ -91,8 +82,10 @@ struct sim_client {
|
||||
/* This is the last confirmed tick of ours that we know this client has received */
|
||||
u64 ack;
|
||||
|
||||
/* This is the last confirmed client tick that the client knows we have received */
|
||||
u64 reverse_ack;
|
||||
/* This is the last confirmed ack of ours that we know this client has received (this
|
||||
* can be used to determine which client ticks will no longer be delta encoded from and
|
||||
* can therefore be released) */
|
||||
u64 double_ack;
|
||||
|
||||
/* Snapshots sorted by tick (low to high) */
|
||||
u64 first_tick;
|
||||
@ -111,7 +104,12 @@ INLINE struct sim_client *sim_client_nil(void)
|
||||
return *_g_sim_client_nil;
|
||||
}
|
||||
|
||||
struct sim_client *sim_client_alloc(struct sim_client_store *store, enum sim_client_kind kind);
|
||||
INLINE b32 sim_client_handle_eq(struct sim_client_handle a, struct sim_client_handle b)
|
||||
{
|
||||
return a.gen == b.gen && a.idx == b.idx;
|
||||
}
|
||||
|
||||
struct sim_client *sim_client_alloc(struct sim_client_store *store);
|
||||
void sim_client_release(struct sim_client *client);
|
||||
|
||||
struct sim_client *sim_client_from_channel_id(struct sim_client_store *store, struct host_channel_id channel_id);
|
||||
@ -123,16 +121,14 @@ struct sim_client *sim_client_from_handle(struct sim_client_store *store, struct
|
||||
* ========================== */
|
||||
|
||||
enum sim_control_flag {
|
||||
SIM_CONTROL_FLAG_NONE = 0,
|
||||
SIM_CONTROL_FLAG_FIRING = 1 << 0,
|
||||
SIM_CONTROL_FLAG_FIRE = 1 << 0,
|
||||
|
||||
/* Testing */
|
||||
SIM_CONTROL_FLAG_CLEAR_ALL = 1 << 1,
|
||||
SIM_CONTROL_FLAG_SPAWN_TEST = 1 << 2,
|
||||
SIM_CONTROL_FLAG_PAUSE = 1 << 3,
|
||||
SIM_CONTROL_FLAG_STEP = 1 << 4,
|
||||
|
||||
SIM_CONTROL_FLAG_DRAGGING = 1 << 5,
|
||||
SIM_CONTROL_FLAG_DRAG = 1 << 1,
|
||||
SIM_CONTROL_FLAG_CLEAR_ALL = 1 << 2,
|
||||
SIM_CONTROL_FLAG_SPAWN_TEST = 1 << 3,
|
||||
SIM_CONTROL_FLAG_PAUSE = 1 << 4,
|
||||
SIM_CONTROL_FLAG_STEP = 1 << 5
|
||||
};
|
||||
|
||||
struct sim_control {
|
||||
@ -153,32 +149,13 @@ struct sim_snapshot {
|
||||
|
||||
struct arena arena;
|
||||
|
||||
struct sim_client_handle producer_client;
|
||||
b32 producer_client_is_local;
|
||||
|
||||
/* ====================================================================== */
|
||||
/* SIM_SNAPSHOT_FLAG_CONTROL */
|
||||
|
||||
struct sim_control control;
|
||||
|
||||
/* ====================================================================== */
|
||||
/* SIM_SNAPSHOT_FLAG_STATE */
|
||||
|
||||
/* Was this snapshot created by the master sim */
|
||||
b32 is_master;
|
||||
|
||||
/* Time of local snapshot publish to user thread */
|
||||
/* Program time of local snapshot publish to user thread (if relevant) */
|
||||
i64 publish_dt_ns;
|
||||
i64 publish_time_ns;
|
||||
|
||||
/* Real time (guaranteed to increase by real_dt_ns each sim step) */
|
||||
i64 real_dt_ns;
|
||||
i64 real_time_ns;
|
||||
|
||||
/* World time (affected by timescale) */
|
||||
f64 world_timescale;
|
||||
i64 world_dt_ns;
|
||||
i64 world_time_ns;
|
||||
/* Sim time (guaranteed to increase by sim_dt_ns each step) */
|
||||
i64 sim_dt_ns;
|
||||
i64 sim_time_ns;
|
||||
|
||||
/* If != previous tick's continuity then don't lerp */
|
||||
u64 continuity_gen;
|
||||
@ -186,8 +163,8 @@ struct sim_snapshot {
|
||||
/* The last physics iteration (used for tracking contact lifetime) */
|
||||
u64 phys_iteration;
|
||||
|
||||
/* The receiver's client handle on the sender's machine (use this to find local client ent in snapshot) */
|
||||
struct sim_client_handle local_client;
|
||||
/* TODO: Replace with networked id that resolves to local client ent? */
|
||||
struct sim_client_handle local_client; /* The receiver's client handle on the master sim (used for finding local client ent in snapshot) */
|
||||
|
||||
/* Entities */
|
||||
struct arena ents_arena;
|
||||
@ -197,16 +174,6 @@ struct sim_snapshot {
|
||||
u64 num_ents_reserved;
|
||||
};
|
||||
|
||||
struct sim_snapshot_list_node {
|
||||
struct sim_snapshot *ss;
|
||||
struct sim_snapshot_list_node *next;
|
||||
};
|
||||
|
||||
struct sim_snapshot_list {
|
||||
struct sim_snapshot_list_node *first;
|
||||
struct sim_snapshot_list_node *last;
|
||||
};
|
||||
|
||||
INLINE struct sim_snapshot *sim_snapshot_nil(void)
|
||||
{
|
||||
extern READONLY struct sim_snapshot **_g_sim_snapshot_nil;
|
||||
|
||||
@ -373,6 +373,10 @@ void sim_ent_encode(struct bitbuff_writer *bw, struct sim_ent *e0, struct sim_en
|
||||
{
|
||||
struct sim_snapshot *ss = e1->ss;
|
||||
|
||||
|
||||
/* FIXME: Things like xforms need to be retreived manually rather than memcopied.
|
||||
* This will also be true for things like ent handles once netids are implemented. */
|
||||
|
||||
/* TODO: Granular delta encoding */
|
||||
|
||||
u64 pos = 0;
|
||||
|
||||
@ -20,9 +20,12 @@ enum sim_ent_prop {
|
||||
SIM_ENT_PROP_RELEASE_THIS_TICK,
|
||||
SIM_ENT_PROP_RELEASE_NEXT_TICK,
|
||||
|
||||
SIM_ENT_PROP_REMOTE,
|
||||
SIM_ENT_PROP_CLIENT,
|
||||
SIM_ENT_PROP_LOCAL_CLIENT,
|
||||
|
||||
SIM_ENT_PROP_CMD_CONTROL,
|
||||
|
||||
SIM_ENT_PROP_PHYSICAL_DYNAMIC,
|
||||
SIM_ENT_PROP_PHYSICAL_KINEMATIC,
|
||||
|
||||
@ -85,10 +88,19 @@ struct sim_ent {
|
||||
struct sim_ent_handle last;
|
||||
struct sim_ent_handle next_free;
|
||||
|
||||
/* ====================================================================== */
|
||||
/* Remote */
|
||||
|
||||
/* SIM_ENT_PROP_REMOTE */
|
||||
|
||||
struct sim_client_handle remote_client;
|
||||
struct sim_ent_handle remote_ent;
|
||||
|
||||
/* ====================================================================== */
|
||||
/* Position */
|
||||
|
||||
/* Access with xform getters/setters */
|
||||
/* TODO: Prefix with '_' to signal against direct access */
|
||||
struct xform local_xform; /* Transform in relation to parent ent (or the world if ent has no parent) */
|
||||
struct xform cached_global_xform; /* Calculated from ent tree */
|
||||
b32 cached_global_xform_dirty;
|
||||
@ -105,6 +117,35 @@ struct sim_ent {
|
||||
i32 layer;
|
||||
i32 final_layer; /* Calculated each tick from ent tree */
|
||||
|
||||
/* ====================================================================== */
|
||||
/* Cmd */
|
||||
|
||||
/* SIM_ENT_PROP_CMD_CONTROL */
|
||||
|
||||
/* FIXME: Lerp */
|
||||
|
||||
struct sim_ent_handle cmd_client;
|
||||
struct sim_control cmd_control;
|
||||
|
||||
/* ====================================================================== */
|
||||
/* Client */
|
||||
|
||||
/* SIM_ENT_PROP_CLIENT */
|
||||
|
||||
/* FIXME: Lerp */
|
||||
|
||||
struct sim_client_handle client_handle; /* The client handle on the master sim's machine */
|
||||
|
||||
struct sim_control client_control;
|
||||
struct v2 client_cursor_pos;
|
||||
|
||||
struct sim_ent_handle client_control_ent;
|
||||
struct sim_ent_handle client_camera_ent;
|
||||
|
||||
struct sim_ent_handle client_dbg_drag_joint_ent;
|
||||
b32 client_dbg_drag_start;
|
||||
b32 client_dbg_drag_stop;
|
||||
|
||||
/* ====================================================================== */
|
||||
/* Collider */
|
||||
|
||||
@ -134,25 +175,6 @@ struct sim_ent {
|
||||
/* SIM_ENT_PROP_MOUSE_JOINT */
|
||||
struct phys_mouse_joint mouse_joint_data;
|
||||
|
||||
/* ====================================================================== */
|
||||
/* Client */
|
||||
|
||||
/* SIM_ENT_PROP_CLIENT */
|
||||
|
||||
/* FIXME: Lerp */
|
||||
|
||||
struct sim_client_handle client_handle;
|
||||
|
||||
struct sim_control client_control;
|
||||
struct v2 client_cursor_pos;
|
||||
|
||||
struct sim_ent_handle client_control_ent;
|
||||
struct sim_ent_handle client_camera_ent;
|
||||
|
||||
struct sim_ent_handle client_dbg_drag_joint_ent;
|
||||
b32 client_dbg_drag_start;
|
||||
b32 client_dbg_drag_stop;
|
||||
|
||||
/* ====================================================================== */
|
||||
/* Control */
|
||||
|
||||
@ -231,7 +253,7 @@ struct sim_ent {
|
||||
|
||||
/* SIM_ENT_PROP_TRIGGERED_THIS_TICK */
|
||||
f32 trigger_delay; /* Minimum time between triggers */
|
||||
f32 last_triggered;
|
||||
i64 last_triggered_ns;
|
||||
|
||||
/* ====================================================================== */
|
||||
/* Bullet */
|
||||
|
||||
138
src/sim_step.c
138
src/sim_step.c
@ -124,6 +124,16 @@ void sim_lookup_remove(struct sim_lookup *l, struct sim_lookup_entry *entry)
|
||||
l->first_free_entry = entry;
|
||||
}
|
||||
|
||||
struct sim_lookup_key sim_lookup_key_from_client_and_ent_handles(struct sim_client_handle client_handle, struct sim_ent_handle ent_handle)
|
||||
{
|
||||
struct sim_lookup_key key = ZI;
|
||||
struct string b0 = STRING_FROM_STRUCT(&client_handle);
|
||||
struct string b1 = STRING_FROM_STRUCT(&ent_handle);
|
||||
key.hash = hash_fnv64(HASH_FNV64_BASIS, b0);
|
||||
key.hash = hash_fnv64(key.hash, b1);
|
||||
return key;
|
||||
}
|
||||
|
||||
struct sim_lookup_key sim_lookup_key_from_two_handles(struct sim_ent_handle h0, struct sim_ent_handle h1)
|
||||
{
|
||||
struct sim_lookup_key key = ZI;
|
||||
@ -149,6 +159,7 @@ struct sim_accel sim_accel_alloc(void)
|
||||
{
|
||||
struct sim_accel accel = ZI;
|
||||
accel.space = space_alloc(1, 256);
|
||||
accel.remote_lookup = sim_lookup_alloc(4096);
|
||||
accel.client_lookup = sim_lookup_alloc(4096);
|
||||
accel.contact_lookup = sim_lookup_alloc(4096);
|
||||
#if COLLIDER_DEBUG
|
||||
@ -164,6 +175,7 @@ void sim_accel_release(struct sim_accel *accel)
|
||||
#endif
|
||||
sim_lookup_release(&accel->contact_lookup);
|
||||
sim_lookup_release(&accel->client_lookup);
|
||||
sim_lookup_release(&accel->remote_lookup);
|
||||
space_release(accel->space);
|
||||
}
|
||||
|
||||
@ -172,6 +184,7 @@ void sim_accel_rebuild(struct sim_snapshot *ss, struct sim_accel *accel)
|
||||
/* FIXME: Rebuild collision debug lookup */
|
||||
|
||||
space_reset(accel->space);
|
||||
sim_lookup_reset(&accel->remote_lookup);
|
||||
sim_lookup_reset(&accel->client_lookup);
|
||||
sim_lookup_reset(&accel->contact_lookup);
|
||||
|
||||
@ -188,6 +201,10 @@ void sim_accel_rebuild(struct sim_snapshot *ss, struct sim_accel *accel)
|
||||
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
||||
struct sim_ent *ent = &ss->ents[sim_ent_index];
|
||||
if (!sim_ent_is_valid_and_active(ent)) continue;
|
||||
if (sim_ent_has_prop(ent, SIM_ENT_PROP_REMOTE)) {
|
||||
struct sim_lookup_key key = sim_lookup_key_from_client_and_ent_handles(ent->remote_client, ent->remote_ent);
|
||||
sim_lookup_set(&accel->remote_lookup, key, ent->handle);
|
||||
}
|
||||
if (sim_ent_has_prop(ent, SIM_ENT_PROP_CLIENT)) {
|
||||
struct sim_lookup_key key = sim_lookup_key_from_client_handle(ent->client_handle);
|
||||
sim_lookup_set(&accel->client_lookup, key, ent->handle);
|
||||
@ -561,40 +578,24 @@ void sim_step(struct sim_step_ctx *step_ctx)
|
||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||
|
||||
struct sim_snapshot *world = step_ctx->world;
|
||||
struct sim_accel *accel = step_ctx->accel;
|
||||
struct sim_snapshot_list *cmd_snapshots = step_ctx->cmd_snapshots;
|
||||
i64 real_dt_ns = step_ctx->real_dt_ns;
|
||||
i64 sim_dt_ns = step_ctx->sim_dt_ns;
|
||||
|
||||
/* ========================== *
|
||||
* Begin frame
|
||||
* ========================== */
|
||||
|
||||
sim_accel_rebuild(world, step_ctx->accel);
|
||||
|
||||
//sys_sleep_precise(rng_rand_f32(0, 0.050));
|
||||
//sys_sleep_precise(0.050);
|
||||
|
||||
world->real_dt_ns = max_i64(0, real_dt_ns);
|
||||
world->real_time_ns += world->real_dt_ns;
|
||||
|
||||
world->world_timescale = SIM_TIMESCALE;
|
||||
world->world_dt_ns = max_i64(0, real_dt_ns * world->world_timescale);
|
||||
world->world_time_ns += world->world_dt_ns;
|
||||
|
||||
f64 real_dt = SECONDS_FROM_NS(world->real_dt_ns);
|
||||
f64 real_time = SECONDS_FROM_NS(world->real_time_ns);
|
||||
f64 world_dt = SECONDS_FROM_NS(world->world_dt_ns);
|
||||
f64 world_time = SECONDS_FROM_NS(world->world_time_ns);
|
||||
(UNUSED)real_dt;
|
||||
(UNUSED)real_time;
|
||||
(UNUSED)world_dt;
|
||||
(UNUSED)world_time;
|
||||
world->sim_dt_ns = max_i64(0, sim_dt_ns);
|
||||
world->sim_time_ns += world->sim_dt_ns;
|
||||
f32 sim_dt = SECONDS_FROM_NS(world->sim_dt_ns);
|
||||
|
||||
struct sprite_scope *sprite_frame_scope = sprite_scope_begin();
|
||||
|
||||
struct sim_ent *root = sim_ent_from_handle(world, SIM_ENT_ROOT_HANDLE);
|
||||
|
||||
if (world->is_master) {
|
||||
if (step_ctx->is_master) {
|
||||
/* ========================== *
|
||||
* Spawn test entities
|
||||
* ========================== */
|
||||
@ -651,6 +652,67 @@ void sim_step(struct sim_step_ctx *step_ctx)
|
||||
* Process client cmds
|
||||
* ========================== */
|
||||
|
||||
for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) {
|
||||
struct sim_ent *cmd_ent = &world->ents[ent_index];
|
||||
if (!sim_ent_is_valid_and_active(cmd_ent)) continue;
|
||||
|
||||
if (sim_ent_has_prop(cmd_ent, SIM_ENT_PROP_CMD_CONTROL)) {
|
||||
struct sim_ent *client_ent = sim_ent_from_handle(world, cmd_ent->cmd_client);
|
||||
if (sim_ent_is_valid_and_active(client_ent)) {
|
||||
/* Process control cmd for client */
|
||||
struct sim_control old_control = client_ent->client_control;
|
||||
struct sim_control *control = &client_ent->client_control;
|
||||
*control = cmd_ent->cmd_control;
|
||||
{
|
||||
client_ent->client_dbg_drag_start = false;
|
||||
client_ent->client_dbg_drag_stop = false;
|
||||
|
||||
if (v2_len_sq(control->move) > 1) {
|
||||
/* Cap movement vector magnitude at 1 */
|
||||
control->move = v2_norm(control->move);
|
||||
}
|
||||
|
||||
/* Determine cursor pos from focus */
|
||||
{
|
||||
struct sim_ent_handle client_control_ent_handle = client_ent->client_control_ent;
|
||||
struct sim_ent *client_control_ent = sim_ent_from_handle(world, client_control_ent_handle);
|
||||
if (client_control_ent->valid || sim_ent_handle_eq(client_control_ent_handle, SIM_ENT_NIL_HANDLE)) {
|
||||
/* Only update cursor pos if focus ent is valid (or nil) */
|
||||
client_ent->client_cursor_pos = v2_add(sim_ent_get_xform(client_control_ent).og, client_ent->client_control.focus);
|
||||
}
|
||||
}
|
||||
|
||||
u32 flags = control->flags;
|
||||
if (flags & SIM_CONTROL_FLAG_DRAG) {
|
||||
if (!(old_control.flags & SIM_CONTROL_FLAG_DRAG)) {
|
||||
client_ent->client_dbg_drag_start = true;
|
||||
}
|
||||
} else {
|
||||
if (old_control.flags & SIM_CONTROL_FLAG_DRAG) {
|
||||
client_ent->client_dbg_drag_stop = true;
|
||||
}
|
||||
}
|
||||
if (flags & SIM_CONTROL_FLAG_CLEAR_ALL) {
|
||||
if (!(old_control.flags & SIM_CONTROL_FLAG_CLEAR_ALL)) {
|
||||
test_clear_level(world);
|
||||
}
|
||||
}
|
||||
if (flags & SIM_CONTROL_FLAG_SPAWN_TEST) {
|
||||
if (!(old_control.flags & SIM_CONTROL_FLAG_SPAWN_TEST)) {
|
||||
logf_info("Spawning (test)");
|
||||
u32 count = 1;
|
||||
f32 spread = 1;
|
||||
for (u32 j = 0; j < count; ++j) {
|
||||
spawn_test_entities(world, V2(0, (((f32)j / (f32)count) - 0.5) * spread));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
for (struct sim_snapshot_list_node *n = cmd_snapshots->first; n; n = n->next) {
|
||||
struct sim_snapshot *cmd_snapshot = n->ss;
|
||||
struct sim_client_handle client_handle = cmd_snapshot->producer_client;
|
||||
@ -698,12 +760,12 @@ void sim_step(struct sim_step_ctx *step_ctx)
|
||||
}
|
||||
|
||||
u32 flags = control->flags;
|
||||
if (flags & SIM_CONTROL_FLAG_DRAGGING) {
|
||||
if (!(old_control.flags & SIM_CONTROL_FLAG_DRAGGING)) {
|
||||
if (flags & SIM_CONTROL_FLAG_DRAG) {
|
||||
if (!(old_control.flags & SIM_CONTROL_FLAG_DRAG)) {
|
||||
client_ent->client_dbg_drag_start = true;
|
||||
}
|
||||
} else {
|
||||
if (old_control.flags & SIM_CONTROL_FLAG_DRAGGING) {
|
||||
if (old_control.flags & SIM_CONTROL_FLAG_DRAG) {
|
||||
client_ent->client_dbg_drag_stop = true;
|
||||
}
|
||||
}
|
||||
@ -724,6 +786,10 @@ void sim_step(struct sim_step_ctx *step_ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/* ========================== *
|
||||
* Create client player ents
|
||||
@ -762,7 +828,7 @@ void sim_step(struct sim_step_ctx *step_ctx)
|
||||
if (client_ent->valid) {
|
||||
ent->control = client_ent->client_control;
|
||||
/* TODO: Move this */
|
||||
if (ent->control.flags & SIM_CONTROL_FLAG_FIRING) {
|
||||
if (ent->control.flags & SIM_CONTROL_FLAG_FIRE) {
|
||||
sim_ent_enable_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED);
|
||||
} else {
|
||||
sim_ent_disable_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED);
|
||||
@ -786,10 +852,10 @@ void sim_step(struct sim_step_ctx *step_ctx)
|
||||
{
|
||||
struct sprite_sheet_span span = sprite_sheet_get_span(sheet, ent->sprite_span_name);
|
||||
if (ent->animation_last_frame_change_time_ns == 0) {
|
||||
ent->animation_last_frame_change_time_ns = world_time;
|
||||
ent->animation_last_frame_change_time_ns = SECONDS_FROM_NS(world->sim_time_ns);
|
||||
}
|
||||
|
||||
f64 time_in_frame = SECONDS_FROM_NS(world->world_time_ns - ent->animation_last_frame_change_time_ns);
|
||||
f64 time_in_frame = SECONDS_FROM_NS(world->sim_time_ns - ent->animation_last_frame_change_time_ns);
|
||||
u64 frame_index = ent->animation_frame;
|
||||
if (frame_index < span.start || frame_index > span.end) {
|
||||
frame_index = span.start;
|
||||
@ -805,7 +871,7 @@ void sim_step(struct sim_step_ctx *step_ctx)
|
||||
frame_index = span.start;
|
||||
}
|
||||
frame = sprite_sheet_get_frame(sheet, frame_index);
|
||||
ent->animation_last_frame_change_time_ns = world->world_time_ns;
|
||||
ent->animation_last_frame_change_time_ns = world->sim_time_ns;
|
||||
}
|
||||
}
|
||||
|
||||
@ -971,9 +1037,9 @@ void sim_step(struct sim_step_ctx *step_ctx)
|
||||
struct sim_ent *ent = &world->ents[ent_index];
|
||||
if (!sim_ent_is_valid_and_active(ent)) continue;
|
||||
if (!sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK)) continue;
|
||||
if ((world_time - ent->last_triggered < ent->trigger_delay) && ent->last_triggered != 0) continue;
|
||||
if ((world->sim_time_ns - ent->last_triggered_ns < NS_FROM_SECONDS(ent->trigger_delay)) && ent->last_triggered_ns != 0) continue;
|
||||
|
||||
ent->last_triggered = world_time;
|
||||
ent->last_triggered_ns = world->sim_time_ns;
|
||||
|
||||
/* Fire weapon */
|
||||
if (sim_ent_has_prop(ent, SIM_ENT_PROP_WEAPON)) {
|
||||
@ -1092,7 +1158,7 @@ void sim_step(struct sim_step_ctx *step_ctx)
|
||||
}
|
||||
|
||||
/* Set correction rate dynamically since motor velocity is only set for one frame */
|
||||
joint_ent->motor_joint_data.correction_rate = 10 * world_dt;
|
||||
joint_ent->motor_joint_data.correction_rate = 10 * sim_dt;
|
||||
|
||||
|
||||
/* Solve for final angle using law of sines */
|
||||
@ -1145,7 +1211,7 @@ void sim_step(struct sim_step_ctx *step_ctx)
|
||||
f32 diff = math_unwind_angle(new_angle - xform_get_rotation(joint_xf));
|
||||
if (math_fabs(diff) > angle_error_allowed) {
|
||||
/* Instantly snap joint ent to new angle */
|
||||
new_vel = diff / real_dt;
|
||||
new_vel = diff / sim_dt;
|
||||
}
|
||||
}
|
||||
sim_ent_set_angular_velocity(joint_ent, new_vel);
|
||||
@ -1260,7 +1326,7 @@ void sim_step(struct sim_step_ctx *step_ctx)
|
||||
struct phys_step_ctx phys = ZI;
|
||||
phys.sim_step_ctx = step_ctx;
|
||||
phys.pre_solve_callback = on_collision;
|
||||
phys_step(&phys, world_dt);
|
||||
phys_step(&phys, sim_dt);
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
@ -1274,7 +1340,7 @@ void sim_step(struct sim_step_ctx *step_ctx)
|
||||
|
||||
struct v2 end = sim_ent_get_xform(ent).og;
|
||||
|
||||
struct v2 tick_velocity = v2_mul(ent->tracer_start_velocity, world_dt);
|
||||
struct v2 tick_velocity = v2_mul(ent->tracer_start_velocity, sim_dt);
|
||||
struct v2 gradient_start = v2_add(ent->tracer_gradient_start, tick_velocity);
|
||||
struct v2 gradient_end = v2_add(ent->tracer_gradient_end, tick_velocity);
|
||||
|
||||
@ -1374,7 +1440,7 @@ void sim_step(struct sim_step_ctx *step_ctx)
|
||||
|
||||
/* Lerp camera */
|
||||
if (ent->camera_applied_lerp_continuity_gen_plus_one == ent->camera_lerp_continuity_gen + 1) {
|
||||
f32 t = 1 - math_pow(2.f, -20.f * (f32)world_dt);
|
||||
f32 t = 1 - math_pow(2.f, -20.f * (f32)sim_dt);
|
||||
xf = xform_lerp(xf, ent->camera_xform_target, t);
|
||||
} else {
|
||||
/* Skip lerp */
|
||||
@ -1407,7 +1473,7 @@ void sim_step(struct sim_step_ctx *step_ctx)
|
||||
if (!sim_ent_is_valid_and_active(ent)) continue;
|
||||
if (!sim_ent_has_prop(ent, SIM_ENT_PROP_QUAKE)) continue;
|
||||
|
||||
ent->quake_intensity = max_f32(0, ent->quake_intensity - (ent->quake_fade * world_dt));
|
||||
ent->quake_intensity = max_f32(0, ent->quake_intensity - (ent->quake_fade * sim_dt));
|
||||
if (ent->quake_intensity <= 0) {
|
||||
sim_ent_enable_prop(ent, SIM_ENT_PROP_RELEASE_NEXT_TICK);
|
||||
}
|
||||
|
||||
@ -44,6 +44,8 @@ struct sim_lookup_entry *sim_lookup_get(struct sim_lookup *l, struct sim_lookup_
|
||||
void sim_lookup_set(struct sim_lookup *l, struct sim_lookup_key key, struct sim_ent_handle handle);
|
||||
void sim_lookup_remove(struct sim_lookup *l, struct sim_lookup_entry *entry);
|
||||
|
||||
|
||||
struct sim_lookup_key sim_lookup_key_from_client_and_ent_handles(struct sim_client_handle client_handle, struct sim_ent_handle ent_handle);
|
||||
struct sim_lookup_key sim_lookup_key_from_two_handles(struct sim_ent_handle h0, struct sim_ent_handle h1);
|
||||
struct sim_lookup_key sim_lookup_key_from_client_handle(struct sim_client_handle handle);
|
||||
|
||||
@ -56,6 +58,7 @@ struct sim_lookup_key sim_lookup_key_from_client_handle(struct sim_client_handle
|
||||
|
||||
struct sim_accel {
|
||||
struct space *space;
|
||||
struct sim_lookup remote_lookup;
|
||||
struct sim_lookup client_lookup;
|
||||
struct sim_lookup contact_lookup;
|
||||
#if COLLIDER_DEBUG
|
||||
@ -72,10 +75,10 @@ void sim_accel_rebuild(struct sim_snapshot *ss, struct sim_accel *accel);
|
||||
* ========================== */
|
||||
|
||||
struct sim_step_ctx {
|
||||
b32 is_master;
|
||||
struct sim_accel *accel;
|
||||
struct sim_snapshot *world;
|
||||
struct sim_snapshot_list *cmd_snapshots;
|
||||
i64 real_dt_ns;
|
||||
i64 sim_dt_ns;
|
||||
};
|
||||
|
||||
void sim_step(struct sim_step_ctx *step_ctx);
|
||||
|
||||
@ -414,7 +414,7 @@ struct sock_read_result sock_read(struct sock *sock, struct string read_buff)
|
||||
|
||||
res.address = sock_address_from_win32_address(ws_addr);
|
||||
if (size >= 0) {
|
||||
atomic_u64_eval_add_u64(&app_statistics()->sock_bytes_sent, size);
|
||||
atomic_u64_eval_add_u64(&app_statistics()->sock_bytes_received, size);
|
||||
res.data.text = read_buff.text;
|
||||
res.data.len = size;
|
||||
res.valid = true;
|
||||
|
||||
718
src/user.c
718
src/user.c
@ -49,14 +49,14 @@ GLOBAL struct {
|
||||
struct string connect_address_str;
|
||||
|
||||
struct sim_client_store *user_client_store;
|
||||
struct sim_client *user_unblended_snapshots_client; /* Contains buffered snapshots received from sim */
|
||||
struct sim_client *user_blended_snapshots_client; /* Contains single world snapshot from result of blending sim snapshots */
|
||||
struct sim_snapshot *ss_blended; /* Points to blended snapshot in blended snapshots client */
|
||||
struct sim_client *user_unblended_client; /* Contains snapshots received from local sim */
|
||||
struct sim_client *user_blended_client; /* Contains single snapshot from result of blending local sim snapshots */
|
||||
struct sim_snapshot *ss_blended; /* Points to blended snapshot contained in blended client */
|
||||
|
||||
/* Usage stats */
|
||||
i64 last_second_reset_ns;
|
||||
struct second_stat client_bytes_read;
|
||||
struct second_stat client_bytes_sent;
|
||||
struct second_stat net_bytes_read;
|
||||
struct second_stat net_bytes_sent;
|
||||
|
||||
/* Render targets */
|
||||
struct renderer_texture final_texture;
|
||||
@ -88,23 +88,21 @@ GLOBAL struct {
|
||||
struct sim_control user_sim_cmd_control;
|
||||
u64 last_user_sim_cmd_gen;
|
||||
u64 user_sim_cmd_gen;
|
||||
u64 user_sim_cmd_ack;
|
||||
|
||||
/* Local sim -> user */
|
||||
struct sys_mutex local_sim_to_user_mutex;
|
||||
struct sim_client_store *local_sim_to_user_client_store;
|
||||
struct sim_client *local_sim_to_user_client;
|
||||
struct sys_mutex local_to_user_client_mutex;
|
||||
struct sim_client_store *local_to_user_client_store;
|
||||
struct sim_client *local_to_user_client;
|
||||
|
||||
/* Rolling window of local sim publish times */
|
||||
i64 last_snapshot_published_at_ns;
|
||||
i64 snapshot_publish_dts_ns[50];
|
||||
i64 snapshot_publish_dts_index;
|
||||
i64 average_snapshot_publish_dt_ns;
|
||||
/* Rolling window of local sim -> user publish time deltas */
|
||||
i64 last_local_to_user_snapshot_published_at_ns;
|
||||
i64 local_to_user_snapshot_publish_dts_ns[50];
|
||||
i64 local_to_user_snapshot_publish_dts_index;
|
||||
i64 average_local_to_user_snapshot_publish_dt_ns;
|
||||
|
||||
/* Calculated from <last snapshot receive time + time since packet receive> */
|
||||
i64 local_sim_predicted_time_ns;
|
||||
i64 render_time_target_ns;
|
||||
i64 render_time_ns;
|
||||
i64 local_sim_predicted_time_ns; /* Calculated from <last local sim to user pubilsh time> + <time since last local sim to user publish> */
|
||||
i64 render_time_target_ns; /* Claculated from <local_sim_rpedicted_time_ns> - <render interp delay> */
|
||||
i64 render_time_ns; /* Incremented at a constant rate based on average local to user publish delta, but snaps to render_time_target_ns if it gets too distant */
|
||||
|
||||
u64 local_sim_last_known_tick;
|
||||
i64 local_sim_last_known_time_ns;
|
||||
@ -205,16 +203,16 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
|
||||
G.sys_events_mutex = sys_mutex_alloc();
|
||||
G.sys_events_arena = arena_alloc(GIGABYTE(64));
|
||||
|
||||
/* Snapshot store */
|
||||
/* User blend clients */
|
||||
G.user_client_store = sim_client_store_alloc();
|
||||
G.user_unblended_snapshots_client = sim_client_alloc(G.user_client_store, SIM_CLIENT_KIND_USER);
|
||||
G.user_blended_snapshots_client = sim_client_alloc(G.user_client_store, SIM_CLIENT_KIND_USER);
|
||||
G.user_unblended_client = sim_client_alloc(G.user_client_store);
|
||||
G.user_blended_client = sim_client_alloc(G.user_client_store);
|
||||
G.ss_blended = sim_snapshot_nil();
|
||||
|
||||
/* Local sim snapshot store */
|
||||
G.local_sim_to_user_mutex = sys_mutex_alloc();
|
||||
G.local_sim_to_user_client_store = sim_client_store_alloc();
|
||||
G.local_sim_to_user_client = sim_client_alloc(G.local_sim_to_user_client_store, SIM_CLIENT_KIND_LOCAL_SIM);
|
||||
/* Local to user client */
|
||||
G.local_to_user_client_mutex = sys_mutex_alloc();
|
||||
G.local_to_user_client_store = sim_client_store_alloc();
|
||||
G.local_to_user_client = sim_client_alloc(G.local_to_user_client_store);
|
||||
|
||||
/* User sim control */
|
||||
G.user_sim_cmd_mutex = sys_mutex_alloc();
|
||||
@ -391,16 +389,16 @@ INTERNAL void user_update(void)
|
||||
* ========================== */
|
||||
|
||||
{
|
||||
struct sys_lock lock = sys_mutex_lock_e(&G.local_sim_to_user_mutex);
|
||||
u64 old_last_tick = G.user_unblended_snapshots_client->last_tick;
|
||||
u64 last_tick = G.local_sim_to_user_client->last_tick;
|
||||
struct sys_lock lock = sys_mutex_lock_e(&G.local_to_user_client_mutex);
|
||||
u64 old_last_tick = G.user_unblended_client->last_tick;
|
||||
u64 last_tick = G.local_to_user_client->last_tick;
|
||||
if (last_tick > old_last_tick) {
|
||||
struct sim_snapshot *src = sim_snapshot_from_tick(G.local_sim_to_user_client, last_tick);
|
||||
sim_snapshot_alloc(G.user_unblended_snapshots_client, src, src->tick);
|
||||
G.last_snapshot_published_at_ns = src->publish_time_ns;
|
||||
G.snapshot_publish_dts_ns[G.snapshot_publish_dts_index++] = src->publish_dt_ns;
|
||||
if (G.snapshot_publish_dts_index >= (i64)ARRAY_COUNT(G.snapshot_publish_dts_ns)) {
|
||||
G.snapshot_publish_dts_index = 0;
|
||||
struct sim_snapshot *src = sim_snapshot_from_tick(G.local_to_user_client, last_tick);
|
||||
sim_snapshot_alloc(G.user_unblended_client, src, src->tick);
|
||||
G.last_local_to_user_snapshot_published_at_ns = src->publish_time_ns;
|
||||
G.local_to_user_snapshot_publish_dts_ns[G.local_to_user_snapshot_publish_dts_index++] = src->publish_dt_ns;
|
||||
if (G.local_to_user_snapshot_publish_dts_index >= (i64)ARRAY_COUNT(G.local_to_user_snapshot_publish_dts_ns)) {
|
||||
G.local_to_user_snapshot_publish_dts_index = 0;
|
||||
}
|
||||
}
|
||||
sys_mutex_unlock(&lock);
|
||||
@ -414,8 +412,8 @@ INTERNAL void user_update(void)
|
||||
i64 average_publish_dt_ns = 0;
|
||||
{
|
||||
i64 num_dts = 0;
|
||||
for (u64 i = 0; i < ARRAY_COUNT(G.snapshot_publish_dts_ns); ++i) {
|
||||
i64 dt_ns = G.snapshot_publish_dts_ns[i];
|
||||
for (u64 i = 0; i < ARRAY_COUNT(G.local_to_user_snapshot_publish_dts_ns); ++i) {
|
||||
i64 dt_ns = G.local_to_user_snapshot_publish_dts_ns[i];
|
||||
if (dt_ns != 0) {
|
||||
average_publish_dt_ns += dt_ns;
|
||||
++num_dts;
|
||||
@ -427,7 +425,7 @@ INTERNAL void user_update(void)
|
||||
average_publish_dt_ns /= num_dts;
|
||||
}
|
||||
}
|
||||
G.average_snapshot_publish_dt_ns = average_publish_dt_ns;
|
||||
G.average_local_to_user_snapshot_publish_dt_ns = average_publish_dt_ns;
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
@ -435,26 +433,26 @@ INTERNAL void user_update(void)
|
||||
* ========================== */
|
||||
|
||||
{
|
||||
/* How along are we between sim ticks (float in range [0, 1]) */
|
||||
/* How along are we between sim ticks (0 = start of tick, 1 = end of tick) */
|
||||
f64 tick_progress = 0;
|
||||
i64 next_tick_expected_ns = G.last_snapshot_published_at_ns + G.average_snapshot_publish_dt_ns;
|
||||
if (next_tick_expected_ns > G.last_snapshot_published_at_ns) {
|
||||
tick_progress = (f64)(G.real_time_ns - G.last_snapshot_published_at_ns) / (f64)(next_tick_expected_ns - G.last_snapshot_published_at_ns);
|
||||
i64 next_tick_expected_ns = G.last_local_to_user_snapshot_published_at_ns + G.average_local_to_user_snapshot_publish_dt_ns;
|
||||
if (next_tick_expected_ns > G.last_local_to_user_snapshot_published_at_ns) {
|
||||
tick_progress = (f64)(G.real_time_ns - G.last_local_to_user_snapshot_published_at_ns) / (f64)(next_tick_expected_ns - G.last_local_to_user_snapshot_published_at_ns);
|
||||
}
|
||||
|
||||
/* Predict local sim time based on average snapshot publish dt. */
|
||||
struct sim_snapshot *newest_snapshot = sim_snapshot_from_tick(G.user_unblended_snapshots_client, G.user_unblended_snapshots_client->last_tick);
|
||||
G.local_sim_last_known_time_ns = newest_snapshot->real_time_ns;
|
||||
struct sim_snapshot *newest_snapshot = sim_snapshot_from_tick(G.user_unblended_client, G.user_unblended_client->last_tick);
|
||||
G.local_sim_last_known_time_ns = newest_snapshot->sim_time_ns;
|
||||
G.local_sim_last_known_tick = newest_snapshot->tick;
|
||||
u64 keep_unblended_tick = newest_snapshot->tick;
|
||||
G.local_sim_predicted_time_ns = newest_snapshot->real_time_ns + (newest_snapshot->real_dt_ns * tick_progress);
|
||||
G.local_sim_predicted_time_ns = newest_snapshot->sim_time_ns + (newest_snapshot->sim_dt_ns * tick_progress);
|
||||
|
||||
#if USER_INTERP_ENABLED
|
||||
/* Determine render time */
|
||||
G.render_time_target_ns = G.local_sim_predicted_time_ns - (USER_INTERP_RATIO * G.average_snapshot_publish_dt_ns);
|
||||
if (G.average_snapshot_publish_dt_ns > 0) {
|
||||
/* Increase render time based on average publish dt */
|
||||
f64 sim_publish_timescale = (f64)newest_snapshot->real_dt_ns / (f64)G.average_snapshot_publish_dt_ns;
|
||||
G.render_time_target_ns = G.local_sim_predicted_time_ns - (USER_INTERP_RATIO * G.average_local_to_user_snapshot_publish_dt_ns);
|
||||
if (G.average_local_to_user_snapshot_publish_dt_ns > 0) {
|
||||
/* Increment render time based on average publish dt */
|
||||
f64 sim_publish_timescale = (f64)newest_snapshot->sim_dt_ns / (f64)G.average_local_to_user_snapshot_publish_dt_ns;
|
||||
G.render_time_ns += G.real_dt_ns * sim_publish_timescale;
|
||||
}
|
||||
i64 render_time_target_diff_ns = G.render_time_target_ns - G.render_time_ns;
|
||||
@ -467,17 +465,17 @@ INTERNAL void user_update(void)
|
||||
struct sim_snapshot *left_snapshot = sim_snapshot_nil();
|
||||
struct sim_snapshot *right_snapshot = newest_snapshot;
|
||||
{
|
||||
struct sim_snapshot *ss = sim_snapshot_from_tick(G.user_unblended_snapshots_client, G.user_unblended_snapshots_client->first_tick);
|
||||
struct sim_snapshot *ss = sim_snapshot_from_tick(G.user_unblended_client, G.user_unblended_client->first_tick);
|
||||
while (ss->valid) {
|
||||
u64 next_tick = ss->next_tick;
|
||||
i64 ss_time_ns = ss->real_time_ns;
|
||||
if (ss_time_ns < G.render_time_ns && ss_time_ns > left_snapshot->real_time_ns) {
|
||||
i64 ss_time_ns = ss->sim_time_ns;
|
||||
if (ss_time_ns < G.render_time_ns && ss_time_ns > left_snapshot->sim_time_ns) {
|
||||
left_snapshot = ss;
|
||||
}
|
||||
if (ss_time_ns > G.render_time_ns && ss_time_ns < right_snapshot->real_time_ns) {
|
||||
if (ss_time_ns > G.render_time_ns && ss_time_ns < right_snapshot->sim_time_ns) {
|
||||
right_snapshot = ss;
|
||||
}
|
||||
ss = sim_snapshot_from_tick(G.user_unblended_snapshots_client, next_tick);
|
||||
ss = sim_snapshot_from_tick(G.user_unblended_client, next_tick);
|
||||
}
|
||||
}
|
||||
|
||||
@ -487,39 +485,39 @@ INTERNAL void user_update(void)
|
||||
|
||||
/* Create world from blended snapshots */
|
||||
if (left_snapshot->valid && right_snapshot->valid) {
|
||||
f64 blend = (f64)(G.render_time_ns - left_snapshot->real_time_ns) / (f64)(right_snapshot->real_time_ns - left_snapshot->real_time_ns);
|
||||
G.ss_blended = sim_snapshot_alloc_from_lerp(G.user_blended_snapshots_client, left_snapshot, right_snapshot, blend);
|
||||
f64 blend = (f64)(G.render_time_ns - left_snapshot->sim_time_ns) / (f64)(right_snapshot->sim_time_ns - left_snapshot->sim_time_ns);
|
||||
G.ss_blended = sim_snapshot_alloc_from_lerp(G.user_blended_client, left_snapshot, right_snapshot, blend);
|
||||
} else if (left_snapshot->valid) {
|
||||
G.ss_blended = sim_snapshot_alloc(G.user_blended_snapshots_client, left_snapshot, left_snapshot->tick);
|
||||
G.ss_blended = sim_snapshot_alloc(G.user_blended_client, left_snapshot, left_snapshot->tick);
|
||||
} else if (right_snapshot->valid) {
|
||||
G.ss_blended = sim_snapshot_alloc(G.user_blended_snapshots_client, right_snapshot, right_snapshot->tick);
|
||||
G.ss_blended = sim_snapshot_alloc(G.user_blended_client, right_snapshot, right_snapshot->tick);
|
||||
}
|
||||
#else
|
||||
/* Interp disabled, just copy latest snapshot */
|
||||
G.render_time_target_ns = newest_snapshot->real_time_ns;
|
||||
G.render_time_ns = newest_snapshot->real_time_ns;
|
||||
G.render_time_target_ns = newest_snapshot->sim_time_ns;
|
||||
G.render_time_ns = newest_snapshot->sim_time_ns;
|
||||
if (G.ss_blended->tick != newest_snapshot->tick) {
|
||||
if (G.ss_blended->valid) {
|
||||
sim_snapshot_release(G.ss_blended);
|
||||
}
|
||||
G.ss_blended = sim_snapshot_alloc(G.user_blended_snapshots_client, newest_snapshot, newest_snapshot->tick);
|
||||
G.ss_blended = sim_snapshot_alloc(G.user_blended_client, newest_snapshot, newest_snapshot->tick);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Release unneeded unblended sim snapshots */
|
||||
if (keep_unblended_tick > 0) {
|
||||
sim_snapshot_release_ticks_in_range(G.user_unblended_snapshots_client, 0, keep_unblended_tick - 1);
|
||||
sim_snapshot_release_ticks_in_range(G.user_unblended_client, 0, keep_unblended_tick - 1);
|
||||
}
|
||||
|
||||
/* Release unused blended snapshots */
|
||||
{
|
||||
struct sim_snapshot *ss = sim_snapshot_from_tick(G.user_blended_snapshots_client, G.user_blended_snapshots_client->first_tick);
|
||||
struct sim_snapshot *ss = sim_snapshot_from_tick(G.user_blended_client, G.user_blended_client->first_tick);
|
||||
while (ss->valid) {
|
||||
u64 next_tick = ss->next_tick;
|
||||
if (ss != G.ss_blended) {
|
||||
sim_snapshot_release(ss);
|
||||
}
|
||||
ss = sim_snapshot_from_tick(G.user_blended_snapshots_client, next_tick);
|
||||
ss = sim_snapshot_from_tick(G.user_blended_client, next_tick);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -648,7 +646,7 @@ INTERNAL void user_update(void)
|
||||
f32 shake = ent->shake;
|
||||
if (shake > 0) {
|
||||
u64 basis = hash_fnv64(HASH_FNV64_BASIS, STRING_FROM_STRUCT(&ent->handle));
|
||||
u64 angle_seed0 = basis + (u64)(G.ss_blended->world_time_ns / frequency_ns);
|
||||
u64 angle_seed0 = basis + (u64)(G.ss_blended->sim_time_ns / frequency_ns);
|
||||
u64 angle_seed1 = angle_seed0 + 1;
|
||||
f32 angle0 = rng_noise_f32(angle_seed0, 0, TAU);
|
||||
f32 angle1 = rng_noise_f32(angle_seed1, 0, TAU);
|
||||
@ -658,7 +656,7 @@ INTERNAL void user_update(void)
|
||||
struct v2 vec1 = v2_with_len(v2_from_angle(angle1), shake);
|
||||
|
||||
/* TODO: Cubic interp? */
|
||||
f32 blend = (f32)(G.ss_blended->world_time_ns % frequency_ns) / (f32)frequency_ns;
|
||||
f32 blend = (f32)(G.ss_blended->sim_time_ns % frequency_ns) / (f32)frequency_ns;
|
||||
struct v2 vec = v2_lerp(vec0, vec1, blend);
|
||||
|
||||
struct xform xf = sim_ent_get_xform(ent);
|
||||
@ -1402,10 +1400,10 @@ INTERNAL void user_update(void)
|
||||
struct bind_state spawn_state = G.bind_states[USER_BIND_KIND_DEBUG_SPAWN];
|
||||
|
||||
if (fire_state.num_presses || fire_state.is_held) {
|
||||
control.flags |= SIM_CONTROL_FLAG_FIRING;
|
||||
control.flags |= SIM_CONTROL_FLAG_FIRE;
|
||||
}
|
||||
if (drag_state.num_presses || drag_state.is_held) {
|
||||
control.flags |= SIM_CONTROL_FLAG_DRAGGING;
|
||||
control.flags |= SIM_CONTROL_FLAG_DRAG;
|
||||
}
|
||||
if (clear_state.num_presses) {
|
||||
control.flags |= SIM_CONTROL_FLAG_CLEAR_ALL;
|
||||
@ -1433,7 +1431,6 @@ INTERNAL void user_update(void)
|
||||
u32 old_flags = G.user_sim_cmd_control.flags;
|
||||
G.user_sim_cmd_control = control;
|
||||
G.user_sim_cmd_control.flags |= old_flags;
|
||||
G.user_sim_cmd_ack = G.local_sim_last_known_tick;
|
||||
sys_mutex_unlock(&lock);
|
||||
}
|
||||
}
|
||||
@ -1450,6 +1447,20 @@ INTERNAL void user_update(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
{
|
||||
/* Update network usage stats */
|
||||
i64 stat_now_ns = sys_time_ns();
|
||||
G.net_bytes_read.last_second_end = atomic_u64_eval(&app_statistics()->sock_bytes_received);
|
||||
G.net_bytes_sent.last_second_end = atomic_u64_eval(&app_statistics()->sock_bytes_sent);
|
||||
if (stat_now_ns - G.last_second_reset_ns > NS_FROM_SECONDS(1)) {
|
||||
G.last_second_reset_ns = stat_now_ns;
|
||||
G.net_bytes_read.last_second = G.net_bytes_read.last_second_end - G.net_bytes_read.last_second_start;
|
||||
G.net_bytes_sent.last_second = G.net_bytes_sent.last_second_end - G.net_bytes_sent.last_second_start;
|
||||
G.net_bytes_read.last_second_start = G.net_bytes_read.last_second_end;
|
||||
G.net_bytes_sent.last_second_start = G.net_bytes_sent.last_second_end;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
@ -1467,24 +1478,17 @@ INTERNAL void user_update(void)
|
||||
pos.y += spacing;
|
||||
#endif
|
||||
|
||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Read from host: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_read.last_second * 8 / 1000 / 1000, 3)));
|
||||
pos.y += spacing;
|
||||
|
||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Sent from host: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_sent.last_second * 8 / 1000 / 1000, 3)));
|
||||
pos.y += spacing;
|
||||
pos.y += spacing;
|
||||
|
||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("blended world entities: %F/%F"), FMT_UINT(G.ss_blended->num_ents_allocated), FMT_UINT(G.ss_blended->num_ents_reserved)));
|
||||
pos.y += spacing;
|
||||
|
||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("blended world tick: %F"), FMT_UINT(G.ss_blended->tick)));
|
||||
pos.y += spacing;
|
||||
|
||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("blended world time: %F"), FMT_FLOAT_P(SECONDS_FROM_NS(G.ss_blended->world_time_ns), 3)));
|
||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("blended world time: %F"), FMT_FLOAT_P(SECONDS_FROM_NS(G.ss_blended->sim_time_ns), 3)));
|
||||
pos.y += spacing;
|
||||
pos.y += spacing;
|
||||
|
||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("average local sim publish dt: %F"), FMT_FLOAT_P(SECONDS_FROM_NS(G.average_snapshot_publish_dt_ns), 3)));
|
||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("average local sim publish dt: %F"), FMT_FLOAT_P(SECONDS_FROM_NS(G.average_local_to_user_snapshot_publish_dt_ns), 3)));
|
||||
pos.y += spacing;
|
||||
|
||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("local sim last known tick: %F"), FMT_UINT(G.local_sim_last_known_tick)));
|
||||
@ -1503,6 +1507,13 @@ INTERNAL void user_update(void)
|
||||
pos.y += spacing;
|
||||
pos.y += spacing;
|
||||
|
||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Network read: %F mbit/s"), FMT_FLOAT_P((f64)G.net_bytes_read.last_second * 8 / 1000 / 1000, 3)));
|
||||
pos.y += spacing;
|
||||
|
||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Network write: %F mbit/s"), FMT_FLOAT_P((f64)G.net_bytes_sent.last_second * 8 / 1000 / 1000, 3)));
|
||||
pos.y += spacing;
|
||||
pos.y += spacing;
|
||||
|
||||
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Memory usage: %F MiB"), FMT_FLOAT_P((f64)atomic_u64_eval(&app_statistics()->memory_committed) / 1024 / 1024, 3)));
|
||||
pos.y += spacing;
|
||||
|
||||
@ -1696,6 +1707,155 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_thread_entry_point, arg)
|
||||
* Local sim thread
|
||||
* ========================== */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* TODO: Move this */
|
||||
|
||||
|
||||
#define translate(fieldname) local->fieldname = _translate(remote_lookup, old.fieldname, remote_client_handle, remote->fieldname)
|
||||
INTERNAL struct sim_ent_handle _translate(struct sim_lookup *remote_lookup, struct sim_ent_handle old_local_handle, struct sim_client_handle remote_client_handle, struct sim_ent_handle remote_ent_handle)
|
||||
{
|
||||
struct sim_lookup_key key = sim_lookup_key_from_client_and_ent_handles(remote_client_handle, remote_ent_handle);
|
||||
struct sim_lookup_entry *entry = sim_lookup_get(remote_lookup, key);
|
||||
return entry ? entry->ent : old_local_handle;
|
||||
}
|
||||
|
||||
|
||||
INTERNAL void sim_ent_sync_with_remote(struct sim_accel *accel, struct sim_ent *local, struct sim_ent *remote, struct sim_ent *client_ent)
|
||||
{
|
||||
struct sim_client_handle remote_client_handle = remote->ss->client->handle;
|
||||
struct sim_lookup *remote_lookup = &accel->remote_lookup;
|
||||
|
||||
struct sim_ent old = *local;
|
||||
MEMCPY_STRUCT(local, remote);
|
||||
|
||||
local->ss = old.ss;
|
||||
local->handle = old.handle;
|
||||
local->remote_client = remote_client_handle;
|
||||
local->remote_ent = remote->handle;
|
||||
sim_ent_enable_prop(local, SIM_ENT_PROP_REMOTE);
|
||||
sim_ent_set_xform(local, sim_ent_get_xform(remote));
|
||||
|
||||
/* Translate remote handles */
|
||||
translate(top);
|
||||
translate(parent);
|
||||
translate(next);
|
||||
translate(prev);
|
||||
translate(first);
|
||||
translate(last);
|
||||
translate(next_free);
|
||||
translate(client_control_ent);
|
||||
translate(client_camera_ent);
|
||||
translate(client_dbg_drag_joint_ent);
|
||||
translate(controlling_client);
|
||||
translate(move_joint);
|
||||
translate(aim_joint);
|
||||
translate(ground_friction_joint);
|
||||
translate(equipped);
|
||||
translate(bullet_src);
|
||||
translate(bullet_tracer);
|
||||
translate(camera_follow);
|
||||
|
||||
if (sim_ent_has_prop(local, SIM_ENT_PROP_CMD_CONTROL)) {
|
||||
local->cmd_client = client_ent->handle;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
INTERNAL void sim_snapshot_sync_with_remote(struct sim_accel *accel, struct sim_snapshot *local_ss, struct sim_snapshot *remote_ss)
|
||||
{
|
||||
__prof;
|
||||
|
||||
/* FIXME: Only sync cmds from non-master remote */
|
||||
struct sim_ent *local_root = sim_ent_from_handle(local_ss, SIM_ENT_ROOT_HANDLE);
|
||||
struct sim_client_handle remote_client_handle = remote_ss->client->handle;
|
||||
|
||||
struct sim_lookup *remote_lookup = &accel->remote_lookup;
|
||||
struct sim_lookup *client_lookup = &accel->client_lookup;
|
||||
|
||||
struct sim_ent *client_ent;
|
||||
{
|
||||
struct sim_lookup_key key = sim_lookup_key_from_client_handle(remote_client_handle);
|
||||
struct sim_lookup_entry *client_entry = sim_lookup_get(client_lookup, key);
|
||||
client_ent = client_entry ? sim_ent_from_handle(local_ss, client_entry->ent) : sim_ent_nil();
|
||||
}
|
||||
|
||||
|
||||
/* Build remote lookup table */
|
||||
for (u64 i = 0; i < local_ss->num_ents_reserved; ++i) {
|
||||
struct sim_ent *ent = &local_ss->ents[i];
|
||||
if (ent->valid && sim_ent_has_prop(ent, SIM_ENT_PROP_REMOTE) && sim_client_handle_eq(ent->remote_client, remote_client_handle)) {
|
||||
struct sim_lookup_key key = sim_lookup_key_from_client_and_ent_handles(remote_client_handle, ent->remote_ent);
|
||||
sim_lookup_set(remote_lookup, key, ent->handle);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create new ents from remote */
|
||||
/* Skipping root entity */
|
||||
for (u64 i = SIM_ENT_ROOT_HANDLE.idx + 1; i < remote_ss->num_ents_reserved; ++i) {
|
||||
struct sim_ent *remote_ent = &remote_ss->ents[i];
|
||||
if (remote_ent->valid) {
|
||||
struct sim_lookup_key key = sim_lookup_key_from_client_and_ent_handles(remote_client_handle, remote_ent->handle);
|
||||
struct sim_ent *local_ent = sim_ent_nil();
|
||||
{
|
||||
struct sim_lookup_entry *entry = sim_lookup_get(remote_lookup, key);
|
||||
if (entry) {
|
||||
local_ent = sim_ent_from_handle(local_ss, entry->ent);
|
||||
}
|
||||
}
|
||||
if (!local_ent->valid) {
|
||||
local_ent = sim_ent_alloc(local_root);
|
||||
sim_ent_enable_prop(local_ent, SIM_ENT_PROP_REMOTE);
|
||||
local_ent->remote_client = remote_client_handle;
|
||||
local_ent->remote_ent = remote_ent->handle;
|
||||
sim_lookup_set(remote_lookup, key, local_ent->handle);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Sync ents with remote */
|
||||
for (u64 i = 0; i < local_ss->num_ents_reserved; ++i) {
|
||||
struct sim_ent *local_ent = &local_ss->ents[i];
|
||||
if (local_ent->valid && sim_ent_has_prop(local_ent, SIM_ENT_PROP_REMOTE) && sim_client_handle_eq(local_ent->remote_client, remote_client_handle)) {
|
||||
struct sim_ent *remote_ent = sim_ent_from_handle(remote_ss, local_ent->remote_ent);
|
||||
if (remote_ent->valid) {
|
||||
/* Copy all ent data from remote */
|
||||
sim_ent_sync_with_remote(accel, local_ent, remote_ent, client_ent);
|
||||
} else {
|
||||
/* Remote ent is no longer valid / networked, release it */
|
||||
sim_ent_enable_prop(local_ent, SIM_ENT_PROP_RELEASE_THIS_TICK);
|
||||
sim_ent_disable_prop(local_ent, SIM_ENT_PROP_REMOTE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
||||
{
|
||||
#if 0
|
||||
@ -1723,9 +1883,10 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
||||
struct sim_accel accel = sim_accel_alloc();
|
||||
|
||||
struct sim_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();
|
||||
struct sim_client *user_input_client = sim_client_alloc(store); /* Stores snapshots containing commands to be published to both local & master clients */
|
||||
struct sim_client *local_client = sim_client_alloc(store); /* Stores snapshots produced locally */
|
||||
struct sim_client *publish_client = sim_client_alloc(store); /* Stores versions of local snapshots that will be published to remote sims */
|
||||
struct sim_client *master_client = sim_client_nil(); /* Stores snapshots received from master (if relevant) */
|
||||
|
||||
u64 step_tick = 0;
|
||||
i64 last_publish_ns = 0;
|
||||
@ -1733,6 +1894,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
||||
i64 step_dt_ns = NS_FROM_SECONDS(1) / SIM_TICKS_PER_SECOND;
|
||||
i64 compute_dt_ns = step_dt_ns;
|
||||
while (!atomic_i32_eval(&G.local_sim_thread_shutdown)) {
|
||||
__profscope(local_sim_loop);
|
||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||
{
|
||||
__profscope(local_sim_sleep);
|
||||
@ -1741,22 +1903,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
||||
}
|
||||
++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->producer_client = user_client->handle;
|
||||
user_cmd_ss->producer_client_is_local = true;
|
||||
user_cmd_ss->control = G.user_sim_cmd_control;
|
||||
user_client->ack = G.user_sim_cmd_ack;
|
||||
user_client->reverse_ack = step_tick;
|
||||
++G.user_sim_cmd_gen;
|
||||
sys_mutex_unlock(&lock);
|
||||
}
|
||||
|
||||
/* Read net snapshots */
|
||||
/* Read net messages */
|
||||
{
|
||||
host_update(host);
|
||||
struct host_event_array host_events = host_pop_events(scratch.arena, host);
|
||||
@ -1767,16 +1914,14 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
||||
switch (event->kind) {
|
||||
case HOST_EVENT_KIND_CHANNEL_OPENED:
|
||||
{
|
||||
/* Create sim client */
|
||||
/* Create remote 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);
|
||||
client = sim_client_alloc(store);
|
||||
sim_client_set_channel_id(client, channel_id);
|
||||
if (!is_master) {
|
||||
master_client = client;
|
||||
}
|
||||
sim_client_set_channel_id(client, channel_id);
|
||||
}
|
||||
} break;
|
||||
|
||||
@ -1787,57 +1932,39 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
||||
struct bitbuff_reader msg_br = br_from_bitbuff(&msg_bb);
|
||||
|
||||
u64 ack = br_read_uv(&msg_br);
|
||||
u64 reverse_ack = br_read_uv(&msg_br);
|
||||
u64 double_ack = br_read_uv(&msg_br);
|
||||
if (ack > client->ack) {
|
||||
client->ack = ack;
|
||||
}
|
||||
if (reverse_ack > client->reverse_ack) {
|
||||
client->reverse_ack = reverse_ack;
|
||||
if (double_ack > client->double_ack) {
|
||||
client->double_ack = double_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);
|
||||
u64 tmp_encoded_len = br_read_uv(&msg_br);
|
||||
u8 *tmp_encoded_bytes = br_read_bytes_raw(&msg_br, tmp_encoded_len);
|
||||
if (!tmp_encoded_bytes) tmp_encoded_len = 0;
|
||||
while (tmp_encoded_len > 0) {
|
||||
struct bitbuff decoder_bb = bitbuff_from_string(STRING(tmp_encoded_len, tmp_encoded_bytes));
|
||||
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) {
|
||||
if (client == master_client || (tick == client->last_tick + 1 || tick > client->last_tick + 100)) {
|
||||
/* Alloc & decode snapshot */
|
||||
struct sim_snapshot *ss = sim_snapshot_alloc(client, base_ss, tick);
|
||||
sim_snapshot_decode(&decoder_br, ss);
|
||||
ss->producer_client = client->handle;
|
||||
}
|
||||
} else {
|
||||
/* We do not have the base tick that the incoming tick is delta encoded from */
|
||||
/* We do not have the tick that the incoming delta is based 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);
|
||||
ss->producer_client = client->handle;
|
||||
} else {
|
||||
/* We do not have the base tick that the incoming tick is delta encoded from */
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
encoded_tmp.len = br_read_uv(&msg_br);
|
||||
encoded_tmp.text = br_read_bytes_raw(&msg_br, encoded_tmp.len);
|
||||
if (!encoded_tmp.text) {
|
||||
encoded_tmp.len = 0;
|
||||
}
|
||||
tmp_encoded_len = br_read_uv(&msg_br);
|
||||
tmp_encoded_bytes = br_read_bytes_raw(&msg_br, tmp_encoded_len);
|
||||
if (!tmp_encoded_bytes) tmp_encoded_len = 0;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
@ -1847,7 +1974,89 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
||||
}
|
||||
}
|
||||
|
||||
/* Create user input */
|
||||
{
|
||||
struct sim_snapshot *prev_user_input_ss = sim_snapshot_from_tick(user_input_client, user_input_client->last_tick);
|
||||
struct sim_snapshot *user_input_ss = sim_snapshot_alloc(user_input_client, prev_user_input_ss, step_tick);
|
||||
struct sim_ent *user_input_root = sim_ent_from_handle(user_input_ss, SIM_ENT_ROOT_HANDLE);
|
||||
/* Find / create local control cmd ent */
|
||||
struct sim_ent *control_cmd_ent = sim_ent_find_first_match_one(user_input_ss, SIM_ENT_PROP_CMD_CONTROL);
|
||||
if (!control_cmd_ent->valid) {
|
||||
control_cmd_ent = sim_ent_alloc(user_input_root);
|
||||
sim_ent_enable_prop(control_cmd_ent, SIM_ENT_PROP_CMD_CONTROL);
|
||||
sim_ent_activate(control_cmd_ent, user_input_ss->tick);
|
||||
}
|
||||
/* Update local control cmd ent */
|
||||
{
|
||||
struct sys_lock lock = sys_mutex_lock_e(&G.user_sim_cmd_mutex);
|
||||
control_cmd_ent->cmd_control = G.user_sim_cmd_control;
|
||||
++G.user_sim_cmd_gen;
|
||||
sys_mutex_unlock(&lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate local snapshot */
|
||||
struct sim_snapshot *prev_local_ss = sim_snapshot_from_tick(local_client, local_client->last_tick);
|
||||
struct sim_snapshot *local_ss = sim_snapshot_alloc(local_client, prev_local_ss, step_tick);
|
||||
|
||||
/* Rebuild acceleration tables */
|
||||
sim_accel_rebuild(local_ss, &accel);
|
||||
|
||||
/* Sync clients */
|
||||
{
|
||||
struct sim_ent *local_root = sim_ent_from_handle(local_ss, SIM_ENT_ROOT_HANDLE);
|
||||
for (u64 client_index = 0; client_index < store->num_clients_reserved; ++client_index) {
|
||||
struct sim_client *client = &store->clients[client_index];
|
||||
if (client->valid) {
|
||||
/* Create client ent if necessary */
|
||||
if (is_master && client != publish_client && client != local_client && client != master_client) {
|
||||
struct sim_lookup_key key = sim_lookup_key_from_client_handle(client->handle);
|
||||
struct sim_lookup_entry *client_entry = sim_lookup_get(&accel.client_lookup, key);
|
||||
struct sim_ent *client_ent = client_entry ? sim_ent_from_handle(local_ss, client_entry->ent) : sim_ent_nil();
|
||||
if (!client_ent->valid) {
|
||||
/* FIXME: Client ent never released upon disconnect */
|
||||
client_ent = sim_ent_alloc(local_root);
|
||||
client_ent->client_handle = client->handle;
|
||||
sim_ent_enable_prop(client_ent, SIM_ENT_PROP_CLIENT);
|
||||
sim_ent_enable_prop(client_ent, SIM_ENT_PROP_ACTIVE);
|
||||
sim_lookup_set(&accel.client_lookup, key, client_ent->handle);
|
||||
if (client == user_input_client) {
|
||||
local_ss->local_client = user_input_client->handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Sync w/ client */
|
||||
if (client != publish_client && client != local_client) {
|
||||
struct sim_snapshot *client_ss = sim_snapshot_from_tick(client, step_tick);
|
||||
if (client_ss->valid) {
|
||||
sim_snapshot_sync_with_remote(&accel, local_ss, client_ss);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Release unneeded snapshots */
|
||||
for (u64 i = 0; i < store->num_clients_reserved; ++i) {
|
||||
struct sim_client *client = &store->clients[i];
|
||||
if (client->valid) {
|
||||
u64 keep_count = 100;
|
||||
if (client->last_tick > keep_count) {
|
||||
u64 keep_tick = client->last_tick - keep_count;
|
||||
sim_snapshot_release_ticks_in_range(client, 0, keep_tick - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Release unneeded snapshots */
|
||||
#if 0
|
||||
#if 0
|
||||
u64 oldest_client_ack = 0;
|
||||
for (u64 i = 0; i < store->num_clients_reserved; ++i) {
|
||||
@ -1855,9 +2064,9 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
||||
enum sim_client_kind kind = client->kind;
|
||||
if (client->valid && kind != SIM_CLIENT_KIND_LOCAL_SIM) {
|
||||
u64 ack = client->ack;
|
||||
u64 reverse_ack = client->reverse_ack;
|
||||
if (reverse_ack > 1) {
|
||||
sim_snapshot_release_ticks_in_range(client, 0, reverse_ack - 1);
|
||||
u64 double_ack = client->double_ack;
|
||||
if (double_ack > 1) {
|
||||
sim_snapshot_release_ticks_in_range(client, 0, double_ack - 1);
|
||||
}
|
||||
if (ack < oldest_client_ack || oldest_client_ack == 0) {
|
||||
oldest_client_ack = ack;
|
||||
@ -1870,184 +2079,127 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
||||
#else
|
||||
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 reverse_ack = client->reverse_ack;
|
||||
if (reverse_ack > 1) {
|
||||
sim_snapshot_release_ticks_in_range(client, 0, reverse_ack - 1);
|
||||
if (client->valid) {
|
||||
if (client->is_remote) {
|
||||
u64 double_ack = client->double_ack;
|
||||
if (double_ack > 1) {
|
||||
sim_snapshot_release_ticks_in_range(client, 0, double_ack - 1);
|
||||
}
|
||||
} else {
|
||||
u64 keep_ticks_count = 500;
|
||||
if (step_tick > keep_ticks_count) {
|
||||
u64 keep_tick = client->last_tick - keep_ticks_count;
|
||||
sim_snapshot_release_ticks_in_range(client, 0, keep_tick);
|
||||
}
|
||||
}
|
||||
}
|
||||
u64 local_keep_ticks_count = 100;
|
||||
if (step_tick > local_keep_ticks_count) {
|
||||
u64 local_keep_tick = step_tick - local_keep_ticks_count;
|
||||
sim_snapshot_release_ticks_in_range(local_client, 0, local_keep_tick);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Pick client snapshot cmds to feed to sim step */
|
||||
struct sim_snapshot_list client_step_cmds = ZI;
|
||||
for (u64 i = 0; i < store->num_clients_reserved; ++i) {
|
||||
struct sim_client *client = &store->clients[i];
|
||||
enum sim_client_kind kind = client->kind;
|
||||
if (client->valid && kind != SIM_CLIENT_KIND_LOCAL_SIM && kind != SIM_CLIENT_KIND_MASTER_SIM) {
|
||||
struct sim_snapshot *cmd_snapshot = sim_snapshot_from_closest_tick_lte(client, step_tick);
|
||||
struct sim_snapshot_list_node *n = arena_push_zero(scratch.arena, struct sim_snapshot_list_node);
|
||||
n->ss = cmd_snapshot;
|
||||
if (client_step_cmds.last) {
|
||||
client_step_cmds.last->next = n;
|
||||
} else {
|
||||
client_step_cmds.first = n;
|
||||
}
|
||||
client_step_cmds.last = n;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create step snapshot */
|
||||
struct sim_snapshot *step_ss;
|
||||
if (is_master) {
|
||||
struct sim_snapshot *prev_step_ss = sim_snapshot_from_tick(local_client, step_tick - 1);
|
||||
step_ss = sim_snapshot_alloc(local_client, prev_step_ss, step_tick);
|
||||
step_ss->is_master = true;
|
||||
} else {
|
||||
struct sim_snapshot *newest_master_ss = sim_snapshot_from_tick(master_client, master_client->last_tick);
|
||||
|
||||
struct sim_snapshot *local_copy = sim_snapshot_from_tick(local_client, newest_master_ss->tick);
|
||||
if (!local_copy->valid) {
|
||||
sim_snapshot_alloc(local_client, newest_master_ss, newest_master_ss->tick);
|
||||
}
|
||||
|
||||
step_ss = sim_snapshot_from_tick(local_client, step_tick);
|
||||
if (!step_ss->valid) {
|
||||
step_ss = sim_snapshot_from_tick(local_client, 1);
|
||||
if (!step_ss->valid) {
|
||||
step_ss = sim_snapshot_alloc(local_client, sim_snapshot_nil(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
step_ss->is_master = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Step */
|
||||
{
|
||||
struct sim_step_ctx step_ctx = ZI;
|
||||
step_ctx.is_master = is_master;
|
||||
step_ctx.accel = &accel;
|
||||
step_ctx.world = step_ss;
|
||||
step_ctx.cmd_snapshots = &client_step_cmds;
|
||||
step_ctx.real_dt_ns = step_dt_ns;
|
||||
step_ctx.world = local_ss;
|
||||
step_ctx.sim_dt_ns = step_dt_ns;
|
||||
sim_step(&step_ctx);
|
||||
}
|
||||
|
||||
/* Publish snapshot to clients */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Construct publishable snapshot */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Publish snapshot to remote clients */
|
||||
for (u64 i = 0; i < store->num_clients_reserved; ++i) {
|
||||
struct sim_client *client = &store->clients[i];
|
||||
if (client->valid && client != user_input_client && client != local_client && client != publish_client) {
|
||||
struct bitbuff_writer msg_bw = bw_from_bitbuff(&msg_writer_bb);
|
||||
if (client->valid) {
|
||||
switch (client->kind) {
|
||||
case SIM_CLIENT_KIND_SLAVE_SIM:
|
||||
{
|
||||
struct sim_snapshot *base_ss = sim_snapshot_from_tick(local_client, client->ack);
|
||||
|
||||
/* Encode single master state sanpshot snapshot */
|
||||
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, client->last_tick); /* ack */
|
||||
bw_write_uv(&msg_bw, client->ack); /* double ack */
|
||||
|
||||
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);
|
||||
|
||||
struct string encoded = ZI;
|
||||
encoded.len = bw_num_bytes_written(&msg_bw);
|
||||
encoded.text = bw_get_written_raw(&msg_bw);
|
||||
host_queue_write(host, client->channel_id, encoded, 0);
|
||||
} break;
|
||||
|
||||
case SIM_CLIENT_KIND_MASTER_SIM:
|
||||
{
|
||||
u64 base_tick = client->last_tick;
|
||||
struct sim_snapshot *base_ss = sim_snapshot_from_tick(client, base_tick);
|
||||
if (base_ss->tick == base_tick) {
|
||||
u64 ack = client->last_tick;
|
||||
u64 reverse_ack = client->ack;
|
||||
bw_write_uv(&msg_bw, ack);
|
||||
bw_write_uv(&msg_bw, reverse_ack);
|
||||
/* Write all user cmd snapshots since last client ack */
|
||||
struct sim_snapshot *send_ss = sim_snapshot_from_tick(local_client, base_ss->tick + 1);
|
||||
while (send_ss->valid) {
|
||||
struct bitbuff_writer snapshot_bw = bw_from_bitbuff(&snapshot_writer_bb);
|
||||
bw_write_uv(&snapshot_bw, base_ss->tick);
|
||||
bw_write_uv(&snapshot_bw, step_ss->tick);
|
||||
sim_snapshot_encode(&snapshot_bw, client, base_ss, step_ss);
|
||||
|
||||
struct string encoded_tmp = ZI;
|
||||
encoded_tmp.len = bw_num_bytes_written(&snapshot_bw);
|
||||
encoded_tmp.text = bw_get_written_raw(&snapshot_bw);
|
||||
|
||||
bw_write_uv(&msg_bw, encoded_tmp.len);
|
||||
bw_write_bytes(&msg_bw, encoded_tmp);
|
||||
|
||||
send_ss = sim_snapshot_from_tick(local_client, send_ss->tick + 1);
|
||||
}
|
||||
bw_write_uv(&msg_bw, 0);
|
||||
|
||||
struct string encoded = ZI;
|
||||
encoded.len = bw_num_bytes_written(&msg_bw);
|
||||
encoded.text = bw_get_written_raw(&msg_bw);
|
||||
host_queue_write(host, client->channel_id, encoded, 0);
|
||||
struct sim_snapshot *base_ss = sim_snapshot_from_tick(publish_client, client->ack);
|
||||
struct sim_snapshot *publish_ss;
|
||||
if (client == master_client) {
|
||||
/* If sending to master, start sending all snapshots since last ack */
|
||||
publish_ss = sim_snapshot_from_tick(publish_client, base_ss->tick + 1);
|
||||
} else {
|
||||
/* We do not have the client's ack tick to delta encode from */
|
||||
ASSERT(false);
|
||||
/* If sending to slave, only send latest snapshot */
|
||||
publish_ss = sim_snapshot_from_tick(publish_client, publish_client->last_tick);
|
||||
}
|
||||
} break;
|
||||
|
||||
case SIM_CLIENT_KIND_USER:
|
||||
while (publish_ss->valid) {
|
||||
struct bitbuff_writer snapshot_bw = bw_from_bitbuff(&snapshot_writer_bb);
|
||||
struct string tmp_snapshot_encoded = ZI;
|
||||
{
|
||||
bw_write_uv(&snapshot_bw, base_ss->tick);
|
||||
bw_write_uv(&snapshot_bw, publish_ss->tick);
|
||||
sim_snapshot_encode(&snapshot_bw, client, base_ss, publish_ss);
|
||||
tmp_snapshot_encoded.len = bw_num_bytes_written(&snapshot_bw);
|
||||
tmp_snapshot_encoded.text = bw_get_written_raw(&snapshot_bw);
|
||||
}
|
||||
bw_write_uv(&msg_bw, tmp_snapshot_encoded.len);
|
||||
bw_write_bytes(&msg_bw, tmp_snapshot_encoded);
|
||||
publish_ss = sim_snapshot_from_tick(publish_client, publish_ss->tick + 1);
|
||||
}
|
||||
bw_write_uv(&msg_bw, 0);
|
||||
|
||||
struct string encoded = ZI;
|
||||
encoded.len = bw_num_bytes_written(&msg_bw);
|
||||
encoded.text = bw_get_written_raw(&msg_bw);
|
||||
host_queue_write(host, client->channel_id, encoded, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy local snapshot to user client */
|
||||
{
|
||||
/* TODO: Double buffer */
|
||||
struct sys_lock lock = sys_mutex_lock_e(&G.local_sim_to_user_mutex);
|
||||
struct sim_snapshot *pub_ss = sim_snapshot_alloc(G.local_sim_to_user_client, step_ss, step_ss->tick);
|
||||
if (is_master) {
|
||||
pub_ss->local_client = client->handle;
|
||||
}
|
||||
struct sys_lock lock = sys_mutex_lock_e(&G.local_to_user_client_mutex);
|
||||
struct sim_snapshot *pub_ss = sim_snapshot_alloc(G.local_to_user_client, local_ss, local_ss->tick);
|
||||
pub_ss->local_client = local_ss->local_client;
|
||||
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;
|
||||
sim_snapshot_release_ticks_in_range(G.local_sim_to_user_client, 0, step_ss->tick - 1);
|
||||
sim_snapshot_release_ticks_in_range(G.local_to_user_client, 0, local_ss->tick - 1);
|
||||
sys_mutex_unlock(&lock);
|
||||
} break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Send host messages */
|
||||
host_update(host);
|
||||
__profframe("Local sim");
|
||||
|
||||
{
|
||||
/* Update network usage stats */
|
||||
i64 stat_now_ns = sys_time_ns();
|
||||
G.client_bytes_read.last_second_end = host->bytes_received;
|
||||
G.client_bytes_sent.last_second_end = host->bytes_sent;
|
||||
if (stat_now_ns - G.last_second_reset_ns > NS_FROM_SECONDS(1)) {
|
||||
G.last_second_reset_ns = stat_now_ns;
|
||||
G.client_bytes_read.last_second = G.client_bytes_read.last_second_end - G.client_bytes_read.last_second_start;
|
||||
G.client_bytes_sent.last_second = G.client_bytes_sent.last_second_end - G.client_bytes_sent.last_second_start;
|
||||
G.client_bytes_read.last_second_start = G.client_bytes_read.last_second_end;
|
||||
G.client_bytes_sent.last_second_start = G.client_bytes_sent.last_second_end;
|
||||
}
|
||||
}
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user