diff --git a/src/host.c b/src/host.c index f0c80bb2..57d99365 100644 --- a/src/host.c +++ b/src/host.c @@ -16,6 +16,10 @@ * * Rolling window for message reassembly. * This would remove the need for random access message buffers. + * + * Connection timeouts. + * + * Challenges to verify receiving address. */ #define PACKET_MAGIC 0xd9e3b8b6 diff --git a/src/sim.c b/src/sim.c index 5a8bf9cc..da7119e1 100644 --- a/src/sim.c +++ b/src/sim.c @@ -519,22 +519,25 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct struct sim_cmd_frame **client_frames; { /* Create connecting clients */ - for (struct sim_cmd_frame *frame = input_frames.first; frame; frame = frame->next) { - struct sim_client *client; - if (frame->sender_is_local) { - client = sim_client_from_handle(world, world->local_client); - if (!client->valid) { - client = sim_client_alloc(world, HOST_CHANNEL_ID_NIL, SIM_CLIENT_KIND_LOCAL); - world->local_client = client->handle; - } - } else { - client = sim_client_from_channel_id(world, frame->sender_channel); - if (!client->valid) { - for (struct sim_cmd *cmd = frame->first; cmd; cmd = cmd->next) { - enum sim_cmd_kind kind = cmd->kind; - if (kind == SIM_CMD_KIND_SIM_CLIENT_CONNECT && !host_channel_id_is_nil(frame->sender_channel)) { - client = sim_client_alloc(world, frame->sender_channel, SIM_CLIENT_KIND_NETSIM); - break; + if (world->is_master) { + /* Create local client if it doesn't exist */ + struct sim_client *local_client = sim_client_from_handle(world, world->local_client); + if (!local_client->valid) { + local_client = sim_client_alloc(world, HOST_CHANNEL_ID_NIL, SIM_CLIENT_KIND_SIM_MASTER); + world->local_client = local_client->handle; + } + /* Create slave sim clients */ + for (struct sim_cmd_frame *frame = input_frames.first; frame; frame = frame->next) { + struct sim_client *client; + if (!frame->sender_is_local) { + client = sim_client_from_channel_id(world, frame->sender_channel); + if (!client->valid) { + for (struct sim_cmd *cmd = frame->first; cmd; cmd = cmd->next) { + enum sim_cmd_kind kind = cmd->kind; + if (kind == SIM_CMD_KIND_SIM_CLIENT_CONNECT && !host_channel_id_is_nil(frame->sender_channel)) { + client = sim_client_alloc(world, frame->sender_channel, SIM_CLIENT_KIND_SIM_SLAVE); + break; + } } } } @@ -549,877 +552,829 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct } else { client = sim_client_from_channel_id(world, frame->sender_channel); } - if (client->valid && frame->tick == world->tick) { + if (client->valid) { client_frames[client->handle.idx] = frame; } } } - /* ========================== * - * Process client sim cmds - * ========================== */ + if (world->is_master) { + /* ========================== * + * Process client sim cmds + * ========================== */ - /* Process client cmds */ - u64 oldest_client_ack = world->tick; - for (u64 i = 0; i < world->num_clients_reserved; ++i) { - struct sim_client *client = &world->clients[i]; - struct sim_cmd_frame *frame = client_frames[client->handle.idx]; - if (client->valid && frame) { - struct sim_control *control = &client->control; + /* Process client cmds */ + u64 oldest_client_ack = world->tick; + for (u64 i = 0; i < world->num_clients_reserved; ++i) { + struct sim_client *client = &world->clients[i]; + struct sim_cmd_frame *frame = client_frames[client->handle.idx]; + if (client->valid && frame) { + struct sim_control *control = &client->control; - client->dbg_drag_start = false; - client->dbg_drag_stop = false; + client->dbg_drag_start = false; + client->dbg_drag_stop = false; - if (frame->ack > client->ack) { - client->ack = frame->ack; - } + if (frame->ack > client->ack) { + client->ack = frame->ack; + } - for (struct sim_cmd *cmd = frame->first; cmd; cmd = cmd->next) { - enum sim_cmd_kind kind = cmd->kind; - switch (kind) { - /* TODO: Combine movement from multiple inputs? E.ctx-> a sudden - * start and immediate stop cmd should still move the player a - * tad. */ - case SIM_CMD_KIND_CLIENT_CONTROL: - { - struct sim_control old_control = client->control; - *control = cmd->control; - if (v2_len_sq(control->move) > 1) { - /* Cap movement vector magnitude at 1 */ - control->move = v2_norm(control->move); - } - - /* Determine cursor pos from focus */ + for (struct sim_cmd *cmd = frame->first; cmd; cmd = cmd->next) { + enum sim_cmd_kind kind = cmd->kind; + switch (kind) { + /* TODO: Combine movement from multiple inputs? E.ctx-> a sudden + * start and immediate stop cmd should still move the player a + * tad. */ + case SIM_CMD_KIND_CLIENT_CONTROL: { - struct sim_ent_handle control_ent_handle = client->control_ent; - struct sim_ent *control_ent = sim_ent_from_handle(world, control_ent_handle); - if (control_ent->valid || sim_ent_handle_eq(control_ent_handle, SIM_ENT_NIL_HANDLE)) { - /* Only update cursor pos if focus ent is valid (or nil) */ - client->cursor_pos = v2_add(sim_ent_get_xform(control_ent).og, client->control.focus); + struct sim_control old_control = client->control; + *control = cmd->control; + if (v2_len_sq(control->move) > 1) { + /* Cap movement vector magnitude at 1 */ + control->move = v2_norm(control->move); } - } - u32 flags = control->flags; - if (flags & SIM_CONTROL_FLAG_DRAGGING) { - if (!(old_control.flags & SIM_CONTROL_FLAG_DRAGGING)) { - client->dbg_drag_start = true; - } - } else { - if (old_control.flags & SIM_CONTROL_FLAG_DRAGGING) { - client->dbg_drag_stop = true; - } - } - if (flags & SIM_CONTROL_FLAG_CLEAR_ALL) { - if (!(old_control.flags & SIM_CONTROL_FLAG_CLEAR_ALL)) { - test_clear_level(world); - } - } - if (flags & SIM_CONTROL_FLAG_SPAWN_TEST) { - if (!(old_control.flags & SIM_CONTROL_FLAG_SPAWN_TEST)) { - logf_info("Spawning (test)"); - u32 count = 1; - f32 spread = 1; - for (u32 j = 0; j < count; ++j) { - spawn_test_entities(world, V2(0, (((f32)j / (f32)count) - 0.5) * spread)); + /* Determine cursor pos from focus */ + { + struct sim_ent_handle control_ent_handle = client->control_ent; + struct sim_ent *control_ent = sim_ent_from_handle(world, control_ent_handle); + if (control_ent->valid || sim_ent_handle_eq(control_ent_handle, SIM_ENT_NIL_HANDLE)) { + /* Only update cursor pos if focus ent is valid (or nil) */ + client->cursor_pos = v2_add(sim_ent_get_xform(control_ent).og, client->control.focus); } } - } - } break; - /* Disconnect client */ - case SIM_CMD_KIND_SIM_CLIENT_DISCONNECT: - { - sim_client_release(client); - } break; + u32 flags = control->flags; + if (flags & SIM_CONTROL_FLAG_DRAGGING) { + if (!(old_control.flags & SIM_CONTROL_FLAG_DRAGGING)) { + client->dbg_drag_start = true; + } + } else { + if (old_control.flags & SIM_CONTROL_FLAG_DRAGGING) { + client->dbg_drag_stop = true; + } + } + if (flags & SIM_CONTROL_FLAG_CLEAR_ALL) { + if (!(old_control.flags & SIM_CONTROL_FLAG_CLEAR_ALL)) { + test_clear_level(world); + } + } + if (flags & SIM_CONTROL_FLAG_SPAWN_TEST) { + if (!(old_control.flags & SIM_CONTROL_FLAG_SPAWN_TEST)) { + logf_info("Spawning (test)"); + u32 count = 1; + f32 spread = 1; + for (u32 j = 0; j < count; ++j) { + spawn_test_entities(world, V2(0, (((f32)j / (f32)count) - 0.5) * spread)); + } + } + } + } break; - default: break; - }; - } + /* Disconnect client */ + case SIM_CMD_KIND_SIM_CLIENT_DISCONNECT: + { + sim_client_release(client); + } break; - if (client->ack < oldest_client_ack || oldest_client_ack == 0) { - oldest_client_ack = client->ack; + default: break; + }; + } + + if (client->ack < oldest_client_ack || oldest_client_ack == 0) { + oldest_client_ack = client->ack; + } } } - } - /* ========================== * - * Create client ents - * ========================== */ + /* ========================== * + * Create client ents + * ========================== */ - for (u64 i = 0; i < world->num_clients_reserved; ++i) { - struct sim_client *client = &world->clients[i]; - if (client->valid) { - /* FIXME: Ents never released when client disconnects */ - struct sim_ent *player_ent = sim_ent_from_handle(world, client->control_ent); - if (!player_ent->valid) { - player_ent = spawn_test_player(world); - sim_ent_enable_prop(player_ent, SIM_ENT_PROP_CONTROLLED); - client->control_ent = player_ent->handle; - player_ent->controlling_client = client->handle; - } - struct sim_ent *camera_ent = sim_ent_from_handle(world, client->camera_ent); - if (!camera_ent->valid) { - camera_ent = spawn_test_player_camera(world, player_ent); - client->camera_ent = camera_ent->handle; - } - } - } - - /* ========================== * - * Update entity control from client control - * ========================== */ - - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - - if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTROLLED)) { - struct sim_client *client = sim_client_from_handle(world, ent->controlling_client); + for (u64 i = 0; i < world->num_clients_reserved; ++i) { + struct sim_client *client = &world->clients[i]; if (client->valid) { - ent->control = client->control; - /* TODO: Move this */ - if (ent->control.flags & SIM_CONTROL_FLAG_FIRING) { - sim_ent_enable_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED); - } else { - sim_ent_disable_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED); + /* FIXME: Ents never released when client disconnects */ + struct sim_ent *player_ent = sim_ent_from_handle(world, client->control_ent); + if (!player_ent->valid) { + player_ent = spawn_test_player(world); + sim_ent_enable_prop(player_ent, SIM_ENT_PROP_CONTROLLED); + client->control_ent = player_ent->handle; + player_ent->controlling_client = client->handle; + } + struct sim_ent *camera_ent = sim_ent_from_handle(world, client->camera_ent); + if (!camera_ent->valid) { + camera_ent = spawn_test_player_camera(world, player_ent); + client->camera_ent = camera_ent->handle; } } } - } - /* ========================== * - * Update entities from sprite - * ========================== */ + /* ========================== * + * Update entity control from client control + * ========================== */ - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (sprite_tag_is_nil(ent->sprite)) continue; + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; - struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite); - - /* Update animation */ - { - struct sprite_sheet_span span = sprite_sheet_get_span(sheet, ent->sprite_span_name); - if (ent->animation_last_frame_change_time_ns == 0) { - ent->animation_last_frame_change_time_ns = world_time; - } - - f64 time_in_frame = SECONDS_FROM_NS(world->world_time_ns - ent->animation_last_frame_change_time_ns); - u64 frame_index = ent->animation_frame; - if (frame_index < span.start || frame_index > span.end) { - frame_index = span.start; - } - - if (span.end > span.start + 1) { - struct sprite_sheet_frame frame = sprite_sheet_get_frame(sheet, frame_index); - while (time_in_frame > frame.duration) { - time_in_frame -= frame.duration; - ++frame_index; - if (frame_index > span.end) { - /* Loop animation */ - frame_index = span.start; + if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTROLLED)) { + struct sim_client *client = sim_client_from_handle(world, ent->controlling_client); + if (client->valid) { + ent->control = client->control; + /* TODO: Move this */ + if (ent->control.flags & SIM_CONTROL_FLAG_FIRING) { + sim_ent_enable_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED); + } else { + sim_ent_disable_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED); } - frame = sprite_sheet_get_frame(sheet, frame_index); - ent->animation_last_frame_change_time_ns = world->world_time_ns; } } - - ent->animation_frame = frame_index; } - /* Update sprite local xform */ - { - struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, LIT("pivot"), ent->animation_frame); - struct v2 sprite_size = v2_div(sheet->frame_size, (f32)IMAGE_PIXELS_PER_UNIT); + /* ========================== * + * Update entities from sprite + * ========================== */ - struct v2 dir = v2_mul_v2(sprite_size, slice.dir); - f32 rot = v2_angle(dir) + PI / 2; + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (sprite_tag_is_nil(ent->sprite)) continue; - struct xform xf = XFORM_IDENT; - xf = xform_rotated(xf, -rot); - xf = xform_scaled(xf, sprite_size); - xf = xform_translated(xf, v2_neg(slice.center)); - ent->sprite_local_xform = xf; - } + struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite); - /* Update collider from sprite */ - if (ent->sprite_collider_slice.len > 0) { - struct xform cxf = ent->sprite_local_xform; - - struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, ent->sprite_collider_slice, ent->animation_frame); - ent->local_collider = collider_from_quad(xform_mul_quad(cxf, quad_from_rect(slice.rect))); - } - - /* Test collider */ -#if 0 - if (sim_ent_has_prop(ent, SIM_ENT_PROP_TEST)) { - //if ((true)) { -#if 0 - ent->local_collider.points[0] = V2(0, 0); - ent->local_collider.count = 1; - ent->local_collider.radius = 0.5; -#elif 0 - ent->local_collider.points[0] = v2_with_len(V2(0.08f, 0.17f), 0.15f); - ent->local_collider.points[1] = v2_with_len(V2(-0.07f, -0.2f), 0.15f); - ent->local_collider.count = 2; - ent->local_collider.radius = 0.075f; -#elif 1 -#if 0 - /* "Bad" winding order */ - ent->local_collider.points[0] = V2(-0.15, 0.15); - ent->local_collider.points[1] = V2(0.15, 0.15); - ent->local_collider.points[2] = V2(0, -0.15); -#else - ent->local_collider.points[0] = V2(0, -0.15); - ent->local_collider.points[1] = V2(0.15, 0.15); - ent->local_collider.points[2] = V2(-0.15, 0.15); -#endif - ent->local_collider.count = 3; - ent->local_collider.radius = 0.25; - //ent->local_collider.radius = math_fabs(math_sin(ctx->tick.time) / 3); -#else - //ent->local_collider.radius = 0.5; - ent->local_collider.radius = 0.25; - //ent->local_collider.radius = 0.; -#endif - } -#endif - } - - /* ========================== * - * Update attachments - * ========================== */ - - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_ATTACHED)) continue; - - struct sim_ent *parent = sim_ent_from_handle(world, ent->parent); - struct sprite_tag parent_sprite = parent->sprite; - struct sprite_sheet *parent_sheet = sprite_sheet_from_tag_await(sprite_frame_scope, parent_sprite); - - struct xform parent_sprite_xf = parent->sprite_local_xform; - - struct sprite_sheet_slice attach_slice = sprite_sheet_get_slice(parent_sheet, ent->attach_slice, parent->animation_frame); - struct v2 attach_pos = xform_mul_v2(parent_sprite_xf, attach_slice.center); - struct v2 attach_dir = xform_basis_mul_v2(parent_sprite_xf, attach_slice.dir); - - struct xform xf = sim_ent_get_local_xform(ent); - xf.og = attach_pos; - xf = xform_basis_with_rotation_world(xf, v2_angle(attach_dir) + PI / 2); - sim_ent_set_local_xform(ent, xf); - } - - /* ========================== * - * Test - * ========================== */ - -#if 0 - for (u64 ent_index = 0; ent_index < ss_blended->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &ss_blended->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_TEST)) continue; - -#if 0 - if (!ent->test_initialized) { - ent->test_initialized = true; - ent->test_start_local_xform = sim_ent_get_local_xform(ent); - ent->test_start_sprite_xform = ent->sprite_local_xform; - } - - f32 t = (f32)time; - struct v2 og = v2_mul(V2(math_cos(t), math_sin(t)), 3); - f32 r = t * 3; - struct v2 s = V2(1 + (math_fabs(math_sin(t * 5)) * 3), 1); - (UNUSED)og; - (UNUSED)r; - (UNUSED)s; - - og = v2_add(og, ent->test_start_local_xform.og); - r += xform_get_rotation(ent->test_start_local_xform); - s = v2_add(s, xform_get_scale(ent->test_start_local_xform)); - - struct xform xf = sim_ent_get_local_xform(ent); - xf.og = og; - xf = xform_rotated_to(xf, r); - xf = xform_scaled_to(xf, s); - sim_ent_set_local_xform(ent, xf); -#else - f32 t = (f32)time; - struct v2 og = v2_mul(V2(math_cos(t), math_sin(t)), 3); - f32 rot = t * PI / 3; - struct v2 scale = V2(1 + (math_fabs(math_sin(t * 5)) * 3), 1); - (UNUSED)og; - (UNUSED)rot; - (UNUSED)scale; - - struct xform xf = sim_ent_get_local_xform(ent); - xf = xform_rotated_to(xf, rot); - xf = xform_scaled_to(xf, scale); - sim_ent_set_local_xform(ent, xf); -#endif - } -#endif - - /* ========================== * - * Trigger equipped - * ========================== */ - - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - - if (sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED)) { - struct sim_ent *eq = sim_ent_from_handle(world, ent->equipped); - if (sim_ent_is_valid_and_active(eq)) { - sim_ent_enable_prop(eq, SIM_ENT_PROP_TRIGGERED_THIS_TICK); - } - } - } - - /* ========================== * - * Process triggered entities - * ========================== */ - - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK)) continue; - if ((world_time - ent->last_triggered < ent->trigger_delay) && ent->last_triggered != 0) continue; - - ent->last_triggered = world_time; - - /* Fire weapon */ - if (sim_ent_has_prop(ent, SIM_ENT_PROP_WEAPON)) { - struct sprite_tag sprite = ent->sprite; - u32 animation_frame = ent->animation_frame; - struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, sprite); - - struct xform sprite_local_xform = ent->sprite_local_xform; - - struct sprite_sheet_slice out_slice = sprite_sheet_get_slice(sheet, LIT("out"), animation_frame); - struct v2 rel_pos = xform_mul_v2(sprite_local_xform, out_slice.center); - struct v2 rel_dir = xform_basis_mul_v2(sprite_local_xform, out_slice.dir); - - /* Spawn bullet */ - struct sim_ent *bullet; + /* Update animation */ { - bullet = sim_ent_alloc(root); - - bullet->bullet_src = ent->handle; - bullet->bullet_src_pos = rel_pos; - bullet->bullet_src_dir = rel_dir; - //bullet->bullet_impulse = 0.75f; - bullet->bullet_impulse = 2.0f; - bullet->bullet_knockback = 10; - bullet->mass_unscaled = 0.04f; - bullet->inertia_unscaled = 0.00001f; - bullet->layer = SIM_LAYER_BULLETS; - -#if 1 - /* Point collider */ - bullet->local_collider.points[0] = V2(0, 0); - bullet->local_collider.count = 1; -#else - bullet->sprite = sprite_tag_from_path(LIT("res/graphics/bullet.ase")); - bullet->sprite_collider_slice = LIT("shape"); -#endif - - sim_ent_enable_prop(bullet, SIM_ENT_PROP_BULLET); - sim_ent_enable_prop(bullet, SIM_ENT_PROP_SENSOR); - } - - /* Spawn tracer */ - { - struct sim_ent *tracer = sim_ent_alloc(root); - tracer->tracer_fade_duration = 0.025f; - tracer->layer = SIM_LAYER_TRACERS; - sim_ent_enable_prop(tracer, SIM_ENT_PROP_TRACER); - - bullet->bullet_tracer = tracer->handle; - } - } - } - - /* ========================== * - * Create motor joints from control move - * ========================== */ - - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - - if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTROLLED)) { - struct sim_ent *joint_ent = sim_ent_from_handle(world, ent->move_joint); - if (!sim_ent_is_valid_and_active(joint_ent)) { - joint_ent = sim_ent_alloc(root); - joint_ent->mass_unscaled = F32_INFINITY; - joint_ent->inertia_unscaled = F32_INFINITY; - sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_MOTOR_JOINT); - sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_ACTIVE); - ent->move_joint = joint_ent->handle; - - struct phys_motor_joint_def def = ZI; - def.e0 = joint_ent->handle; /* Re-using joint entity as e0 */ - def.e1 = ent->handle; - def.correction_rate = 0; - def.max_force = ent->control_force; - def.max_torque = 0; - joint_ent->motor_joint_data = phys_motor_joint_from_def(def); - } - - sim_ent_set_xform(joint_ent, XFORM_IDENT); /* Reset joint ent position */ - sim_ent_set_linear_velocity(joint_ent, v2_mul(v2_clamp_len(ent->control.move, 1), ent->control_force_max_speed)); - } - } - - /* ========================== * - * Create motor joints from control focus (aim) - * ========================== */ - -#if SIM_PLAYER_AIM - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - - if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTROLLED)) { - struct xform xf = sim_ent_get_xform(ent); - struct xform sprite_xf = xform_mul(xf, ent->sprite_local_xform); - - /* Retrieve / create aim joint */ - struct sim_ent *joint_ent = sim_ent_from_handle(world, ent->aim_joint); - if (!sim_ent_is_valid_and_active(joint_ent)) { - joint_ent = sim_ent_alloc(root); - joint_ent->mass_unscaled = F32_INFINITY; - joint_ent->inertia_unscaled = F32_INFINITY; - sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_PHYSICAL_KINEMATIC); /* Since we'll be setting velocity manually */ - sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_MOTOR_JOINT); - sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_ACTIVE); - ent->aim_joint = joint_ent->handle; - - struct phys_motor_joint_def def = ZI; - def.e0 = joint_ent->handle; /* Re-using joint entity as e0 */ - def.e1 = ent->handle; - def.max_force = 0; - def.max_torque = ent->control_torque; - joint_ent->motor_joint_data = phys_motor_joint_from_def(def); - } - - /* Set correction rate dynamically since motor velocity is only set for one frame */ - joint_ent->motor_joint_data.correction_rate = 10 * world_dt; - - - /* Solve for final angle using law of sines */ - f32 new_angle; - { - struct v2 ent_pos = xf.og; - struct v2 focus_pos = v2_add(ent_pos, ent->control.focus); - - struct v2 sprite_hold_pos; - struct v2 sprite_hold_dir; - { - struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite); - struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, LIT("attach.wep"), ent->animation_frame); - sprite_hold_pos = slice.center; - sprite_hold_dir = slice.dir; + struct sprite_sheet_span span = sprite_sheet_get_span(sheet, ent->sprite_span_name); + if (ent->animation_last_frame_change_time_ns == 0) { + ent->animation_last_frame_change_time_ns = world_time; } - struct v2 hold_dir = xform_basis_mul_v2(sprite_xf, sprite_hold_dir); - struct v2 hold_pos = xform_mul_v2(sprite_xf, sprite_hold_pos); - if (v2_eq(hold_pos, ent_pos)) { - /* If hold pos is same as origin (E.G if pivot is being used as hold pos), then move hold pos forward a tad to avoid issue */ - sprite_hold_pos = v2_add(sprite_hold_pos, V2(0, -1)); - hold_pos = xform_mul_v2(sprite_xf, sprite_hold_pos); + f64 time_in_frame = SECONDS_FROM_NS(world->world_time_ns - ent->animation_last_frame_change_time_ns); + u64 frame_index = ent->animation_frame; + if (frame_index < span.start || frame_index > span.end) { + frame_index = span.start; } - f32 forward_hold_angle_offset; - { - struct xform xf_unrotated = xform_basis_with_rotation_world(xf, 0); - struct v2 hold_pos_unrotated = xform_mul_v2(xf_unrotated, xform_mul_v2(ent->sprite_local_xform, sprite_hold_pos)); - forward_hold_angle_offset = v2_angle_from_dirs(V2(0, -1), v2_sub(hold_pos_unrotated, xf_unrotated.og)); - } - - struct v2 hold_ent_dir = v2_sub(ent_pos, hold_pos); - struct v2 focus_ent_dir = v2_sub(ent_pos, focus_pos); - - f32 hold_ent_len = v2_len(hold_ent_dir); - f32 focus_ent_len = v2_len(focus_ent_dir); - - f32 final_hold_angle_btw_ent_and_focus = v2_angle_from_dirs(hold_ent_dir, hold_dir); - f32 final_focus_angle_btw_ent_and_hold = math_asin((math_sin(final_hold_angle_btw_ent_and_focus) * hold_ent_len) / focus_ent_len); - f32 final_ent_angle_btw_focus_and_hold = PI - (final_focus_angle_btw_ent_and_hold + final_hold_angle_btw_ent_and_focus); - - new_angle = math_unwind_angle(v2_angle_from_dirs(V2(0, -1), v2_sub(focus_pos, ent_pos)) + final_ent_angle_btw_focus_and_hold - forward_hold_angle_offset); - } - - f32 new_vel = 0; - if (!F32_IS_NAN(new_angle)) { - const f32 angle_error_allowed = 0.001f; - struct xform joint_xf = sim_ent_get_xform(joint_ent); - f32 diff = math_unwind_angle(new_angle - xform_get_rotation(joint_xf)); - if (math_fabs(diff) > angle_error_allowed) { - /* Instantly snap joint ent to new angle */ - new_vel = diff / real_dt; - } - } - sim_ent_set_angular_velocity(joint_ent, new_vel); - } - } -#endif - - /* ========================== * - * Create motor joints from ground friction (gravity) - * ========================== */ - - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_PHYSICAL_DYNAMIC)) continue; - - struct sim_ent *joint_ent = sim_ent_from_handle(world, ent->ground_friction_joint); - - struct phys_motor_joint_def def = ZI; - def.e0 = root->handle; - def.e1 = ent->handle; - def.correction_rate = 0; - def.max_force = ent->linear_ground_friction; - def.max_torque = ent->angular_ground_friction; - if (joint_ent->motor_joint_data.max_force != def.max_force || joint_ent->motor_joint_data.max_torque != def.max_torque) { - if (!sim_ent_is_valid_and_active(joint_ent)) { - joint_ent = sim_ent_alloc(root); - sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_MOTOR_JOINT); - sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_ACTIVE); - joint_ent->motor_joint_data = phys_motor_joint_from_def(def); - ent->ground_friction_joint = joint_ent->handle; - } - } - } - - /* ========================== * - * Create mouse joints from client debug drag - * ========================== */ - - for (u64 i = 0; i < world->num_clients_reserved; ++i) { - struct sim_client *client = &world->clients[i]; - if (client->valid) { - struct v2 cursor = client->cursor_pos; - b32 start_dragging = client->dbg_drag_start; - b32 stop_dragging = client->dbg_drag_stop; - - struct sim_ent *joint_ent = sim_ent_from_handle(world, client->dbg_drag_joint_ent); - struct sim_ent *target_ent = sim_ent_from_handle(world, joint_ent->mouse_joint_data.target); - - if (start_dragging) { - struct xform mouse_xf = xform_from_pos(cursor); - struct collider_shape mouse_shape = ZI; - mouse_shape.points[0] = V2(0, 0); - mouse_shape.count = 1; - - for (u64 sim_ent_index = 0; sim_ent_index < world->num_ents_reserved; ++sim_ent_index) { - struct sim_ent *ent = &world->ents[sim_ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_PHYSICAL_DYNAMIC)) continue; - - struct collider_shape ent_collider = ent->local_collider; - if (ent_collider.count > 0) { - struct xform ent_xf = sim_ent_get_xform(ent); - /* TODO: Can just use boolean GJK */ - struct collider_collision_points_result res = collider_collision_points(&ent_collider, &mouse_shape, ent_xf, mouse_xf); - if (res.num_points > 0) { - target_ent = ent; - break; + if (span.end > span.start + 1) { + struct sprite_sheet_frame frame = sprite_sheet_get_frame(sheet, frame_index); + while (time_in_frame > frame.duration) { + time_in_frame -= frame.duration; + ++frame_index; + if (frame_index > span.end) { + /* Loop animation */ + frame_index = span.start; } + frame = sprite_sheet_get_frame(sheet, frame_index); + ent->animation_last_frame_change_time_ns = world->world_time_ns; } } - } - if (stop_dragging) { - target_ent = sim_ent_nil(); + + ent->animation_frame = frame_index; } - if (sim_ent_is_valid_and_active(target_ent)) { + /* Update sprite local xform */ + { + struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, LIT("pivot"), ent->animation_frame); + struct v2 sprite_size = v2_div(sheet->frame_size, (f32)IMAGE_PIXELS_PER_UNIT); + + struct v2 dir = v2_mul_v2(sprite_size, slice.dir); + f32 rot = v2_angle(dir) + PI / 2; + + struct xform xf = XFORM_IDENT; + xf = xform_rotated(xf, -rot); + xf = xform_scaled(xf, sprite_size); + xf = xform_translated(xf, v2_neg(slice.center)); + ent->sprite_local_xform = xf; + } + + /* Update collider from sprite */ + if (ent->sprite_collider_slice.len > 0) { + struct xform cxf = ent->sprite_local_xform; + + struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, ent->sprite_collider_slice, ent->animation_frame); + ent->local_collider = collider_from_quad(xform_mul_quad(cxf, quad_from_rect(slice.rect))); + } + + /* Test collider */ + #if 0 + if (sim_ent_has_prop(ent, SIM_ENT_PROP_TEST)) { + //if ((true)) { + #if 0 + ent->local_collider.points[0] = V2(0, 0); + ent->local_collider.count = 1; + ent->local_collider.radius = 0.5; + #elif 0 + ent->local_collider.points[0] = v2_with_len(V2(0.08f, 0.17f), 0.15f); + ent->local_collider.points[1] = v2_with_len(V2(-0.07f, -0.2f), 0.15f); + ent->local_collider.count = 2; + ent->local_collider.radius = 0.075f; + #elif 1 + #if 0 + /* "Bad" winding order */ + ent->local_collider.points[0] = V2(-0.15, 0.15); + ent->local_collider.points[1] = V2(0.15, 0.15); + ent->local_collider.points[2] = V2(0, -0.15); + #else + ent->local_collider.points[0] = V2(0, -0.15); + ent->local_collider.points[1] = V2(0.15, 0.15); + ent->local_collider.points[2] = V2(-0.15, 0.15); + #endif + ent->local_collider.count = 3; + ent->local_collider.radius = 0.25; + //ent->local_collider.radius = math_fabs(math_sin(ctx->tick.time) / 3); + #else + //ent->local_collider.radius = 0.5; + ent->local_collider.radius = 0.25; + //ent->local_collider.radius = 0.; + #endif + } + #endif + } + + /* ========================== * + * Update attachments + * ========================== */ + + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_ATTACHED)) continue; + + struct sim_ent *parent = sim_ent_from_handle(world, ent->parent); + struct sprite_tag parent_sprite = parent->sprite; + struct sprite_sheet *parent_sheet = sprite_sheet_from_tag_await(sprite_frame_scope, parent_sprite); + + struct xform parent_sprite_xf = parent->sprite_local_xform; + + struct sprite_sheet_slice attach_slice = sprite_sheet_get_slice(parent_sheet, ent->attach_slice, parent->animation_frame); + struct v2 attach_pos = xform_mul_v2(parent_sprite_xf, attach_slice.center); + struct v2 attach_dir = xform_basis_mul_v2(parent_sprite_xf, attach_slice.dir); + + struct xform xf = sim_ent_get_local_xform(ent); + xf.og = attach_pos; + xf = xform_basis_with_rotation_world(xf, v2_angle(attach_dir) + PI / 2); + sim_ent_set_local_xform(ent, xf); + } + + /* ========================== * + * Test + * ========================== */ + + #if 0 + for (u64 ent_index = 0; ent_index < ss_blended->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &ss_blended->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_TEST)) continue; + + #if 0 + if (!ent->test_initialized) { + ent->test_initialized = true; + ent->test_start_local_xform = sim_ent_get_local_xform(ent); + ent->test_start_sprite_xform = ent->sprite_local_xform; + } + + f32 t = (f32)time; + struct v2 og = v2_mul(V2(math_cos(t), math_sin(t)), 3); + f32 r = t * 3; + struct v2 s = V2(1 + (math_fabs(math_sin(t * 5)) * 3), 1); + (UNUSED)og; + (UNUSED)r; + (UNUSED)s; + + og = v2_add(og, ent->test_start_local_xform.og); + r += xform_get_rotation(ent->test_start_local_xform); + s = v2_add(s, xform_get_scale(ent->test_start_local_xform)); + + struct xform xf = sim_ent_get_local_xform(ent); + xf.og = og; + xf = xform_rotated_to(xf, r); + xf = xform_scaled_to(xf, s); + sim_ent_set_local_xform(ent, xf); + #else + f32 t = (f32)time; + struct v2 og = v2_mul(V2(math_cos(t), math_sin(t)), 3); + f32 rot = t * PI / 3; + struct v2 scale = V2(1 + (math_fabs(math_sin(t * 5)) * 3), 1); + (UNUSED)og; + (UNUSED)rot; + (UNUSED)scale; + + struct xform xf = sim_ent_get_local_xform(ent); + xf = xform_rotated_to(xf, rot); + xf = xform_scaled_to(xf, scale); + sim_ent_set_local_xform(ent, xf); + #endif + } + #endif + + /* ========================== * + * Trigger equipped + * ========================== */ + + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + + if (sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGERING_EQUIPPED)) { + struct sim_ent *eq = sim_ent_from_handle(world, ent->equipped); + if (sim_ent_is_valid_and_active(eq)) { + sim_ent_enable_prop(eq, SIM_ENT_PROP_TRIGGERED_THIS_TICK); + } + } + } + + /* ========================== * + * Process triggered entities + * ========================== */ + + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_TRIGGERED_THIS_TICK)) continue; + if ((world_time - ent->last_triggered < ent->trigger_delay) && ent->last_triggered != 0) continue; + + ent->last_triggered = world_time; + + /* Fire weapon */ + if (sim_ent_has_prop(ent, SIM_ENT_PROP_WEAPON)) { + struct sprite_tag sprite = ent->sprite; + u32 animation_frame = ent->animation_frame; + struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, sprite); + + struct xform sprite_local_xform = ent->sprite_local_xform; + + struct sprite_sheet_slice out_slice = sprite_sheet_get_slice(sheet, LIT("out"), animation_frame); + struct v2 rel_pos = xform_mul_v2(sprite_local_xform, out_slice.center); + struct v2 rel_dir = xform_basis_mul_v2(sprite_local_xform, out_slice.dir); + + /* Spawn bullet */ + struct sim_ent *bullet; + { + bullet = sim_ent_alloc(root); + + bullet->bullet_src = ent->handle; + bullet->bullet_src_pos = rel_pos; + bullet->bullet_src_dir = rel_dir; + //bullet->bullet_impulse = 0.75f; + bullet->bullet_impulse = 2.0f; + bullet->bullet_knockback = 10; + bullet->mass_unscaled = 0.04f; + bullet->inertia_unscaled = 0.00001f; + bullet->layer = SIM_LAYER_BULLETS; + + #if 1 + /* Point collider */ + bullet->local_collider.points[0] = V2(0, 0); + bullet->local_collider.count = 1; + #else + bullet->sprite = sprite_tag_from_path(LIT("res/graphics/bullet.ase")); + bullet->sprite_collider_slice = LIT("shape"); + #endif + + sim_ent_enable_prop(bullet, SIM_ENT_PROP_BULLET); + sim_ent_enable_prop(bullet, SIM_ENT_PROP_SENSOR); + } + + /* Spawn tracer */ + { + struct sim_ent *tracer = sim_ent_alloc(root); + tracer->tracer_fade_duration = 0.025f; + tracer->layer = SIM_LAYER_TRACERS; + sim_ent_enable_prop(tracer, SIM_ENT_PROP_TRACER); + + bullet->bullet_tracer = tracer->handle; + } + } + } + + /* ========================== * + * Create motor joints from control move + * ========================== */ + + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + + if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTROLLED)) { + struct sim_ent *joint_ent = sim_ent_from_handle(world, ent->move_joint); if (!sim_ent_is_valid_and_active(joint_ent)) { - /* FIXME: Joint ent may never release */ joint_ent = sim_ent_alloc(root); joint_ent->mass_unscaled = F32_INFINITY; joint_ent->inertia_unscaled = F32_INFINITY; - client->dbg_drag_joint_ent = joint_ent->handle; - sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_MOUSE_JOINT); + sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_MOTOR_JOINT); sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_ACTIVE); - } - struct xform xf = sim_ent_get_xform(target_ent); - f32 mass = target_ent->mass_unscaled * math_fabs(xform_get_determinant(xf)); + ent->move_joint = joint_ent->handle; - struct phys_mouse_joint_def def = ZI; - def.target = target_ent->handle; - if (sim_ent_handle_eq(joint_ent->mouse_joint_data.target, target_ent->handle)) { - def.point_local_start = joint_ent->mouse_joint_data.point_local_start; + struct phys_motor_joint_def def = ZI; + def.e0 = joint_ent->handle; /* Re-using joint entity as e0 */ + def.e1 = ent->handle; + def.correction_rate = 0; + def.max_force = ent->control_force; + def.max_torque = 0; + joint_ent->motor_joint_data = phys_motor_joint_from_def(def); + } + + sim_ent_set_xform(joint_ent, XFORM_IDENT); /* Reset joint ent position */ + sim_ent_set_linear_velocity(joint_ent, v2_mul(v2_clamp_len(ent->control.move, 1), ent->control_force_max_speed)); + } + } + + /* ========================== * + * Create motor joints from control focus (aim) + * ========================== */ + + #if SIM_PLAYER_AIM + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + + if (sim_ent_has_prop(ent, SIM_ENT_PROP_CONTROLLED)) { + struct xform xf = sim_ent_get_xform(ent); + struct xform sprite_xf = xform_mul(xf, ent->sprite_local_xform); + + /* Retrieve / create aim joint */ + struct sim_ent *joint_ent = sim_ent_from_handle(world, ent->aim_joint); + if (!sim_ent_is_valid_and_active(joint_ent)) { + joint_ent = sim_ent_alloc(root); + joint_ent->mass_unscaled = F32_INFINITY; + joint_ent->inertia_unscaled = F32_INFINITY; + sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_PHYSICAL_KINEMATIC); /* Since we'll be setting velocity manually */ + sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_MOTOR_JOINT); + sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_ACTIVE); + ent->aim_joint = joint_ent->handle; + + struct phys_motor_joint_def def = ZI; + def.e0 = joint_ent->handle; /* Re-using joint entity as e0 */ + def.e1 = ent->handle; + def.max_force = 0; + def.max_torque = ent->control_torque; + joint_ent->motor_joint_data = phys_motor_joint_from_def(def); + } + + /* Set correction rate dynamically since motor velocity is only set for one frame */ + joint_ent->motor_joint_data.correction_rate = 10 * world_dt; + + + /* Solve for final angle using law of sines */ + f32 new_angle; + { + struct v2 ent_pos = xf.og; + struct v2 focus_pos = v2_add(ent_pos, ent->control.focus); + + struct v2 sprite_hold_pos; + struct v2 sprite_hold_dir; + { + struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite); + struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, LIT("attach.wep"), ent->animation_frame); + sprite_hold_pos = slice.center; + sprite_hold_dir = slice.dir; + } + + struct v2 hold_dir = xform_basis_mul_v2(sprite_xf, sprite_hold_dir); + struct v2 hold_pos = xform_mul_v2(sprite_xf, sprite_hold_pos); + if (v2_eq(hold_pos, ent_pos)) { + /* If hold pos is same as origin (E.G if pivot is being used as hold pos), then move hold pos forward a tad to avoid issue */ + sprite_hold_pos = v2_add(sprite_hold_pos, V2(0, -1)); + hold_pos = xform_mul_v2(sprite_xf, sprite_hold_pos); + } + + f32 forward_hold_angle_offset; + { + struct xform xf_unrotated = xform_basis_with_rotation_world(xf, 0); + struct v2 hold_pos_unrotated = xform_mul_v2(xf_unrotated, xform_mul_v2(ent->sprite_local_xform, sprite_hold_pos)); + forward_hold_angle_offset = v2_angle_from_dirs(V2(0, -1), v2_sub(hold_pos_unrotated, xf_unrotated.og)); + } + + struct v2 hold_ent_dir = v2_sub(ent_pos, hold_pos); + struct v2 focus_ent_dir = v2_sub(ent_pos, focus_pos); + + f32 hold_ent_len = v2_len(hold_ent_dir); + f32 focus_ent_len = v2_len(focus_ent_dir); + + f32 final_hold_angle_btw_ent_and_focus = v2_angle_from_dirs(hold_ent_dir, hold_dir); + f32 final_focus_angle_btw_ent_and_hold = math_asin((math_sin(final_hold_angle_btw_ent_and_focus) * hold_ent_len) / focus_ent_len); + f32 final_ent_angle_btw_focus_and_hold = PI - (final_focus_angle_btw_ent_and_hold + final_hold_angle_btw_ent_and_focus); + + new_angle = math_unwind_angle(v2_angle_from_dirs(V2(0, -1), v2_sub(focus_pos, ent_pos)) + final_ent_angle_btw_focus_and_hold - forward_hold_angle_offset); + } + + f32 new_vel = 0; + if (!F32_IS_NAN(new_angle)) { + const f32 angle_error_allowed = 0.001f; + struct xform joint_xf = sim_ent_get_xform(joint_ent); + f32 diff = math_unwind_angle(new_angle - xform_get_rotation(joint_xf)); + if (math_fabs(diff) > angle_error_allowed) { + /* Instantly snap joint ent to new angle */ + new_vel = diff / real_dt; + } + } + sim_ent_set_angular_velocity(joint_ent, new_vel); + } + } + #endif + + /* ========================== * + * Create motor joints from ground friction (gravity) + * ========================== */ + + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_PHYSICAL_DYNAMIC)) continue; + + struct sim_ent *joint_ent = sim_ent_from_handle(world, ent->ground_friction_joint); + + struct phys_motor_joint_def def = ZI; + def.e0 = root->handle; + def.e1 = ent->handle; + def.correction_rate = 0; + def.max_force = ent->linear_ground_friction; + def.max_torque = ent->angular_ground_friction; + if (joint_ent->motor_joint_data.max_force != def.max_force || joint_ent->motor_joint_data.max_torque != def.max_torque) { + if (!sim_ent_is_valid_and_active(joint_ent)) { + joint_ent = sim_ent_alloc(root); + sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_MOTOR_JOINT); + sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_ACTIVE); + joint_ent->motor_joint_data = phys_motor_joint_from_def(def); + ent->ground_friction_joint = joint_ent->handle; + } + } + } + + /* ========================== * + * Create mouse joints from client debug drag + * ========================== */ + + for (u64 i = 0; i < world->num_clients_reserved; ++i) { + struct sim_client *client = &world->clients[i]; + if (client->valid) { + struct v2 cursor = client->cursor_pos; + b32 start_dragging = client->dbg_drag_start; + b32 stop_dragging = client->dbg_drag_stop; + + struct sim_ent *joint_ent = sim_ent_from_handle(world, client->dbg_drag_joint_ent); + struct sim_ent *target_ent = sim_ent_from_handle(world, joint_ent->mouse_joint_data.target); + + if (start_dragging) { + struct xform mouse_xf = xform_from_pos(cursor); + struct collider_shape mouse_shape = ZI; + mouse_shape.points[0] = V2(0, 0); + mouse_shape.count = 1; + + for (u64 sim_ent_index = 0; sim_ent_index < world->num_ents_reserved; ++sim_ent_index) { + struct sim_ent *ent = &world->ents[sim_ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_PHYSICAL_DYNAMIC)) continue; + + struct collider_shape ent_collider = ent->local_collider; + if (ent_collider.count > 0) { + struct xform ent_xf = sim_ent_get_xform(ent); + /* TODO: Can just use boolean GJK */ + struct collider_collision_points_result res = collider_collision_points(&ent_collider, &mouse_shape, ent_xf, mouse_xf); + if (res.num_points > 0) { + target_ent = ent; + break; + } + } + } + } + if (stop_dragging) { + target_ent = sim_ent_nil(); + } + + if (sim_ent_is_valid_and_active(target_ent)) { + if (!sim_ent_is_valid_and_active(joint_ent)) { + /* FIXME: Joint ent may never release */ + joint_ent = sim_ent_alloc(root); + joint_ent->mass_unscaled = F32_INFINITY; + joint_ent->inertia_unscaled = F32_INFINITY; + client->dbg_drag_joint_ent = joint_ent->handle; + sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_MOUSE_JOINT); + sim_ent_enable_prop(joint_ent, SIM_ENT_PROP_ACTIVE); + } + struct xform xf = sim_ent_get_xform(target_ent); + f32 mass = target_ent->mass_unscaled * math_fabs(xform_get_determinant(xf)); + + struct phys_mouse_joint_def def = ZI; + def.target = target_ent->handle; + if (sim_ent_handle_eq(joint_ent->mouse_joint_data.target, target_ent->handle)) { + def.point_local_start = joint_ent->mouse_joint_data.point_local_start; + } else { + def.point_local_start = xform_invert_mul_v2(xf, cursor); + } + def.point_local_end = xform_invert_mul_v2(xf, cursor); + def.max_force = mass * 1000; + joint_ent->mouse_joint_data = phys_mouse_joint_from_def(def); + } else if (sim_ent_is_valid_and_active(joint_ent)) { + joint_ent->mouse_joint_data.target = target_ent->handle; + } + } + } + + /* ========================== * + * Physics + * ========================== */ + + { + struct phys_ctx phys = ZI; + phys.ss = world; + phys.pre_solve_callback = on_collision; + phys.space = space; + phys.contact_lookup = &contact_lookup; + #if COLLIDER_DEBUG + phys.collision_debug_lookup = collision_debug_lookup; + #endif + + /* Step */ + phys_step(&phys, world_dt); + } + + /* ========================== * + * Update tracers + * ========================== */ + + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_TRACER)) continue; + + struct v2 end = sim_ent_get_xform(ent).og; + + struct v2 tick_velocity = v2_mul(ent->tracer_start_velocity, world_dt); + struct v2 gradient_start = v2_add(ent->tracer_gradient_start, tick_velocity); + struct v2 gradient_end = v2_add(ent->tracer_gradient_end, tick_velocity); + + if (v2_dot(tick_velocity, v2_sub(gradient_start, end)) > 0) { + /* Tracer has disappeared */ + sim_ent_enable_prop(ent, SIM_ENT_PROP_RELEASE_NEXT_TICK); + } + + ent->tracer_gradient_start = gradient_start; + ent->tracer_gradient_end = gradient_end; + } + + /* ========================== * + * Initialize bullet kinematics from sources + * ========================== */ + + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_BULLET)) continue; + + if (ent->activation_tick == world->tick) { + struct sim_ent *src = sim_ent_from_handle(world, ent->bullet_src); + struct xform src_xf = sim_ent_get_xform(src); + + struct v2 pos = xform_mul_v2(src_xf, ent->bullet_src_pos); + struct v2 impulse = xform_basis_mul_v2(src_xf, ent->bullet_src_dir); + impulse = v2_with_len(impulse, ent->bullet_impulse); + + #if 0 + /* Add shooter velocity to bullet */ + { + /* TODO: Add angular velocity as well? */ + struct sim_ent *top = sim_ent_from_handle(ss_blended, src->top); + impulse = v2_add(impulse, v2_mul(top->linear_velocity, dt)); + } + #endif + + struct xform xf = XFORM_TRS(.t = pos, .r = v2_angle(impulse) + PI / 2); + sim_ent_set_xform(ent, xf); + sim_ent_enable_prop(ent, SIM_ENT_PROP_PHYSICAL_KINEMATIC); + + sim_ent_apply_linear_impulse_to_center(ent, impulse); + + /* Initialize tracer */ + struct sim_ent *tracer = sim_ent_from_handle(world, ent->bullet_tracer); + if (sim_ent_is_valid_and_active(tracer)) { + sim_ent_set_xform(tracer, xf); + sim_ent_enable_prop(tracer, SIM_ENT_PROP_PHYSICAL_KINEMATIC); + sim_ent_set_linear_velocity(tracer, ent->linear_velocity); + tracer->tracer_start = pos; + tracer->tracer_start_velocity = ent->linear_velocity; + tracer->tracer_gradient_end = pos; + tracer->tracer_gradient_start = v2_sub(pos, v2_mul(ent->linear_velocity, tracer->tracer_fade_duration)); + } + + /* Spawn quake */ + { + struct sim_ent *quake = sim_ent_alloc(root); + sim_ent_set_xform(quake, XFORM_POS(pos)); + quake->quake_intensity = 0.2f; + quake->quake_fade = quake->quake_intensity / 0.1f; + sim_ent_enable_prop(quake, SIM_ENT_PROP_QUAKE); + } + } + } + + /* ========================== * + * Update cameras + * ========================== */ + + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_CAMERA)) continue; + + struct xform xf = sim_ent_get_xform(ent); + + /* Camera follow */ + { + struct sim_ent *follow = sim_ent_from_handle(world, ent->camera_follow); + + f32 aspect_ratio = 1.0; + { + struct xform quad_xf = xform_mul(sim_ent_get_xform(ent), ent->camera_quad_xform); + struct v2 camera_size = xform_get_scale(quad_xf); + if (!v2_is_zero(camera_size)) { + aspect_ratio = camera_size.x / camera_size.y; + } + } + f32 ratio_y = 0.33f; + f32 ratio_x = ratio_y / aspect_ratio; + struct v2 camera_focus_dir = v2_mul_v2(follow->control.focus, V2(ratio_x, ratio_y)); + struct v2 camera_focus_pos = v2_add(sim_ent_get_xform(follow).og, camera_focus_dir); + ent->camera_xform_target = xf; + ent->camera_xform_target.og = camera_focus_pos; + + /* Lerp camera */ + if (ent->camera_applied_lerp_continuity_gen_plus_one == ent->camera_lerp_continuity_gen + 1) { + f32 t = 1 - math_pow(2.f, -20.f * (f32)world_dt); + xf = xform_lerp(xf, ent->camera_xform_target, t); } else { - def.point_local_start = xform_invert_mul_v2(xf, cursor); + /* Skip lerp */ + xf = ent->camera_xform_target; } - def.point_local_end = xform_invert_mul_v2(xf, cursor); - def.max_force = mass * 1000; - joint_ent->mouse_joint_data = phys_mouse_joint_from_def(def); - } else if (sim_ent_is_valid_and_active(joint_ent)) { - joint_ent->mouse_joint_data.target = target_ent->handle; + ent->camera_applied_lerp_continuity_gen_plus_one = ent->camera_lerp_continuity_gen + 1; } - } - } - /* ========================== * - * Physics - * ========================== */ - - { - struct phys_ctx phys = ZI; - phys.ss = world; - phys.pre_solve_callback = on_collision; - phys.space = space; - phys.contact_lookup = &contact_lookup; -#if COLLIDER_DEBUG - phys.collision_debug_lookup = collision_debug_lookup; -#endif - - /* Step */ - phys_step(&phys, world_dt); - } - - /* ========================== * - * Update tracers - * ========================== */ - - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_TRACER)) continue; - - struct v2 end = sim_ent_get_xform(ent).og; - - struct v2 tick_velocity = v2_mul(ent->tracer_start_velocity, world_dt); - struct v2 gradient_start = v2_add(ent->tracer_gradient_start, tick_velocity); - struct v2 gradient_end = v2_add(ent->tracer_gradient_end, tick_velocity); - - if (v2_dot(tick_velocity, v2_sub(gradient_start, end)) > 0) { - /* Tracer has disappeared */ - sim_ent_enable_prop(ent, SIM_ENT_PROP_RELEASE_NEXT_TICK); - } - - ent->tracer_gradient_start = gradient_start; - ent->tracer_gradient_end = gradient_end; - } - - /* ========================== * - * Initialize bullet kinematics from sources - * ========================== */ - - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_BULLET)) continue; - - if (ent->activation_tick == world->tick) { - struct sim_ent *src = sim_ent_from_handle(world, ent->bullet_src); - struct xform src_xf = sim_ent_get_xform(src); - - struct v2 pos = xform_mul_v2(src_xf, ent->bullet_src_pos); - struct v2 impulse = xform_basis_mul_v2(src_xf, ent->bullet_src_dir); - impulse = v2_with_len(impulse, ent->bullet_impulse); - -#if 0 - /* Add shooter velocity to bullet */ + /* Camera shake */ { - /* TODO: Add angular velocity as well? */ - struct sim_ent *top = sim_ent_from_handle(ss_blended, src->top); - impulse = v2_add(impulse, v2_mul(top->linear_velocity, dt)); + /* TODO: Update based on distance to quake */ + ent->shake = 0; + for (u64 quake_ent_index = 0; quake_ent_index < world->num_ents_reserved; ++quake_ent_index) { + struct sim_ent *quake = &world->ents[quake_ent_index]; + if (!sim_ent_is_valid_and_active(quake)) continue; + if (!sim_ent_has_prop(quake, SIM_ENT_PROP_QUAKE)) continue; + ent->shake += quake->quake_intensity; + } } -#endif - struct xform xf = XFORM_TRS(.t = pos, .r = v2_angle(impulse) + PI / 2); sim_ent_set_xform(ent, xf); - sim_ent_enable_prop(ent, SIM_ENT_PROP_PHYSICAL_KINEMATIC); + } - sim_ent_apply_linear_impulse_to_center(ent, impulse); + /* ========================== * + * Update quakes + * ========================== */ - /* Initialize tracer */ - struct sim_ent *tracer = sim_ent_from_handle(world, ent->bullet_tracer); - if (sim_ent_is_valid_and_active(tracer)) { - sim_ent_set_xform(tracer, xf); - sim_ent_enable_prop(tracer, SIM_ENT_PROP_PHYSICAL_KINEMATIC); - sim_ent_set_linear_velocity(tracer, ent->linear_velocity); - tracer->tracer_start = pos; - tracer->tracer_start_velocity = ent->linear_velocity; - tracer->tracer_gradient_end = pos; - tracer->tracer_gradient_start = v2_sub(pos, v2_mul(ent->linear_velocity, tracer->tracer_fade_duration)); - } + for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { + struct sim_ent *ent = &world->ents[ent_index]; + if (!sim_ent_is_valid_and_active(ent)) continue; + if (!sim_ent_has_prop(ent, SIM_ENT_PROP_QUAKE)) continue; - /* Spawn quake */ - { - struct sim_ent *quake = sim_ent_alloc(root); - sim_ent_set_xform(quake, XFORM_POS(pos)); - quake->quake_intensity = 0.2f; - quake->quake_fade = quake->quake_intensity / 0.1f; - sim_ent_enable_prop(quake, SIM_ENT_PROP_QUAKE); + ent->quake_intensity = max_f32(0, ent->quake_intensity - (ent->quake_fade * world_dt)); + if (ent->quake_intensity <= 0) { + sim_ent_enable_prop(ent, SIM_ENT_PROP_RELEASE_NEXT_TICK); } } - } - /* ========================== * - * Update cameras - * ========================== */ + /* ========================== * + * Update relative layers + * ========================== */ - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_CAMERA)) continue; - - struct xform xf = sim_ent_get_xform(ent); - - /* Camera follow */ { - struct sim_ent *follow = sim_ent_from_handle(world, ent->camera_follow); - - f32 aspect_ratio = 1.0; - { - struct xform quad_xf = xform_mul(sim_ent_get_xform(ent), ent->camera_quad_xform); - struct v2 camera_size = xform_get_scale(quad_xf); - if (!v2_is_zero(camera_size)) { - aspect_ratio = camera_size.x / camera_size.y; - } - } - f32 ratio_y = 0.33f; - f32 ratio_x = ratio_y / aspect_ratio; - struct v2 camera_focus_dir = v2_mul_v2(follow->control.focus, V2(ratio_x, ratio_y)); - struct v2 camera_focus_pos = v2_add(sim_ent_get_xform(follow).og, camera_focus_dir); - ent->camera_xform_target = xf; - ent->camera_xform_target.og = camera_focus_pos; - - /* Lerp camera */ - if (ent->camera_applied_lerp_continuity_gen_plus_one == ent->camera_lerp_continuity_gen + 1) { - f32 t = 1 - math_pow(2.f, -20.f * (f32)world_dt); - xf = xform_lerp(xf, ent->camera_xform_target, t); - } else { - /* Skip lerp */ - xf = ent->camera_xform_target; - } - ent->camera_applied_lerp_continuity_gen_plus_one = ent->camera_lerp_continuity_gen + 1; - } - - /* Camera shake */ - { - /* TODO: Update based on distance to quake */ - ent->shake = 0; - for (u64 quake_ent_index = 0; quake_ent_index < world->num_ents_reserved; ++quake_ent_index) { - struct sim_ent *quake = &world->ents[quake_ent_index]; - if (!sim_ent_is_valid_and_active(quake)) continue; - if (!sim_ent_has_prop(quake, SIM_ENT_PROP_QUAKE)) continue; - ent->shake += quake->quake_intensity; - } - } - - sim_ent_set_xform(ent, xf); - } - - /* ========================== * - * Update quakes - * ========================== */ - - for (u64 ent_index = 0; ent_index < world->num_ents_reserved; ++ent_index) { - struct sim_ent *ent = &world->ents[ent_index]; - if (!sim_ent_is_valid_and_active(ent)) continue; - if (!sim_ent_has_prop(ent, SIM_ENT_PROP_QUAKE)) continue; - - ent->quake_intensity = max_f32(0, ent->quake_intensity - (ent->quake_fade * world_dt)); - if (ent->quake_intensity <= 0) { - sim_ent_enable_prop(ent, SIM_ENT_PROP_RELEASE_NEXT_TICK); - } - } - - /* ========================== * - * Update relative layers - * ========================== */ - - { - struct temp_arena temp = arena_temp_begin(scratch.arena); - - struct sim_ent **stack = arena_push(temp.arena, struct sim_ent *); - u64 stack_count = 1; - *stack = root; - - while (stack_count > 0) { - struct sim_ent *parent; - arena_pop(temp.arena, struct sim_ent *, &parent); - --stack_count; - - i32 parent_layer = parent->final_layer; - for (struct sim_ent *child = sim_ent_from_handle(world, parent->first); child->valid; child = sim_ent_from_handle(world, child->next)) { - if (sim_ent_is_valid_and_active(child)) { - child->final_layer = parent_layer + child->layer; - *arena_push(temp.arena, struct sim_ent *) = child; - ++stack_count; - } - } - } - - arena_temp_end(temp); - } - - /* ========================== * - * Release entities - * ========================== */ - - release_entities_with_prop(world, SIM_ENT_PROP_RELEASE_THIS_TICK); - - /* ========================== * - * Publish tick - * ========================== */ - -#if 0 - struct sim_cmd_queue_list output_cmds = ZI; - for (u64 i = 0; i < ss_blended->num_clients_reserved; ++i) { - struct sim_client *client = &ss_blended->clients[i]; - if (client->valid) { struct temp_arena temp = arena_temp_begin(scratch.arena); - struct sim_snapshot *ss0 = sim_snapshot_from_tick(snapshot_store, client->ack); - struct sim_snapshot *ss1 = ss_blended; + struct sim_ent **stack = arena_push(temp.arena, struct sim_ent *); + u64 stack_count = 1; + *stack = root; - /* Create & encode snapshot cmd */ - { - struct sim_cmd *cmd = arena_push(arena, struct sim_cmd); - cmd->kind = SIM_CMD_KIND_SNAPSHOT; - cmd->tick = ss_blended->tick; - cmd->snapshot_tick_start = ss0->tick; - cmd->snapshot_tick_end = ss1->tick; + while (stack_count > 0) { + struct sim_ent *parent; + arena_pop(temp.arena, struct sim_ent *, &parent); + --stack_count; - { - struct bitbuff_writer bw = bw_from_bitbuff(encoder_bitbuff); - sim_snapshot_encode(&bw, ss0, ss1, client); - cmd->snapshot_encoded = bw_get_written(temp.arena, &bw); + i32 parent_layer = parent->final_layer; + for (struct sim_ent *child = sim_ent_from_handle(world, parent->first); child->valid; child = sim_ent_from_handle(world, child->next)) { + if (sim_ent_is_valid_and_active(child)) { + child->final_layer = parent_layer + child->layer; + *arena_push(temp.arena, struct sim_ent *) = child; + ++stack_count; + } } - - if (output_cmds.last) { - output_cmds.last->next = cmd; - } else { - output_cmds.first = cmd; - } - output_cmds.last = cmd; } - /* Encode cmds */ - struct string cmds_msg = ZI; - { - struct bitbuff_writer bw = bw_from_bitbuff(encoder_bitbuff); - sim_cmds_encode(&bw, output_cmds, 0); - cmds_msg = bw_get_written(temp.arena, &bw); - } - - host_queue_write(host, client->channel_id, cmds_msg, 0); - arena_temp_end(temp); } + + /* ========================== * + * Release entities + * ========================== */ + + release_entities_with_prop(world, SIM_ENT_PROP_RELEASE_THIS_TICK); } -#endif + /* ========================== * * End frame @@ -1451,12 +1406,12 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct * Cmd frame * ========================== */ -void sim_cmd_frames_encode(struct bitbuff_writer *bw, struct sim_cmd_frame_list frames, u64 ack_tick) +void sim_cmd_frames_encode(struct bitbuff_writer *bw, struct sim_cmd_frame_list frames) { __prof; for (struct sim_cmd_frame *frame = frames.first; frame; frame = frame->next) { bw_write_uv(bw, frame->tick); - bw_write_uv(bw, ack_tick); + bw_write_uv(bw, frame->ack); for (struct sim_cmd *cmd = frame->first; cmd; cmd = cmd->next) { bw_write_bit(bw, 1); diff --git a/src/sim.h b/src/sim.h index 770037f3..d5812604 100644 --- a/src/sim.h +++ b/src/sim.h @@ -131,7 +131,7 @@ struct sim_snapshot *sim_step(struct sim_snapshot_store *snapshot_store, struct #include "host.h" -void sim_cmd_frames_encode(struct bitbuff_writer *bw, struct sim_cmd_frame_list frames, u64 ack_tick); +void sim_cmd_frames_encode(struct bitbuff_writer *bw, struct sim_cmd_frame_list frames); void sim_cmd_frames_decode(struct arena *arena, struct host_event_array host_events, struct sim_cmd_frame_list *frames_out); diff --git a/src/sim_client.c b/src/sim_client.c index 04688aab..e3c0053e 100644 --- a/src/sim_client.c +++ b/src/sim_client.c @@ -57,30 +57,13 @@ struct sim_client *sim_client_alloc(struct sim_snapshot *ss, struct host_channel } ++ss->num_clients_allocated; *client = *sim_client_nil(); + client->ss = ss; client->valid = true; client->kind = kind; client->handle = handle; - if (kind == SIM_CLIENT_KIND_NETSIM) { - ASSERT(!host_channel_id_is_nil(channel_id)); - u64 channel_hash = hash_from_channel_id(channel_id); - client->channel_id = channel_id; - client->channel_hash = channel_hash; - - /* Insert into channel lookup */ - 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; - } - } + /* Insert into channel lookup */ + sim_client_set_channel_id(client, channel_id); return client; } @@ -95,7 +78,16 @@ void sim_client_release(struct sim_client *client) --ss->num_clients_allocated; /* Remove from channel lookup */ - if (client->kind == SIM_CLIENT_KIND_NETSIM) { + 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); @@ -111,6 +103,25 @@ void sim_client_release(struct sim_client *client) 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; + } + } } /* ========================== * diff --git a/src/sim_client.h b/src/sim_client.h index e5484a91..34ccf767 100644 --- a/src/sim_client.h +++ b/src/sim_client.h @@ -10,8 +10,8 @@ struct sim_snapshot; enum sim_client_kind { SIM_CLIENT_KIND_INVALID, - SIM_CLIENT_KIND_LOCAL, - SIM_CLIENT_KIND_NETSIM + SIM_CLIENT_KIND_SIM_SLAVE, + SIM_CLIENT_KIND_SIM_MASTER }; struct sim_client_lookup_bucket { @@ -60,6 +60,7 @@ struct sim_client *sim_client_from_handle(struct sim_snapshot *ss, struct sim_cl 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 diff --git a/src/sim_snapshot.c b/src/sim_snapshot.c index dc6714d4..296e3b00 100644 --- a/src/sim_snapshot.c +++ b/src/sim_snapshot.c @@ -194,6 +194,7 @@ struct sim_snapshot *sim_snapshot_alloc(struct sim_snapshot_store *store, struct ++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; @@ -272,6 +273,8 @@ struct sim_snapshot *sim_snapshot_alloc(struct sim_snapshot_store *store, struct } 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); @@ -281,6 +284,7 @@ struct sim_snapshot *sim_snapshot_alloc(struct sim_snapshot_store *store, struct } else { store->last_tick = tick; } + ss->next_tick = store->first_tick; store->first_tick = tick; } } @@ -435,10 +439,13 @@ struct sim_snapshot *sim_snapshot_alloc_from_lerp(struct sim_snapshot_store *sto * Encode * ========================== */ -void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_snapshot *ss0, struct sim_snapshot *ss1, struct sim_client *client) +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); @@ -449,8 +456,8 @@ void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_snapshot *ss0, st bw_write_uv(bw, ss1->continuity_gen); bw_write_uv(bw, ss1->phys_iteration); - bw_write_uv(bw, client->handle.gen); - bw_write_uv(bw, client->handle.idx); + bw_write_uv(bw, receiver->handle.gen); + bw_write_uv(bw, receiver->handle.idx); /* Clients */ if (ss1->num_clients_allocated == ss0->num_clients_allocated) { @@ -505,6 +512,9 @@ 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); diff --git a/src/sim_snapshot.h b/src/sim_snapshot.h index 0c91ed5d..d008ef77 100644 --- a/src/sim_snapshot.h +++ b/src/sim_snapshot.h @@ -42,6 +42,9 @@ struct sim_snapshot { struct arena arena; + /* Is this snapshot created by the master sim */ + b32 is_master; + /* Time of local snapshot publish to user */ i64 publish_dt_ns; i64 publish_time_ns; @@ -132,7 +135,7 @@ struct sim_snapshot *sim_snapshot_from_tick(struct sim_snapshot_store *store, u6 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_snapshot *ss0, struct sim_snapshot *ss1, struct sim_client *client); +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); diff --git a/src/user.c b/src/user.c index bf856d53..0f0455d9 100644 --- a/src/user.c +++ b/src/user.c @@ -199,6 +199,9 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr, G.arena = arena_alloc(GIGABYTE(64)); + /* TODO: Remove this */ + 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(); @@ -228,25 +231,7 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr, G.window = window; sys_window_register_event_callback(G.window, &window_event_callback); - /* TODO: Remove this */ -#if 0 - connect_address_str = STRING(0, 0); - if (connect_address_str.len == 0) { - G.local_sim_ctx = sim_ctx_alloc(sprite_sr, phys_sr, host_sr, sim_snapshot_sr, 12345); - 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); - } -#else - 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.local_sim_thread = sys_thread_alloc(&user_local_sim_thread_entry_point, G.local_sim_ctx, LIT("[P8] Local sim thread")); G.debug_draw = true; @@ -1477,10 +1462,10 @@ INTERNAL void user_update(void) pos.y += spacing; #endif - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Read from local sim: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_read.last_second * 8 / 1000 / 1000, 3))); + draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Read from host: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_read.last_second * 8 / 1000 / 1000, 3))); pos.y += spacing; - draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Send to local sim: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_sent.last_second * 8 / 1000 / 1000, 3))); + draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Sent from host: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_sent.last_second * 8 / 1000 / 1000, 3))); pos.y += spacing; pos.y += spacing; @@ -1733,10 +1718,30 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) #endif (UNUSED)arg; - struct bitbuff encoder_bitbuff = bitbuff_alloc(GIGABYTE(64)); - struct sim_snapshot_store *snapshot_store = sim_snapshot_store_alloc(); + 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(); - struct sim_snapshot *prev_ss = sim_snapshot_nil(); i64 last_publish_ns = 0; i64 last_tick_ns = 0; @@ -1752,15 +1757,16 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) /* Release old snapshots */ /* TODO: Remove this */ { - u64 keep_count = 50; - u64 keep_tick = prev_ss->tick > keep_count ? prev_ss->tick - keep_count : 0; + 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(snapshot_store, 0, keep_tick); + 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 cmds from host */ { @@ -1770,11 +1776,10 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) } /* Generate user sim cmd from user thread */ - struct sim_cmd_frame *user_cmd_frame; { 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 = prev_ss->tick + 1; + 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; @@ -1794,14 +1799,71 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) 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); + } + } 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 *ss = sim_step(snapshot_store, prev_ss, input_cmds, target_dt_ns); + 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, ss, ss->tick); + 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; @@ -1810,53 +1872,84 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) sys_mutex_unlock(&lock); } - /* Publish snapshot cmds to networked clients */ - 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 && client->kind == SIM_CLIENT_KIND_NETSIM) { - struct temp_arena temp = arena_temp_begin(scratch.arena); + 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; - } + 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; + 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; + /* 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_snapshot_encode(&bw, ss0, ss1, client); - snapshot_cmd.snapshot_encoded = bw_get_written(temp.arena, &bw); + 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); - struct sim_cmd_frame snapshot_cmd_frame = ZI; - snapshot_cmd_frame.first = &snapshot_cmd; - snapshot_cmd_frame.last = &snapshot_cmd; + user_cmd_frame->ack = remote_snapshot_store->last_tick; + user_cmd_frame->sender_is_local = false; - struct sim_cmd_frame_list cmd_frames = ZI; - cmd_frames.first = &snapshot_cmd_frame; - cmd_frames.last = &snapshot_cmd_frame; + 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, cmd_frames, 0); - cmds_msg = bw_get_written(temp.arena, &bw); + /* 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); } - - host_queue_write(host, client->channel_id, cmds_msg, 0); - - arena_temp_end(temp); } } @@ -1864,12 +1957,28 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) 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); - prev_ss = ss; + if (local_ss->is_master) { + local_ss_prev = local_ss; + } } - sim_snapshot_store_release(snapshot_store); + sim_snapshot_store_release(local_snapshot_store); bitbuff_release(&encoder_bitbuff); host_release(host); }