sim ctx decoupling progress

This commit is contained in:
jacob 2025-02-18 11:14:41 -06:00
parent f6aa25760e
commit 253104b2ce
9 changed files with 475 additions and 378 deletions

View File

@ -132,7 +132,7 @@ void app_entry_point(struct string args_str)
struct temp_arena scratch = scratch_begin_no_conflict();
#if !RTC
/* Check that test modes aren't left on by accident in release mode */
/* Verify test modes aren't left on by accident in release mode */
CT_ASSERT(BITBUFF_DEBUG == 0);
CT_ASSERT(BITBUFF_TEST == 0);
#endif

View File

@ -671,6 +671,9 @@ void bitbuff_test(void)
{ kind_ibits, .ibits = { 4, 7 } },
{ kind_ibits, .ibits = { 1, 7 } },
{ kind_ibits, .ibits = { 3, 3 } },
{ kind_ibits, .ibits = { 1, 2 } },
{ kind_ibits, .ibits = { 0, 2 } },
{ kind_ibits, .ibits = { -1, 2 } },
{ kind_uv, .uv = { 0 } },
{ kind_uv, .uv = { 100 } },

View File

@ -63,7 +63,7 @@
/* If enabled, bitbuffs will insert/verify magic numbers & length for each read & write */
#define BITBUFF_DEBUG 0
#define BITBUFF_TEST 0
#define BITBUFF_TEST RTC
/* ========================== *
* Settings

View File

@ -985,7 +985,6 @@ void phys_integrate_velocities(struct phys_ctx *ctx, f32 dt)
{
__prof;
struct sim_snapshot *ss = ctx->ss;
struct space *space = ss->space;
for (u64 sim_ent_index = 0; sim_ent_index < ss->num_ents_reserved; ++sim_ent_index) {
struct sim_ent *ent = &ss->ents[sim_ent_index];
if (!sim_ent_is_valid_and_active(ent)) continue;
@ -993,11 +992,6 @@ void phys_integrate_velocities(struct phys_ctx *ctx, f32 dt)
struct xform xf = get_derived_xform(ent, dt);
sim_ent_set_xform(ent, xf);
struct space_entry *space_entry = space_entry_from_handle(space, ent->space_handle);
if (space_entry->valid) {
space_entry_update_aabb(space_entry, collider_aabb_from_collider(&ent->local_collider, xf));
}
}
}
@ -1071,7 +1065,8 @@ void phys_update_aabbs(struct phys_ctx *ctx)
space_entry = space_entry_alloc(space, ent->handle);
ent->space_handle = space_entry->handle;
}
space_entry_update_aabb(space_entry, collider_aabb_from_collider(&ent->local_collider, xf));
struct aabb aabb = collider_aabb_from_collider(&ent->local_collider, xf);
space_entry_update_aabb(space_entry, aabb);
}
}
@ -1141,6 +1136,8 @@ void phys_step(struct phys_ctx *ctx, f32 timestep)
/* Integrate */
phys_integrate_velocities(ctx, substep_dt);
phys_update_aabbs(ctx);
/* Relaxation solve */
#if SIM_PHYSICS_ENABLE_COLLISION && SIM_PHYSICS_ENABLE_RELAXATION
phys_solve_contacts(ctx, substep_dt, false);

371
src/sim.c
View File

@ -208,11 +208,16 @@ INTERNAL struct sim_ent *spawn_test_player(struct sim_snapshot *world)
e->linear_ground_friction = 250;
e->angular_ground_friction = 200;
e->friction = 0;
//e->control_force = 500;
e->control_force = 500;
//e->control_force_max_speed = 4;
e->control_torque = 5000;
e->control_force_max_speed = 4;
e->control_torque = 5000;
//e->control_torque = F32_INFINITY;
//e->control_force_max_speed = F32_INFINITY;
sim_ent_enable_prop(e, SIM_ENT_PROP_PHYSICAL_DYNAMIC);
@ -403,7 +408,8 @@ INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, collision_data_array, wo
* Update
* ========================== */
void sim_step(struct host *host, struct bitbuff *encoder_bitbuff, struct sim_snapshot_store *snapshot_store, struct sim_snapshot *prev_snapshot, i64 real_dt_ns, struct sim_cmd_list user_sim_cmds)
/* Simulates and generates a snapshot at tick `prev_snapshot` + 1 for duration `real_dt_ns`. */
struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct sim_snapshot *prev_snapshot, struct sim_cmd_frame_list input_frames, i64 real_dt_ns)
{
__prof;
struct temp_arena scratch = scratch_begin_no_conflict();
@ -428,6 +434,26 @@ void sim_step(struct host *host, struct bitbuff *encoder_bitbuff, struct sim_sna
}
#endif
/* TODO: Remove this */
world->space = prev_snapshot->space;
world->contact_lookup = prev_snapshot->contact_lookup;
if (!world->space) {
world->space = space_alloc(1, 256);
}
if (!world->contact_lookup.arena.base) {
world->contact_lookup = sim_ent_lookup_alloc(4096);
}
world->real_dt_ns = max_i64(0, real_dt_ns);
world->real_time_ns += world->real_dt_ns;
@ -504,50 +530,33 @@ void sim_step(struct host *host, struct bitbuff *encoder_bitbuff, struct sim_sna
* Create user client if it doesn't exist
* ========================== */
(UNUSED)user_sim_cmds;
/* ========================== *
* Process host events into sim cmds
* Sort incoming frames by client
* ========================== */
struct sim_cmd_list *client_cmds;
struct sim_cmd_frame **client_frames;
{
host_update(host);
struct host_event_array host_events = host_pop_events(scratch.arena, host);
struct sim_cmd_list sim_cmds = ZI;
sim_cmds_decode(scratch.arena, host_events, &sim_cmds);
/* Create connecting clients */
for (struct sim_cmd *cmd = sim_cmds.first; cmd; cmd = cmd->next) {
for (struct sim_cmd_frame *frame = input_frames.first; frame; frame = frame->next) {
struct host_channel_id channel_id = frame->channel;
struct sim_client *client = sim_client_from_channel_id(world, channel_id);
if (!client->valid) {
for (struct sim_cmd *cmd = frame->first; cmd; cmd = cmd->next) {
enum sim_cmd_kind kind = cmd->kind;
struct host_channel_id channel_id = cmd->channel_id;
struct sim_client *client = sim_client_from_channel_id(world, channel_id);
if (!client->valid && kind == SIM_CMD_KIND_SIM_CLIENT_CONNECT && !host_channel_id_is_nil(channel_id)) {
if (kind == SIM_CMD_KIND_SIM_CLIENT_CONNECT && !host_channel_id_is_nil(channel_id)) {
client = sim_client_alloc(world, channel_id);
break;
}
}
/* Split cmds by client */
client_cmds = arena_push_array_zero(scratch.arena, struct sim_cmd_list, world->num_clients_reserved);
{
struct sim_cmd *cmd = sim_cmds.first;
while (cmd) {
struct sim_cmd *next = cmd->next;
struct host_channel_id channel_id = cmd->channel_id;
struct sim_client *client = sim_client_from_channel_id(world, channel_id);
if (client->valid) {
struct sim_cmd_list *cmd_list = &client_cmds[client->handle.idx];
if (cmd_list->last) {
cmd_list->last->next = cmd;
} else {
cmd_list->first = cmd;
}
cmd_list->last = cmd;
cmd->next = NULL;
}
cmd = next;
/* Sort cmd frames by client */
client_frames = arena_push_array_zero(scratch.arena, struct sim_cmd_frame *, world->num_clients_reserved);
for (struct sim_cmd_frame *frame = input_frames.first; frame; frame = frame->next) {
struct sim_client *client = sim_client_from_channel_id(world, frame->channel);
if (client->valid && frame->tick == world->tick) {
client_frames[client->handle.idx] = frame;
}
}
}
@ -557,30 +566,27 @@ void sim_step(struct host *host, struct bitbuff *encoder_bitbuff, struct sim_sna
* ========================== */
/* Process client cmds */
u64 oldest_client_ack_tick = world->tick;
u64 oldest_client_ack = world->tick;
for (u64 i = 0; i < world->num_clients_reserved; ++i) {
struct sim_client *client = &world->clients[i];
if (client->valid) {
struct host_channel_id channel_id = client->channel_id;
struct sim_cmd_list cmds = client_cmds[client->handle.idx];
struct sim_cmd_frame *frame = client_frames[client->handle.idx];
if (client->valid && frame) {
struct sim_control *control = &client->control;
client->dbg_drag_start = false;
client->dbg_drag_stop = false;
for (struct sim_cmd *cmd = cmds.first; cmd; cmd = cmd->next) {
if (cmd->ack_tick > client->ack_tick) {
client->ack_tick = cmd->ack_tick;
if (frame->ack > client->ack) {
client->ack = frame->ack;
}
for (struct sim_cmd *cmd = frame->first; cmd; cmd = cmd->next) {
enum sim_cmd_kind kind = cmd->kind;
b32 start = cmd->state == SIM_CMD_STATE_START;
b32 stop = cmd->state == SIM_CMD_STATE_STOP;
switch (kind) {
/* TODO: Combine movement from multiple inputs? E.ctx-> a sudden
* start and immediate stop cmd should still move the player a
* tad. */
case SIM_CMD_KIND_PLAYER_CONTROL:
case SIM_CMD_KIND_CLIENT_CONTROL:
{
struct v2 move = cmd->move_dir;
struct v2 focus = cmd->aim_dir;
@ -590,34 +596,22 @@ void sim_step(struct host *host, struct bitbuff *encoder_bitbuff, struct sim_sna
}
control->move = move;
control->focus = focus;
} break;
case SIM_CMD_KIND_PLAYER_FIRE:
{
/* TODO: Allow sub-tick firing (start & stop on same tick should still fire for one tick */
b32 firing = control->firing;
if (start) {
firing = true;
}
if (stop) {
firing = false;
}
control->firing = firing;
} break;
/* Cursor */
case SIM_CMD_KIND_CURSOR_MOVE:
{
control->firing = cmd->is_firing;
client->cursor_pos = cmd->cursor_pos;
if (cmd->is_firing) {
DEBUGBREAKABLE;
}
} break;
case SIM_CMD_KIND_DRAG_OBJECT:
case SIM_CMD_KIND_DRAG_OBJECT_START:
{
if (cmd->state == SIM_CMD_STATE_START) {
client->dbg_drag_start = true;
} else if (cmd->state == SIM_CMD_STATE_STOP) {
} break;
case SIM_CMD_KIND_DRAG_OBJECT_STOP:
{
client->dbg_drag_stop = true;
}
} break;
/* Clear level */
@ -640,7 +634,6 @@ void sim_step(struct host *host, struct bitbuff *encoder_bitbuff, struct sim_sna
/* Disconnect client */
case SIM_CMD_KIND_SIM_CLIENT_DISCONNECT:
{
host_queue_disconnect(host, channel_id);
sim_client_release(client);
} break;
@ -648,8 +641,8 @@ void sim_step(struct host *host, struct bitbuff *encoder_bitbuff, struct sim_sna
};
}
if (client->ack_tick < oldest_client_ack_tick || oldest_client_ack_tick == 0) {
oldest_client_ack_tick = client->ack_tick;
if (client->ack < oldest_client_ack || oldest_client_ack == 0) {
oldest_client_ack = client->ack;
}
}
}
@ -1375,57 +1368,52 @@ void sim_step(struct host *host, struct bitbuff *encoder_bitbuff, struct sim_sna
* Publish tick
* ========================== */
#if 0
struct sim_cmd_queue_list output_cmds = ZI;
for (u64 i = 0; i < world->num_clients_reserved; ++i) {
struct sim_client *client = &world->clients[i];
if (client->valid) {
struct temp_arena temp = arena_temp_begin(scratch.arena);
u64 ss0_tick = client->ack_tick;
u64 ss1_tick = world->tick;
struct sim_snapshot *ss0 = sim_snapshot_from_tick(snapshot_store, ss0_tick);
struct sim_snapshot *ss0 = sim_snapshot_from_tick(snapshot_store, client->ack);
struct sim_snapshot *ss1 = world;
ss0_tick = ss0->tick; /* In case ack tick is no longer in store we need to do a full resend */
struct sim_event_list l = ZI;
struct sim_event snapshot_event = ZI;
/* Create & encode snapshot event */
/* Create & encode snapshot cmd */
{
snapshot_event.kind = SIM_EVENT_KIND_SNAPSHOT;
snapshot_event.tick = world->tick;
snapshot_event.snapshot_tick_start = ss0_tick;
snapshot_event.snapshot_tick_end = ss1_tick;
struct sim_cmd *cmd = arena_push(arena, struct sim_cmd);
cmd->kind = SIM_CMD_KIND_SNAPSHOT;
cmd->tick = world->tick;
cmd->snapshot_tick_start = ss0->tick;
cmd->snapshot_tick_end = ss1->tick;
{
struct bitbuff_writer bw = bw_from_bitbuff(encoder_bitbuff);
sim_snapshot_encode(&bw, ss0, ss1, client);
snapshot_event.snapshot_encoded = bw_get_written(temp.arena, &bw);
cmd->snapshot_encoded = bw_get_written(temp.arena, &bw);
}
if (l.last) {
l.last->next = &snapshot_event;
if (output_cmds.last) {
output_cmds.last->next = cmd;
} else {
l.first = &snapshot_event;
output_cmds.first = cmd;
}
l.last = &snapshot_event;
output_cmds.last = cmd;
}
/* Encode events */
struct string events_msg = ZI;
/* Encode cmds */
struct string cmds_msg = ZI;
{
struct bitbuff_writer bw = bw_from_bitbuff(encoder_bitbuff);
sim_events_encode(&bw, l);
events_msg = bw_get_written(temp.arena, &bw);
sim_cmds_encode(&bw, output_cmds, 0);
cmds_msg = bw_get_written(temp.arena, &bw);
}
host_queue_write(host, client->channel_id, events_msg, 0);
host_queue_write(host, client->channel_id, cmds_msg, 0);
arena_temp_end(temp);
}
}
host_update(host);
__profframe("Sim");
#endif
/* ========================== *
* End frame cache scopes
@ -1434,45 +1422,58 @@ void sim_step(struct host *host, struct bitbuff *encoder_bitbuff, struct sim_sna
sprite_scope_end(sprite_frame_scope);
scratch_end(scratch);
return world;
}
/* ========================== *
* Sim cmd
* Cmd frame
* ========================== */
void sim_cmds_encode(struct bitbuff_writer *bw, struct sim_cmd_list cmds, u64 ack_tick)
void sim_cmd_frames_encode(struct bitbuff_writer *bw, struct sim_cmd_frame_list frames, u64 ack_tick)
{
__prof;
for (struct sim_cmd *cmd = cmds.first; cmd; cmd = cmd->next) {
bw_write_ibits(bw, cmd->kind, 8);
bw_write_ibits(bw, cmd->state, 8);
for (struct sim_cmd_frame *frame = frames.first; frame; frame = frame->next) {
bw_write_uv(bw, frame->tick);
bw_write_uv(bw, ack_tick);
for (struct sim_cmd *cmd = frame->first; cmd; cmd = cmd->next) {
bw_write_bit(bw, 1);
bw_write_ibits(bw, cmd->kind, 8);
#if COLLIDER_DEBUG
bw_write_ubits(bw, cmd->collider_gjk_steps, 32);
#endif
switch (cmd->kind) {
case SIM_CMD_KIND_PLAYER_CONTROL:
case SIM_CMD_KIND_CLIENT_CONTROL:
{
bw_write_f32(bw, cmd->move_dir.x);
bw_write_f32(bw, cmd->move_dir.y);
bw_write_f32(bw, cmd->aim_dir.x);
bw_write_f32(bw, cmd->aim_dir.y);
} break;
case SIM_CMD_KIND_CURSOR_MOVE:
{
bw_write_f32(bw, cmd->cursor_pos.x);
bw_write_f32(bw, cmd->cursor_pos.y);
bw_write_bit(bw, cmd->is_firing);
#if COLLIDER_DEBUG
cmd->collider_gjk_steps = br_read_ubits(&br, 32);
#endif
} break;
case SIM_CMD_KIND_SNAPSHOT:
{
bw_write_uv(bw, cmd->snapshot_tick_start);
bw_write_uv(bw, cmd->snapshot_tick_end);
bw_write_string(bw, cmd->snapshot_encoded);
} break;
default: break;
}
}
bw_write_bit(bw, 0);
}
}
void sim_cmds_decode(struct arena *arena, struct host_event_array host_events, struct sim_cmd_list *cmds_out)
void sim_cmd_frames_decode(struct arena *arena, struct host_event_array host_events, struct sim_cmd_frame_list *frames_out)
{
__prof;
for (u64 i = 0; i < host_events.count; ++i) {
@ -1481,148 +1482,100 @@ void sim_cmds_decode(struct arena *arena, struct host_event_array host_events, s
switch (host_event_kind) {
case HOST_EVENT_KIND_CHANNEL_OPENED:
{
/* Create a stand-alone cmd frame for connecting */
struct sim_cmd_frame *frame = arena_push_zero(arena, struct sim_cmd_frame);
frame->channel = host_event.channel_id;
struct sim_cmd *cmd = arena_push_zero(arena, struct sim_cmd);
cmd->kind = SIM_CMD_KIND_SIM_CLIENT_CONNECT;
cmd->channel_id = host_event.channel_id;
if (cmds_out->last) {
cmds_out->last->next = cmd;
frame->first = cmd;
frame->last = cmd;
if (frames_out->last) {
frames_out->last->next = frame;
} else {
cmds_out->first = cmd;
frames_out->first = frame;
}
cmds_out->last = cmd;
frames_out->last = frame;
} break;
case HOST_EVENT_KIND_CHANNEL_CLOSED:
{
/* Create a stand-alone cmd frame for disconnecting */
struct sim_cmd_frame *frame = arena_push_zero(arena, struct sim_cmd_frame);
frame->channel = host_event.channel_id;
struct sim_cmd *cmd = arena_push_zero(arena, struct sim_cmd);
cmd->kind = SIM_CMD_KIND_SIM_CLIENT_DISCONNECT;
cmd->disconnect_reason = LIT("Connection lost");
if (cmds_out->last) {
cmds_out->last->next = cmd;
frame->first = cmd;
frame->last = cmd;
if (frames_out->last) {
frames_out->last->next = frame;
} else {
cmds_out->first = cmd;
frames_out->first = frame;
}
cmds_out->last = cmd;
frames_out->last = frame;
} break;
case HOST_EVENT_KIND_MSG:
{
struct bitbuff bb = bitbuff_from_string(host_event.msg);
struct bitbuff_reader br = br_from_bitbuff(&bb);
while (br_num_bits_left(&br) > 0) {
struct sim_cmd_frame *frame = arena_push_zero(arena, struct sim_cmd_frame);
frame->channel = host_event.channel_id;
frame->tick = br_read_uv(&br);
frame->ack = br_read_uv(&br);
while (br_read_bit(&br)) {
struct sim_cmd *cmd = arena_push_zero(arena, struct sim_cmd);
cmd->kind = br_read_ibits(&br, 8);
cmd->channel_id = host_event.channel_id;
cmd->state = br_read_ibits(&br, 8);
cmd->ack_tick = br_read_uv(&br);
#if COLLIDER_DEBUG
cmd->collider_gjk_steps = br_read_ubits(&br, 32);
#endif
switch (cmd->kind) {
case SIM_CMD_KIND_PLAYER_CONTROL:
case SIM_CMD_KIND_CLIENT_CONTROL:
{
cmd->move_dir.x = br_read_f32(&br);
cmd->move_dir.y = br_read_f32(&br);
cmd->aim_dir.x = br_read_f32(&br);
cmd->aim_dir.y = br_read_f32(&br);
} break;
case SIM_CMD_KIND_CURSOR_MOVE:
{
cmd->cursor_pos.x = br_read_f32(&br);
cmd->cursor_pos.y = br_read_f32(&br);
cmd->is_firing = br_read_bit(&br);
#if COLLIDER_DEBUG
cmd->collider_gjk_steps = br_read_ubits(&br, 32);
#endif
} break;
case SIM_CMD_KIND_SNAPSHOT:
{
cmd->snapshot_tick_start = br_read_uv(&br);
cmd->snapshot_tick_end = br_read_uv(&br);
cmd->snapshot_encoded = br_read_string(arena, &br);
} break;
default: break;
}
if (cmds_out->last) {
cmds_out->last->next = cmd;
if (frame->last) {
frame->last->next = cmd;
} else {
cmds_out->first = cmd;
}
cmds_out->last = cmd;
}
} break;
default: break;
}
frame->first = cmd;
}
frame->last = cmd;
}
/* ========================== *
* Sim event
* ========================== */
void sim_events_encode(struct bitbuff_writer *bw, struct sim_event_list events)
{
__prof;
for (struct sim_event *event = events.first; event; event = event->next) {
bw_write_uv(bw, event->tick);
bw_write_ibits(bw, event->kind, 8);
switch (event->kind) {
case SIM_EVENT_KIND_SNAPSHOT:
{
bw_write_uv(bw, event->snapshot_tick_start);
bw_write_uv(bw, event->snapshot_tick_end);
bw_write_string(bw, event->snapshot_encoded);
} break;
default: break;
}
}
}
void sim_events_decode(struct arena *arena, struct host_event_array host_events, struct sim_event_list *events_out)
{
__prof;
for (u64 i = 0; i < host_events.count; ++i) {
struct sim_event *sim_event = arena_push_zero(arena, struct sim_event);
struct host_event host_event = host_events.events[i];
enum host_event_kind host_event_kind = host_event.kind;
sim_event->channel_id = host_event.channel_id;
switch (host_event_kind) {
case HOST_EVENT_KIND_CHANNEL_OPENED:
{
sim_event->kind = SIM_EVENT_KIND_CONNECT;
} break;
case HOST_EVENT_KIND_CHANNEL_CLOSED:
{
sim_event->kind = SIM_EVENT_KIND_DISCONNECT;
sim_event->disconnect_reason = LIT("Connection lost");
} break;
case HOST_EVENT_KIND_MSG:
{
struct bitbuff bb = bitbuff_from_string(host_event.msg);
struct bitbuff_reader br = br_from_bitbuff(&bb);
while (br_num_bits_left(&br) > 0) {
sim_event->tick = br_read_uv(&br);
sim_event->kind = br_read_ibits(&br, 8);
switch (sim_event->kind) {
case SIM_EVENT_KIND_SNAPSHOT:
{
sim_event->snapshot_tick_start = br_read_uv(&br);
sim_event->snapshot_tick_end = br_read_uv(&br);
sim_event->snapshot_encoded = br_read_string(arena, &br);
} break;
default: break;
}
}
} break;
default: break;
}
if (events_out->last) {
events_out->last->next = sim_event;
if (frames_out->last) {
frames_out->last->next = frame;
} else {
events_out->first = sim_event;
frames_out->first = frame;
}
frames_out->last = frame;
} break;
default: break;
}
events_out->last = sim_event;
}
}

120
src/sim.h
View File

@ -23,8 +23,7 @@ enum sim_cmd_state {
enum sim_cmd_kind {
SIM_CMD_KIND_NONE,
SIM_CMD_KIND_PLAYER_CONTROL,
SIM_CMD_KIND_PLAYER_FIRE,
SIM_CMD_KIND_CLIENT_CONTROL,
SIM_CMD_KIND_SIM_CLIENT_CONNECT,
SIM_CMD_KIND_SIM_CLIENT_DISCONNECT,
@ -34,74 +33,88 @@ enum sim_cmd_kind {
SIM_CMD_KIND_SPAWN_TEST,
SIM_CMD_KIND_PAUSE,
SIM_CMD_KIND_STEP,
SIM_CMD_KIND_DRAG_OBJECT,
SIM_CMD_KIND_CURSOR_MOVE,
SIM_CMD_KIND_DRAG_OBJECT_START,
SIM_CMD_KIND_DRAG_OBJECT_STOP,
SIM_CMD_KIND_CONNECT,
SIM_CMD_KIND_DISCONNECT,
SIM_CMD_KIND_SNAPSHOT,
//SIM_CMD_KIND_ENTITY_UPDATE,
//SIM_CMD_KIND_ENTITY_CREATE,
//SIM_CMD_KIND_ENTITY_DESTROY
SIM_CMD_KIND_COUNT
};
struct sim_cmd {
enum sim_cmd_kind kind;
enum sim_cmd_state state;
struct host_channel_id channel_id;
u64 ack_tick;
struct sim_cmd *next;
/* Cmd metadata */
enum sim_cmd_kind kind;
u64 tick;
/* ====================================================================== */
/* SIM_CMD_KIND_CLIENT_CONTROL */
/* SIM_CMD_KIND_PLAYER_CONTROL */
struct v2 move_dir;
struct v2 aim_dir;
/* SIM_CMD_KIND_CURSOR_MOVE */
struct v2 cursor_pos;
/* SIM_CMD_KIND_PLAYER_DISCONNECT */
struct string disconnect_reason;
b32 is_firing;
#if RTC
u32 collider_gjk_steps;
#endif
struct sim_cmd *next;
};
struct sim_cmd_list {
struct sim_cmd *first;
struct sim_cmd *last;
};
/* ========================== *
* Event
* ========================== */
enum sim_event_kind {
SIM_EVENT_KIND_NONE,
SIM_EVENT_KIND_CONNECT,
SIM_EVENT_KIND_DISCONNECT,
SIM_EVENT_KIND_SNAPSHOT,
//SIM_EVENT_KIND_ENTITY_UPDATE,
//SIM_EVENT_KIND_ENTITY_CREATE,
//SIM_EVENT_KIND_ENTITY_DESTROY
};
struct sim_event {
u64 tick;
enum sim_event_kind kind;
struct host_channel_id channel_id;
/* ====================================================================== */
/* SIM_CMD_KIND_CLIENT_DISCONNECT */
struct string disconnect_reason;
/* SIM_EVENT_KIND_SNAPSHOT */
/* =========================================================== */
/* SIM_CMD_KIND_SNAPSHOT */
u64 snapshot_tick_start;
u64 snapshot_tick_end;
struct string snapshot_encoded; /* Delta encoded snapshot containing changes between start & end ticks */
struct sim_event *next;
/* Delta encoded snapshot containing changes between start & end ticks */
struct string snapshot_encoded;
};
struct sim_event_list {
struct sim_event *first;
struct sim_event *last;
/* Represents all cmds generated by a user/sim for a particular channel in a single tick. */
struct sim_cmd_frame {
struct host_channel_id channel; /* Sender's channel id (if this cmd frame was received by a host) */
u64 tick; /* Sender's tick (this will always be 0 for user -> local sim cmds) */
u64 ack; /* Sender's last received cmd frame tick from dst channel */
struct sim_cmd *first;
struct sim_cmd *last;
struct sim_cmd_frame *next;
};
struct sim_cmd_frame_list {
struct sim_cmd_frame *first;
struct sim_cmd_frame *last;
};
/* ========================== *
@ -168,21 +181,18 @@ void sim_ctx_release(struct sim_ctx *ctx);
struct host;
struct bitbuff;
struct sim_snapshot;
void sim_step(struct host *host, struct bitbuff *encoder_bitbuff, struct sim_snapshot_store *snapshot_store, struct sim_snapshot *prev_snapshot, i64 real_dt_ns, struct sim_cmd_list user_sim_cmds);
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);
/* ========================== *
* Event & cmd encode/decode
* Cmd frame encode / decode
* ========================== */
/* TODO: Move this */
#include "host.h"
void sim_cmds_encode(struct bitbuff_writer *bw, struct sim_cmd_list cmds, u64 ack_tick);
void sim_cmds_decode(struct arena *arena, struct host_event_array host_events, struct sim_cmd_list *cmds_out);
void sim_events_encode(struct bitbuff_writer *bw, struct sim_event_list events);
void sim_events_decode(struct arena *arena, struct host_event_array host_events, struct sim_event_list *events_out);
void sim_cmd_frames_encode(struct bitbuff_writer *bw, struct sim_cmd_frame_list frames, u64 ack_tick);
void sim_cmd_frames_decode(struct arena *arena, struct host_event_array host_events, struct sim_cmd_frame_list *frames_out);

View File

@ -26,7 +26,7 @@ struct sim_client {
struct sim_client_handle prev_in_bucket;
/* This is the last tick we know that this client has received */
u64 ack_tick;
u64 ack;
struct v2 cursor_pos;
struct sim_ent_handle camera_ent;

View File

@ -50,6 +50,16 @@ struct sim_snapshot {
/* FIXME: Remove this */
struct space *space;
/* Bookkeeping structures */
/* TODO: Store in snapshot for determinism */
struct sim_ent_lookup contact_lookup;
#if COLLIDER_DEBUG
struct sim_ent_lookup collision_debug_lookup;
#endif
@ -60,16 +70,6 @@ struct sim_snapshot {
u64 phys_iteration;
/* This is the oldest tick stored in ctx that we need to hold a reference to for delta encoding */
u64 oldest_client_ack_tick;
/* Bookkeeping structures */
/* TODO: Store in snapshot for determinism */
struct sim_ent_lookup contact_lookup;
#if COLLIDER_DEBUG
struct sim_ent_lookup collision_debug_lookup;
#endif

View File

@ -215,7 +215,13 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
G.connect_address_str = string_copy(&G.arena, connect_address_str);
}
#else
(UNUSED)connect_address_str;
connect_address_str = STRING(0, 0);
if (connect_address_str.len == 0) {
G.connect_address_str = LIT("127.0.0.1:12345");
G.local_sim_thread = sys_thread_alloc(&user_local_sim_thread_entry_point, G.local_sim_ctx, LIT("[P8] Local sim thread"));
} else {
G.connect_address_str = string_copy(&G.arena, connect_address_str);
}
#endif
G.debug_draw = true;
@ -368,16 +374,16 @@ INTERNAL SORT_COMPARE_FUNC_DEF(ent_draw_order_cmp, arg_a, arg_b, udata)
INTERNAL void queue_sim_cmd(struct arena *arena, struct sim_cmd_list *list, struct sim_cmd src)
INTERNAL void queue_sim_cmd(struct arena *arena, struct sim_cmd_frame *frame, struct sim_cmd src)
{
struct sim_cmd *cmd = arena_push(arena, struct sim_cmd);
*cmd = src;
if (list->last) {
list->last->next = cmd;
if (frame->last) {
frame->last->next = cmd;
} else {
list->first = cmd;
frame->first = cmd;
}
list->last = cmd;
frame->last = cmd;
}
@ -413,21 +419,29 @@ INTERNAL void user_update(void)
G.screen_size = sys_window_get_size(G.window);
struct sprite_scope *sprite_frame_scope = sprite_scope_begin();
struct sim_cmd_list cmd_list = ZI;
struct sim_cmd_frame cmd_frame = ZI;
/* ========================== *
* Process host events into sim events
* Process host events into sim cmds
* ========================== */
struct sim_event_list sim_events = ZI;
struct sim_cmd_frame incoming_cmd_frame = ZI;
{
host_update(G.host);
struct host_event_array host_events = host_pop_events(scratch.arena, G.host);
sim_events_decode(scratch.arena, host_events, &sim_events);
struct sim_cmd_frame_list l = ZI;
sim_cmd_frames_decode(scratch.arena, host_events, &l);
for (struct sim_cmd_frame *frame = l.first; frame; frame = frame->next) {
if (frame->tick == 0 || frame->tick > incoming_cmd_frame.tick) {
incoming_cmd_frame = *frame;
incoming_cmd_frame.next = NULL;
}
}
}
/* ========================== *
* Process sim events
* Process sim cmd frame
* ========================== */
{
@ -439,27 +453,26 @@ INTERNAL void user_update(void)
last_try_connect = now;
}
for (struct sim_event *event = sim_events.first; event; event = event->next) {
u64 event_tick = event->tick;
enum sim_event_kind kind = event->kind;
for (struct sim_cmd *cmd = incoming_cmd_frame.first; cmd; cmd = cmd->next) {
enum sim_cmd_kind kind = cmd->kind;
switch (kind) {
case SIM_EVENT_KIND_CONNECT:
case SIM_CMD_KIND_CONNECT:
{
last_try_connect = F64_INFINITY;
} break;
case SIM_EVENT_KIND_DISCONNECT:
case SIM_CMD_KIND_DISCONNECT:
{
last_try_connect = 0;
} break;
case SIM_EVENT_KIND_SNAPSHOT:
case SIM_CMD_KIND_SNAPSHOT:
{
/* TODO: Only read newest tick event */
if (event_tick > G.world->tick) {
u64 ss0_tick = event->snapshot_tick_start;
u64 ss1_tick = event->snapshot_tick_end;
/* TODO: Only read newest tick cmd */
if (cmd->snapshot_tick_end > G.world->tick) {
u64 ss0_tick = cmd->snapshot_tick_start;
u64 ss1_tick = cmd->snapshot_tick_end;
struct sim_snapshot *ss0 = sim_snapshot_from_tick(G.sim_snapshot_store, ss0_tick);
struct sim_snapshot *ss1 = sim_snapshot_from_tick(G.sim_snapshot_store, ss1_tick);
if (ss0->tick == ss0_tick) {
@ -467,7 +480,7 @@ INTERNAL void user_update(void)
ss1 = sim_snapshot_alloc(G.sim_snapshot_store, ss0, ss1_tick);
ss1->received_at_ns = G.real_time_ns;
struct bitbuff bb = bitbuff_from_string(event->snapshot_encoded);
struct bitbuff bb = bitbuff_from_string(cmd->snapshot_encoded);
struct bitbuff_reader br = br_from_bitbuff(&bb);
sim_snapshot_decode(&br, ss1);
}
@ -492,10 +505,10 @@ INTERNAL void user_update(void)
G.local_sim_last_known_time_ns = newest_snapshot->real_time_ns;
G.local_sim_last_known_tick = newest_snapshot->tick;
/* This is the ack tick that we know the sim has received our ack of.
/* This is the the tick that we know the sim has received our ack of.
* Therefore we must keep it around or else risk the server sending us
* a snapshot delta for a snapshot we've released. */
u64 oldest_possible_delta_base_tick = sim_client_from_handle(newest_snapshot, newest_snapshot->local_client)->ack_tick;
u64 oldest_possible_delta_base_tick = sim_client_from_handle(newest_snapshot, newest_snapshot->local_client)->ack;
/* Predict local sim time based on last received snapshot time,
* then smooth it out to prevent sudden jumps in rendering due
@ -1424,36 +1437,23 @@ INTERNAL void user_update(void)
}
struct v2 input_aim_dir = v2_sub(G.world_cursor, sim_ent_get_xform(local_player).og);
/* Queue cursor move cmd */
queue_sim_cmd(scratch.arena, &cmd_list, (struct sim_cmd) {
.kind = SIM_CMD_KIND_CURSOR_MOVE,
.cursor_pos = G.world_cursor
});
/* Queue player input cmd */
/* Queue player control cmd */
if (!G.debug_camera) {
queue_sim_cmd(scratch.arena, &cmd_list, (struct sim_cmd) {
.kind = SIM_CMD_KIND_PLAYER_CONTROL,
.move_dir = input_move_dir,
.aim_dir = input_aim_dir
});
}
/* Queue player fire cmd */
i32 fire_presses = G.bind_states[USER_BIND_KIND_FIRE].num_presses - G.bind_states[USER_BIND_KIND_FIRE].num_releases;
if (!G.debug_camera && fire_presses) {
queue_sim_cmd(scratch.arena, &cmd_list, (struct sim_cmd) {
.kind = SIM_CMD_KIND_PLAYER_FIRE,
.state = fire_presses > 0 ? SIM_CMD_STATE_START : SIM_CMD_STATE_STOP
queue_sim_cmd(scratch.arena, &cmd_frame, (struct sim_cmd) {
.kind = SIM_CMD_KIND_CLIENT_CONTROL,
.move_dir = input_move_dir,
.aim_dir = input_aim_dir,
.cursor_pos = G.world_cursor,
.is_firing = fire_presses > 0
});
}
/* Queue physics drag cmd */
i32 drag_presses = G.bind_states[USER_BIND_KIND_DEBUG_DRAG].num_presses - G.bind_states[USER_BIND_KIND_DEBUG_DRAG].num_releases;
if (drag_presses) {
queue_sim_cmd(scratch.arena, &cmd_list, (struct sim_cmd) {
.kind = SIM_CMD_KIND_DRAG_OBJECT,
.state = drag_presses > 0 ? SIM_CMD_STATE_START : SIM_CMD_STATE_STOP
if (drag_presses != 0) {
queue_sim_cmd(scratch.arena, &cmd_frame, (struct sim_cmd) {
.kind = drag_presses > 0 ? SIM_CMD_KIND_DRAG_OBJECT_START : SIM_CMD_KIND_DRAG_OBJECT_STOP,
});
}
@ -1478,7 +1478,7 @@ INTERNAL void user_update(void)
{
struct bind_state state = G.bind_states[USER_BIND_KIND_DEBUG_CLEAR];
if (state.num_presses) {
queue_sim_cmd(scratch.arena, &cmd_list, (struct sim_cmd) {
queue_sim_cmd(scratch.arena, &cmd_frame, (struct sim_cmd) {
.kind = SIM_CMD_KIND_CLEAR_ALL
});
}
@ -1488,7 +1488,7 @@ INTERNAL void user_update(void)
{
struct bind_state state = G.bind_states[USER_BIND_KIND_DEBUG_PAUSE];
if (state.num_presses) {
queue_sim_cmd(scratch.arena, &cmd_list, (struct sim_cmd) {
queue_sim_cmd(scratch.arena, &cmd_frame, (struct sim_cmd) {
.kind = SIM_CMD_KIND_PAUSE
});
}
@ -1498,7 +1498,7 @@ INTERNAL void user_update(void)
{
struct bind_state state = G.bind_states[USER_BIND_KIND_DEBUG_STEP];
for (u32 i = 0; i < state.num_presses_and_repeats; ++i) {
queue_sim_cmd(scratch.arena, &cmd_list, (struct sim_cmd) {
queue_sim_cmd(scratch.arena, &cmd_frame, (struct sim_cmd) {
.kind = SIM_CMD_KIND_STEP
});
}
@ -1508,7 +1508,7 @@ INTERNAL void user_update(void)
{
struct bind_state state = G.bind_states[USER_BIND_KIND_DEBUG_SPAWN];
if (state.num_presses) {
queue_sim_cmd(scratch.arena, &cmd_list, (struct sim_cmd) {
queue_sim_cmd(scratch.arena, &cmd_frame, (struct sim_cmd) {
.kind = SIM_CMD_KIND_SPAWN_TEST
});
}
@ -1628,8 +1628,12 @@ INTERNAL void user_update(void)
/* Publish sim cmds */
{
struct sim_cmd_frame_list l = ZI;
l.first = &cmd_frame;
l.last = &cmd_frame;
struct bitbuff_writer bw = bw_from_bitbuff(&G.encoder_bitbuff);
sim_cmds_encode(&bw, cmd_list, G.local_sim_last_known_tick);
sim_cmd_frames_encode(&bw, l, G.local_sim_last_known_tick);
struct string cmds_str = bw_get_written(scratch.arena, &bw);
host_queue_write(G.host, HOST_CHANNEL_ID_ALL, cmds_str, 0);
}
@ -1778,56 +1782,186 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_thread_entry_point, arg)
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
struct bitbuff encoder_bitbuff = bitbuff_alloc(GIGABYTE(64));
struct sim_snapshot_store *ss_store = sim_snapshot_store_alloc();
struct sim_snapshot_store *snapshot_store = sim_snapshot_store_alloc();
(UNUSED)arg;
struct sim_ctx *ctx = (struct sim_ctx *)arg;
#if 1
struct sim_snapshot *prev_ss = sim_snapshot_nil();
i64 last_tick_ns = 0;
i64 target_dt_ns = NS_FROM_SECONDS(1) / SIM_TICKS_PER_SECOND;;
while (!atomic_i32_eval(&G.local_sim_thread_shutdown)) {
struct temp_arena scratch = scratch_begin_no_conflict();
{
__profscope(local_sim_sleep);
sleep_frame(last_tick_ns, target_dt_ns);
last_tick_ns = sys_time_ns();
}
struct temp_arena scratch = scratch_begin_no_conflict();
/* Copy user sim cmds */
struct sim_cmd_list user_sim_cmds = ZI;
/* TODO: Remove this */
{
struct sys_lock lock = sys_mutex_lock_s(&G.user_sim_cmds_arena);
for (struct sim_cmd *cmd = G.user_sim_cmds.first; cmd; cmd = cmd->next) {
struct sim_cmd *dst = arena_push(scratch.arena, struct sim_cmd);
*dst = *cmd;
dst->next = NULL;
if (user_sim_cmds.last) {
user_sim_cmds.last->next = dst;
u64 keep_count = 40;
u64 keep_tick = prev_ss->tick > keep_count ? prev_ss->tick - keep_count : 0;
struct sim_snapshot *remss = sim_snapshot_from_tick(snapshot_store, snapshot_store->first_tick);
while (remss->valid) {
u64 next_tick = remss->next_tick;
if (remss->tick < keep_tick) {
sim_snapshot_release(remss);
} else {
user_sim_cmds.first = dst;
break;
}
user_sim_cmds.last = dst;
remss = sim_snapshot_from_tick(snapshot_store, next_tick);
}
sys_mutex_unlock(&lock);
}
sim_step(host, &encoder_bitbuff, snapshot_store, snapshot, target_dt_ns, user_sim_cmds);
struct sim_cmd_frame_list input_frames = ZI;
{
host_update(host);
struct host_event_array host_events = host_pop_events(scratch.arena, host);
sim_cmd_frames_decode(scratch.arena, host_events, &input_frames);
}
/* Automatically set all user cmd frames to current local sim tick */
/* FIXME: Only do this for local user cmds */
{
for (struct sim_cmd_frame *frame = input_frames.first; frame; frame = frame->next) {
frame->tick = prev_ss->tick + 1;
}
}
/* Step */
struct sim_snapshot *ss = sim_step(snapshot_store, prev_ss, input_frames, target_dt_ns);
/* Publish snapshot cmds */
u64 oldest_ack_tick = 0;
for (u64 i = 0; i < ss->num_clients_reserved; ++i) {
struct sim_client *client = &ss->clients[i];
if (client->valid) {
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(snapshot_store, client->ack);
struct sim_snapshot *ss1 = ss;
/* Create & encode snapshot cmd */
struct sim_cmd snapshot_cmd = ZI;
{
snapshot_cmd.kind = SIM_CMD_KIND_SNAPSHOT;
snapshot_cmd.tick = ss->tick;
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, ss0, ss1, client);
snapshot_cmd.snapshot_encoded = bw_get_written(temp.arena, &bw);
}
}
struct sim_cmd_frame snapshot_cmd_frame = ZI;
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);
/* FIXME: Ack tick */
sim_cmd_frames_encode(&bw, cmd_frames, 0);
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");
scratch_end(scratch);
prev_ss = ss;
}
#else
i64 last_tick_ns = 0;
i64 target_dt_ns = NS_FROM_SECONDS(1) / SIM_TICKS_PER_SECOND;;
while (!atomic_i32_eval(&G.local_sim_thread_shutdown)) {
struct temp_arena scratch = scratch_begin_no_conflict();
{
__profscope(local_sim_sleep);
sleep_frame(last_tick_ns, target_dt_ns);
last_tick_ns = sys_time_ns();
}
struct sim_cmd_frame user_frame = ZI;
user_frame.tick = prev_ss->tick + 1;
user_frame.ack = prev_ss->tick;
/* Read cmds from host */
host_update(host);
struct host_event_array host_events = host_pop_events(scratch.arena, host);
sim_cmds_decode(scratch.arena, host_events, &sim_cmds);
/* Step */
struct sim_snapshot *ss = sim_step(ss_store, prev_ss, user_cmds, target_dt_ns);
/* Encode & enqueue sim_cmd frames */
for (struct sim_cmd_frame *f = output_cmds.first; f; f = f->next) {
struct host_channel_id channel_id = f->dst_channel;
struct string cmd_frame_msg = ZI;
struct bitbuff_writer bw = bw_from_bitbuff(&encoder_bitbuff);
sim_cmd_frame_encode(&bw, q);
cmd_frame_msg = bw_get_written(temp.arena, &bw);
host_queue_write(host, channel_id, cmd_frame_msg, 0);
}
/* Send host messages */
//host_update(host);
__profframe("Local sim");
scratch_end(scratch);
}
#endif
sim_snapshot_store_release(ss_store);
sim_snapshot_store_release(snapshot_store);
bitbuff_release(&encoder_bitbuff);
host_release(host);
}