diff --git a/src/app.c b/src/app.c index e8f1b210..7b86c5a7 100644 --- a/src/app.c +++ b/src/app.c @@ -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 diff --git a/src/bitbuff.c b/src/bitbuff.c index 1729def6..f46c5010 100644 --- a/src/bitbuff.c +++ b/src/bitbuff.c @@ -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 } }, diff --git a/src/config.h b/src/config.h index 9dfafd41..1c0de12a 100644 --- a/src/config.h +++ b/src/config.h @@ -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 diff --git a/src/phys.c b/src/phys.c index 3b5488fa..7de51621 100644 --- a/src/phys.c +++ b/src/phys.c @@ -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); diff --git a/src/sim.c b/src/sim.c index 8d89c9f7..c5daaf8d 100644 --- a/src/sim.c +++ b/src/sim.c @@ -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) { - enum sim_cmd_kind kind = cmd->kind; - struct host_channel_id channel_id = cmd->channel_id; + 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 && kind == SIM_CMD_KIND_SIM_CLIENT_CONNECT && !host_channel_id_is_nil(channel_id)) { - client = sim_client_alloc(world, channel_id); + if (!client->valid) { + for (struct sim_cmd *cmd = frame->first; cmd; cmd = cmd->next) { + enum sim_cmd_kind kind = cmd->kind; + if (kind == SIM_CMD_KIND_SIM_CLIENT_CONNECT && !host_channel_id_is_nil(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) { - client->dbg_drag_stop = true; - } + client->dbg_drag_start = true; + } 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); + bw_write_ubits(bw, cmd->collider_gjk_steps, 32); #endif - switch (cmd->kind) { - case SIM_CMD_KIND_PLAYER_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; + switch (cmd->kind) { + 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); + 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_CURSOR_MOVE: - { - bw_write_f32(bw, cmd->cursor_pos.x); - bw_write_f32(bw, cmd->cursor_pos.y); - } 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; + 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; - default: break; - } - - if (cmds_out->last) { - cmds_out->last->next = cmd; - } else { - cmds_out->first = cmd; - } - cmds_out->last = cmd; - } - } break; - - default: break; - } - } -} - -/* ========================== * - * 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: + case SIM_CMD_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); + 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 (frame->last) { + frame->last->next = cmd; + } else { + frame->first = cmd; + } + frame->last = cmd; } + + if (frames_out->last) { + frames_out->last->next = frame; + } else { + frames_out->first = frame; + } + frames_out->last = frame; } break; default: break; } - - if (events_out->last) { - events_out->last->next = sim_event; - } else { - events_out->first = sim_event; - } - events_out->last = sim_event; } } diff --git a/src/sim.h b/src/sim.h index cadbac14..6c5e14b6 100644 --- a/src/sim.h +++ b/src/sim.h @@ -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); diff --git a/src/sim_client.h b/src/sim_client.h index ece3a446..047b0bfe 100644 --- a/src/sim_client.h +++ b/src/sim_client.h @@ -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; diff --git a/src/sim_snapshot.h b/src/sim_snapshot.h index 9ef752df..6ce3f576 100644 --- a/src/sim_snapshot.h +++ b/src/sim_snapshot.h @@ -50,19 +50,6 @@ struct sim_snapshot { /* FIXME: Remove this */ struct space *space; - - - - - - - /* FIXME: Encode / decode*/ - - 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; @@ -79,6 +66,19 @@ struct sim_snapshot { + /* FIXME: Encode / decode */ + + u64 phys_iteration; + + + + + + + + + + diff --git a/src/user.c b/src/user.c index 80d202c6..4bf5b7e9 100644 --- a/src/user.c +++ b/src/user.c @@ -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, + i32 fire_presses = G.bind_states[USER_BIND_KIND_FIRE].num_presses - G.bind_states[USER_BIND_KIND_FIRE].num_releases; + 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 - }); - } - - /* 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 + .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); - - - - - - }