sim snasphot store -> client refactor
This commit is contained in:
parent
044fc1db9d
commit
e0dee3e9e8
@ -6,7 +6,7 @@
|
|||||||
#include "work.h"
|
#include "work.h"
|
||||||
#include "user.h"
|
#include "user.h"
|
||||||
#include "sim.h"
|
#include "sim.h"
|
||||||
#include "sim_snapshot.h"
|
#include "sim.h"
|
||||||
#include "playback.h"
|
#include "playback.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
@ -25,6 +25,7 @@
|
|||||||
#include "rng.h"
|
#include "rng.h"
|
||||||
#include "sock.h"
|
#include "sock.h"
|
||||||
#include "host.h"
|
#include "host.h"
|
||||||
|
#include "bitbuff.h"
|
||||||
|
|
||||||
struct exit_callback {
|
struct exit_callback {
|
||||||
app_exit_callback_func *func;
|
app_exit_callback_func *func;
|
||||||
@ -235,8 +236,8 @@ void app_entry_point(struct string args_str)
|
|||||||
struct sound_startup_receipt sound_sr = sound_startup(&work_sr, &asset_cache_sr, &resource_sr);
|
struct sound_startup_receipt sound_sr = sound_startup(&work_sr, &asset_cache_sr, &resource_sr);
|
||||||
struct draw_startup_receipt draw_sr = draw_startup(&renderer_sr, &font_sr);
|
struct draw_startup_receipt draw_sr = draw_startup(&renderer_sr, &font_sr);
|
||||||
struct phys_startup_receipt phys_sr = phys_startup();
|
struct phys_startup_receipt phys_sr = phys_startup();
|
||||||
struct sim_snapshot_startup_receipt sim_snapshot_sr = sim_snapshot_startup();
|
struct sim_startup_receipt sim_sr = sim_startup();
|
||||||
struct user_startup_receipt user_sr = user_startup(&work_sr, &renderer_sr, &font_sr, &sprite_sr, &draw_sr, &asset_cache_sr, &sound_sr, &mixer_sr, &phys_sr, &host_sr, &sim_snapshot_sr, args_str, &window);
|
struct user_startup_receipt user_sr = user_startup(&work_sr, &renderer_sr, &font_sr, &sprite_sr, &draw_sr, &asset_cache_sr, &sound_sr, &mixer_sr, &phys_sr, &host_sr, &sim_sr, args_str, &window);
|
||||||
struct playback_startup_receipt playback_sr = playback_startup(&mixer_sr);
|
struct playback_startup_receipt playback_sr = playback_startup(&mixer_sr);
|
||||||
|
|
||||||
(UNUSED)user_sr;
|
(UNUSED)user_sr;
|
||||||
|
|||||||
@ -140,16 +140,19 @@ struct bitbuff_writer bw_from_bitbuff_no_debug(struct bitbuff *bb)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FIXME: Handle overflowed bw */
|
||||||
u64 bw_num_bits_written(struct bitbuff_writer *bw)
|
u64 bw_num_bits_written(struct bitbuff_writer *bw)
|
||||||
{
|
{
|
||||||
return bw->cur_bit;
|
return bw->cur_bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FIXME: Handle overflowed bw */
|
||||||
u64 bw_num_bytes_written(struct bitbuff_writer *bw)
|
u64 bw_num_bytes_written(struct bitbuff_writer *bw)
|
||||||
{
|
{
|
||||||
return (bw->cur_bit + 7) >> 3;
|
return (bw->cur_bit + 7) >> 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FIXME: Handle overflowed bw */
|
||||||
struct string bw_get_written(struct arena *arena, struct bitbuff_writer *bw)
|
struct string bw_get_written(struct arena *arena, struct bitbuff_writer *bw)
|
||||||
{
|
{
|
||||||
struct string res = ZI;
|
struct string res = ZI;
|
||||||
@ -159,6 +162,12 @@ struct string bw_get_written(struct arena *arena, struct bitbuff_writer *bw)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FIXME: Handle overflowed bw */
|
||||||
|
u8 *bw_get_written_raw(struct bitbuff_writer *bw)
|
||||||
|
{
|
||||||
|
return bw->base;
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns true if num_bits would cause the writer to overflow its fixed buffer size (if writer is not backed by a dynamic arena bitbuff) */
|
/* Returns true if num_bits would cause the writer to overflow its fixed buffer size (if writer is not backed by a dynamic arena bitbuff) */
|
||||||
b32 bw_check_overflow_bits(struct bitbuff_writer *bw, u64 num_bits)
|
b32 bw_check_overflow_bits(struct bitbuff_writer *bw, u64 num_bits)
|
||||||
{
|
{
|
||||||
@ -374,24 +383,28 @@ struct bitbuff_reader br_from_bitbuff_no_debug(struct bitbuff *bb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the number of bits read from the bitbuff */
|
/* Returns the number of bits read from the bitbuff */
|
||||||
|
/* FIXME: Handle overflowed br */
|
||||||
u64 br_cur_bit(struct bitbuff_reader *br)
|
u64 br_cur_bit(struct bitbuff_reader *br)
|
||||||
{
|
{
|
||||||
return br->cur_bit;
|
return br->cur_bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the number of *full* bytes read from the bitbuff */
|
/* Returns the number of *full* bytes read from the bitbuff */
|
||||||
|
/* FIXME: Handle overflowed br */
|
||||||
u64 br_cur_byte(struct bitbuff_reader *br)
|
u64 br_cur_byte(struct bitbuff_reader *br)
|
||||||
{
|
{
|
||||||
return br->cur_bit >> 3;
|
return br->cur_bit >> 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the number of bits left until the bitbuff overflows */
|
/* Returns the number of bits left until the bitbuff overflows */
|
||||||
|
/* FIXME: Handle overflowed br */
|
||||||
u64 br_num_bits_left(struct bitbuff_reader *br)
|
u64 br_num_bits_left(struct bitbuff_reader *br)
|
||||||
{
|
{
|
||||||
return (br->base_len << 3) - br->cur_bit;
|
return (br->base_len << 3) - br->cur_bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the number of *full* bytes left until the bitbuff overflows */
|
/* Returns the number of *full* bytes left until the bitbuff overflows */
|
||||||
|
/* FIXME: Handle overflowed br */
|
||||||
u64 br_num_bytes_left(struct bitbuff_reader *br)
|
u64 br_num_bytes_left(struct bitbuff_reader *br)
|
||||||
{
|
{
|
||||||
return br->base_len - (br->cur_bit >> 3);
|
return br->base_len - (br->cur_bit >> 3);
|
||||||
|
|||||||
@ -42,6 +42,7 @@ u64 bw_num_bits_written(struct bitbuff_writer *bw);
|
|||||||
u64 bw_num_bytes_written(struct bitbuff_writer *bw);
|
u64 bw_num_bytes_written(struct bitbuff_writer *bw);
|
||||||
|
|
||||||
struct string bw_get_written(struct arena *arena, struct bitbuff_writer *bw);
|
struct string bw_get_written(struct arena *arena, struct bitbuff_writer *bw);
|
||||||
|
u8 *bw_get_written_raw(struct bitbuff_writer *bw);
|
||||||
|
|
||||||
b32 bw_check_overflow_bits(struct bitbuff_writer *bw, u64 num_bits);
|
b32 bw_check_overflow_bits(struct bitbuff_writer *bw, u64 num_bits);
|
||||||
|
|
||||||
|
|||||||
107
src/phys.c
107
src/phys.c
@ -1,6 +1,6 @@
|
|||||||
#include "phys.h"
|
#include "phys.h"
|
||||||
#include "sim_snapshot.h"
|
|
||||||
#include "sim_ent.h"
|
#include "sim_ent.h"
|
||||||
|
#include "sim_step.h"
|
||||||
#include "math.h"
|
#include "math.h"
|
||||||
#include "scratch.h"
|
#include "scratch.h"
|
||||||
#include "space.h"
|
#include "space.h"
|
||||||
@ -39,16 +39,16 @@ struct phys_startup_receipt phys_startup(void)
|
|||||||
* Contact
|
* Contact
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
struct phys_collision_data_array phys_create_and_update_contacts(struct arena *arena, struct phys_ctx *ctx, f32 elapsed_dt, u64 phys_iteration)
|
struct phys_collision_data_array phys_create_and_update_contacts(struct arena *arena, struct phys_step_ctx *ctx, f32 elapsed_dt, u64 phys_iteration)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct phys_collision_data_array res = ZI;
|
struct phys_collision_data_array res = ZI;
|
||||||
res.a = arena_dry_push(arena, struct phys_collision_data);
|
res.a = arena_dry_push(arena, struct phys_collision_data);
|
||||||
struct sim_snapshot *ss = ctx->ss;
|
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
|
||||||
struct space *space = ctx->space;
|
struct space *space = ctx->sim_step_ctx->accel->space;
|
||||||
struct sim_ent_lookup *contact_lookup = ctx->contact_lookup;
|
struct sim_lookup *contact_lookup = &ctx->sim_step_ctx->accel->contact_lookup;
|
||||||
#if COLLIDER_DEBUG
|
#if COLLIDER_DEBUG
|
||||||
struct sim_ent_lookup *debug_lookup = ctx->collision_debug_lookup;
|
struct sim_lookup *debug_lookup = ctx->collision_debug_lookup;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct sim_ent *root = sim_ent_from_handle(ss, SIM_ENT_ROOT_HANDLE);
|
struct sim_ent *root = sim_ent_from_handle(ss, SIM_ENT_ROOT_HANDLE);
|
||||||
@ -96,8 +96,8 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
|
|||||||
e1_collider = check0_collider;
|
e1_collider = check0_collider;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sim_ent_lookup_key key = sim_ent_lookup_key_from_two_handles(e0->handle, e1->handle);
|
struct sim_lookup_key key = sim_lookup_key_from_two_handles(e0->handle, e1->handle);
|
||||||
struct sim_ent_lookup_entry *constraint_entry = sim_ent_lookup_get(contact_lookup, key);
|
struct sim_lookup_entry *constraint_entry = sim_lookup_get(contact_lookup, key);
|
||||||
|
|
||||||
struct sim_ent *constraint_ent = sim_ent_nil();
|
struct sim_ent *constraint_ent = sim_ent_nil();
|
||||||
if (constraint_entry) {
|
if (constraint_entry) {
|
||||||
@ -111,7 +111,7 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Constraint ent no longer valid, delete constraint_entry*/
|
/* Constraint ent no longer valid, delete constraint_entry*/
|
||||||
sim_ent_lookup_remove(contact_lookup, constraint_entry);
|
sim_lookup_remove(contact_lookup, constraint_entry);
|
||||||
constraint_entry= NULL;
|
constraint_entry= NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,7 +139,7 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
|
|||||||
sim_ent_enable_prop(constraint_ent, SIM_ENT_PROP_CONTACT_CONSTRAINT);
|
sim_ent_enable_prop(constraint_ent, SIM_ENT_PROP_CONTACT_CONSTRAINT);
|
||||||
sim_ent_activate(constraint_ent, tick);
|
sim_ent_activate(constraint_ent, tick);
|
||||||
ASSERT(!constraint_entry); /* Existing entry should never be present here */
|
ASSERT(!constraint_entry); /* Existing entry should never be present here */
|
||||||
sim_ent_lookup_set(contact_lookup, key, constraint_ent->handle);
|
sim_lookup_set(contact_lookup, key, constraint_ent->handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Push collision data */
|
/* Push collision data */
|
||||||
@ -238,7 +238,7 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
|
|||||||
#if COLLIDER_DEBUG && COLLIDER_DEBUG_DETAILED
|
#if COLLIDER_DEBUG && COLLIDER_DEBUG_DETAILED
|
||||||
{
|
{
|
||||||
struct sim_ent *dbg_ent = sim_ent_nil();
|
struct sim_ent *dbg_ent = sim_ent_nil();
|
||||||
struct sim_ent_lookup_entry *dbg_entry = sim_ent_lookup_get(debug_lookup, key);
|
struct sim_lookup_entry *dbg_entry = sim_lookup_get(debug_lookup, key);
|
||||||
if (dbg_entry) {
|
if (dbg_entry) {
|
||||||
dbg_ent = sim_ent_from_handle(ss, dbg_entry->entity);
|
dbg_ent = sim_ent_from_handle(ss, dbg_entry->entity);
|
||||||
}
|
}
|
||||||
@ -247,7 +247,7 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
|
|||||||
/* FIXME: Entity never released */
|
/* FIXME: Entity never released */
|
||||||
dbg_ent = sim_ent_alloc(root);
|
dbg_ent = sim_ent_alloc(root);
|
||||||
sim_ent_enable_prop(dbg_ent, SIM_ENT_PROP_COLLISION_DEBUG);
|
sim_ent_enable_prop(dbg_ent, SIM_ENT_PROP_COLLISION_DEBUG);
|
||||||
sim_ent_lookup_set(debug_lookup, key, dbg_ent->handle);
|
sim_lookup_set(debug_lookup, key, dbg_ent->handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct phys_collision_debug *dbg = &dbg_ent->collision_debug_data;
|
struct phys_collision_debug *dbg = &dbg_ent->collision_debug_data;
|
||||||
@ -280,11 +280,11 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void phys_prepare_contacts(struct phys_ctx *ctx, u64 phys_iteration)
|
void phys_prepare_contacts(struct phys_step_ctx *ctx, u64 phys_iteration)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct sim_snapshot *ss = ctx->ss;
|
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
|
||||||
struct sim_ent_lookup *contact_lookup = ctx->contact_lookup;
|
struct sim_lookup *contact_lookup = &ctx->sim_step_ctx->accel->contact_lookup;
|
||||||
|
|
||||||
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
||||||
struct sim_ent *constraint_ent = &ss->ents[sim_ent_index];
|
struct sim_ent *constraint_ent = &ss->ents[sim_ent_index];
|
||||||
@ -365,10 +365,10 @@ void phys_prepare_contacts(struct phys_ctx *ctx, u64 phys_iteration)
|
|||||||
sim_ent_disable_prop(constraint_ent, SIM_ENT_PROP_ACTIVE);
|
sim_ent_disable_prop(constraint_ent, SIM_ENT_PROP_ACTIVE);
|
||||||
sim_ent_enable_prop(constraint_ent, SIM_ENT_PROP_RELEASE_THIS_TICK);
|
sim_ent_enable_prop(constraint_ent, SIM_ENT_PROP_RELEASE_THIS_TICK);
|
||||||
/* Remove from lookup */
|
/* Remove from lookup */
|
||||||
struct sim_ent_lookup_key key = sim_ent_lookup_key_from_two_handles(constraint->e0, constraint->e1);
|
struct sim_lookup_key key = sim_lookup_key_from_two_handles(constraint->e0, constraint->e1);
|
||||||
struct sim_ent_lookup_entry *entry = sim_ent_lookup_get(contact_lookup, key);
|
struct sim_lookup_entry *entry = sim_lookup_get(contact_lookup, key);
|
||||||
if (entry) {
|
if (entry) {
|
||||||
sim_ent_lookup_remove(contact_lookup, entry);
|
sim_lookup_remove(contact_lookup, entry);
|
||||||
} else {
|
} else {
|
||||||
ASSERT(false); /* This should always exist */
|
ASSERT(false); /* This should always exist */
|
||||||
}
|
}
|
||||||
@ -376,7 +376,7 @@ void phys_prepare_contacts(struct phys_ctx *ctx, u64 phys_iteration)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if COLLIDER_DEBUG
|
#if COLLIDER_DEBUG
|
||||||
struct sim_ent_lookup *debug_lookup = ctx->debug_lookup;
|
struct sim_lookup *debug_lookup = ctx->debug_lookup;
|
||||||
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
||||||
struct sim_ent *dbg_ent = &ss->ents[sim_ent_index];
|
struct sim_ent *dbg_ent = &ss->ents[sim_ent_index];
|
||||||
if (!sim_ent_is_valid_and_active(dbg_ent)) continue;
|
if (!sim_ent_is_valid_and_active(dbg_ent)) continue;
|
||||||
@ -395,11 +395,11 @@ void phys_prepare_contacts(struct phys_ctx *ctx, u64 phys_iteration)
|
|||||||
sim_ent_enable_prop(dbg_ent, SIM_ENT_PROP_RELEASE_THIS_TICK);
|
sim_ent_enable_prop(dbg_ent, SIM_ENT_PROP_RELEASE_THIS_TICK);
|
||||||
|
|
||||||
/* Remove from lookup */
|
/* Remove from lookup */
|
||||||
struct sim_ent_lookup_key key = sim_ent_lookup_key_from_two_handles(dbg->e0, dbg->e1);
|
struct sim_lookup_key key = sim_lookup_key_from_two_handles(dbg->e0, dbg->e1);
|
||||||
struct sim_ent_lookup_entry *entry = sim_ent_lookup_get(debug_lookup, key);
|
struct sim_lookup_entry *entry = sim_lookup_get(debug_lookup, key);
|
||||||
|
|
||||||
if (entry) {
|
if (entry) {
|
||||||
sim_ent_lookup_remove(debug_lookup, entry);
|
sim_lookup_remove(debug_lookup, entry);
|
||||||
} else {
|
} else {
|
||||||
ASSERT(false); /* This should always exist */
|
ASSERT(false); /* This should always exist */
|
||||||
}
|
}
|
||||||
@ -408,10 +408,10 @@ void phys_prepare_contacts(struct phys_ctx *ctx, u64 phys_iteration)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void phys_warm_start_contacts(struct phys_ctx *ctx)
|
void phys_warm_start_contacts(struct phys_step_ctx *ctx)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct sim_snapshot *ss = ctx->ss;
|
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
|
||||||
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
||||||
struct sim_ent *constraint_ent = &ss->ents[sim_ent_index];
|
struct sim_ent *constraint_ent = &ss->ents[sim_ent_index];
|
||||||
if (!sim_ent_is_valid_and_active(constraint_ent)) continue;
|
if (!sim_ent_is_valid_and_active(constraint_ent)) continue;
|
||||||
@ -463,10 +463,10 @@ void phys_warm_start_contacts(struct phys_ctx *ctx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void phys_solve_contacts(struct phys_ctx *ctx, f32 dt, b32 apply_bias)
|
void phys_solve_contacts(struct phys_step_ctx *ctx, f32 dt, b32 apply_bias)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct sim_snapshot *ss = ctx->ss;
|
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
|
||||||
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
||||||
struct sim_ent *constraint_ent = &ss->ents[sim_ent_index];
|
struct sim_ent *constraint_ent = &ss->ents[sim_ent_index];
|
||||||
if (!sim_ent_is_valid_and_active(constraint_ent)) continue;
|
if (!sim_ent_is_valid_and_active(constraint_ent)) continue;
|
||||||
@ -594,10 +594,10 @@ struct phys_motor_joint phys_motor_joint_from_def(struct phys_motor_joint_def de
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void phys_prepare_motor_joints(struct phys_ctx *ctx)
|
void phys_prepare_motor_joints(struct phys_step_ctx *ctx)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct sim_snapshot *ss = ctx->ss;
|
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
|
||||||
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
||||||
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
|
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
|
||||||
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
|
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
|
||||||
@ -658,10 +658,10 @@ void phys_prepare_motor_joints(struct phys_ctx *ctx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void phys_warm_start_motor_joints(struct phys_ctx *ctx)
|
void phys_warm_start_motor_joints(struct phys_step_ctx *ctx)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct sim_snapshot *ss = ctx->ss;
|
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
|
||||||
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
||||||
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
|
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
|
||||||
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
|
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
|
||||||
@ -690,10 +690,10 @@ void phys_warm_start_motor_joints(struct phys_ctx *ctx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void phys_solve_motor_joints(struct phys_ctx *ctx, f32 dt)
|
void phys_solve_motor_joints(struct phys_step_ctx *ctx, f32 dt)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct sim_snapshot *ss = ctx->ss;
|
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
|
||||||
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
||||||
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
|
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
|
||||||
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
|
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
|
||||||
@ -783,10 +783,10 @@ struct phys_mouse_joint phys_mouse_joint_from_def(struct phys_mouse_joint_def de
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void phys_prepare_mouse_joints(struct phys_ctx *ctx)
|
void phys_prepare_mouse_joints(struct phys_step_ctx *ctx)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct sim_snapshot *ss = ctx->ss;
|
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
|
||||||
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
||||||
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
|
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
|
||||||
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
|
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
|
||||||
@ -830,10 +830,10 @@ void phys_prepare_mouse_joints(struct phys_ctx *ctx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void phys_warm_start_mouse_joints(struct phys_ctx *ctx)
|
void phys_warm_start_mouse_joints(struct phys_step_ctx *ctx)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct sim_snapshot *ss = ctx->ss;
|
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
|
||||||
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
||||||
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
|
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
|
||||||
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
|
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
|
||||||
@ -852,10 +852,10 @@ void phys_warm_start_mouse_joints(struct phys_ctx *ctx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void phys_solve_mouse_joints(struct phys_ctx *ctx, f32 dt)
|
void phys_solve_mouse_joints(struct phys_step_ctx *ctx, f32 dt)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct sim_snapshot *ss = ctx->ss;
|
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
|
||||||
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
||||||
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
|
struct sim_ent *joint_ent = &ss->ents[sim_ent_index];
|
||||||
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
|
if (!sim_ent_is_valid_and_active(joint_ent)) continue;
|
||||||
@ -939,10 +939,10 @@ INTERNAL struct xform get_derived_xform(struct sim_ent *ent, f32 dt)
|
|||||||
return xf;
|
return xf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void phys_integrate_forces(struct phys_ctx *ctx, f32 dt)
|
void phys_integrate_forces(struct phys_step_ctx *ctx, f32 dt)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct sim_snapshot *ss = ctx->ss;
|
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
|
||||||
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
||||||
struct sim_ent *ent = &ss->ents[sim_ent_index];
|
struct sim_ent *ent = &ss->ents[sim_ent_index];
|
||||||
if (!sim_ent_is_valid_and_active(ent)) continue;
|
if (!sim_ent_is_valid_and_active(ent)) continue;
|
||||||
@ -981,10 +981,10 @@ void phys_integrate_forces(struct phys_ctx *ctx, f32 dt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void phys_integrate_velocities(struct phys_ctx *ctx, f32 dt)
|
void phys_integrate_velocities(struct phys_step_ctx *ctx, f32 dt)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct sim_snapshot *ss = ctx->ss;
|
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
|
||||||
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
||||||
struct sim_ent *ent = &ss->ents[sim_ent_index];
|
struct sim_ent *ent = &ss->ents[sim_ent_index];
|
||||||
if (!sim_ent_is_valid_and_active(ent)) continue;
|
if (!sim_ent_is_valid_and_active(ent)) continue;
|
||||||
@ -999,11 +999,11 @@ void phys_integrate_velocities(struct phys_ctx *ctx, f32 dt)
|
|||||||
* Earliest time of impact
|
* Earliest time of impact
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
f32 phys_determine_earliest_toi_for_bullets(struct phys_ctx *ctx, f32 step_dt, f32 tolerance, u32 max_iterations)
|
f32 phys_determine_earliest_toi_for_bullets(struct phys_step_ctx *ctx, f32 step_dt, f32 tolerance, u32 max_iterations)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct sim_snapshot *ss = ctx->ss;
|
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
|
||||||
struct space *space = ctx->space;
|
struct space *space = ctx->sim_step_ctx->accel->space;
|
||||||
f32 smallest_t = 1;
|
f32 smallest_t = 1;
|
||||||
|
|
||||||
for (u64 e0_index = 0; e0_index < ss->num_ents_reserved; ++e0_index) {
|
for (u64 e0_index = 0; e0_index < ss->num_ents_reserved; ++e0_index) {
|
||||||
@ -1050,10 +1050,10 @@ f32 phys_determine_earliest_toi_for_bullets(struct phys_ctx *ctx, f32 step_dt, f
|
|||||||
* Space
|
* Space
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
void phys_update_aabbs(struct phys_ctx *ctx)
|
void phys_update_aabbs(struct phys_step_ctx *ctx)
|
||||||
{
|
{
|
||||||
struct sim_snapshot *ss = ctx->ss;
|
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
|
||||||
struct space *space = ctx->space;
|
struct space *space = ctx->sim_step_ctx->accel->space;
|
||||||
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
|
||||||
struct sim_ent *ent = &ss->ents[sim_ent_index];
|
struct sim_ent *ent = &ss->ents[sim_ent_index];
|
||||||
if (!sim_ent_is_valid_and_active(ent)) continue;
|
if (!sim_ent_is_valid_and_active(ent)) continue;
|
||||||
@ -1075,11 +1075,12 @@ void phys_update_aabbs(struct phys_ctx *ctx)
|
|||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
/* Returns phys iteration to be fed into next step. Supplied iteration must be > 0. */
|
/* Returns phys iteration to be fed into next step. Supplied iteration must be > 0. */
|
||||||
void phys_step(struct phys_ctx *ctx, f32 timestep)
|
void phys_step(struct phys_step_ctx *ctx, f32 timestep)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
phys_integrate_forces(ctx, timestep);
|
phys_integrate_forces(ctx, timestep);
|
||||||
u64 phys_iteration = ctx->ss->phys_iteration;
|
struct sim_snapshot *ss = ctx->sim_step_ctx->world;
|
||||||
|
u64 phys_iteration = ss->phys_iteration;
|
||||||
|
|
||||||
phys_update_aabbs(ctx);
|
phys_update_aabbs(ctx);
|
||||||
|
|
||||||
@ -1112,7 +1113,7 @@ void phys_step(struct phys_ctx *ctx, f32 timestep)
|
|||||||
|
|
||||||
if (ctx->pre_solve_callback) {
|
if (ctx->pre_solve_callback) {
|
||||||
__profscope(pre_solve_callback);
|
__profscope(pre_solve_callback);
|
||||||
ctx->pre_solve_callback(collision_data, ctx->ss);
|
ctx->pre_solve_callback(collision_data, ctx->sim_step_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
f32 substep_dt = step_dt / SIM_PHYSICS_SUBSTEPS;
|
f32 substep_dt = step_dt / SIM_PHYSICS_SUBSTEPS;
|
||||||
@ -1146,11 +1147,11 @@ void phys_step(struct phys_ctx *ctx, f32 timestep)
|
|||||||
|
|
||||||
if (ctx->post_solve_callback) {
|
if (ctx->post_solve_callback) {
|
||||||
__profscope(post_solve_callback);
|
__profscope(post_solve_callback);
|
||||||
ctx->post_solve_callback(collision_data, ctx->ss);
|
ctx->post_solve_callback(collision_data, ctx->sim_step_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->ss->phys_iteration = phys_iteration;
|
ss->phys_iteration = phys_iteration;
|
||||||
}
|
}
|
||||||
|
|||||||
46
src/phys.h
46
src/phys.h
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
struct space;
|
struct space;
|
||||||
struct sim_ent_lookup;
|
struct sim_ent_lookup;
|
||||||
struct sim_snapshot;
|
struct sim_step_ctx;
|
||||||
|
|
||||||
struct phys_contact_constraint;
|
struct phys_contact_constraint;
|
||||||
struct phys_collision_data {
|
struct phys_collision_data {
|
||||||
@ -25,20 +25,14 @@ struct phys_collision_data_array {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct phys_collision_data;
|
struct phys_collision_data;
|
||||||
#define PHYS_COLLISION_CALLBACK_FUNC_DEF(name, arg_collision_data, arg_ss) void name(struct phys_collision_data_array arg_collision_data, struct sim_snapshot *arg_ss)
|
#define PHYS_COLLISION_CALLBACK_FUNC_DEF(name, arg_collision_data, arg_sim_step_ctx) void name(struct phys_collision_data_array arg_collision_data, struct sim_step_ctx *arg_sim_step_ctx)
|
||||||
typedef PHYS_COLLISION_CALLBACK_FUNC_DEF(phys_collision_callback_func, collision_data, ss);
|
typedef PHYS_COLLISION_CALLBACK_FUNC_DEF(phys_collision_callback_func, collision_data, ctx);
|
||||||
|
|
||||||
/* Structure containing data used for a single physics step */
|
/* Structure containing data used for a single physics step */
|
||||||
struct phys_ctx {
|
struct phys_step_ctx {
|
||||||
struct sim_snapshot *ss;
|
struct sim_step_ctx *sim_step_ctx;
|
||||||
phys_collision_callback_func *pre_solve_callback;
|
phys_collision_callback_func *pre_solve_callback;
|
||||||
phys_collision_callback_func *post_solve_callback;
|
phys_collision_callback_func *post_solve_callback;
|
||||||
|
|
||||||
struct space *space;
|
|
||||||
struct sim_ent_lookup *contact_lookup;
|
|
||||||
#if COLLIDER_DEBUG
|
|
||||||
struct sim_ent_lookup *collision_debug_lookup;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
@ -107,10 +101,10 @@ struct phys_collision_debug {
|
|||||||
struct xform xf1;
|
struct xform xf1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct phys_collision_data_array phys_create_and_update_contacts(struct arena *arena, struct phys_ctx *ctx, f32 elapsed_dt, u64 phys_iteration);
|
struct phys_collision_data_array phys_create_and_update_contacts(struct arena *arena, struct phys_step_ctx *ctx, f32 elapsed_dt, u64 phys_iteration);
|
||||||
void phys_prepare_contacts(struct phys_ctx *ctx, u64 phys_iteration);
|
void phys_prepare_contacts(struct phys_step_ctx *ctx, u64 phys_iteration);
|
||||||
void phys_warm_start_contacts(struct phys_ctx *ctx);
|
void phys_warm_start_contacts(struct phys_step_ctx *ctx);
|
||||||
void phys_solve_contacts(struct phys_ctx *ctx, f32 dt, b32 apply_bias);
|
void phys_solve_contacts(struct phys_step_ctx *ctx, f32 dt, b32 apply_bias);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Motor joint
|
* Motor joint
|
||||||
@ -147,9 +141,9 @@ struct phys_motor_joint {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct phys_motor_joint phys_motor_joint_from_def(struct phys_motor_joint_def def);
|
struct phys_motor_joint phys_motor_joint_from_def(struct phys_motor_joint_def def);
|
||||||
void phys_prepare_motor_joints(struct phys_ctx *ctx);
|
void phys_prepare_motor_joints(struct phys_step_ctx *ctx);
|
||||||
void phys_warm_start_motor_joints(struct phys_ctx *ctx);
|
void phys_warm_start_motor_joints(struct phys_step_ctx *ctx);
|
||||||
void phys_solve_motor_joints(struct phys_ctx *ctx, f32 dt);
|
void phys_solve_motor_joints(struct phys_step_ctx *ctx, f32 dt);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Mouse joint
|
* Mouse joint
|
||||||
@ -180,33 +174,33 @@ struct phys_mouse_joint {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct phys_mouse_joint phys_mouse_joint_from_def(struct phys_mouse_joint_def def);
|
struct phys_mouse_joint phys_mouse_joint_from_def(struct phys_mouse_joint_def def);
|
||||||
void phys_prepare_mouse_joints(struct phys_ctx *ctx);
|
void phys_prepare_mouse_joints(struct phys_step_ctx *ctx);
|
||||||
void phys_warm_start_mouse_joints(struct phys_ctx *ctx);
|
void phys_warm_start_mouse_joints(struct phys_step_ctx *ctx);
|
||||||
void phys_solve_mouse_joints(struct phys_ctx *ctx, f32 dt);
|
void phys_solve_mouse_joints(struct phys_step_ctx *ctx, f32 dt);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Integration
|
* Integration
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
void phys_integrate_forces(struct phys_ctx *ctx, f32 dt);
|
void phys_integrate_forces(struct phys_step_ctx *ctx, f32 dt);
|
||||||
void phys_integrate_velocities(struct phys_ctx *ctx, f32 dt);
|
void phys_integrate_velocities(struct phys_step_ctx *ctx, f32 dt);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Earliest time of impact
|
* Earliest time of impact
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
f32 phys_determine_earliest_toi_for_bullets(struct phys_ctx *ctx, f32 step_dt, f32 tolerance, u32 max_iterations);
|
f32 phys_determine_earliest_toi_for_bullets(struct phys_step_ctx *ctx, f32 step_dt, f32 tolerance, u32 max_iterations);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Space
|
* Space
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
void phys_update_aabbs(struct phys_ctx *ctx);
|
void phys_update_aabbs(struct phys_step_ctx *ctx);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Step
|
* Step
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
void phys_step(struct phys_ctx *ctx, f32 timestep);
|
void phys_step(struct phys_step_ctx *ctx, f32 timestep);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
223
src/sim.h
223
src/sim.h
@ -1,15 +1,125 @@
|
|||||||
#ifndef SIM_H
|
#ifndef SIM_H
|
||||||
#define SIM_H
|
#define SIM_H
|
||||||
|
|
||||||
#include "bitbuff.h"
|
#define SIM_CLIENT_NIL_HANDLE ((struct sim_client_handle) { .gen = 0, .idx = 0 })
|
||||||
|
|
||||||
struct sprite_startup_receipt;
|
/* Absolute layers */
|
||||||
struct phys_startup_receipt;
|
#define SIM_LAYER_FLOOR_DECALS (-300)
|
||||||
struct host_startup_receipt;
|
#define SIM_LAYER_BULLETS (-200)
|
||||||
struct sim_snapshot_startup_receipt;
|
#define SIM_LAYER_TRACERS (-100)
|
||||||
|
#define SIM_LAYER_SHOULDERS (0)
|
||||||
|
|
||||||
|
/* Relative layers */
|
||||||
|
#define SIM_LAYER_RELATIVE_DEFAULT (0)
|
||||||
|
#define SIM_LAYER_RELATIVE_WEAPON (1)
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Control
|
* Startup
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct sim_startup_receipt { i32 _; };
|
||||||
|
struct sim_startup_receipt sim_startup(void);
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Client store
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct sim_client_lookup_bucket {
|
||||||
|
struct sim_client_handle first;
|
||||||
|
struct sim_client_handle last;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sim_client_store {
|
||||||
|
b32 valid;
|
||||||
|
struct arena arena;
|
||||||
|
|
||||||
|
/* Client lookup */
|
||||||
|
struct sim_client_lookup_bucket *client_lookup_buckets;
|
||||||
|
u64 num_client_lookup_buckets;
|
||||||
|
|
||||||
|
/* Clients */
|
||||||
|
struct arena clients_arena;
|
||||||
|
struct sim_client *clients;
|
||||||
|
struct sim_client_handle first_free_client;
|
||||||
|
u64 num_clients_allocated;
|
||||||
|
u64 num_clients_reserved;
|
||||||
|
};
|
||||||
|
|
||||||
|
INLINE struct sim_client_store *sim_client_store_nil(void)
|
||||||
|
{
|
||||||
|
extern READONLY struct sim_client_store **_g_sim_client_store_nil;
|
||||||
|
return *_g_sim_client_store_nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sim_client_store *sim_client_store_alloc(void);
|
||||||
|
void sim_client_store_release(struct sim_client_store *store);
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Client
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sim_client {
|
||||||
|
b32 valid;
|
||||||
|
enum sim_client_kind kind;
|
||||||
|
struct sim_client_handle handle;
|
||||||
|
struct sim_client_store *store;
|
||||||
|
|
||||||
|
struct arena snapshots_arena;
|
||||||
|
|
||||||
|
struct host_channel_id channel_id;
|
||||||
|
u64 channel_hash;
|
||||||
|
|
||||||
|
struct sim_client_handle next_free;
|
||||||
|
struct sim_client_handle next_in_bucket;
|
||||||
|
struct sim_client_handle prev_in_bucket;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* Snapshots sorted by tick (low to high) */
|
||||||
|
u64 first_tick;
|
||||||
|
u64 last_tick;
|
||||||
|
u64 num_ticks;
|
||||||
|
struct sim_snapshot *first_free_snapshot;
|
||||||
|
|
||||||
|
/* Tick -> snapshot lookup */
|
||||||
|
u64 num_snapshot_lookup_buckets;
|
||||||
|
struct sim_snapshot_lookup_bucket *snapshot_lookup_buckets;
|
||||||
|
};
|
||||||
|
|
||||||
|
INLINE struct sim_client *sim_client_nil(void)
|
||||||
|
{
|
||||||
|
extern READONLY struct sim_client **_g_sim_client_nil;
|
||||||
|
return *_g_sim_client_nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sim_client *sim_client_alloc(struct sim_client_store *store, enum sim_client_kind kind);
|
||||||
|
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);
|
||||||
|
void sim_client_set_channel_id(struct sim_client *client, struct host_channel_id channel_id);
|
||||||
|
struct sim_client *sim_client_from_handle(struct sim_client_store *store, struct sim_client_handle handle);
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Snapshot
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
enum sim_control_flag {
|
enum sim_control_flag {
|
||||||
@ -31,22 +141,95 @@ struct sim_control {
|
|||||||
u32 flags;
|
u32 flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ========================== *
|
struct sim_snapshot {
|
||||||
* Layers
|
b32 valid;
|
||||||
* ========================== */
|
u64 tick;
|
||||||
|
struct sim_client *client;
|
||||||
|
struct sim_snapshot *next_free;
|
||||||
|
struct sim_snapshot *next_in_bucket;
|
||||||
|
struct sim_snapshot *prev_in_bucket;
|
||||||
|
u64 prev_tick;
|
||||||
|
u64 next_tick;
|
||||||
|
|
||||||
/* Absolute layers */
|
struct arena arena;
|
||||||
#define SIM_LAYER_FLOOR_DECALS (-300)
|
|
||||||
#define SIM_LAYER_BULLETS (-200)
|
|
||||||
#define SIM_LAYER_TRACERS (-100)
|
|
||||||
#define SIM_LAYER_SHOULDERS (0)
|
|
||||||
|
|
||||||
/* Relative layers */
|
struct sim_client_handle producer_client;
|
||||||
#define SIM_LAYER_RELATIVE_DEFAULT (0)
|
b32 producer_client_is_local;
|
||||||
#define SIM_LAYER_RELATIVE_WEAPON (1)
|
|
||||||
|
|
||||||
struct sim_snapshot;
|
/* ====================================================================== */
|
||||||
struct sim_snapshot_store;
|
/* SIM_SNAPSHOT_FLAG_CONTROL */
|
||||||
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);
|
|
||||||
|
struct sim_control control;
|
||||||
|
|
||||||
|
/* ====================================================================== */
|
||||||
|
/* SIM_SNAPSHOT_FLAG_STATE */
|
||||||
|
|
||||||
|
/* Was this snapshot created by the master sim */
|
||||||
|
b32 is_master;
|
||||||
|
|
||||||
|
/* Time of local snapshot publish to user thread */
|
||||||
|
i64 publish_dt_ns;
|
||||||
|
i64 publish_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;
|
||||||
|
|
||||||
|
/* If != previous tick's continuity then don't lerp */
|
||||||
|
u64 continuity_gen;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* Entities */
|
||||||
|
struct arena ents_arena;
|
||||||
|
struct sim_ent *ents;
|
||||||
|
struct sim_ent_handle first_free_ent;
|
||||||
|
u64 num_ents_allocated;
|
||||||
|
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;
|
||||||
|
return *_g_sim_snapshot_nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct bitbuff_writer;
|
||||||
|
struct bitbuff_reader;
|
||||||
|
|
||||||
|
/* Alloc */
|
||||||
|
struct sim_snapshot *sim_snapshot_alloc(struct sim_client *client, struct sim_snapshot *src, u64 tick);
|
||||||
|
void sim_snapshot_release(struct sim_snapshot *sim_snapshot);
|
||||||
|
void sim_snapshot_release_ticks_in_range(struct sim_client *client, u64 start, u64 end);
|
||||||
|
|
||||||
|
/* Lookup */
|
||||||
|
struct sim_snapshot *sim_snapshot_from_tick(struct sim_client *client, u64 tick);
|
||||||
|
struct sim_snapshot *sim_snapshot_from_closest_tick_lte(struct sim_client *client, u64 tick);
|
||||||
|
|
||||||
|
/* Lerp */
|
||||||
|
struct sim_snapshot *sim_snapshot_alloc_from_lerp(struct sim_client *client, struct sim_snapshot *ss0, struct sim_snapshot *ss1, f64 blend);
|
||||||
|
|
||||||
|
/* Encode / decode */
|
||||||
|
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
|
#endif
|
||||||
|
|||||||
189
src/sim_client.c
189
src/sim_client.c
@ -1,189 +0,0 @@
|
|||||||
#include "sim_client.h"
|
|
||||||
#include "sim.h"
|
|
||||||
#include "sim_snapshot.h"
|
|
||||||
#include "host.h"
|
|
||||||
#include "arena.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "arena.h"
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Client
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
INTERNAL u64 hash_from_channel_id(struct host_channel_id channel_id)
|
|
||||||
{
|
|
||||||
return hash_fnv64(HASH_FNV64_BASIS, STRING_FROM_STRUCT(&channel_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sim_client *sim_client_from_handle(struct sim_snapshot *ss, struct sim_client_handle handle)
|
|
||||||
{
|
|
||||||
if (handle.gen != 0 && handle.idx < ss->num_clients_reserved) {
|
|
||||||
struct sim_client *client = &ss->clients[handle.idx];
|
|
||||||
if (client->handle.gen == handle.gen) {
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sim_client_nil();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sim_client *sim_client_from_channel_id(struct sim_snapshot *ss, struct host_channel_id channel_id)
|
|
||||||
{
|
|
||||||
struct sim_client *res = sim_client_nil();
|
|
||||||
u64 channel_hash = hash_from_channel_id(channel_id);
|
|
||||||
u64 bucket_index = channel_hash % ss->num_client_lookup_buckets;
|
|
||||||
struct sim_client_lookup_bucket *bucket = &ss->client_lookup_buckets[bucket_index];
|
|
||||||
for (struct sim_client *client = sim_client_from_handle(ss, bucket->first); client->valid; client = sim_client_from_handle(ss, client->next_in_bucket)) {
|
|
||||||
if (client->channel_hash == channel_hash) {
|
|
||||||
res = client;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sim_client *sim_client_alloc(struct sim_snapshot *ss, struct host_channel_id channel_id, enum sim_client_kind kind)
|
|
||||||
{
|
|
||||||
struct sim_client_handle handle = ZI;
|
|
||||||
struct sim_client *client = sim_client_from_handle(ss, ss->first_free_client);
|
|
||||||
if (client->valid) {
|
|
||||||
ss->first_free_client = client->next_free;
|
|
||||||
handle = client->handle;
|
|
||||||
++handle.gen;
|
|
||||||
} else {
|
|
||||||
client = arena_push(&ss->clients_arena, struct sim_client);
|
|
||||||
handle.gen = 1;
|
|
||||||
handle.idx = ss->num_clients_reserved;
|
|
||||||
++ss->num_clients_reserved;
|
|
||||||
}
|
|
||||||
++ss->num_clients_allocated;
|
|
||||||
*client = *sim_client_nil();
|
|
||||||
client->ss = ss;
|
|
||||||
client->valid = true;
|
|
||||||
client->kind = kind;
|
|
||||||
client->handle = handle;
|
|
||||||
|
|
||||||
/* Insert into channel lookup */
|
|
||||||
sim_client_set_channel_id(client, channel_id);
|
|
||||||
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sim_client_release(struct sim_client *client)
|
|
||||||
{
|
|
||||||
struct sim_snapshot *ss = client->ss;
|
|
||||||
client->valid = false;
|
|
||||||
++client->handle.gen;
|
|
||||||
client->next_free = ss->first_free_client;
|
|
||||||
ss->first_free_client = client->handle;
|
|
||||||
--ss->num_clients_allocated;
|
|
||||||
|
|
||||||
/* Remove from channel lookup */
|
|
||||||
sim_client_set_channel_id(client, HOST_CHANNEL_ID_NIL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sim_client_set_channel_id(struct sim_client *client, struct host_channel_id channel_id)
|
|
||||||
{
|
|
||||||
struct sim_snapshot *ss = client->ss;
|
|
||||||
struct host_channel_id old_channel_id = client->channel_id;
|
|
||||||
|
|
||||||
/* Remove from channel lookup */
|
|
||||||
if (!host_channel_id_is_nil(old_channel_id)) {
|
|
||||||
u64 bucket_index = client->channel_hash % ss->num_client_lookup_buckets;
|
|
||||||
struct sim_client_lookup_bucket *bucket = &ss->client_lookup_buckets[bucket_index];
|
|
||||||
struct sim_client *prev = sim_client_from_handle(ss, client->prev_in_bucket);
|
|
||||||
struct sim_client *next = sim_client_from_handle(ss, client->next_in_bucket);
|
|
||||||
if (prev->valid) {
|
|
||||||
prev->next_in_bucket = next->handle;
|
|
||||||
} else {
|
|
||||||
bucket->first = next->handle;
|
|
||||||
}
|
|
||||||
if (next->valid) {
|
|
||||||
next->prev_in_bucket = prev->handle;
|
|
||||||
} else {
|
|
||||||
bucket->last = prev->handle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Insert into channel lookup */
|
|
||||||
u64 channel_hash = hash_from_channel_id(channel_id);
|
|
||||||
client->channel_id = channel_id;
|
|
||||||
client->channel_hash = channel_hash;
|
|
||||||
if (!host_channel_id_is_nil(channel_id)) {
|
|
||||||
u64 bucket_index = channel_hash % ss->num_client_lookup_buckets;
|
|
||||||
struct sim_client_lookup_bucket *bucket = &ss->client_lookup_buckets[bucket_index];
|
|
||||||
{
|
|
||||||
struct sim_client *prev_in_bucket = sim_client_from_handle(ss, bucket->last);
|
|
||||||
if (prev_in_bucket->valid) {
|
|
||||||
prev_in_bucket->next_in_bucket = client->handle;
|
|
||||||
client->prev_in_bucket = prev_in_bucket->handle;
|
|
||||||
} else {
|
|
||||||
bucket->first = client->handle;
|
|
||||||
}
|
|
||||||
bucket->last = client->handle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Lerp
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
void sim_client_lerp(struct sim_client *c, struct sim_client *c0, struct sim_client *c1, f64 blend)
|
|
||||||
{
|
|
||||||
if (c0->valid && c1->valid && c0->handle.gen == c1->handle.gen) {
|
|
||||||
c0->cursor_pos = v2_lerp(c0->cursor_pos, c1->cursor_pos, blend);
|
|
||||||
c->control.move = v2_lerp(c0->control.move, c1->control.move, blend);
|
|
||||||
c->control.focus = v2_lerp(c0->control.focus, c1->control.focus, blend);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Encode
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
void sim_client_encode(struct bitbuff_writer *bw, struct sim_client *c0, struct sim_client *c1)
|
|
||||||
{
|
|
||||||
struct sim_snapshot *ss = c1->ss;
|
|
||||||
|
|
||||||
/* TODO: Granular delta encoding */
|
|
||||||
|
|
||||||
u64 pos = 0;
|
|
||||||
c1->ss = c0->ss;
|
|
||||||
while (pos < sizeof(*c1)) {
|
|
||||||
u64 chunk_size = min_u64(pos + 8, sizeof(*c1)) - pos;
|
|
||||||
u8 *chunk0 = (u8 *)c0 + pos;
|
|
||||||
u8 *chunk1 = (u8 *)c1 + pos;
|
|
||||||
if (MEMEQ(chunk0, chunk1, chunk_size)) {
|
|
||||||
bw_write_bit(bw, 0);
|
|
||||||
} else {
|
|
||||||
bw_write_bit(bw, 1);
|
|
||||||
u64 bits = 0;
|
|
||||||
MEMCPY(&bits, chunk1, chunk_size);
|
|
||||||
bw_write_ubits(bw, bits, 64);
|
|
||||||
}
|
|
||||||
pos += 8;
|
|
||||||
}
|
|
||||||
c1->ss = ss;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Decode
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
void sim_client_decode(struct bitbuff_reader *br, struct sim_client *e)
|
|
||||||
{
|
|
||||||
struct sim_snapshot *ss = e->ss;
|
|
||||||
|
|
||||||
u64 pos = 0;
|
|
||||||
while (pos < sizeof(*e)) {
|
|
||||||
u8 *chunk = (u8 *)e + pos;
|
|
||||||
if (br_read_bit(br)) {
|
|
||||||
u64 chunk_size = min_u64(pos + 8, sizeof(*e)) - pos;
|
|
||||||
u64 bits = br_read_ubits(br, 64);
|
|
||||||
MEMCPY(chunk, &bits, chunk_size);
|
|
||||||
}
|
|
||||||
pos += 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
e->ss = ss;
|
|
||||||
}
|
|
||||||
@ -1,81 +0,0 @@
|
|||||||
#ifndef SIM_CLIENT_H
|
|
||||||
#define SIM_CLIENT_H
|
|
||||||
|
|
||||||
#include "sim_ent.h"
|
|
||||||
|
|
||||||
#define SIM_CLIENT_NIL_HANDLE ((struct sim_client_handle) { .gen = 0, .idx = 0 })
|
|
||||||
|
|
||||||
struct sim_client_channel_lookup_bucket;
|
|
||||||
struct sim_snapshot;
|
|
||||||
|
|
||||||
enum sim_client_kind {
|
|
||||||
SIM_CLIENT_KIND_INVALID,
|
|
||||||
SIM_CLIENT_KIND_SIM_SLAVE,
|
|
||||||
SIM_CLIENT_KIND_SIM_MASTER
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sim_client_lookup_bucket {
|
|
||||||
struct sim_client_handle first;
|
|
||||||
struct sim_client_handle last;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sim_client {
|
|
||||||
b32 valid;
|
|
||||||
enum sim_client_kind kind;
|
|
||||||
struct sim_client_handle handle;
|
|
||||||
struct sim_snapshot *ss;
|
|
||||||
|
|
||||||
struct host_channel_id channel_id;
|
|
||||||
u64 channel_hash;
|
|
||||||
|
|
||||||
struct sim_client_handle next_free;
|
|
||||||
struct sim_client_handle next_in_bucket;
|
|
||||||
struct sim_client_handle prev_in_bucket;
|
|
||||||
|
|
||||||
/* This is the last tick we know that this client has received */
|
|
||||||
u64 ack;
|
|
||||||
|
|
||||||
struct v2 cursor_pos;
|
|
||||||
struct sim_ent_handle camera_ent;
|
|
||||||
struct sim_ent_handle control_ent;
|
|
||||||
|
|
||||||
struct sim_control control;
|
|
||||||
|
|
||||||
b32 dbg_drag_start;
|
|
||||||
b32 dbg_drag_stop;
|
|
||||||
struct sim_ent_handle dbg_drag_joint_ent;
|
|
||||||
};
|
|
||||||
|
|
||||||
INLINE struct sim_client *sim_client_nil(void)
|
|
||||||
{
|
|
||||||
extern READONLY struct sim_client **_g_sim_client_nil;
|
|
||||||
return *_g_sim_client_nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Client
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
struct sim_client *sim_client_from_handle(struct sim_snapshot *ss, struct sim_client_handle handle);
|
|
||||||
struct sim_client *sim_client_from_channel_id(struct sim_snapshot *ss, struct host_channel_id channel_id);
|
|
||||||
struct sim_client *sim_client_alloc(struct sim_snapshot *ss, struct host_channel_id channel_id, enum sim_client_kind kind);
|
|
||||||
void sim_client_release(struct sim_client *client);
|
|
||||||
void sim_client_set_channel_id(struct sim_client *client, struct host_channel_id channel_id);
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Lerp
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
void sim_client_lerp(struct sim_client *c, struct sim_client *c0, struct sim_client *c1, f64 blend);
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Encode
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
struct bitbuff_writer;
|
|
||||||
struct bitbuff_reader;
|
|
||||||
|
|
||||||
void sim_client_encode(struct bitbuff_writer *bw, struct sim_client *c0, struct sim_client *c1);
|
|
||||||
void sim_client_decode(struct bitbuff_reader *br, struct sim_client *c);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
271
src/sim_ent.c
271
src/sim_ent.c
@ -1,10 +1,10 @@
|
|||||||
#include "sim_ent.h"
|
#include "sim_ent.h"
|
||||||
#include "sim.h"
|
#include "sim.h"
|
||||||
#include "sim_snapshot.h"
|
|
||||||
#include "math.h"
|
#include "math.h"
|
||||||
|
#include "bitbuff.h"
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Entity allocation
|
* Ent allocation
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
INTERNAL struct sim_ent *sim_ent_alloc_internal(struct sim_snapshot *ss)
|
INTERNAL struct sim_ent *sim_ent_alloc_internal(struct sim_snapshot *ss)
|
||||||
@ -68,7 +68,7 @@ void sim_ent_release(struct sim_ent *ent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Query
|
* Ent query
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
/* Returns a valid ent or read-only nil ent. Always safe to read result, need to check `valid` to write. */
|
/* Returns a valid ent or read-only nil ent. Always safe to read result, need to check `valid` to write. */
|
||||||
@ -119,7 +119,79 @@ struct sim_ent *sim_ent_find_first_match_all(struct sim_snapshot *ss, struct sim
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Xform
|
* Ent tree
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
void sim_ent_link_parent(struct sim_ent *ent, struct sim_ent *parent)
|
||||||
|
{
|
||||||
|
struct sim_snapshot *ss = ent->ss;
|
||||||
|
|
||||||
|
if (ent->parent.gen) {
|
||||||
|
/* Unlink from current parent */
|
||||||
|
sim_ent_unlink_from_parent(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sim_ent_handle handle = ent->handle;
|
||||||
|
struct sim_ent_handle parent_handle = parent->handle;
|
||||||
|
|
||||||
|
ent->parent = parent_handle;
|
||||||
|
|
||||||
|
struct sim_ent_handle last_child_handle = parent->last;
|
||||||
|
struct sim_ent *last_child = sim_ent_from_handle(ss, last_child_handle);
|
||||||
|
if (last_child->valid) {
|
||||||
|
ent->prev = last_child_handle;
|
||||||
|
last_child->next = handle;
|
||||||
|
} else {
|
||||||
|
parent->first = handle;
|
||||||
|
}
|
||||||
|
parent->last = handle;
|
||||||
|
|
||||||
|
if (parent->is_root) {
|
||||||
|
ent->is_top = true;
|
||||||
|
ent->top = handle;
|
||||||
|
} else {
|
||||||
|
ent->top = parent->top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NOTE: Entity will be dangling after calling this, should re-link to root ent. */
|
||||||
|
void sim_ent_unlink_from_parent(struct sim_ent *ent)
|
||||||
|
{
|
||||||
|
struct sim_snapshot *ss = ent->ss;
|
||||||
|
|
||||||
|
struct sim_ent_handle parent_handle = ent->parent;
|
||||||
|
struct sim_ent *parent = sim_ent_from_handle(ss, parent_handle);
|
||||||
|
struct sim_ent *prev = sim_ent_from_handle(ss, ent->prev);
|
||||||
|
struct sim_ent *next = sim_ent_from_handle(ss, ent->next);
|
||||||
|
|
||||||
|
/* Unlink from parent & siblings */
|
||||||
|
if (prev->valid) {
|
||||||
|
prev->next = next->handle;
|
||||||
|
} else {
|
||||||
|
parent->first = next->handle;
|
||||||
|
}
|
||||||
|
if (next->valid) {
|
||||||
|
next->prev = prev->handle;
|
||||||
|
} else {
|
||||||
|
parent->last = prev->handle;
|
||||||
|
}
|
||||||
|
ent->prev = sim_ent_nil()->handle;
|
||||||
|
ent->next = sim_ent_nil()->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Activate
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
void sim_ent_activate(struct sim_ent *ent, u64 current_tick)
|
||||||
|
{
|
||||||
|
sim_ent_enable_prop(ent, SIM_ENT_PROP_ACTIVE);
|
||||||
|
ent->activation_tick = current_tick;
|
||||||
|
++ent->continuity_gen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Ent xform
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
INTERNAL void sim_ent_mark_child_xforms_dirty(struct sim_snapshot *ss, struct sim_ent *ent)
|
INTERNAL void sim_ent_mark_child_xforms_dirty(struct sim_snapshot *ss, struct sim_ent *ent)
|
||||||
@ -210,7 +282,7 @@ void sim_ent_set_local_xform(struct sim_ent *ent, struct xform xf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Movement
|
* Ent movement
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
void sim_ent_apply_linear_impulse(struct sim_ent *ent, struct v2 impulse, struct v2 point)
|
void sim_ent_apply_linear_impulse(struct sim_ent *ent, struct v2 impulse, struct v2 point)
|
||||||
@ -254,190 +326,7 @@ void sim_ent_apply_torque(struct sim_ent *ent, f32 torque)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Tree
|
* Ent lerp
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
void sim_ent_link_parent(struct sim_ent *ent, struct sim_ent *parent)
|
|
||||||
{
|
|
||||||
struct sim_snapshot *ss = ent->ss;
|
|
||||||
|
|
||||||
if (ent->parent.gen) {
|
|
||||||
/* Unlink from current parent */
|
|
||||||
sim_ent_unlink_from_parent(ent);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sim_ent_handle handle = ent->handle;
|
|
||||||
struct sim_ent_handle parent_handle = parent->handle;
|
|
||||||
|
|
||||||
ent->parent = parent_handle;
|
|
||||||
|
|
||||||
struct sim_ent_handle last_child_handle = parent->last;
|
|
||||||
struct sim_ent *last_child = sim_ent_from_handle(ss, last_child_handle);
|
|
||||||
if (last_child->valid) {
|
|
||||||
ent->prev = last_child_handle;
|
|
||||||
last_child->next = handle;
|
|
||||||
} else {
|
|
||||||
parent->first = handle;
|
|
||||||
}
|
|
||||||
parent->last = handle;
|
|
||||||
|
|
||||||
if (parent->is_root) {
|
|
||||||
ent->is_top = true;
|
|
||||||
ent->top = handle;
|
|
||||||
} else {
|
|
||||||
ent->top = parent->top;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NOTE: Entity will be dangling after calling this, should re-link to root ent. */
|
|
||||||
void sim_ent_unlink_from_parent(struct sim_ent *ent)
|
|
||||||
{
|
|
||||||
struct sim_snapshot *ss = ent->ss;
|
|
||||||
|
|
||||||
struct sim_ent_handle parent_handle = ent->parent;
|
|
||||||
struct sim_ent *parent = sim_ent_from_handle(ss, parent_handle);
|
|
||||||
struct sim_ent *prev = sim_ent_from_handle(ss, ent->prev);
|
|
||||||
struct sim_ent *next = sim_ent_from_handle(ss, ent->next);
|
|
||||||
|
|
||||||
/* Unlink from parent & siblings */
|
|
||||||
if (prev->valid) {
|
|
||||||
prev->next = next->handle;
|
|
||||||
} else {
|
|
||||||
parent->first = next->handle;
|
|
||||||
}
|
|
||||||
if (next->valid) {
|
|
||||||
next->prev = prev->handle;
|
|
||||||
} else {
|
|
||||||
parent->last = prev->handle;
|
|
||||||
}
|
|
||||||
ent->prev = sim_ent_nil()->handle;
|
|
||||||
ent->next = sim_ent_nil()->handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Entity lookup
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
struct sim_ent_lookup sim_ent_lookup_alloc(u64 num_buckets)
|
|
||||||
{
|
|
||||||
ASSERT(num_buckets > 0);
|
|
||||||
struct sim_ent_lookup l = ZI;
|
|
||||||
l.arena = arena_alloc(GIGABYTE(64));
|
|
||||||
l.buckets = arena_push_array_zero(&l.arena, struct sim_ent_lookup_bucket, num_buckets);
|
|
||||||
l.num_buckets = num_buckets;
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sim_ent_lookup_release(struct sim_ent_lookup *l)
|
|
||||||
{
|
|
||||||
arena_release(&l->arena);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sim_ent_lookup_entry *sim_ent_lookup_get(struct sim_ent_lookup *l, struct sim_ent_lookup_key key)
|
|
||||||
{
|
|
||||||
u64 index = key.hash % l->num_buckets;
|
|
||||||
struct sim_ent_lookup_bucket *bucket = &l->buckets[index];
|
|
||||||
struct sim_ent_lookup_entry *res = NULL;
|
|
||||||
for (struct sim_ent_lookup_entry *e = bucket->first; e; e = e->next) {
|
|
||||||
if (e->key.hash == key.hash) {
|
|
||||||
res = e;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sim_ent_lookup_set(struct sim_ent_lookup *l, struct sim_ent_lookup_key key, struct sim_ent_handle handle)
|
|
||||||
{
|
|
||||||
u64 index = key.hash % l->num_buckets;
|
|
||||||
struct sim_ent_lookup_bucket *bucket = &l->buckets[index];
|
|
||||||
|
|
||||||
struct sim_ent_lookup_entry *prev = NULL;
|
|
||||||
struct sim_ent_lookup_entry **slot = &bucket->first;
|
|
||||||
while (*slot) {
|
|
||||||
if ((*slot)->key.hash == key.hash) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
prev = *slot;
|
|
||||||
slot = &(*slot)->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sim_ent_lookup_entry *entry = *slot;
|
|
||||||
if (entry) {
|
|
||||||
/* Set existing entry */
|
|
||||||
entry->ent = handle;
|
|
||||||
} else {
|
|
||||||
/* Allocate entry */
|
|
||||||
if (l->first_free_entry) {
|
|
||||||
entry = l->first_free_entry;
|
|
||||||
l->first_free_entry->prev = NULL;
|
|
||||||
l->first_free_entry = entry->next;
|
|
||||||
} else {
|
|
||||||
entry = arena_push(&l->arena, struct sim_ent_lookup_entry);
|
|
||||||
}
|
|
||||||
MEMZERO_STRUCT(entry);
|
|
||||||
|
|
||||||
entry->key = key;
|
|
||||||
entry->ent = handle;
|
|
||||||
if (prev) {
|
|
||||||
entry->prev = prev;
|
|
||||||
prev->next = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
bucket->last = entry;
|
|
||||||
*slot = entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sim_ent_lookup_remove(struct sim_ent_lookup *l, struct sim_ent_lookup_entry *entry)
|
|
||||||
{
|
|
||||||
struct sim_ent_lookup_bucket *bucket = &l->buckets[entry->key.hash % l->num_buckets];
|
|
||||||
struct sim_ent_lookup_entry *prev = entry->prev;
|
|
||||||
struct sim_ent_lookup_entry *next = entry->next;
|
|
||||||
|
|
||||||
if (prev) {
|
|
||||||
prev->next = next;
|
|
||||||
} else {
|
|
||||||
bucket->first = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next) {
|
|
||||||
next->prev = prev;
|
|
||||||
} else {
|
|
||||||
bucket->last = prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (l->first_free_entry) {
|
|
||||||
l->first_free_entry->prev = entry;
|
|
||||||
}
|
|
||||||
entry->next = l->first_free_entry;
|
|
||||||
entry->prev = NULL;
|
|
||||||
l->first_free_entry = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sim_ent_lookup_key sim_ent_lookup_key_from_two_handles(struct sim_ent_handle h0, struct sim_ent_handle h1)
|
|
||||||
{
|
|
||||||
struct sim_ent_lookup_key key = ZI;
|
|
||||||
struct string b0 = STRING_FROM_STRUCT(&h0);
|
|
||||||
struct string b1 = STRING_FROM_STRUCT(&h1);
|
|
||||||
key.hash = hash_fnv64(HASH_FNV64_BASIS, b0);
|
|
||||||
key.hash = hash_fnv64(key.hash, b1);
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Activate
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
void sim_ent_activate(struct sim_ent *ent, u64 current_tick)
|
|
||||||
{
|
|
||||||
sim_ent_enable_prop(ent, SIM_ENT_PROP_ACTIVE);
|
|
||||||
ent->activation_tick = current_tick;
|
|
||||||
++ent->continuity_gen;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Lerp
|
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
void sim_ent_lerp(struct sim_ent *e, struct sim_ent *e0, struct sim_ent *e1, f64 blend)
|
void sim_ent_lerp(struct sim_ent *e, struct sim_ent *e0, struct sim_ent *e1, f64 blend)
|
||||||
@ -477,7 +366,7 @@ void sim_ent_lerp(struct sim_ent *e, struct sim_ent *e0, struct sim_ent *e1, f64
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Encode
|
* Ent encode
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
void sim_ent_encode(struct bitbuff_writer *bw, struct sim_ent *e0, struct sim_ent *e1)
|
void sim_ent_encode(struct bitbuff_writer *bw, struct sim_ent *e0, struct sim_ent *e1)
|
||||||
@ -506,7 +395,7 @@ void sim_ent_encode(struct bitbuff_writer *bw, struct sim_ent *e0, struct sim_en
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Decode
|
* Ent decode
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
void sim_ent_decode(struct bitbuff_reader *br, struct sim_ent *e)
|
void sim_ent_decode(struct bitbuff_reader *br, struct sim_ent *e)
|
||||||
|
|||||||
@ -20,6 +20,9 @@ enum sim_ent_prop {
|
|||||||
SIM_ENT_PROP_RELEASE_THIS_TICK,
|
SIM_ENT_PROP_RELEASE_THIS_TICK,
|
||||||
SIM_ENT_PROP_RELEASE_NEXT_TICK,
|
SIM_ENT_PROP_RELEASE_NEXT_TICK,
|
||||||
|
|
||||||
|
SIM_ENT_PROP_CLIENT,
|
||||||
|
SIM_ENT_PROP_LOCAL_CLIENT,
|
||||||
|
|
||||||
SIM_ENT_PROP_PHYSICAL_DYNAMIC,
|
SIM_ENT_PROP_PHYSICAL_DYNAMIC,
|
||||||
SIM_ENT_PROP_PHYSICAL_KINEMATIC,
|
SIM_ENT_PROP_PHYSICAL_KINEMATIC,
|
||||||
|
|
||||||
@ -54,29 +57,6 @@ enum sim_ent_prop {
|
|||||||
SIM_ENT_PROP_COUNT
|
SIM_ENT_PROP_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sim_ent_lookup_key {
|
|
||||||
u64 hash;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sim_ent_lookup_entry {
|
|
||||||
struct sim_ent_lookup_key key;
|
|
||||||
struct sim_ent_handle ent;
|
|
||||||
struct sim_ent_lookup_entry *next;
|
|
||||||
struct sim_ent_lookup_entry *prev;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sim_ent_lookup_bucket {
|
|
||||||
struct sim_ent_lookup_entry *first;
|
|
||||||
struct sim_ent_lookup_entry *last;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sim_ent_lookup {
|
|
||||||
struct arena arena;
|
|
||||||
struct sim_ent_lookup_bucket *buckets;
|
|
||||||
u64 num_buckets;
|
|
||||||
struct sim_ent_lookup_entry *first_free_entry;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sim_ent {
|
struct sim_ent {
|
||||||
/* ====================================================================== */
|
/* ====================================================================== */
|
||||||
/* Metadata */
|
/* Metadata */
|
||||||
@ -154,12 +134,31 @@ struct sim_ent {
|
|||||||
/* SIM_ENT_PROP_MOUSE_JOINT */
|
/* SIM_ENT_PROP_MOUSE_JOINT */
|
||||||
struct phys_mouse_joint mouse_joint_data;
|
struct phys_mouse_joint mouse_joint_data;
|
||||||
|
|
||||||
|
/* ====================================================================== */
|
||||||
|
/* Client */
|
||||||
|
|
||||||
|
/* SIM_ENT_PROP_CLIENT */
|
||||||
|
|
||||||
|
/* FIXME: Lerp */
|
||||||
|
|
||||||
|
struct sim_client_handle client_handle;
|
||||||
|
|
||||||
|
struct sim_control client_control;
|
||||||
|
struct v2 client_cursor_pos;
|
||||||
|
|
||||||
|
struct sim_ent_handle client_control_ent;
|
||||||
|
struct sim_ent_handle client_camera_ent;
|
||||||
|
|
||||||
|
struct sim_ent_handle client_dbg_drag_joint_ent;
|
||||||
|
b32 client_dbg_drag_start;
|
||||||
|
b32 client_dbg_drag_stop;
|
||||||
|
|
||||||
/* ====================================================================== */
|
/* ====================================================================== */
|
||||||
/* Control */
|
/* Control */
|
||||||
|
|
||||||
/* SIM_ENT_PROP_CONTROLLED */
|
/* SIM_ENT_PROP_CONTROLLED */
|
||||||
|
|
||||||
struct sim_client_handle controlling_client;
|
struct sim_ent_handle controlling_client;
|
||||||
|
|
||||||
f32 control_force; /* How much force is applied to achieve desired control movement */
|
f32 control_force; /* How much force is applied to achieve desired control movement */
|
||||||
f32 control_force_max_speed; /* Maximum linear velocity achieved by force (m/s) */
|
f32 control_force_max_speed; /* Maximum linear velocity achieved by force (m/s) */
|
||||||
@ -362,6 +361,18 @@ INLINE b32 sim_ent_is_valid_and_active(struct sim_ent *ent)
|
|||||||
struct sim_ent *sim_ent_alloc(struct sim_ent *parent);
|
struct sim_ent *sim_ent_alloc(struct sim_ent *parent);
|
||||||
void sim_ent_release(struct sim_ent *ent);
|
void sim_ent_release(struct sim_ent *ent);
|
||||||
|
|
||||||
|
/* Query */
|
||||||
|
struct sim_ent *sim_ent_from_handle(struct sim_snapshot *ss, struct sim_ent_handle handle);
|
||||||
|
struct sim_ent *sim_ent_find_first_match_one(struct sim_snapshot *ss, enum sim_ent_prop prop);
|
||||||
|
struct sim_ent *sim_ent_find_first_match_all(struct sim_snapshot *ss, struct sim_ent_prop_array props);
|
||||||
|
|
||||||
|
/* Tree */
|
||||||
|
void sim_ent_link_parent(struct sim_ent *parent, struct sim_ent *child);
|
||||||
|
void sim_ent_unlink_from_parent(struct sim_ent *ent);
|
||||||
|
|
||||||
|
/* Activate */
|
||||||
|
void sim_ent_activate(struct sim_ent *ent, u64 current_tick);
|
||||||
|
|
||||||
/* Xform */
|
/* Xform */
|
||||||
struct xform sim_ent_get_xform(struct sim_ent *ent);
|
struct xform sim_ent_get_xform(struct sim_ent *ent);
|
||||||
struct xform sim_ent_get_local_xform(struct sim_ent *ent);
|
struct xform sim_ent_get_local_xform(struct sim_ent *ent);
|
||||||
@ -377,26 +388,6 @@ void sim_ent_apply_force_to_center(struct sim_ent *ent, struct v2 force);
|
|||||||
void sim_ent_apply_angular_impulse(struct sim_ent *ent, f32 impulse);
|
void sim_ent_apply_angular_impulse(struct sim_ent *ent, f32 impulse);
|
||||||
void sim_ent_apply_torque(struct sim_ent *ent, f32 torque);
|
void sim_ent_apply_torque(struct sim_ent *ent, f32 torque);
|
||||||
|
|
||||||
/* Query */
|
|
||||||
struct sim_ent *sim_ent_from_handle(struct sim_snapshot *ss, struct sim_ent_handle handle);
|
|
||||||
struct sim_ent *sim_ent_find_first_match_one(struct sim_snapshot *ss, enum sim_ent_prop prop);
|
|
||||||
struct sim_ent *sim_ent_find_first_match_all(struct sim_snapshot *ss, struct sim_ent_prop_array props);
|
|
||||||
|
|
||||||
/* Tree */
|
|
||||||
void sim_ent_link_parent(struct sim_ent *parent, struct sim_ent *child);
|
|
||||||
void sim_ent_unlink_from_parent(struct sim_ent *ent);
|
|
||||||
|
|
||||||
/* Lookup */
|
|
||||||
struct sim_ent_lookup sim_ent_lookup_alloc(u64 num_buckets);
|
|
||||||
void sim_ent_lookup_release(struct sim_ent_lookup *l);
|
|
||||||
struct sim_ent_lookup_entry *sim_ent_lookup_get(struct sim_ent_lookup *l, struct sim_ent_lookup_key key);
|
|
||||||
void sim_ent_lookup_set(struct sim_ent_lookup *l, struct sim_ent_lookup_key key, struct sim_ent_handle handle);
|
|
||||||
void sim_ent_lookup_remove(struct sim_ent_lookup *l, struct sim_ent_lookup_entry *entry);
|
|
||||||
struct sim_ent_lookup_key sim_ent_lookup_key_from_two_handles(struct sim_ent_handle h0, struct sim_ent_handle h1);
|
|
||||||
|
|
||||||
/* Activate */
|
|
||||||
void sim_ent_activate(struct sim_ent *ent, u64 current_tick);
|
|
||||||
|
|
||||||
/* Lerp */
|
/* Lerp */
|
||||||
void sim_ent_lerp(struct sim_ent *e, struct sim_ent *e0, struct sim_ent *e1, f64 blend);
|
void sim_ent_lerp(struct sim_ent *e, struct sim_ent *e0, struct sim_ent *e1, f64 blend);
|
||||||
|
|
||||||
|
|||||||
@ -1,575 +0,0 @@
|
|||||||
#include "sim_snapshot.h"
|
|
||||||
#include "sim.h"
|
|
||||||
#include "sim_ent.h"
|
|
||||||
#include "sim_client.h"
|
|
||||||
#include "arena.h"
|
|
||||||
#include "scratch.h"
|
|
||||||
|
|
||||||
#define TICK_LOOKUP_BUCKETS 127
|
|
||||||
#define CLIENT_LOOKUP_BUCKETS 127
|
|
||||||
|
|
||||||
struct sim_snapshot_lookup_bucket {
|
|
||||||
struct sim_snapshot *first;
|
|
||||||
struct sim_snapshot *last;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Startup
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
GLOBAL struct {
|
|
||||||
struct arena nil_arena;
|
|
||||||
struct sim_snapshot_store *nil_snapshot_store;
|
|
||||||
struct sim_snapshot *nil_snapshot;
|
|
||||||
struct sim_ent *nil_ent;
|
|
||||||
struct sim_client *nil_client;
|
|
||||||
} G = ZI, DEBUG_ALIAS(G, G_sim_snapshot);
|
|
||||||
|
|
||||||
/* Accessed via `sim_snapshot_store_nil()` */
|
|
||||||
READONLY struct sim_snapshot_store **_g_sim_snapshot_store_nil = &G.nil_snapshot_store;
|
|
||||||
|
|
||||||
/* Accessed via `sim_snapshot_nil()` */
|
|
||||||
READONLY struct sim_snapshot **_g_sim_snapshot_nil = &G.nil_snapshot;
|
|
||||||
|
|
||||||
/* Accessed via `sim_ent_nil()` */
|
|
||||||
READONLY struct sim_ent **_g_sim_ent_nil = &G.nil_ent;
|
|
||||||
|
|
||||||
/* Accessed via `sim_client_nil()` */
|
|
||||||
READONLY struct sim_client **_g_sim_client_nil = &G.nil_client;
|
|
||||||
|
|
||||||
struct sim_snapshot_startup_receipt sim_snapshot_startup(void)
|
|
||||||
{
|
|
||||||
G.nil_arena = arena_alloc(GIGABYTE(1));
|
|
||||||
|
|
||||||
/* Nil snapshot store */
|
|
||||||
G.nil_snapshot_store = arena_push_zero(&G.nil_arena, struct sim_snapshot_store);
|
|
||||||
G.nil_snapshot_store->valid = false;
|
|
||||||
|
|
||||||
/* Nil snapshot */
|
|
||||||
G.nil_snapshot = arena_push_zero(&G.nil_arena, struct sim_snapshot);
|
|
||||||
G.nil_snapshot->valid = false;
|
|
||||||
G.nil_snapshot->store = sim_snapshot_store_nil();
|
|
||||||
|
|
||||||
/* Nil ent */
|
|
||||||
G.nil_ent = arena_push_zero(&G.nil_arena, struct sim_ent);
|
|
||||||
G.nil_ent->ss = sim_snapshot_nil();
|
|
||||||
G.nil_ent->valid = false;
|
|
||||||
G.nil_ent->handle = SIM_ENT_NIL_HANDLE;
|
|
||||||
G.nil_ent->local_xform = XFORM_IDENT;
|
|
||||||
G.nil_ent->cached_global_xform = XFORM_IDENT;
|
|
||||||
G.nil_ent->cached_global_xform_dirty = false;
|
|
||||||
G.nil_ent->friction = 0.5f;
|
|
||||||
G.nil_ent->mass_unscaled = 1;
|
|
||||||
G.nil_ent->inertia_unscaled = 1;
|
|
||||||
G.nil_ent->sprite_local_xform = XFORM_IDENT;
|
|
||||||
G.nil_ent->sprite_tint = COLOR_WHITE;
|
|
||||||
|
|
||||||
/* Nil client */
|
|
||||||
G.nil_client = arena_push_zero(&G.nil_arena, struct sim_client);
|
|
||||||
G.nil_client->ss = sim_snapshot_nil();
|
|
||||||
G.nil_client->valid = false;
|
|
||||||
|
|
||||||
/* Lock nil arena */
|
|
||||||
arena_set_readonly(&G.nil_arena);
|
|
||||||
return (struct sim_snapshot_startup_receipt ) { 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Store
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
struct sim_snapshot_store *sim_snapshot_store_alloc(void)
|
|
||||||
{
|
|
||||||
__prof;
|
|
||||||
struct sim_snapshot_store *store;
|
|
||||||
{
|
|
||||||
struct arena arena = arena_alloc(GIGABYTE(64));
|
|
||||||
store = arena_push_zero(&arena, struct sim_snapshot_store);
|
|
||||||
store->arena = arena;
|
|
||||||
}
|
|
||||||
store->valid = true;
|
|
||||||
store->num_lookup_buckets = TICK_LOOKUP_BUCKETS;
|
|
||||||
store->lookup_buckets = arena_push_array_zero(&store->arena, struct sim_snapshot_lookup_bucket, store->num_lookup_buckets);
|
|
||||||
return store;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sim_snapshot_store_release(struct sim_snapshot_store *store)
|
|
||||||
{
|
|
||||||
__prof;
|
|
||||||
/* Release snapshot internal memory */
|
|
||||||
for (u64 i = 0; i < store->num_lookup_buckets; ++i) {
|
|
||||||
struct sim_snapshot_lookup_bucket *bucket = &store->lookup_buckets[i];
|
|
||||||
struct sim_snapshot *ss = bucket->first;
|
|
||||||
while (ss) {
|
|
||||||
struct sim_snapshot *next = ss->next_in_bucket;
|
|
||||||
arena_release(&ss->clients_arena);
|
|
||||||
arena_release(&ss->ents_arena);
|
|
||||||
arena_release(&ss->arena);
|
|
||||||
ss = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
struct sim_snapshot *ss = store->first_free_snapshot;
|
|
||||||
while (ss) {
|
|
||||||
struct sim_snapshot *next = ss->next_free;
|
|
||||||
arena_release(&ss->clients_arena);
|
|
||||||
arena_release(&ss->ents_arena);
|
|
||||||
arena_release(&ss->arena);
|
|
||||||
ss = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Release store */
|
|
||||||
arena_release(&store->arena);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Releases all snapshots with tick in range [start, end] */
|
|
||||||
void sim_snapshot_store_release_ticks_in_range(struct sim_snapshot_store *store, u64 start, u64 end)
|
|
||||||
{
|
|
||||||
if (start > end) {
|
|
||||||
u64 swp = start;
|
|
||||||
start = end;
|
|
||||||
end = swp;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sim_snapshot *ss = sim_snapshot_from_tick(store, store->first_tick);
|
|
||||||
while (ss->valid) {
|
|
||||||
u64 tick = ss->tick;
|
|
||||||
u64 next_tick = ss->next_tick;
|
|
||||||
if (tick >= start) {
|
|
||||||
if (tick <= end) {
|
|
||||||
sim_snapshot_release(ss);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ss = sim_snapshot_from_tick(store, next_tick);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Alloc
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
/* Produces a new snapshot at `tick` with data copied from `src` snapshot. */
|
|
||||||
struct sim_snapshot *sim_snapshot_alloc(struct sim_snapshot_store *store, struct sim_snapshot *src, u64 tick)
|
|
||||||
{
|
|
||||||
if (tick == 0) {
|
|
||||||
return sim_snapshot_nil();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sim_snapshot *ss;
|
|
||||||
{
|
|
||||||
struct arena arena = ZI;
|
|
||||||
struct arena clients_arena = ZI;
|
|
||||||
struct arena ents_arena = ZI;
|
|
||||||
{
|
|
||||||
ss = store->first_free_snapshot;
|
|
||||||
if (ss) {
|
|
||||||
/* Re-use existing snasphot arenas */
|
|
||||||
store->first_free_snapshot = ss->next_free;
|
|
||||||
ents_arena = ss->ents_arena;
|
|
||||||
clients_arena = ss->clients_arena;
|
|
||||||
arena = ss->arena;
|
|
||||||
} else {
|
|
||||||
/* Arenas allocated here will be released along with the entire snasphot store */
|
|
||||||
arena = arena_alloc(GIGABYTE(8));
|
|
||||||
clients_arena = arena_alloc(GIGABYTE(8));
|
|
||||||
ents_arena = arena_alloc(GIGABYTE(8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
arena_reset(&arena);
|
|
||||||
ss = arena_push_zero(&arena, struct sim_snapshot);
|
|
||||||
ss->arena = arena;
|
|
||||||
|
|
||||||
ss->clients_arena = clients_arena;
|
|
||||||
arena_reset(&ss->clients_arena);
|
|
||||||
|
|
||||||
ss->ents_arena = ents_arena;
|
|
||||||
arena_reset(&ss->ents_arena);
|
|
||||||
}
|
|
||||||
|
|
||||||
ss->tick = tick;
|
|
||||||
ss->valid = true;
|
|
||||||
ss->store = store;
|
|
||||||
++store->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->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) {
|
|
||||||
ss->num_client_lookup_buckets = src->num_client_lookup_buckets;
|
|
||||||
ss->client_lookup_buckets = arena_push_array(&ss->arena, struct sim_client_lookup_bucket, ss->num_client_lookup_buckets);
|
|
||||||
MEMCPY(ss->client_lookup_buckets, src->client_lookup_buckets, sizeof(*ss->client_lookup_buckets) * ss->num_client_lookup_buckets);
|
|
||||||
} else {
|
|
||||||
ss->num_client_lookup_buckets = CLIENT_LOOKUP_BUCKETS;
|
|
||||||
ss->client_lookup_buckets = arena_push_array_zero(&ss->arena, struct sim_client_lookup_bucket, ss->num_client_lookup_buckets);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy clients */
|
|
||||||
ss->first_free_client = src->first_free_client;
|
|
||||||
ss->num_clients_allocated = src->num_clients_allocated;
|
|
||||||
ss->num_clients_reserved = src->num_clients_reserved;
|
|
||||||
ss->clients = arena_push_array(&ss->clients_arena, struct sim_client, ss->num_clients_reserved);
|
|
||||||
for (u64 i = 0; i < ss->num_clients_reserved; ++i) {
|
|
||||||
struct sim_client *dst_client = &ss->clients[i];
|
|
||||||
struct sim_client *src_client = &src->clients[i];
|
|
||||||
*dst_client = *src_client;
|
|
||||||
dst_client->ss = ss;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy entities */
|
|
||||||
ss->first_free_ent = src->first_free_ent;
|
|
||||||
ss->num_ents_allocated = src->num_ents_allocated;
|
|
||||||
ss->num_ents_reserved = src->num_ents_reserved;
|
|
||||||
ss->ents = arena_push_array(&ss->ents_arena, struct sim_ent, ss->num_ents_reserved);
|
|
||||||
if (ss->num_ents_reserved == 0) {
|
|
||||||
/* Create root ent if copying from nil store */
|
|
||||||
struct sim_ent *root = arena_push(&ss->ents_arena, struct sim_ent);
|
|
||||||
*root = *sim_ent_nil();
|
|
||||||
root->ss = ss;
|
|
||||||
root->handle = SIM_ENT_ROOT_HANDLE;
|
|
||||||
root->valid = true;
|
|
||||||
root->is_root = true;
|
|
||||||
++ss->num_ents_allocated;
|
|
||||||
++ss->num_ents_reserved;
|
|
||||||
} else {
|
|
||||||
for (u64 i = 0; i < ss->num_ents_reserved; ++i) {
|
|
||||||
struct sim_ent *dst_ent = &ss->ents[i];
|
|
||||||
struct sim_ent *src_ent = &src->ents[i];
|
|
||||||
*dst_ent = *src_ent;
|
|
||||||
dst_ent->ss = ss;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Release duplicate tick if it exists */
|
|
||||||
{
|
|
||||||
struct sim_snapshot *existing = sim_snapshot_from_tick(store, tick);
|
|
||||||
if (existing->valid) {
|
|
||||||
sim_snapshot_release(existing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Linear search to insert snapshot in tick order */
|
|
||||||
{
|
|
||||||
struct sim_snapshot *prev = sim_snapshot_from_tick(store, store->last_tick);
|
|
||||||
while (prev->valid) {
|
|
||||||
if (prev->tick < tick) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
prev = sim_snapshot_from_tick(store, prev->prev_tick);
|
|
||||||
}
|
|
||||||
if (prev->valid) {
|
|
||||||
struct sim_snapshot *next = sim_snapshot_from_tick(store, prev->next_tick);
|
|
||||||
if (next->valid) {
|
|
||||||
next->prev_tick = tick;
|
|
||||||
} else {
|
|
||||||
store->last_tick = tick;
|
|
||||||
}
|
|
||||||
ss->next_tick = prev->next_tick;
|
|
||||||
ss->prev_tick = prev->tick;
|
|
||||||
prev->next_tick = ss->tick;
|
|
||||||
} else {
|
|
||||||
struct sim_snapshot *first = sim_snapshot_from_tick(store, store->first_tick);
|
|
||||||
if (first->valid) {
|
|
||||||
ss->next_tick = first->tick;
|
|
||||||
first->prev_tick = tick;
|
|
||||||
} else {
|
|
||||||
store->last_tick = tick;
|
|
||||||
}
|
|
||||||
ss->next_tick = store->first_tick;
|
|
||||||
store->first_tick = tick;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Insert into lookup */
|
|
||||||
{
|
|
||||||
u64 bucket_index = tick % store->num_lookup_buckets;
|
|
||||||
struct sim_snapshot_lookup_bucket *bucket = &store->lookup_buckets[bucket_index];
|
|
||||||
if (bucket->last) {
|
|
||||||
bucket->last->next_in_bucket = ss;
|
|
||||||
ss->prev_in_bucket = bucket->last;
|
|
||||||
} else {
|
|
||||||
bucket->first = ss;
|
|
||||||
}
|
|
||||||
bucket->last = ss;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ss;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sim_snapshot_release(struct sim_snapshot *ss)
|
|
||||||
{
|
|
||||||
struct sim_snapshot_store *store = ss->store;
|
|
||||||
|
|
||||||
/* Remove from lookup */
|
|
||||||
{
|
|
||||||
u64 bucket_index = ss->tick % store->num_lookup_buckets;
|
|
||||||
struct sim_snapshot_lookup_bucket *bucket = &store->lookup_buckets[bucket_index];
|
|
||||||
struct sim_snapshot *prev = ss->prev_in_bucket;
|
|
||||||
struct sim_snapshot *next = ss->next_in_bucket;
|
|
||||||
if (prev) {
|
|
||||||
prev->next_in_bucket = next;
|
|
||||||
} else {
|
|
||||||
bucket->first = next;
|
|
||||||
}
|
|
||||||
if (next) {
|
|
||||||
next->prev_in_bucket = prev;
|
|
||||||
} else {
|
|
||||||
bucket->last = prev;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove from snapshot list */
|
|
||||||
{
|
|
||||||
struct sim_snapshot *prev = sim_snapshot_from_tick(store, ss->prev_tick);
|
|
||||||
struct sim_snapshot *next = sim_snapshot_from_tick(store, ss->next_tick);
|
|
||||||
if (prev->valid) {
|
|
||||||
prev->next_tick = next->tick;
|
|
||||||
} else {
|
|
||||||
store->first_tick = next->tick;
|
|
||||||
}
|
|
||||||
if (next->valid) {
|
|
||||||
next->prev_tick = prev->tick;
|
|
||||||
} else {
|
|
||||||
store->last_tick = prev->tick;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ss->valid = false;
|
|
||||||
ss->next_free = store->first_free_snapshot;
|
|
||||||
store->first_free_snapshot = ss;
|
|
||||||
--store->num_ticks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Lookup
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
struct sim_snapshot *sim_snapshot_from_tick(struct sim_snapshot_store *store, u64 tick)
|
|
||||||
{
|
|
||||||
struct sim_snapshot *ss = sim_snapshot_nil();
|
|
||||||
if (tick > 0) {
|
|
||||||
u64 bucket_index = tick % store->num_lookup_buckets;
|
|
||||||
struct sim_snapshot_lookup_bucket *bucket = &store->lookup_buckets[bucket_index];
|
|
||||||
for (struct sim_snapshot *search = bucket->first; search; search = search->next_in_bucket) {
|
|
||||||
if (search->tick == tick) {
|
|
||||||
ss = search;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ss;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Lerp
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
struct sim_snapshot *sim_snapshot_alloc_from_lerp(struct sim_snapshot_store *store, struct sim_snapshot *ss0, struct sim_snapshot *ss1, f64 blend)
|
|
||||||
{
|
|
||||||
__prof;
|
|
||||||
|
|
||||||
/* New snapshot will be allocated with same tick as ss0 or ss1, so the result should go into a different store */
|
|
||||||
ASSERT(ss0->store != store && ss1->store != store);
|
|
||||||
|
|
||||||
struct sim_snapshot *ss;
|
|
||||||
b32 should_blend = true;
|
|
||||||
if (ss0->continuity_gen == ss1->continuity_gen && 0 < blend && blend < 1) {
|
|
||||||
ss = sim_snapshot_alloc(store, ss0, ss0->tick);
|
|
||||||
} else if (math_round_to_int64(blend) <= 0) {
|
|
||||||
ss = sim_snapshot_alloc(store, ss0, ss0->tick);
|
|
||||||
should_blend = false;
|
|
||||||
} else {
|
|
||||||
ss = sim_snapshot_alloc(store, ss1, ss1->tick);
|
|
||||||
should_blend = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ss0->valid || !ss1->valid) {
|
|
||||||
/* New snapshot allocation caused one of the src snapshots original to release.
|
|
||||||
* ss0 & ss1 should be from a separate store than the allocating one. */
|
|
||||||
ASSERT(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
/* Blend clients */
|
|
||||||
{
|
|
||||||
__profscope(snapshot_lerp_clients);
|
|
||||||
u64 num_clients = min_u64(ss0->num_clients_reserved, ss1->num_clients_reserved);
|
|
||||||
for (u64 i = 0; i < num_clients; ++i) {
|
|
||||||
struct sim_client *c = &ss->clients[i];
|
|
||||||
struct sim_client *c0 = &ss0->clients[i];
|
|
||||||
struct sim_client *c1 = &ss1->clients[i];
|
|
||||||
sim_client_lerp(c, c0, c1, blend);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Blend entities */
|
|
||||||
{
|
|
||||||
__profscope(snapshot_lerp_entities);
|
|
||||||
u64 num_entities = min_u64(ss0->num_ents_reserved, ss1->num_ents_reserved);
|
|
||||||
for (u64 i = 0; i < num_entities; ++i) {
|
|
||||||
struct sim_ent *e = &ss->ents[i];
|
|
||||||
struct sim_ent *e0 = &ss0->ents[i];
|
|
||||||
struct sim_ent *e1 = &ss1->ents[i];
|
|
||||||
sim_ent_lerp(e, e0, e1, blend);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return ss;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Encode
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_client *receiver, struct sim_snapshot *ss0, struct sim_snapshot *ss1)
|
|
||||||
{
|
|
||||||
__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_uv(bw, ss1->continuity_gen);
|
|
||||||
bw_write_uv(bw, ss1->phys_iteration);
|
|
||||||
|
|
||||||
bw_write_uv(bw, receiver->handle.gen);
|
|
||||||
bw_write_uv(bw, receiver->handle.idx);
|
|
||||||
|
|
||||||
/* Clients */
|
|
||||||
if (ss1->num_clients_allocated == ss0->num_clients_allocated) {
|
|
||||||
bw_write_bit(bw, 0);
|
|
||||||
} else {
|
|
||||||
bw_write_bit(bw, 1);
|
|
||||||
bw_write_uv(bw, ss1->num_clients_allocated);
|
|
||||||
}
|
|
||||||
if (ss1->num_clients_reserved == ss0->num_clients_reserved) {
|
|
||||||
bw_write_bit(bw, 0);
|
|
||||||
} else {
|
|
||||||
bw_write_bit(bw, 1);
|
|
||||||
bw_write_uv(bw, ss1->num_clients_reserved);
|
|
||||||
}
|
|
||||||
for (u64 i = 0; i < ss1->num_clients_reserved; ++i) {
|
|
||||||
struct sim_client *c0 = sim_client_nil();
|
|
||||||
if (i < ss0->num_clients_reserved) {
|
|
||||||
c0 = &ss0->clients[i];
|
|
||||||
}
|
|
||||||
struct sim_client *c1 = &ss1->clients[i];
|
|
||||||
sim_client_encode(bw, c0, c1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ents */
|
|
||||||
if (ss1->num_ents_allocated == ss0->num_ents_allocated) {
|
|
||||||
bw_write_bit(bw, 0);
|
|
||||||
} else {
|
|
||||||
bw_write_bit(bw, 1);
|
|
||||||
bw_write_uv(bw, ss1->num_ents_allocated);
|
|
||||||
}
|
|
||||||
if (ss1->num_ents_reserved == ss0->num_ents_reserved) {
|
|
||||||
bw_write_bit(bw, 0);
|
|
||||||
} else {
|
|
||||||
bw_write_bit(bw, 1);
|
|
||||||
bw_write_uv(bw, ss1->num_ents_reserved);
|
|
||||||
}
|
|
||||||
for (u64 i = 0; i < ss1->num_ents_reserved; ++i) {
|
|
||||||
struct sim_ent *e0 = sim_ent_nil();
|
|
||||||
if (i < ss0->num_ents_reserved) {
|
|
||||||
e0 = &ss0->ents[i];
|
|
||||||
}
|
|
||||||
struct sim_ent *e1 = &ss1->ents[i];
|
|
||||||
sim_ent_encode(bw, e0, e1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Decode
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
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->continuity_gen = br_read_uv(br);
|
|
||||||
ss->phys_iteration = br_read_uv(br);
|
|
||||||
|
|
||||||
ss->local_client.gen = br_read_uv(br);
|
|
||||||
ss->local_client.idx = br_read_uv(br);
|
|
||||||
|
|
||||||
/* Clients */
|
|
||||||
if (br_read_bit(br)) {
|
|
||||||
ss->num_clients_allocated = br_read_uv(br);
|
|
||||||
}
|
|
||||||
if (br_read_bit(br)) {
|
|
||||||
u64 old_num_clients_reserved = ss->num_clients_reserved;
|
|
||||||
ss->num_clients_reserved = br_read_uv(br);
|
|
||||||
i64 reserve_diff = (i64)ss->num_clients_reserved - (i64)old_num_clients_reserved;
|
|
||||||
if (reserve_diff > 0) {
|
|
||||||
arena_push_array(&ss->clients_arena, struct sim_client, reserve_diff);
|
|
||||||
for (u64 i = old_num_clients_reserved; i < ss->num_clients_reserved; ++i) {
|
|
||||||
struct sim_client *c = &ss->clients[i];
|
|
||||||
*c = *sim_client_nil();
|
|
||||||
c->ss = ss;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (u64 i = 0; i < ss->num_clients_reserved; ++i) {
|
|
||||||
struct sim_client *c = &ss->clients[i];
|
|
||||||
sim_client_decode(br, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ents */
|
|
||||||
if (br_read_bit(br)) {
|
|
||||||
ss->num_ents_allocated = br_read_uv(br);
|
|
||||||
}
|
|
||||||
if (br_read_bit(br)) {
|
|
||||||
u64 old_num_ents_reserved = ss->num_ents_reserved;
|
|
||||||
ss->num_ents_reserved = br_read_uv(br);
|
|
||||||
i64 reserve_diff = (i64)ss->num_ents_reserved - (i64)old_num_ents_reserved;
|
|
||||||
if (reserve_diff > 0) {
|
|
||||||
arena_push_array(&ss->ents_arena, struct sim_ent, reserve_diff);
|
|
||||||
for (u64 i = old_num_ents_reserved; i < ss->num_ents_reserved; ++i) {
|
|
||||||
struct sim_ent *e = &ss->ents[i];
|
|
||||||
*e = *sim_ent_nil();
|
|
||||||
e->ss = ss;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (u64 i = 0; i < ss->num_ents_reserved; ++i) {
|
|
||||||
struct sim_ent *e = &ss->ents[i];
|
|
||||||
sim_ent_decode(br, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,160 +0,0 @@
|
|||||||
#ifndef SIM_SNAPSHOT_H
|
|
||||||
#define SIM_SNAPSHOT_H
|
|
||||||
|
|
||||||
#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),
|
|
||||||
|
|
||||||
/* The snapshot contains entity state data */
|
|
||||||
SIM_SNAPSHOT_FLAG_STATE = (1 << 1)
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct sim_snapshot_store {
|
|
||||||
b32 valid;
|
|
||||||
struct arena arena;
|
|
||||||
|
|
||||||
/* Snapshots sorted by tick (low to high) */
|
|
||||||
u64 first_tick;
|
|
||||||
u64 last_tick;
|
|
||||||
u64 num_ticks;
|
|
||||||
|
|
||||||
struct sim_snapshot *first_free_snapshot;
|
|
||||||
|
|
||||||
/* Tick -> snapshot lookup */
|
|
||||||
u64 num_lookup_buckets;
|
|
||||||
struct sim_snapshot_lookup_bucket *lookup_buckets;
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
struct sim_snapshot *prev_in_bucket;
|
|
||||||
u64 prev_tick;
|
|
||||||
u64 next_tick;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
/* Time of local snapshot publish to user thread */
|
|
||||||
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;
|
|
||||||
|
|
||||||
/* If != previous tick's continuity then don't lerp */
|
|
||||||
u64 continuity_gen;
|
|
||||||
|
|
||||||
/* The last physics iteration (used for tracking contact lifetime) */
|
|
||||||
u64 phys_iteration;
|
|
||||||
|
|
||||||
/* Which client in the client store represents the local host in the original snapshot */
|
|
||||||
struct sim_client_handle local_client;
|
|
||||||
|
|
||||||
/* Client lookup */
|
|
||||||
struct sim_client_lookup_bucket *client_lookup_buckets;
|
|
||||||
u64 num_client_lookup_buckets;
|
|
||||||
|
|
||||||
/* Clients */
|
|
||||||
struct arena clients_arena;
|
|
||||||
struct sim_client *clients;
|
|
||||||
struct sim_client_handle first_free_client;
|
|
||||||
u64 num_clients_allocated;
|
|
||||||
u64 num_clients_reserved;
|
|
||||||
|
|
||||||
/* Entities */
|
|
||||||
struct arena ents_arena;
|
|
||||||
struct sim_ent *ents;
|
|
||||||
struct sim_ent_handle first_free_ent;
|
|
||||||
u64 num_ents_allocated;
|
|
||||||
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
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
struct sim_snapshot_startup_receipt { i32 _; };
|
|
||||||
struct sim_snapshot_startup_receipt sim_snapshot_startup(void);
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Store
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
struct sim_snapshot_store *sim_snapshot_store_alloc(void);
|
|
||||||
void sim_snapshot_store_release(struct sim_snapshot_store *store);
|
|
||||||
void sim_snapshot_store_release_ticks_in_range(struct sim_snapshot_store *store, u64 start, u64 end);
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Nil
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
INLINE struct sim_snapshot *sim_snapshot_nil(void)
|
|
||||||
{
|
|
||||||
extern READONLY struct sim_snapshot **_g_sim_snapshot_nil;
|
|
||||||
return *_g_sim_snapshot_nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
INLINE struct sim_snapshot_store *sim_snapshot_store_nil(void)
|
|
||||||
{
|
|
||||||
extern READONLY struct sim_snapshot_store **_g_sim_snapshot_store_nil;
|
|
||||||
return *_g_sim_snapshot_store_nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Snapshot
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
struct bitbuff_writer;
|
|
||||||
struct bitbuff_reader;
|
|
||||||
|
|
||||||
/* Alloc */
|
|
||||||
struct sim_snapshot *sim_snapshot_alloc(struct sim_snapshot_store *store, struct sim_snapshot *src, u64 tick);
|
|
||||||
void sim_snapshot_release(struct sim_snapshot *sim_snapshot);
|
|
||||||
|
|
||||||
/* Lookup */
|
|
||||||
struct sim_snapshot *sim_snapshot_from_tick(struct sim_snapshot_store *store, u64 tick);
|
|
||||||
|
|
||||||
/* Lerp */
|
|
||||||
struct sim_snapshot *sim_snapshot_alloc_from_lerp(struct sim_snapshot_store *store, struct sim_snapshot *ss0, struct sim_snapshot *ss1, f64 blend);
|
|
||||||
|
|
||||||
/* Encode / decode */
|
|
||||||
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
|
|
||||||
1459
src/sim_step.c
Normal file
1459
src/sim_step.c
Normal file
File diff suppressed because it is too large
Load Diff
83
src/sim_step.h
Normal file
83
src/sim_step.h
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#ifndef SIM_STEP_H
|
||||||
|
#define SIM_STEP_H
|
||||||
|
|
||||||
|
struct space;
|
||||||
|
struct sim_snapshot;
|
||||||
|
struct sim_snapshot_list;
|
||||||
|
struct sim_lookup;
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Sim lookup
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
/* Structure used to accelerate up entity lookup (rebuilt every step) */
|
||||||
|
/* TODO: Remove this and do something better. Just a hack to de-couple old sim ctx from step. */
|
||||||
|
|
||||||
|
struct sim_lookup_key {
|
||||||
|
u64 hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sim_lookup_entry {
|
||||||
|
struct sim_lookup_key key;
|
||||||
|
struct sim_ent_handle ent;
|
||||||
|
struct sim_lookup_entry *next;
|
||||||
|
struct sim_lookup_entry *prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sim_lookup_bucket {
|
||||||
|
struct sim_lookup_entry *first;
|
||||||
|
struct sim_lookup_entry *last;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sim_lookup {
|
||||||
|
struct arena arena;
|
||||||
|
struct sim_lookup_bucket *buckets;
|
||||||
|
u64 num_buckets;
|
||||||
|
struct sim_lookup_entry *first_free_entry;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sim_lookup sim_lookup_alloc(u64 num_buckets);
|
||||||
|
void sim_lookup_release(struct sim_lookup *l);
|
||||||
|
void sim_lookup_reset(struct sim_lookup *l);
|
||||||
|
|
||||||
|
struct sim_lookup_entry *sim_lookup_get(struct sim_lookup *l, struct sim_lookup_key key);
|
||||||
|
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_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);
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Sim accel
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
/* Structure used to accelerate up entity lookup (rebuilt every step) */
|
||||||
|
/* TODO: Remove this and do something better. Just a hack to de-couple old sim ctx from step. */
|
||||||
|
|
||||||
|
struct sim_accel {
|
||||||
|
struct space *space;
|
||||||
|
struct sim_lookup client_lookup;
|
||||||
|
struct sim_lookup contact_lookup;
|
||||||
|
#if COLLIDER_DEBUG
|
||||||
|
struct sim_lookup collision_debug_lookup;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sim_accel sim_accel_alloc(void);
|
||||||
|
void sim_accel_release(struct sim_accel *accel);
|
||||||
|
void sim_accel_rebuild(struct sim_snapshot *ss, struct sim_accel *accel);
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Sim step
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct sim_step_ctx {
|
||||||
|
struct sim_accel *accel;
|
||||||
|
struct sim_snapshot *world;
|
||||||
|
struct sim_snapshot_list *cmd_snapshots;
|
||||||
|
i64 real_dt_ns;
|
||||||
|
};
|
||||||
|
|
||||||
|
void sim_step(struct sim_step_ctx *step_ctx);
|
||||||
|
|
||||||
|
#endif
|
||||||
18
src/space.c
18
src/space.c
@ -21,12 +21,15 @@ READONLY struct space _g_space_nil = { .valid = false };
|
|||||||
* The number of buckets determines how often tiles will collide in the spatial hash.
|
* The number of buckets determines how often tiles will collide in the spatial hash.
|
||||||
* For example, at `num_buckets_sqrt` = 256 (65536 buckets), tiles <1, 1>, <1, 257>, and <257, 257> will collide. */
|
* For example, at `num_buckets_sqrt` = 256 (65536 buckets), tiles <1, 1>, <1, 257>, and <257, 257> will collide. */
|
||||||
struct space *space_alloc(f32 cell_size, u32 num_buckets_sqrt)
|
struct space *space_alloc(f32 cell_size, u32 num_buckets_sqrt)
|
||||||
|
{
|
||||||
|
struct space *space;
|
||||||
{
|
{
|
||||||
struct arena arena = arena_alloc(GIGABYTE(64));
|
struct arena arena = arena_alloc(GIGABYTE(64));
|
||||||
struct space *space = arena_push_zero(&arena, struct space);
|
space = arena_push_zero(&arena, struct space);
|
||||||
|
space->entry_arena = arena;
|
||||||
|
}
|
||||||
|
|
||||||
space->valid = true;
|
space->valid = true;
|
||||||
space->entry_arena = arena;
|
|
||||||
space->entries = arena_dry_push(&space->entry_arena, struct space_entry);
|
space->entries = arena_dry_push(&space->entry_arena, struct space_entry);
|
||||||
|
|
||||||
space->cell_arena = arena_alloc(GIGABYTE(64));
|
space->cell_arena = arena_alloc(GIGABYTE(64));
|
||||||
@ -44,6 +47,17 @@ void space_release(struct space *space)
|
|||||||
arena_release(&space->entry_arena);
|
arena_release(&space->entry_arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void space_reset(struct space *space)
|
||||||
|
{
|
||||||
|
arena_pop_to(&space->entry_arena, (u64)space->entries - (u64)space->entry_arena.base);
|
||||||
|
arena_reset(&space->cell_arena);
|
||||||
|
space->buckets = arena_push_array_zero(&space->cell_arena, struct space_cell_bucket, space->num_buckets);;
|
||||||
|
space->num_entries_reserved = 0;
|
||||||
|
space->first_free_cell = NULL;
|
||||||
|
space->first_free_cell_node = NULL;
|
||||||
|
space->first_free_entry = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
struct space *space_from_entry(struct space_entry *entry)
|
struct space *space_from_entry(struct space_entry *entry)
|
||||||
{
|
{
|
||||||
if (entry->valid) {
|
if (entry->valid) {
|
||||||
|
|||||||
@ -106,6 +106,8 @@ INLINE struct space *space_nil(void)
|
|||||||
|
|
||||||
struct space *space_alloc(f32 cell_size, u32 num_buckets_sqrt);
|
struct space *space_alloc(f32 cell_size, u32 num_buckets_sqrt);
|
||||||
void space_release(struct space *space);
|
void space_release(struct space *space);
|
||||||
|
void space_reset(struct space *space);
|
||||||
|
|
||||||
struct space *space_from_entry(struct space_entry *entry);
|
struct space *space_from_entry(struct space_entry *entry);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
|
|||||||
496
src/user.c
496
src/user.c
@ -2,7 +2,7 @@
|
|||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "sim.h"
|
#include "sim.h"
|
||||||
#include "sim_ent.h"
|
#include "sim_ent.h"
|
||||||
#include "sim_snapshot.h"
|
#include "sim_step.h"
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
#include "sprite.h"
|
#include "sprite.h"
|
||||||
@ -48,12 +48,10 @@ GLOBAL struct {
|
|||||||
struct sys_window *window;
|
struct sys_window *window;
|
||||||
struct string connect_address_str;
|
struct string connect_address_str;
|
||||||
|
|
||||||
struct sim_snapshot_store *unblended_snapshot_store; /* Contains buffered snapshots from sim */
|
struct sim_client_store *user_client_store;
|
||||||
struct sim_snapshot_store *blended_snapshot_store; /* Contains single world snapshot from result of blending sim snapshots */
|
struct sim_client *user_unblended_snapshots_client; /* Contains buffered snapshots received from sim */
|
||||||
struct sim_snapshot *ss_blended;
|
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 */
|
||||||
/* Dynamic bitbuff used by encoders */
|
|
||||||
struct bitbuff encoder_bitbuff;
|
|
||||||
|
|
||||||
/* Usage stats */
|
/* Usage stats */
|
||||||
i64 last_second_reset_ns;
|
i64 last_second_reset_ns;
|
||||||
@ -93,8 +91,9 @@ GLOBAL struct {
|
|||||||
u64 user_sim_cmd_ack;
|
u64 user_sim_cmd_ack;
|
||||||
|
|
||||||
/* Local sim -> user */
|
/* Local sim -> user */
|
||||||
struct sys_mutex local_sim_ss_mutex;
|
struct sys_mutex local_sim_to_user_mutex;
|
||||||
struct sim_snapshot_store *local_sim_ss_store;
|
struct sim_client_store *local_sim_to_user_client_store;
|
||||||
|
struct sim_client *local_sim_to_user_client;
|
||||||
|
|
||||||
/* Rolling window of local sim publish times */
|
/* Rolling window of local sim publish times */
|
||||||
i64 last_snapshot_published_at_ns;
|
i64 last_snapshot_published_at_ns;
|
||||||
@ -180,7 +179,7 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
|
|||||||
struct mixer_startup_receipt *mixer_sr,
|
struct mixer_startup_receipt *mixer_sr,
|
||||||
struct phys_startup_receipt *phys_sr,
|
struct phys_startup_receipt *phys_sr,
|
||||||
struct host_startup_receipt *host_sr,
|
struct host_startup_receipt *host_sr,
|
||||||
struct sim_snapshot_startup_receipt *sim_snapshot_sr,
|
struct sim_startup_receipt *sim_sr,
|
||||||
struct string connect_address_str,
|
struct string connect_address_str,
|
||||||
struct sys_window *window)
|
struct sys_window *window)
|
||||||
{
|
{
|
||||||
@ -194,30 +193,31 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
|
|||||||
(UNUSED)mixer_sr;
|
(UNUSED)mixer_sr;
|
||||||
(UNUSED)phys_sr;
|
(UNUSED)phys_sr;
|
||||||
(UNUSED)host_sr;
|
(UNUSED)host_sr;
|
||||||
(UNUSED)sim_snapshot_sr;
|
(UNUSED)sim_sr;
|
||||||
|
|
||||||
G.arena = arena_alloc(GIGABYTE(64));
|
G.arena = arena_alloc(GIGABYTE(64));
|
||||||
|
G.real_time_ns = sys_time_ns();
|
||||||
|
|
||||||
/* TODO: Remove this */
|
/* TODO: Remove this */
|
||||||
G.connect_address_str = string_copy(&G.arena, connect_address_str);
|
G.connect_address_str = string_copy(&G.arena, connect_address_str);
|
||||||
|
|
||||||
/* Snapshot store */
|
|
||||||
G.unblended_snapshot_store = sim_snapshot_store_alloc();
|
|
||||||
G.blended_snapshot_store = sim_snapshot_store_alloc();
|
|
||||||
G.ss_blended = sim_snapshot_nil();
|
|
||||||
|
|
||||||
/* Sys events */
|
/* Sys events */
|
||||||
G.sys_events_mutex = sys_mutex_alloc();
|
G.sys_events_mutex = sys_mutex_alloc();
|
||||||
G.sys_events_arena = arena_alloc(GIGABYTE(64));
|
G.sys_events_arena = arena_alloc(GIGABYTE(64));
|
||||||
|
|
||||||
/* User sim control */
|
/* Snapshot store */
|
||||||
G.user_sim_cmd_mutex = sys_mutex_alloc();
|
G.user_client_store = sim_client_store_alloc();
|
||||||
|
G.user_unblended_snapshots_client = sim_client_alloc(G.user_client_store, SIM_CLIENT_KIND_USER);
|
||||||
|
G.user_blended_snapshots_client = sim_client_alloc(G.user_client_store, SIM_CLIENT_KIND_USER);
|
||||||
|
G.ss_blended = sim_snapshot_nil();
|
||||||
|
|
||||||
/* Local sim snapshot store */
|
/* Local sim snapshot store */
|
||||||
G.local_sim_ss_mutex = sys_mutex_alloc();
|
G.local_sim_to_user_mutex = sys_mutex_alloc();
|
||||||
G.local_sim_ss_store = sim_snapshot_store_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);
|
||||||
|
|
||||||
G.encoder_bitbuff = bitbuff_alloc(GIGABYTE(64));
|
/* User sim control */
|
||||||
|
G.user_sim_cmd_mutex = sys_mutex_alloc();
|
||||||
|
|
||||||
G.world_to_ui_xf = XFORM_IDENT;
|
G.world_to_ui_xf = XFORM_IDENT;
|
||||||
G.world_cmd_buffer = renderer_cmd_buffer_alloc();
|
G.world_cmd_buffer = renderer_cmd_buffer_alloc();
|
||||||
@ -225,8 +225,6 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
|
|||||||
G.final_cmd_buffer = renderer_cmd_buffer_alloc();
|
G.final_cmd_buffer = renderer_cmd_buffer_alloc();
|
||||||
G.backbuffer_cmd_buffer = renderer_cmd_buffer_alloc();
|
G.backbuffer_cmd_buffer = renderer_cmd_buffer_alloc();
|
||||||
|
|
||||||
G.real_time_ns = sys_time_ns();
|
|
||||||
|
|
||||||
G.window = window;
|
G.window = window;
|
||||||
sys_window_register_event_callback(G.window, &window_event_callback);
|
sys_window_register_event_callback(G.window, &window_event_callback);
|
||||||
|
|
||||||
@ -393,22 +391,17 @@ INTERNAL void user_update(void)
|
|||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
{
|
{
|
||||||
struct sys_lock lock = sys_mutex_lock_e(&G.local_sim_ss_mutex);
|
struct sys_lock lock = sys_mutex_lock_e(&G.local_sim_to_user_mutex);
|
||||||
u64 old_last_tick = G.unblended_snapshot_store->last_tick;
|
u64 old_last_tick = G.user_unblended_snapshots_client->last_tick;
|
||||||
u64 last_tick = G.local_sim_ss_store->last_tick;
|
u64 last_tick = G.local_sim_to_user_client->last_tick;
|
||||||
if (last_tick > old_last_tick) {
|
if (last_tick > old_last_tick) {
|
||||||
struct sim_snapshot *src = sim_snapshot_from_tick(G.local_sim_ss_store, last_tick);
|
struct sim_snapshot *src = sim_snapshot_from_tick(G.local_sim_to_user_client, last_tick);
|
||||||
sim_snapshot_alloc(G.unblended_snapshot_store, src, src->tick);
|
sim_snapshot_alloc(G.user_unblended_snapshots_client, src, src->tick);
|
||||||
|
|
||||||
#if 0
|
|
||||||
G.last_snapshot_received_at_ns = G.real_time_ns;
|
|
||||||
#else
|
|
||||||
G.last_snapshot_published_at_ns = src->publish_time_ns;
|
G.last_snapshot_published_at_ns = src->publish_time_ns;
|
||||||
G.snapshot_publish_dts_ns[G.snapshot_publish_dts_index++] = src->publish_dt_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)) {
|
if (G.snapshot_publish_dts_index >= (i64)ARRAY_COUNT(G.snapshot_publish_dts_ns)) {
|
||||||
G.snapshot_publish_dts_index = 0;
|
G.snapshot_publish_dts_index = 0;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
sys_mutex_unlock(&lock);
|
sys_mutex_unlock(&lock);
|
||||||
}
|
}
|
||||||
@ -450,7 +443,7 @@ INTERNAL void user_update(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Predict local sim time based on average snapshot publish dt. */
|
/* Predict local sim time based on average snapshot publish dt. */
|
||||||
struct sim_snapshot *newest_snapshot = sim_snapshot_from_tick(G.unblended_snapshot_store, G.unblended_snapshot_store->last_tick);
|
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;
|
G.local_sim_last_known_time_ns = newest_snapshot->real_time_ns;
|
||||||
G.local_sim_last_known_tick = newest_snapshot->tick;
|
G.local_sim_last_known_tick = newest_snapshot->tick;
|
||||||
u64 keep_unblended_tick = newest_snapshot->tick;
|
u64 keep_unblended_tick = newest_snapshot->tick;
|
||||||
@ -474,7 +467,7 @@ INTERNAL void user_update(void)
|
|||||||
struct sim_snapshot *left_snapshot = sim_snapshot_nil();
|
struct sim_snapshot *left_snapshot = sim_snapshot_nil();
|
||||||
struct sim_snapshot *right_snapshot = newest_snapshot;
|
struct sim_snapshot *right_snapshot = newest_snapshot;
|
||||||
{
|
{
|
||||||
struct sim_snapshot *ss = sim_snapshot_from_tick(G.unblended_snapshot_store, G.unblended_snapshot_store->first_tick);
|
struct sim_snapshot *ss = sim_snapshot_from_tick(G.user_unblended_snapshots_client, G.user_unblended_snapshots_client->first_tick);
|
||||||
while (ss->valid) {
|
while (ss->valid) {
|
||||||
u64 next_tick = ss->next_tick;
|
u64 next_tick = ss->next_tick;
|
||||||
i64 ss_time_ns = ss->real_time_ns;
|
i64 ss_time_ns = ss->real_time_ns;
|
||||||
@ -484,7 +477,7 @@ INTERNAL void user_update(void)
|
|||||||
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->real_time_ns) {
|
||||||
right_snapshot = ss;
|
right_snapshot = ss;
|
||||||
}
|
}
|
||||||
ss = sim_snapshot_from_tick(G.unblended_snapshot_store, next_tick);
|
ss = sim_snapshot_from_tick(G.user_unblended_snapshots_client, next_tick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,11 +488,11 @@ INTERNAL void user_update(void)
|
|||||||
/* Create world from blended snapshots */
|
/* Create world from blended snapshots */
|
||||||
if (left_snapshot->valid && right_snapshot->valid) {
|
if (left_snapshot->valid && right_snapshot->valid) {
|
||||||
f64 blend = (f64)(G.render_time_ns - left_snapshot->real_time_ns) / (f64)(right_snapshot->real_time_ns - left_snapshot->real_time_ns);
|
f64 blend = (f64)(G.render_time_ns - left_snapshot->real_time_ns) / (f64)(right_snapshot->real_time_ns - left_snapshot->real_time_ns);
|
||||||
G.ss_blended = sim_snapshot_alloc_from_lerp(G.blended_snapshot_store, left_snapshot, right_snapshot, blend);
|
G.ss_blended = sim_snapshot_alloc_from_lerp(G.user_blended_snapshots_client, left_snapshot, right_snapshot, blend);
|
||||||
} else if (left_snapshot->valid) {
|
} else if (left_snapshot->valid) {
|
||||||
G.ss_blended = sim_snapshot_alloc(G.blended_snapshot_store, left_snapshot, left_snapshot->tick);
|
G.ss_blended = sim_snapshot_alloc(G.user_blended_snapshots_client, left_snapshot, left_snapshot->tick);
|
||||||
} else if (right_snapshot->valid) {
|
} else if (right_snapshot->valid) {
|
||||||
G.ss_blended = sim_snapshot_alloc(G.blended_snapshot_store, right_snapshot, right_snapshot->tick);
|
G.ss_blended = sim_snapshot_alloc(G.user_blended_snapshots_client, right_snapshot, right_snapshot->tick);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
/* Interp disabled, just copy latest snapshot */
|
/* Interp disabled, just copy latest snapshot */
|
||||||
@ -509,24 +502,24 @@ INTERNAL void user_update(void)
|
|||||||
if (G.ss_blended->valid) {
|
if (G.ss_blended->valid) {
|
||||||
sim_snapshot_release(G.ss_blended);
|
sim_snapshot_release(G.ss_blended);
|
||||||
}
|
}
|
||||||
G.ss_blended = sim_snapshot_alloc(G.blended_snapshot_store, newest_snapshot, newest_snapshot->tick);
|
G.ss_blended = sim_snapshot_alloc(G.user_blended_snapshots_client, newest_snapshot, newest_snapshot->tick);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Release unneeded unblended sim snapshots */
|
/* Release unneeded unblended sim snapshots */
|
||||||
if (keep_unblended_tick > 0) {
|
if (keep_unblended_tick > 0) {
|
||||||
sim_snapshot_store_release_ticks_in_range(G.unblended_snapshot_store, 0, keep_unblended_tick - 1);
|
sim_snapshot_release_ticks_in_range(G.user_unblended_snapshots_client, 0, keep_unblended_tick - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Release unused blended snapshots */
|
/* Release unused blended snapshots */
|
||||||
{
|
{
|
||||||
struct sim_snapshot *ss = sim_snapshot_from_tick(G.blended_snapshot_store, G.blended_snapshot_store->first_tick);
|
struct sim_snapshot *ss = sim_snapshot_from_tick(G.user_blended_snapshots_client, G.user_blended_snapshots_client->first_tick);
|
||||||
while (ss->valid) {
|
while (ss->valid) {
|
||||||
u64 next_tick = ss->next_tick;
|
u64 next_tick = ss->next_tick;
|
||||||
if (ss != G.ss_blended) {
|
if (ss != G.ss_blended) {
|
||||||
sim_snapshot_release(ss);
|
sim_snapshot_release(ss);
|
||||||
}
|
}
|
||||||
ss = sim_snapshot_from_tick(G.blended_snapshot_store, next_tick);
|
ss = sim_snapshot_from_tick(G.user_blended_snapshots_client, next_tick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -624,9 +617,23 @@ INTERNAL void user_update(void)
|
|||||||
* Find local entities
|
* Find local entities
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
struct sim_client *local_client = sim_client_from_handle(G.ss_blended, G.ss_blended->local_client);
|
struct sim_ent *local_client_ent = sim_ent_nil();
|
||||||
struct sim_ent *local_player = sim_ent_from_handle(G.ss_blended, local_client->control_ent);
|
{
|
||||||
struct sim_ent *local_camera = sim_ent_from_handle(G.ss_blended, local_client->camera_ent);
|
struct sim_client_handle local_client_handle = G.ss_blended->local_client;
|
||||||
|
for (u64 ent_index = 0; ent_index < G.ss_blended->num_ents_reserved; ++ent_index) {
|
||||||
|
struct sim_ent *ent = &G.ss_blended->ents[ent_index];
|
||||||
|
if (!sim_ent_is_valid_and_active(ent)) continue;
|
||||||
|
if (sim_ent_has_prop(ent, SIM_ENT_PROP_CLIENT)) {
|
||||||
|
struct sim_client_handle h = ent->client_handle;
|
||||||
|
if (h.gen != 0 && h.gen == local_client_handle.gen && h.idx == local_client_handle.idx) {
|
||||||
|
local_client_ent = ent;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct sim_ent *local_player = sim_ent_from_handle(G.ss_blended, local_client_ent->client_control_ent);
|
||||||
|
struct sim_ent *local_camera = sim_ent_from_handle(G.ss_blended, local_client_ent->client_camera_ent);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Apply shake
|
* Apply shake
|
||||||
@ -1426,7 +1433,6 @@ INTERNAL void user_update(void)
|
|||||||
u32 old_flags = G.user_sim_cmd_control.flags;
|
u32 old_flags = G.user_sim_cmd_control.flags;
|
||||||
G.user_sim_cmd_control = control;
|
G.user_sim_cmd_control = control;
|
||||||
G.user_sim_cmd_control.flags |= old_flags;
|
G.user_sim_cmd_control.flags |= old_flags;
|
||||||
G.user_sim_cmd_control_cursor_pos = G.world_cursor;
|
|
||||||
G.user_sim_cmd_ack = G.local_sim_last_known_tick;
|
G.user_sim_cmd_ack = G.local_sim_last_known_tick;
|
||||||
sys_mutex_unlock(&lock);
|
sys_mutex_unlock(&lock);
|
||||||
}
|
}
|
||||||
@ -1561,19 +1567,6 @@ INTERNAL void user_update(void)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* Update network usage stats */
|
|
||||||
G.client_bytes_read.last_second_end = G.host->bytes_received;
|
|
||||||
G.client_bytes_sent.last_second_end = G.host->bytes_sent;
|
|
||||||
if (G.real_time_ns - G.last_second_reset_ns > NS_FROM_SECONDS(1)) {
|
|
||||||
G.last_second_reset_ns = G.real_time_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;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Render
|
* Render
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -1703,9 +1696,6 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_thread_entry_point, arg)
|
|||||||
* Local sim thread
|
* Local sim thread
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
#if 1
|
|
||||||
|
|
||||||
|
|
||||||
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
||||||
{
|
{
|
||||||
#if 0
|
#if 0
|
||||||
@ -1727,34 +1717,10 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
|||||||
} else {
|
} else {
|
||||||
is_master = true;
|
is_master = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct bitbuff msg_writer_bb = bitbuff_alloc(GIGABYTE(64));
|
struct bitbuff msg_writer_bb = bitbuff_alloc(GIGABYTE(64));
|
||||||
struct bitbuff snapshot_writer_bb = bitbuff_alloc(GIGABYTE(64));
|
struct bitbuff snapshot_writer_bb = bitbuff_alloc(GIGABYTE(64));
|
||||||
|
struct sim_accel accel = sim_accel_alloc();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct sim_client_store *store = sim_client_store_alloc();
|
struct sim_client_store *store = sim_client_store_alloc();
|
||||||
struct sim_client *local_client = sim_client_alloc(store, SIM_CLIENT_KIND_LOCAL_SIM);
|
struct sim_client *local_client = sim_client_alloc(store, SIM_CLIENT_KIND_LOCAL_SIM);
|
||||||
@ -1770,7 +1736,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
|||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||||
{
|
{
|
||||||
__profscope(local_sim_sleep);
|
__profscope(local_sim_sleep);
|
||||||
sleep_frame(last_tick_ns, target_dt_ns);
|
sleep_frame(last_tick_ns, compute_dt_ns);
|
||||||
last_tick_ns = sys_time_ns();
|
last_tick_ns = sys_time_ns();
|
||||||
}
|
}
|
||||||
++step_tick;
|
++step_tick;
|
||||||
@ -1780,8 +1746,11 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
|||||||
struct sim_snapshot *prev_user_cmd_ss = sim_snapshot_from_tick(user_client, user_client->last_tick);
|
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 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);
|
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->producer_client = user_client->handle;
|
||||||
|
user_cmd_ss->producer_client_is_local = true;
|
||||||
user_cmd_ss->control = G.user_sim_cmd_control;
|
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;
|
++G.user_sim_cmd_gen;
|
||||||
sys_mutex_unlock(&lock);
|
sys_mutex_unlock(&lock);
|
||||||
}
|
}
|
||||||
@ -1810,7 +1779,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
|||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case HOST_EVENT_KIND_CHANNEL_MSG:
|
case HOST_EVENT_KIND_MSG:
|
||||||
{
|
{
|
||||||
if (client->valid) {
|
if (client->valid) {
|
||||||
struct bitbuff msg_bb = bitbuff_from_string(event->msg);
|
struct bitbuff msg_bb = bitbuff_from_string(event->msg);
|
||||||
@ -1869,6 +1838,8 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1882,7 +1853,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
|||||||
u64 ack = client->ack;
|
u64 ack = client->ack;
|
||||||
u64 reverse_ack = client->reverse_ack;
|
u64 reverse_ack = client->reverse_ack;
|
||||||
if (reverse_ack > 1) {
|
if (reverse_ack > 1) {
|
||||||
sim_client_release_snapshot_ticks_in_range(client, 0, reverse_ack - 1);
|
sim_snapshot_release_ticks_in_range(client, 0, reverse_ack - 1);
|
||||||
}
|
}
|
||||||
if (ack < oldest_client_ack || oldest_client_ack == 0) {
|
if (ack < oldest_client_ack || oldest_client_ack == 0) {
|
||||||
oldest_client_ack = ack;
|
oldest_client_ack = ack;
|
||||||
@ -1890,7 +1861,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (oldest_client_ack > 1) {
|
if (oldest_client_ack > 1) {
|
||||||
sim_client_release_snapshot_ticks_in_range(local_client, 0, oldest_client_ack - 1);
|
sim_snapshot_release_ticks_in_range(local_client, 0, oldest_client_ack - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pick client snapshot cmds to feed to sim step */
|
/* Pick client snapshot cmds to feed to sim step */
|
||||||
@ -1916,13 +1887,22 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
|||||||
if (is_master) {
|
if (is_master) {
|
||||||
struct sim_snapshot *prev_step_ss = sim_snapshot_from_tick(local_client, step_tick - 1);
|
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 = sim_snapshot_alloc(local_client, prev_step_ss, step_tick);
|
||||||
|
step_ss->is_master = true;
|
||||||
} else {
|
} else {
|
||||||
struct sim_snapshot *newest_master_ss = sim_snapshot_from_tick(master_client, master_client->last_tick);
|
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_ss = sim_snapshot_alloc(local_client, newest_master_ss, step_tick);
|
||||||
|
step_ss->is_master = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step */
|
/* Step */
|
||||||
sim_step(step_ss, client_step_cmds, step_dt_ns);
|
{
|
||||||
|
struct sim_step_ctx step_ctx = ZI;
|
||||||
|
step_ctx.accel = &accel;
|
||||||
|
step_ctx.world = step_ss;
|
||||||
|
step_ctx.cmd_snapshots = &client_step_cmds;
|
||||||
|
step_ctx.real_dt_ns = step_dt_ns;
|
||||||
|
sim_step(&step_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
/* Publish snapshot to clients */
|
/* Publish snapshot to clients */
|
||||||
for (u64 i = 0; i < store->num_clients_reserved; ++i) {
|
for (u64 i = 0; i < store->num_clients_reserved; ++i) {
|
||||||
@ -1959,13 +1939,13 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
|||||||
|
|
||||||
case SIM_CLIENT_KIND_MASTER_SIM:
|
case SIM_CLIENT_KIND_MASTER_SIM:
|
||||||
{
|
{
|
||||||
u64 ack = client->last_tick;
|
u64 base_tick = client->last_tick;
|
||||||
struct sim_snapshot *base_ss = sim_snapshot_from_tick(client, base_tick);
|
struct sim_snapshot *base_ss = sim_snapshot_from_tick(client, base_tick);
|
||||||
if (base_ss->tick == base_tick) {
|
if (base_ss->tick == base_tick) {
|
||||||
u64 ack = client->last_tick;
|
u64 ack = client->last_tick;
|
||||||
u64 reverse_ack = client->ack;
|
u64 reverse_ack = client->ack;
|
||||||
bw_write_uv(&bw, ack);
|
bw_write_uv(&msg_bw, ack);
|
||||||
bw_write_uv(&bw, reverse_ack);
|
bw_write_uv(&msg_bw, reverse_ack);
|
||||||
/* Write all user cmd snapshots since last client 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);
|
struct sim_snapshot *send_ss = sim_snapshot_from_tick(user_client, base_ss->tick + 1);
|
||||||
while (send_ss->valid) {
|
while (send_ss->valid) {
|
||||||
@ -1973,6 +1953,11 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
|||||||
bw_write_uv(&snapshot_bw, base_ss->tick);
|
bw_write_uv(&snapshot_bw, base_ss->tick);
|
||||||
bw_write_uv(&snapshot_bw, step_ss->tick);
|
bw_write_uv(&snapshot_bw, step_ss->tick);
|
||||||
sim_snapshot_encode(&snapshot_bw, client, base_ss, step_ss);
|
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_uv(&msg_bw, encoded_tmp.len);
|
||||||
bw_write_bytes(&msg_bw, encoded_tmp);
|
bw_write_bytes(&msg_bw, encoded_tmp);
|
||||||
}
|
}
|
||||||
@ -1986,12 +1971,14 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
|||||||
case SIM_CLIENT_KIND_USER:
|
case SIM_CLIENT_KIND_USER:
|
||||||
{
|
{
|
||||||
/* TODO: Double buffer */
|
/* TODO: Double buffer */
|
||||||
struct sys_lock lock = sys_mutex_lock_e(&G.local_to_user_snapshot_mutex);
|
struct sys_lock lock = sys_mutex_lock_e(&G.local_sim_to_user_mutex);
|
||||||
struct sim_snapshot *pub_ss = sim_snapshot_alloc(G.local_to_user_snapshot_client, step_ss, step_ss->tick);
|
struct sim_snapshot *pub_ss = sim_snapshot_alloc(G.local_sim_to_user_client, step_ss, step_ss->tick);
|
||||||
|
pub_ss->local_client = client->handle;
|
||||||
i64 publish_ns = sys_time_ns();
|
i64 publish_ns = sys_time_ns();
|
||||||
pub_ss->publish_dt_ns = publish_ns - last_publish_ns;
|
pub_ss->publish_dt_ns = publish_ns - last_publish_ns;
|
||||||
pub_ss->publish_time_ns = publish_ns;
|
pub_ss->publish_time_ns = publish_ns;
|
||||||
last_publish_ns = publish_ns;
|
last_publish_ns = publish_ns;
|
||||||
|
sim_snapshot_release_ticks_in_range(G.local_sim_to_user_client, 0, step_ss->tick - 1);
|
||||||
sys_mutex_unlock(&lock);
|
sys_mutex_unlock(&lock);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@ -2005,316 +1992,9 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
|
|||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sim_client_store_release(store);
|
||||||
|
sim_accel_release(&accel);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
sim_snapshot_store_release(store);
|
|
||||||
bitbuff_release(&snapshot_writer_bb);
|
bitbuff_release(&snapshot_writer_bb);
|
||||||
bitbuff_release(&msg_writer_bb);
|
bitbuff_release(&msg_writer_bb);
|
||||||
host_release(host);
|
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));
|
|
||||||
|
|
||||||
struct sim_snapshot_store *local_snapshot_store = sim_snapshot_store_alloc();
|
|
||||||
struct sim_snapshot_store *remote_snapshot_store = sim_snapshot_store_alloc();
|
|
||||||
u64 oldest_needed_remote_tick = 0;
|
|
||||||
u64 remote_ack = 0;
|
|
||||||
(UNUSED)remote_ack;
|
|
||||||
|
|
||||||
struct sim_snapshot *local_ss_prev = sim_snapshot_nil();
|
|
||||||
|
|
||||||
|
|
||||||
i64 last_publish_ns = 0;
|
|
||||||
i64 last_tick_ns = 0;
|
|
||||||
i64 target_dt_ns = NS_FROM_SECONDS(1) / SIM_TICKS_PER_SECOND;
|
|
||||||
while (!atomic_i32_eval(&G.local_sim_thread_shutdown)) {
|
|
||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
|
||||||
{
|
|
||||||
__profscope(local_sim_sleep);
|
|
||||||
sleep_frame(last_tick_ns, target_dt_ns);
|
|
||||||
last_tick_ns = sys_time_ns();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Release old snapshots */
|
|
||||||
/* TODO: Remove this */
|
|
||||||
{
|
|
||||||
u64 keep_count = 100;
|
|
||||||
u64 keep_tick = local_ss_prev->tick > keep_count ? local_ss_prev->tick - keep_count : 0;
|
|
||||||
if (keep_tick > 0) {
|
|
||||||
sim_snapshot_store_release_ticks_in_range(local_snapshot_store, 0, keep_tick);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Retrieve cmds */
|
|
||||||
struct sim_cmd_frame_list input_cmds = ZI;
|
|
||||||
struct sim_cmd_frame *user_cmd_frame;
|
|
||||||
{
|
|
||||||
/* Grab snapshots from host */
|
|
||||||
{
|
|
||||||
host_update(host);
|
|
||||||
struct host_event_array host_events = host_pop_events(scratch.arena, host);
|
|
||||||
sim_cmd_frames_decode(scratch.arena, host_events, &input_cmds);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Generate user sim cmd from user thread */
|
|
||||||
{
|
|
||||||
struct sys_lock lock = sys_mutex_lock_e(&G.user_sim_cmd_mutex);
|
|
||||||
user_cmd_frame = arena_push_zero(scratch.arena, struct sim_cmd_frame);
|
|
||||||
user_cmd_frame->tick = local_ss_prev->tick + 1;
|
|
||||||
user_cmd_frame->ack = G.user_sim_cmd_ack;
|
|
||||||
user_cmd_frame->sender_is_local = true;
|
|
||||||
|
|
||||||
struct sim_cmd *user_cmd = arena_push_zero(scratch.arena, struct sim_cmd);
|
|
||||||
user_cmd->kind = SIM_CMD_KIND_CLIENT_CONTROL;
|
|
||||||
user_cmd->control = G.user_sim_cmd_control;
|
|
||||||
user_cmd_frame->first = user_cmd;
|
|
||||||
user_cmd_frame->last = user_cmd;
|
|
||||||
++G.user_sim_cmd_gen;
|
|
||||||
sys_mutex_unlock(&lock);
|
|
||||||
}
|
|
||||||
if (input_cmds.last) {
|
|
||||||
input_cmds.last->next = user_cmd_frame;
|
|
||||||
} else {
|
|
||||||
input_cmds.first = user_cmd_frame;
|
|
||||||
}
|
|
||||||
input_cmds.last = user_cmd_frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (struct sim_cmd_frame *frame = input_cmds.first; frame; frame = frame->next) {
|
|
||||||
/* FIXME: Verify cmd is from master */
|
|
||||||
if (!is_master && !frame->sender_is_local) {
|
|
||||||
struct sim_cmd *snapshot_cmd = NULL;
|
|
||||||
|
|
||||||
/* Grab newest snapshot cmd */
|
|
||||||
for (struct sim_cmd *cmd = frame->first; cmd; cmd = cmd->next) {
|
|
||||||
if (cmd->kind == SIM_CMD_KIND_SNAPSHOT && (!snapshot_cmd || cmd->snapshot_tick_end > snapshot_cmd->snapshot_tick_end)) {
|
|
||||||
snapshot_cmd = cmd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Decode newest snapshot cmd */
|
|
||||||
if (snapshot_cmd && snapshot_cmd->snapshot_tick_end > remote_snapshot_store->last_tick) {
|
|
||||||
remote_ack = frame->ack;
|
|
||||||
|
|
||||||
u64 ss0_tick = snapshot_cmd->snapshot_tick_start;
|
|
||||||
u64 ss1_tick = snapshot_cmd->snapshot_tick_end;
|
|
||||||
struct sim_snapshot *ss0 = sim_snapshot_from_tick(remote_snapshot_store, ss0_tick);
|
|
||||||
struct sim_snapshot *ss1 = sim_snapshot_from_tick(remote_snapshot_store, ss1_tick);
|
|
||||||
if (ss0->tick == ss0_tick) {
|
|
||||||
if (!ss1->valid) {
|
|
||||||
/* Decode remote snapshot */
|
|
||||||
ss1 = sim_snapshot_alloc(remote_snapshot_store, ss0, ss1_tick);
|
|
||||||
struct bitbuff bb = bitbuff_from_string(snapshot_cmd->snapshot_encoded);
|
|
||||||
struct bitbuff_reader br = br_from_bitbuff(&bb);
|
|
||||||
sim_snapshot_decode(&br, ss1);
|
|
||||||
|
|
||||||
if (ss0->tick > oldest_needed_remote_tick) {
|
|
||||||
oldest_needed_remote_tick = ss0->tick;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set master client channel id */
|
|
||||||
for (u64 i = 0; i < ss1->num_clients_reserved; ++i) {
|
|
||||||
struct sim_client *client = &ss1->clients[i];
|
|
||||||
if (client->valid && client->kind == SIM_CLIENT_KIND_SIM_MASTER) {
|
|
||||||
sim_client_set_channel_id(client, frame->sender_channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
local_ss_prev = sim_snapshot_alloc(local_snapshot_store, ss1, ss1_tick + 20);
|
|
||||||
local_ss_prev->is_master = is_master;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* User should always have src tick present */
|
|
||||||
ASSERT(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Release old remote ticks */
|
|
||||||
if (oldest_needed_remote_tick > 0) {
|
|
||||||
sim_snapshot_store_release_ticks_in_range(remote_snapshot_store, 0, oldest_needed_remote_tick - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Step */
|
|
||||||
struct sim_snapshot *local_ss = sim_step(local_snapshot_store, local_ss_prev, input_cmds, target_dt_ns);
|
|
||||||
local_ss->is_master = is_master;
|
|
||||||
|
|
||||||
/* Publish snapshot to user */
|
|
||||||
/* TODO: Double buffer */
|
|
||||||
{
|
|
||||||
struct sys_lock lock = sys_mutex_lock_e(&G.local_sim_ss_mutex);
|
|
||||||
struct sim_snapshot *pub_ss = sim_snapshot_alloc(G.local_sim_ss_store, local_ss, local_ss->tick);
|
|
||||||
sim_snapshot_store_release_ticks_in_range(G.local_sim_ss_store, 0, pub_ss->tick - 1);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (local_ss->is_master) {
|
|
||||||
/* Publish snapshot cmds to slave clients */
|
|
||||||
u64 oldest_ack_tick = 0;
|
|
||||||
for (u64 i = 0; i < local_ss->num_clients_reserved; ++i) {
|
|
||||||
struct sim_client *client = &local_ss->clients[i];
|
|
||||||
if (client->valid && client->kind == SIM_CLIENT_KIND_SIM_SLAVE) {
|
|
||||||
struct temp_arena temp = arena_temp_begin(scratch.arena);
|
|
||||||
|
|
||||||
if (oldest_ack_tick == 0 || client->ack < oldest_ack_tick) {
|
|
||||||
oldest_ack_tick = client->ack;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sim_snapshot *ss0 = sim_snapshot_from_tick(local_snapshot_store, client->ack);
|
|
||||||
struct sim_snapshot *ss1 = local_ss;
|
|
||||||
|
|
||||||
/* Create & encode snapshot cmd */
|
|
||||||
struct sim_cmd snapshot_cmd = ZI;
|
|
||||||
{
|
|
||||||
snapshot_cmd.kind = SIM_CMD_KIND_SNAPSHOT;
|
|
||||||
snapshot_cmd.snapshot_tick_start = ss0->tick;
|
|
||||||
snapshot_cmd.snapshot_tick_end = ss1->tick;
|
|
||||||
{
|
|
||||||
struct bitbuff_writer bw = bw_from_bitbuff(&encoder_bitbuff);
|
|
||||||
sim_snapshot_encode(&bw, client, ss0, ss1);
|
|
||||||
snapshot_cmd.snapshot_encoded = bw_get_written(temp.arena, &bw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sim_cmd_frame snapshot_cmd_frame = ZI;
|
|
||||||
snapshot_cmd_frame.tick = ss1->tick;
|
|
||||||
snapshot_cmd_frame.ack = client->ack;
|
|
||||||
snapshot_cmd_frame.first = &snapshot_cmd;
|
|
||||||
snapshot_cmd_frame.last = &snapshot_cmd;
|
|
||||||
|
|
||||||
struct sim_cmd_frame_list cmd_frames = ZI;
|
|
||||||
cmd_frames.first = &snapshot_cmd_frame;
|
|
||||||
cmd_frames.last = &snapshot_cmd_frame;
|
|
||||||
|
|
||||||
/* Encode cmds */
|
|
||||||
struct string cmds_msg = ZI;
|
|
||||||
{
|
|
||||||
struct bitbuff_writer bw = bw_from_bitbuff(&encoder_bitbuff);
|
|
||||||
sim_cmd_frames_encode(&bw, cmd_frames);
|
|
||||||
cmds_msg = bw_get_written(temp.arena, &bw);
|
|
||||||
}
|
|
||||||
|
|
||||||
host_queue_write(host, client->channel_id, cmds_msg, 0);
|
|
||||||
|
|
||||||
arena_temp_end(temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Publish user cmd to master client */
|
|
||||||
for (u64 i = 0; i < local_ss->num_clients_reserved; ++i) {
|
|
||||||
struct sim_client *client = &local_ss->clients[i];
|
|
||||||
if (client->valid && client->kind == SIM_CLIENT_KIND_SIM_MASTER) {
|
|
||||||
struct temp_arena temp = arena_temp_begin(scratch.arena);
|
|
||||||
|
|
||||||
user_cmd_frame->ack = remote_snapshot_store->last_tick;
|
|
||||||
user_cmd_frame->sender_is_local = false;
|
|
||||||
|
|
||||||
struct sim_cmd_frame_list l = ZI;
|
|
||||||
l.first = user_cmd_frame;
|
|
||||||
l.last = user_cmd_frame;
|
|
||||||
|
|
||||||
/* Encode cmds */
|
|
||||||
struct string cmds_msg = ZI;
|
|
||||||
{
|
|
||||||
struct bitbuff_writer bw = bw_from_bitbuff(&encoder_bitbuff);
|
|
||||||
/* FIXME: Ack tick */
|
|
||||||
sim_cmd_frames_encode(&bw, l);
|
|
||||||
cmds_msg = bw_get_written(temp.arena, &bw);
|
|
||||||
}
|
|
||||||
|
|
||||||
host_queue_write(host, client->channel_id, cmds_msg, 0);
|
|
||||||
|
|
||||||
arena_temp_end(temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
if (local_ss->is_master) {
|
|
||||||
local_ss_prev = local_ss;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sim_snapshot_store_release(local_snapshot_store);
|
|
||||||
bitbuff_release(&encoder_bitbuff);
|
|
||||||
host_release(host);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ struct sound_startup_receipt;
|
|||||||
struct mixer_startup_receipt;
|
struct mixer_startup_receipt;
|
||||||
struct phys_startup_receipt;
|
struct phys_startup_receipt;
|
||||||
struct host_startup_receipt;
|
struct host_startup_receipt;
|
||||||
struct sim_snapshot_startup_receipt;
|
struct sim_startup_receipt;
|
||||||
|
|
||||||
enum user_bind_kind {
|
enum user_bind_kind {
|
||||||
USER_BIND_KIND_NONE,
|
USER_BIND_KIND_NONE,
|
||||||
@ -61,7 +61,7 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
|
|||||||
struct mixer_startup_receipt *mixer_sr,
|
struct mixer_startup_receipt *mixer_sr,
|
||||||
struct phys_startup_receipt *phys_sr,
|
struct phys_startup_receipt *phys_sr,
|
||||||
struct host_startup_receipt *host_sr,
|
struct host_startup_receipt *host_sr,
|
||||||
struct sim_snapshot_startup_receipt *sim_snapshot_sr,
|
struct sim_startup_receipt *sim_sr,
|
||||||
struct string connect_address_str,
|
struct string connect_address_str,
|
||||||
struct sys_window *window);
|
struct sys_window *window);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user