From 36be5165844574a2aa34719a93c6a3ac3afbbf87 Mon Sep 17 00:00:00 2001 From: jacob Date: Mon, 19 Jan 2026 20:28:56 -0600 Subject: [PATCH] reflect client acks --- src/pp/pp_sim/pp_sim_core.c | 333 +---- src/pp/pp_sim/pp_sim_core.h | 1 + src/pp/pp_vis/pp_vis_core.c | 2433 ++++++++++++++++++----------------- 3 files changed, 1245 insertions(+), 1522 deletions(-) diff --git a/src/pp/pp_sim/pp_sim_core.c b/src/pp/pp_sim/pp_sim_core.c index a66bce02..a405bf56 100644 --- a/src/pp/pp_sim/pp_sim_core.c +++ b/src/pp/pp_sim/pp_sim_core.c @@ -318,7 +318,7 @@ void S_TickForever(WaveLaneCtx *lane) } ////////////////////////////// - //- Read client snapshots + //- Read snapshots for (P_MsgNode *msg_node = in_msgs.first; msg_node; msg_node = msg_node->next) { @@ -333,7 +333,7 @@ void S_TickForever(WaveLaneCtx *lane) //- Read header BB_ReadBit(&bbr); // Raw - i64 remote_ack = BB_ReadIV(&bbr); + i64 tmp_remote_ack = BB_ReadIV(&bbr); //- Read control b32 done = 0; @@ -374,6 +374,12 @@ void S_TickForever(WaveLaneCtx *lane) } } + //- Update remote ack + if (tmp_remote_ack > client->remote_ack) + { + client->remote_ack = tmp_remote_ack; + } + //- Update ack if (control->tick < world_frame->tick) { @@ -538,77 +544,10 @@ void S_TickForever(WaveLaneCtx *lane) // } // } - ////////////////////////////// - //- Apply player snapshots - // P_MsgList user_msgs = Zi; - // { - // i64 user_msgs_min_tick = world_frame->tick + user_msg_tick_range.min; - // i64 user_msgs_max_tick = world_frame->tick + user_msg_tick_range.max; - // // Prune queued player msgs - // { - // for (P_MsgNode *msg_node = queued_user_msgs.first; msg_node;) - // { - // P_MsgNode *next = msg_node->next; - // b32 prune = 0; - // if (msg_node->msg.predicted && (msg_node->msg.tick < user_msgs_min_tick || msg_node->msg.tick > user_msgs_max_tick)) - // { - // prune = 1; - // } - // if (prune) - // { - // DllQueueRemove(queued_user_msgs.first, queued_user_msgs.last, msg_node); - // SllStackPush(first_free_user_msg_node, msg_node); - // --queued_user_msgs.count; - // } - // msg_node = next; - // } - // } - // // Push player msgs to queue - // { - // for (P_MsgNode *src_msg_node = input->msgs.first; src_msg_node; src_msg_node = src_msg_node->next) - // { - // if (!src_msg_node->msg.predicted || (src_msg_node->msg.tick >= user_msgs_min_tick && src_msg_node->msg.tick <= user_msgs_max_tick)) - // { - // P_MsgNode *dst_msg_node = first_free_user_msg_node; - // if (dst_msg_node) - // { - // SllStackPop(first_free_user_msg_node); - // ZeroStruct(dst_msg_node); - // } - // else - // { - // dst_msg_node = PushStruct(perm, P_MsgNode); - // } - // dst_msg_node->msg = src_msg_node->msg; - // DllQueuePush(queued_user_msgs.first, queued_user_msgs.last, dst_msg_node); - // ++queued_user_msgs.count; - // } - // } - // } - // // Pop frame player msgs from queue - // { - // for (P_MsgNode *src_msg_node = queued_user_msgs.first; src_msg_node;) - // { - // P_MsgNode *next = src_msg_node->next; - // if (!src_msg_node->msg.predicted || src_msg_node->msg.tick == world_frame->tick) - // { - // { - // P_MsgNode *dst_msg_node = PushStruct(frame_arena, P_MsgNode); - // dst_msg_node->msg = src_msg_node->msg; - // DllQueuePush(user_msgs.first, user_msgs.last, dst_msg_node); - // ++user_msgs.count; - // } - // { - // DllQueueRemove(queued_user_msgs.first, queued_user_msgs.last, src_msg_node); - // SllStackPush(first_free_user_msg_node, src_msg_node); - // --queued_user_msgs.count; - // } - // } - // src_msg_node = next; - // } - // } - // } + + + ////////////////////////////// //- Process player edit messages @@ -749,29 +688,6 @@ void S_TickForever(WaveLaneCtx *lane) } } - ////////////////////////////// - //- Update ent controls - - // for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) - // { - // ent->fire_presses = 0; - // } - - // for (P_Ent *player = P_FirstEnt(frame); !P_IsEntNil(player); player = P_NextEnt(player)) - // { - // if (player->is_iser) - // { - // P_Ent *target = P_EntFromKey(player->guy); - // if (!P_IsEntNil(target)) - // { - // target->move = ClampVec2Len(player->move, 1); - // target->look = player->look; - // target->fire_held = player->fire_held; - // target->fire_presses += player->fire_presses; - // } - // } - // } - ////////////////////////////// //- Step frame @@ -799,58 +715,6 @@ void S_TickForever(WaveLaneCtx *lane) } } - ////////////////////////////// - //- Push snapshot messages - - // for (P_Ent *player = P_FirstEnt(world_frame); !P_IsEntNil(player); player = P_NextEnt(player)) - // { - // if (player->is_user) - // { - // Arena *msgs_arena = P_tl.out_msgs_arena; - // P_Msg *msg = P_PushMsg(P_MsgKind_SimSnapshot, Zstr); - // msg->dst_user = player->key; - // NET_SendFlag_None = 1; - // P_SimSnapshot *snapshot = &msg->sim_snapshot; - // { - // snapshot->world_seed = world->seed; - // snapshot->tick = world_frame->tick; - // snapshot->time_ns = world_frame->time_ns; - // //- Push entity deltas - // for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) - // { - // P_Delta *delta = 0; - // { - // P_DeltaNode *dn = PushStruct(frame_arena, P_DeltaNode); - // snapshot->deltas_count += 1; - // SllQueuePush(snapshot->first_delta_node, snapshot->last_delta_node, dn); - // delta = &dn->delta; - // } - // delta->kind = P_DeltaKind_RawEnt; - // delta->ent = *ent; - // } - // //- Push debug draw information - // { - // i64 dst_idx = 0; - // snapshot->first_debug_draw_node = 0; - // snapshot->last_debug_draw_node = 0; - // snapshot->debug_draw_nodes_count = P_tl.debug_draw_nodes_count; - // P_DebugDrawNode *dst_nodes = PushStructsNoZero(frame_arena, P_DebugDrawNode, snapshot->debug_draw_nodes_count); - // for (P_DebugDrawNode *src = P_tl.first_debug_draw_node; src; src = src->next) - // { - // P_DebugDrawNode *dst = &dst_nodes[dst_idx]; - // *dst = *src; - // dst_idx += 1; - // SllQueuePush(snapshot->first_debug_draw_node, snapshot->last_debug_draw_node, dst); - // } - // ResetArena(P_tl.debug_arena); - // P_tl.first_debug_draw_node = 0; - // P_tl.last_debug_draw_node = 0; - // P_tl.debug_draw_nodes_count = 0; - // } - // } - // } - // } - ////////////////////////////// //- Send snapshots @@ -866,7 +730,8 @@ void S_TickForever(WaveLaneCtx *lane) PackedDeltaNode *last_delta_node = 0; //- Pack entity deltas - P_Frame *src_frame = &P_NilFrame; + // P_Frame *src_frame = P_FrameFromTick(world, client->remote_ack); + P_Frame *src_frame = P_FrameFromTick(world, 0); for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) { b32 should_transmit = 0; @@ -936,6 +801,7 @@ void S_TickForever(WaveLaneCtx *lane) BB_WriteUBits(&packer_bbw, world->seed, 64); BB_WriteUBits(&packer_bbw, client->player.v, 64); BB_WriteIV(&packer_bbw, client->ack); + BB_WriteIV(&packer_bbw, client->remote_ack); } //- Append packed delta @@ -1039,177 +905,6 @@ void S_TickForever(WaveLaneCtx *lane) ZeroStruct(&P_tl.out_msgs); } - - - - - // { - // // Group messages by client - // for (P_MsgNode *msg_node = P_tl.out_msgs.first; msg_node; msg_node = msg_node->next) - // { - // P_Msg *msg = &msg_node->msg; - // if (NET_IsKeyNil(msg->dst)) - // { - // // Broadcast msg - // for (S_Client *client = S.first_client; client; client = client->next) - // { - // P_MsgNode *dst_msg_node = PushStruct(frame_arena, P_MsgNode); - // *dst_msg_node = *msg_node; - // DllQueuePush(client->out_msgs.first, client->out_msgs.last, dst_msg_node); - // ++client->out_msgs.count; - // } - // } - // else - // { - // // Targeted msg - // S_Client client = S_ClientFromNetKey(msg->dst); - // if (!S_IsClientNil(client)) - // { - - // } - // } - // } - - // for (S_Client *client = first_client; client; client = client->next) - // { - // } - // } - - - // for (P_MsgNode *msg_node = P_tl.out_msgs.first; msg_node; msg_node = msg_node->next) - // { - // P_Msg *msg = &msg_node->msg; - // if (P_IsKeyNil(msg->dst_user)) - // { - // // Broadcast - // String packed = P_PackMessage(frame_arena, msg); - // for (P_Ent *player = P_FirstEnt(world_frame); !P_IsEntNil(player); player = P_NextEnt(player)) - // { - // if (player->is_user) - // { - // NET_Send(net_pipe, player->net, packed, NET_SendFlag_None); - // } - // } - // } - // else - // { - // P_Ent *player = P_EntFromKey(world_frame, msg->dst_user); - // if (!NET_IsKeyNil(player->net)) - // { - // String packed = P_PackMessage(frame_arena, msg); - // NET_Send(net_pipe, player->net, packed, NET_SendFlag_None); - // } - // } - // } - - - - - - - - - // for (P_MsgNode *msg_node = P_tl.out_msgs.first; msg_node; msg_node = msg_node->next) - // { - // P_Msg *msg = &msg_node->msg; - // if (P_IsKeyNil(msg->dst_user)) - // { - // // Broadcast - // String packed = P_PackMessage(frame_arena, msg); - // for (P_Ent *player = P_FirstEnt(world_frame); !P_IsEntNil(player); player = P_NextEnt(player)) - // { - // if (player->is_user) - // { - // NET_Send(net_pipe, player->net, packed, NET_SendFlag_None); - // } - // } - // } - // else - // { - // P_Ent *player = P_EntFromKey(world_frame, msg->dst_user); - // if (!NET_IsKeyNil(player->net)) - // { - // String packed = P_PackMessage(frame_arena, msg); - // NET_Send(net_pipe, player->net, packed, NET_SendFlag_None); - // } - // } - // } - - - - - // // TODO: Only copy active entities - // LockTicketMutex(&P.sim_output_back_tm); - // { - // P_OutputState *output = &P.sim_output_states[P.sim_output_back_idx]; - - // // Build messages - // { - // // Push tiles - // if (tiles_dirty) - // { - // P_Msg *msg = 0; - // { - // P_MsgNode *msg_node = PushStruct(output->arena, P_MsgNode); - // DllQueuePush(output->msgs.first, output->msgs.last, msg_node); - // ++output->msgs.count; - // msg = &msg_node->msg; - // } - // msg->kind = P_MsgKind_Tiles; - // msg->tiles_hash = world->tiles_hash; - // msg->raw_tiles = PushStructsNoZero(output->arena, u8, P_TilesCount); - // msg->tile_range = RNG2I32(VEC2I32(0, 0), VEC2I32(P_TilesPitch, P_TilesPitch)); - // CopyBytes(msg->raw_tiles, world->tiles, P_TilesCount); - // has_sent_initial_tick = 1; - // } - // } - - // // Build snapshot - // { - // P_Snapshot *snapshot = &output->snapshot; - - // snapshot->world_seed = world->seed; - // snapshot->tick = world_frame->tick; - // snapshot->time_ns = world_frame->time_ns; - - // // Push entity deltas - // for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) - // { - // P_Delta *delta = 0; - // { - // P_DeltaNode *dn = PushStruct(output->arena, P_DeltaNode); - // snapshot->deltas_count += 1; - // SllQueuePush(snapshot->first_delta_node, snapshot->last_delta_node, dn); - // delta = &dn->delta; - // } - // delta->kind = P_DeltaKind_RawEnt; - // delta->ent = *ent; - // } - - // // Push debug draw information - // { - // i64 dst_idx = 0; - // snapshot->first_debug_draw_node = 0; - // snapshot->last_debug_draw_node = 0; - // snapshot->debug_draw_nodes_count = P_tl.debug_draw_nodes_count; - // P_DebugDrawNode *dst_nodes = PushStructsNoZero(output->arena, P_DebugDrawNode, snapshot->debug_draw_nodes_count); - // for (P_DebugDrawNode *src = P_tl.first_debug_draw_node; src; src = src->next) - // { - // P_DebugDrawNode *dst = &dst_nodes[dst_idx]; - // *dst = *src; - // dst_idx += 1; - // SllQueuePush(snapshot->first_debug_draw_node, snapshot->last_debug_draw_node, dst); - // } - - // ResetArena(P_tl.debug_arena); - // P_tl.first_debug_draw_node = 0; - // P_tl.last_debug_draw_node = 0; - // P_tl.debug_draw_nodes_count = 0; - // } - // } - // } - // UnlockTicketMutex(&P.sim_output_back_tm); - ////////////////////////////// //- Prune ents diff --git a/src/pp/pp_sim/pp_sim_core.h b/src/pp/pp_sim/pp_sim_core.h index 68cace82..3efa0959 100644 --- a/src/pp/pp_sim/pp_sim_core.h +++ b/src/pp/pp_sim/pp_sim_core.h @@ -20,6 +20,7 @@ Struct(S_Client) i64 max_controls; P_Control *controls; + i64 remote_ack; i64 ack; u64 remote_tiles_hash; diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index c7180b45..deb9bab1 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -367,13 +367,21 @@ void V_TickForever(WaveLaneCtx *lane) i64 max_local_controls = SIM_MAX_PING * SIM_TICKS_PER_SECOND; P_Control *local_controls = PushStructs(perm, P_Control, max_local_controls); + // Remote ack represents player control snapshots that the sim has received i64 remote_ack_received_at_ns = 0; i64 remote_ack = 0; i64 prev_snapshot_sent_at_ns = 0; - i64 ready_sim_tick = 0; + // Which tick snapshots we have received and finished assembling from the server + i64 ack = 0; + i64 ack_mirror = 0; + + // Basically same as ack, except it includes unassembled snapshots. i64 known_sim_tick = 0; + f32 smooth_remote_buffered_ticks = 0; + f32 smooth_remote_buffered_ticks_target = 2; + Vec2I32 tiles_dims = VEC2I32(P_TilesPitch, P_TilesPitch); Vec2I32 cells_dims = VEC2I32(V_CellsPerMeter * P_WorldPitch, V_CellsPerMeter * P_WorldPitch); @@ -999,6 +1007,1227 @@ void V_TickForever(WaveLaneCtx *lane) } } + ////////////////////////////// + //- Try to connect to server + + frame->desired_sim_key = NET_KeyFromString(Lit("127.0.0.1"), Lit("22121")); + if (!NET_MatchKey(frame->sim_key, frame->desired_sim_key)) + { + i64 retry_rate_ns = NsFromSeconds(0.100); + // i64 retry_rate_ns = NsFromSeconds(0); + + b32 should_try = 0; + if (!NET_MatchKey(prev_frame->desired_sim_key, frame->desired_sim_key)) + { + should_try = 1; + } + if (V.connect_try_ns == 0 || frame->time_ns - V.connect_try_ns > retry_rate_ns) + { + should_try = 1; + } + + if (should_try) + { + P_MsgNode tmp_msg_node = Zi; + tmp_msg_node.msg.kind = P_MsgKind_Connect; + tmp_msg_node.msg.data = Lit("Bro"); + + P_MsgList tmp_msglist = Zi; + tmp_msglist.count = 1; + tmp_msglist.first = &tmp_msg_node; + tmp_msglist.last = &tmp_msg_node; + + BB_ResetWriter(&packer_bbw); + String packed = P_PackMessages(&packer_bbw, tmp_msglist); + + NET_Send(net_pipe, frame->desired_sim_key, packed, NET_SendFlag_Raw); + V.connect_try_ns = frame->time_ns; + } + } + + ////////////////////////////// + //- Pop messages from sim + + + + + + // FIXME: Reject messages if not from currently connected sim + + + P_MsgList in_msgs = Zi; + { + NET_MsgList net_msgs = NET_Swap(frame->arena, net_pipe); + for (NET_Msg *net_msg = net_msgs.first; net_msg; net_msg = net_msg->next) + { + NET_Key net_key = net_msg->sender; + + // String address_str = NET_StringFromKey(frame->arena, net_key); + // LogDebugF("Received message from server \"%F\"", FmtString(address_str)); + + if (NET_MatchKey(net_key, frame->desired_sim_key)) + { + frame->sim_key = net_key; + + // String address_str = NET_StringFromKey(frame->arena, net_key); + // LogDebugF("Received message from server \"%F\"", FmtString(address_str)); + + String packed = net_msg->data; + + P_MsgList server_msgs = P_UnpackMessages(frame->arena, packed, net_key); + if (server_msgs.first) + { + if (in_msgs.last) + { + in_msgs.last->next = server_msgs.first; + } + else + { + in_msgs.first = server_msgs.first; + } + server_msgs.first->prev = in_msgs.last; + in_msgs.last = server_msgs.last; + in_msgs.count += server_msgs.count; + } + } + + } + } + + ////////////////////////////// + //- Read snapshots + + { + for (P_MsgNode *msg_node = in_msgs.first; msg_node; msg_node = msg_node->next) + { + P_Msg *msg = &msg_node->msg; + if (msg->kind == P_MsgKind_Raw) + { + BB_Buff bb = BB_BuffFromString(msg->data); + BB_Reader bbr = BB_ReaderFromBuff(&bb); + + //- Read header + BB_ReadBit(&bbr); // Raw + i64 fragment_idx = BB_ReadIV(&bbr); + i64 fragments_count = BB_ReadIV(&bbr); + i64 src_tick = BB_ReadIV(&bbr); + i64 dst_tick = BB_ReadIV(&bbr); + i64 time_ns = BB_ReadIV(&bbr); + u64 world_seed = BB_ReadUBits(&bbr, 64); + P_Key player_key = { .v = BB_ReadUBits(&bbr, 64) }; + i64 tmp_remote_ack = BB_ReadIV(&bbr); + i64 tmp_ack_mirror = BB_ReadIV(&bbr); + + if (dst_tick > known_sim_tick) + { + known_sim_tick = dst_tick; + remote_ack_received_at_ns = frame->time_ns; + remote_ack = tmp_remote_ack; + ack_mirror = tmp_ack_mirror; + } + + if (dst_tick >= known_sim_tick) + { + i64 remote_buffered_cmds = dst_tick - tmp_remote_ack; + } + + if (dst_tick > ack && fragments_count < P_MaxFrameSnapshotFragments) + { + P_Frame *src_frame = P_FrameFromTick(sim_world, src_tick); + if (src_frame->tick == src_tick) + { + V.player_key = player_key; + P_tl.local_player = player_key; + + P_Frame *dst_frame = P_FrameFromTick(sim_world, dst_tick); + if (P_IsFrameNil(dst_frame)) + { + dst_frame = P_PushFrame(sim_world, src_frame, dst_tick); + } + dst_frame->fragments_count = fragments_count; + dst_frame->time_ns = time_ns; + sim_world->seed = world_seed; + + if (fragment_idx < P_MaxFrameSnapshotFragments) + { + u64 *frag_bit_chunk = &dst_frame->received_fragment_bits[fragment_idx / 64]; + u64 frag_bit = (u64)1 << fragment_idx; + b32 is_new = !(*frag_bit_chunk & frag_bit); + if (is_new) + { + *frag_bit_chunk |= frag_bit; + dst_frame->received_fragments_count += 1; + + //- Read deltas + BB_ReadAlignToNextByte(&bbr); + + b32 done = 0; + while (!done && BB_NumBitsRemaining(&bbr) > 0) + { + // TODO: Delta compress + P_Ent *raw_ent = (P_Ent *)BB_ReadBytesRaw(&bbr, sizeof(P_Ent)); + if (raw_ent) + { + P_EntListNode tmp_ent_node = Zi; + CopyStruct(&tmp_ent_node.ent, raw_ent); + P_EntList ent_list = Zi; + ent_list.first = &tmp_ent_node; + ent_list.last = &tmp_ent_node; + ++ent_list.count; + P_SpawnEntsFromList(dst_frame, ent_list); + } + else + { + done = 1; + } + } + + // Mark snapshot as ready if assembled + if (dst_frame->received_fragments_count >= dst_frame->fragments_count) + { + ack = dst_tick; + } + } + } + } + } + + } + } + } + + ////////////////////////////// + //- Apply sim msgs + + { + for (P_MsgNode *msg_node = in_msgs.first; msg_node; msg_node = msg_node->next) + { + P_Msg *msg = &msg_node->msg; + + //- Chat + if (msg->kind == P_MsgKind_Chat) + { + String txt = msg->data; + V_PushNotif(txt); + } + + //- Tiles + if (msg->kind == P_MsgKind_Tiles && sim_world->tiles_hash != msg->tiles_hash) + { + // TODO: Remove this check. Allow non-whole-map tile ranges. + Rng2I32 range = msg->tile_range; + if (msg->data.len == P_TilesCount) + { + for (i32 tile_y = range.p0.y; tile_y < range.p1.y; ++tile_y) + { + i32 src_tile_y = tile_y - range.p0.y; + for (i32 tile_x = range.p0.x; tile_x < range.p1.x; ++tile_x) + { + i32 src_tile_x = tile_x - range.p0.x; + Vec2 src_tile_pos = VEC2(src_tile_x, src_tile_y); + i32 src_tile_idx = P_TileIdxFromTilePos(src_tile_pos); + u8 src_tile = msg->data.text[src_tile_idx]; + + Vec2 tile_pos = VEC2(tile_x, tile_y); + i32 tile_idx = P_TileIdxFromTilePos(tile_pos); + sim_world->tiles_hash = msg->tiles_hash; + sim_world->tiles[tile_idx] = src_tile; + } + } + } + } + } + } + + ////////////////////////////// + //- Compute movement & look + + if (frame->predict_to != prev_frame->predict_to) + { + Vec2 move = Zi; + { + if (frame->held_buttons[Button_A]) move.x -= 1; + if (frame->held_buttons[Button_D]) move.x += 1; + if (frame->held_buttons[Button_W]) move.y -= 1; + if (frame->held_buttons[Button_S]) move.y += 1; + } + move = ClampVec2Len(move, 1); + f32 fire_held = frame->held_buttons[Button_M1]; + f32 fire_presses = fire_held && !prev_frame->held_buttons[Button_M1]; + Vec2 look = Zi; + { + Vec2 center = P_WorldShapeFromEnt(local_guy).centroid; + look = SubVec2(frame->world_cursor, center); + } + if (frame->is_editing) + { + if (!frame->is_panning) + { + f32 edit_move_speed = 20.0 * MaxF32(frame->edit_camera_zoom, min_zoom); + frame->edit_camera_pos = AddVec2(frame->edit_camera_pos, MulVec2(move, edit_move_speed * frame->dt)); + } + + // FIXME: Remove this + frame->move = prev_frame->move; + frame->look = prev_frame->look; + frame->fire_held = prev_frame->fire_held; + frame->fire_presses = prev_frame->fire_presses; + + } + else + { + frame->move = move; + frame->look = look; + frame->fire_held = fire_held; + frame->fire_presses = fire_presses; + } + } + + ////////////////////////////// + //- Determine local simulation bounds + + // FIXME: Real ping + // f64 ping = 0.250; + f64 ping = 0; + i64 ping_ns = NsFromSeconds(ping); + + // frame->predict_to = sim_world->last_frame->tick + MaxF64(CeilF64(ping * SIM_TICKS_PER_SECOND), 1.0); + + // frame->predict_to = ack + 10; + frame->predict_to = MaxI64(prev_frame->predict_to, ack + 10); + + ////////////////////////////// + //- Create player control + + if (frame->predict_to > prev_frame->predict_to) + { + i64 control_tick = frame->predict_to; + + P_Control control = Zi; + control.tick = control_tick; + control.orig_tick = control_tick; + control.move = frame->move; + control.look = frame->look; + control.fire_held = frame->fire_held; + control.fire_presses = frame->fire_presses; + + //- Fill controls buffer backwards + i64 max_fill_count = SIM_TICKS_PER_SECOND / 4; + for (i64 dst_tick = control_tick; dst_tick > MaxI64(prev_frame->predict_to, (dst_tick - max_local_controls)); --dst_tick) + { + P_Control *dst_control = &local_controls[dst_tick % max_local_controls]; + if (dst_tick > dst_control->orig_tick) + { + *dst_control = control; + dst_control->tick = dst_tick; + dst_control->orig_tick = control_tick; + } + else + { + break; + } + } + } + + ////////////////////////////// + //- Send snapshots + + { + i64 snapshot_send_threshold_ns = NsFromSeconds(1.0) / SIM_TICKS_PER_SECOND; + if (prev_snapshot_sent_at_ns == 0 || (frame->time_ns - prev_snapshot_sent_at_ns) > snapshot_send_threshold_ns) + { + // FIXME: Base this on ping + i64 max_control_sends = SIM_TICKS_PER_SECOND; + + //- Pack control deltas + Struct(PackedDeltaNode) + { + PackedDeltaNode *next; + String packed; + }; + i64 total_delta_bytes = 0; + PackedDeltaNode *first_delta_node = 0; + PackedDeltaNode *last_delta_node = 0; + if (frame->predict_to >= ack) + { + i64 last_send_tick = frame->predict_to; + i64 first_send_tick = MaxI64(ack, remote_ack + 1); + first_send_tick = MaxI64(first_send_tick, last_send_tick - max_control_sends); + for (i64 send_tick = first_send_tick; send_tick <= last_send_tick; ++send_tick) + { + b32 should_transmit = 0; + BB_ResetWriter(&packer_bbw); + u64 delta_start = BB_GetNumBytesWritten(&packer_bbw); + { + // TODO: Delta compress + should_transmit = 1; + P_Control *control = &local_controls[send_tick % max_local_controls]; + BB_WriteBytes(&packer_bbw, StringFromStruct(control)); + } + u64 delta_end = BB_GetNumBytesWritten(&packer_bbw); + if (should_transmit) + { + String packed = Zi; + packed.text = BB_GetWrittenRaw(&packer_bbw) + delta_start; + packed.len = delta_end - delta_start; + { + PackedDeltaNode *delta_node = PushStruct(frame->arena, PackedDeltaNode); + delta_node->packed = PushString(frame->arena, packed); + SllQueuePush(first_delta_node, last_delta_node, delta_node); + total_delta_bytes += packed.len; + } + } + } + } + + //- Collect & send snapshots + { + u64 snapshot_cutoff_threshold = NET_PacketSize / 2; + PackedDeltaNode *delta_node = first_delta_node; + + u64 snapshot_start = 0; + b32 new_snapshot = 1; + b32 done = 0; + while (!done) + { + PackedDeltaNode *next_delta_node = delta_node ? delta_node->next : 0; + { + //- Init snapshot + if (new_snapshot) + { + new_snapshot = 0; + BB_ResetWriter(&packer_bbw); + snapshot_start = BB_GetNumBytesWritten(&packer_bbw); + BB_WriteBit(&packer_bbw, 1); // Raw + BB_WriteIV(&packer_bbw, ack); + } + + //- Append packed delta + BB_WriteAlignToNextByte(&packer_bbw); + if (delta_node) + { + BB_WriteBytes(&packer_bbw, delta_node->packed); + } + + //- Submit snapshot + { + if (!delta_node || !next_delta_node) + { + new_snapshot = 1; + done = 1; + } + u64 next_len = 0; + if (next_delta_node) + { + next_len = next_delta_node->packed.len; + } + u64 cur_snapshot_len = BB_GetNumBytesWritten(&packer_bbw); + if ((cur_snapshot_len - snapshot_start) + next_len >= snapshot_cutoff_threshold) + { + new_snapshot = 1; + } + } + if (new_snapshot) + { + u64 snapshot_end = BB_GetNumBytesWritten(&packer_bbw); + String snapshot = Zi; + snapshot.text = BB_GetWrittenRaw(&packer_bbw) + snapshot_start; + snapshot.len = snapshot_end - snapshot_start; + NET_Send(net_pipe, frame->sim_key, snapshot, NET_SendFlag_Raw); + + // Sanity check to catch whenever we end up packing too much information + // into a single raw snapshot, causing it to drop. This isn't necessarily + // an error, but signals we may have botched the packing code or need tighter + // compression + Assert(snapshot.len <= NET_PacketSize); + } + } + delta_node = next_delta_node; + } + } + + prev_snapshot_sent_at_ns = frame->time_ns; + } + } + + ////////////////////////////// + //- Predict + + + // TODO: Only predict when new sim snapshot is received + + + + + // P_Frame *predict_frame = &P_NilFrame; + // { + // if (predict_world->tiles_hash != sim_world->tiles_hash) + // { + // predict_world->tiles_hash = sim_world->tiles_hash; + // CopyStructs(predict_world->tiles, sim_world->tiles, P_TilesCount); + // } + // predict_world->seed = sim_world->seed; + + // P_ClearFrames(predict_world, I64Min, I64Max); + // predict_frame = P_PushFrame(predict_world, sim_world->last_frame, sim_world->last_frame->tick); + // P_DebugDrawFrame(predict_frame); + + // predict_frame = predict_world->last_frame; + // } + + + + + + + + + i64 first_predict_tick = 0; + i64 last_predict_tick = 0; + { + // FIXME: Not like this + i64 max_predict_ticks = SIM_TICKS_PER_SECOND; + last_predict_tick = frame->predict_to; + // first_predict_tick = ack - 2; + first_predict_tick = ack; + first_predict_tick = MaxI64(first_predict_tick, last_predict_tick - max_predict_ticks); + } + + + + // TODO: Remove this + // TODO: Delete all frames except for prediction base & remote ack + // TODO: Limit max sim frames stored + { + i64 bottom_tick = I64Max; + bottom_tick = MinI64(bottom_tick, ack_mirror); + bottom_tick = MinI64(bottom_tick, first_predict_tick); + bottom_tick = MinI64(bottom_tick, last_predict_tick); + bottom_tick = MinI64(bottom_tick, ack); + P_ClearFrames(sim_world, I64Min, bottom_tick - 1); + } + // P_ClearFrames(sim_world, I64Min, ack - SIM_TICKS_PER_SECOND); + // P_ClearFrames(sim_world, I64Min, sim_world->last_frame->tick - 1); + + + + + + + + // Predict + P_Frame *predict_frame = predict_world->last_frame; + if (frame->predict_to != prev_frame->predict_to) + { + if (predict_world->tiles_hash != sim_world->tiles_hash) + { + predict_world->tiles_hash = sim_world->tiles_hash; + CopyStructs(predict_world->tiles, sim_world->tiles, P_TilesCount); + } + predict_world->seed = sim_world->seed; + + P_ClearFrames(predict_world, I64Min, first_predict_tick - 1); + predict_frame = P_PushFrame(predict_world, P_FrameFromTick(sim_world, first_predict_tick), first_predict_tick); + + for (i64 predict_tick = first_predict_tick + 1; predict_tick <= last_predict_tick; ++predict_tick) + { + predict_frame = P_PushFrame(predict_world, predict_world->last_frame, predict_tick); + P_Ent *predict_player = P_EntFromKey(predict_frame, V.player_key); + if (!P_IsEntNil(predict_player)) + { + P_Control *predict_control = &local_controls[predict_tick % max_local_controls]; + if (predict_control->tick == predict_tick) + { + predict_player->control = *predict_control; + } + } + // for (P_Ent *ent = P_FirstEnt(predict_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) + // { + // if (ent != predict_player) + // { + // ZeroStruct(&ent->control); + // } + // } + + b32 old_debug_draw = P_tl.debug_draw_enabled; + Vec4 old_debug_tint = P_tl.debug_tint; + { + P_tl.debug_draw_enabled = TweakBool("Debug draw intermediate prediction steps", 0); + P_tl.debug_tint = VEC4(0.8, 0, 0, 0.25); + P_StepFrame(predict_frame); + } + P_tl.debug_draw_enabled = old_debug_draw; + P_tl.debug_tint = old_debug_tint; + } + + predict_frame = predict_world->last_frame; + // P_DebugDrawFrame(predict_frame); + + // TODO: Extract information that occurred between first & last prediction, like bullet hits etc? + } + + + + + + + + + + + + // // Predict + // P_Frame *predict_frame = &P_NilFrame; + // { + // if (predict_world->tiles_hash != sim_world->tiles_hash) + // { + // predict_world->tiles_hash = sim_world->tiles_hash; + // CopyStructs(predict_world->tiles, sim_world->tiles, P_TilesCount); + // } + // predict_world->seed = sim_world->seed; + + + + // // P_ClearFrames(predict_world, I64Min, I64Max); + // // predict_frame = P_PushFrame(predict_world, sim_world->last_frame, sim_world->last_frame->tick); + + // // FIXME: Not like this + // i64 max_predict_ticks = SIM_TICKS_PER_SECOND; + + // i64 last_predict_tick = frame->predict_to; + // i64 first_predict_tick = sim_world->last_frame->tick; + // first_predict_tick = MaxI64(first_predict_tick, last_predict_tick - max_predict_ticks); + + // P_ClearFrames(predict_world, I64Min, first_predict_tick - 1); + // predict_frame = P_PushFrame(predict_world, sim_world->last_frame, first_predict_tick); + + // for (i64 predict_tick = first_predict_tick + 1; predict_tick <= last_predict_tick; ++predict_tick) + // { + // predict_frame = P_PushFrame(predict_world, predict_world->last_frame, predict_tick); + + // P_Ent *predict_player = P_EntFromKey(predict_frame, local_player->key); + // if (!P_IsEntNil(predict_player)) + // { + // P_Control *predict_control = &local_controls[predict_tick % max_local_controls]; + // if (predict_control->tick == predict_tick) + // { + // predict_player->control = *predict_control; + // } + // } + + // P_StepFrame(predict_frame); + // } + + // predict_frame = predict_world->last_frame; + // P_DebugDrawFrame(predict_frame); + + // // TODO: Extract information that occurred between first & last prediction, like bullet hits etc? + // } + + + + + + + + // ////////////////////////////// + // //- Update corrected world + + + + // P_Frame *correct_frame = &P_NilFrame; + // { + // if (correct_world->tiles_hash != predict_world->tiles_hash) + // { + // correct_world->tiles_hash = predict_world->tiles_hash; + // CopyStructs(correct_world->tiles, predict_world->tiles, P_TilesCount); + // tiles_dirty = 1; + // } + // correct_world->seed = predict_world->seed; + + // P_ClearFrames(correct_world, I64Min, I64Max); + // correct_frame = P_PushFrame(correct_world, predict_world->last_frame, predict_world->last_frame->tick); + // } + + + + + + // // P_Frame *correct_frame = correct_world->last_frame; + // // { + // // if (correct_world->tiles_hash != predict_world->tiles_hash) + // // { + // // correct_world->tiles_hash = predict_world->tiles_hash; + // // CopyStructs(correct_world->tiles, predict_world->tiles, P_TilesCount); + // // } + // // correct_world->seed = predict_world->seed; + + // // if (correct_world->last_frame->tick != predict_frame->tick) + // // { + // // correct_frame = P_PushFrame(correct_world, correct_world->last_frame, predict_frame->tick); + // // } + + // // // P_ClearFrames(correct_world, I64Min, correct_world->last_frame->tick - 1); + // // // P_Frame *prev_correct_frame = correct_world->last_frame; + + // // { + // // f32 correction_rate = 1; + // // // f32 correction_rate = 30 * frame->dt; + // // P_EntList spawn_ents = Zi; + // // for (P_Ent *predict_ent = P_FirstEnt(predict_frame); !P_IsEntNil(predict_ent); predict_ent = P_NextEnt(predict_ent)) + // // { + // // P_Ent *correct_ent = P_EntFromKey(correct_frame, predict_ent->key); + // // if (P_IsEntNil(correct_ent)) + // // { + // // correct_ent = P_PushTempEnt(frame->arena, &spawn_ents); + // // *correct_ent = *predict_ent; + // // } + // // else + // // { + // // // TODO: Unified blend logic between local world & correct world + // // correct_ent->xf = LerpXform(correct_ent->xf, predict_ent->xf, correction_rate); + // // correct_ent->solved_v = LerpVec2(correct_ent->solved_v, predict_ent->solved_v, correction_rate); + // // correct_ent->solved_w = LerpF32(correct_ent->solved_w, predict_ent->solved_w, correction_rate); + // // } + // // } + // // P_SpawnEntsFromList(correct_frame, spawn_ents); + // // } + + // // // Prune ents + // // { + // // i64 ents_to_prune_count = 0; + // // P_Ent **ents_to_prune = PushStructsNoZero(frame->arena, P_Ent *, correct_frame->ents_count); + // // for (P_Ent *ent = P_FirstEnt(correct_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) + // // { + // // if (ent->exists <= 0 || P_IsEntNil(P_EntFromKey(predict_frame, ent->key))) + // // { + // // ents_to_prune[ents_to_prune_count] = ent; + // // ents_to_prune_count += 1; + // // } + // // } + // // for (i64 prune_idx = 0; prune_idx < ents_to_prune_count; ++prune_idx) + // // { + // // P_Ent *ent = ents_to_prune[prune_idx]; + // // P_EntBin *bin = &correct_frame->ent_bins[ent->key.v % correct_frame->ent_bins_count]; + // // DllQueueRemoveNP(bin->first, bin->last, ent, next_in_bin, prev_in_bin); + // // DllQueueRemoveNPZ(&P_NilEnt, correct_frame->first_ent, correct_frame->last_ent, ent, next, prev); + // // correct_frame->ents_count -= 1; + // // SllStackPush(correct_world->first_free_ent, ent); + // // } + // // } + + + // // // P_Frame *prev_correct_frame = P_PushFrame(correct_world, predict_world->last_frame, predict_world->last_frame->tick); + + // // P_DebugDrawFrame(correct_frame); + // // } + + + + + + ////////////////////////////// + //- Update local world + + // TODO: Remove this + P_Frame *local_frame = &P_NilFrame; + { + if (local_world->tiles_hash != predict_world->tiles_hash) + { + local_world->tiles_hash = predict_world->tiles_hash; + CopyStructs(local_world->tiles, predict_world->tiles, P_TilesCount); + tiles_dirty = 1; + } + local_world->seed = predict_world->seed; + + P_ClearFrames(local_world, I64Min, I64Max); + local_frame = P_PushFrame(local_world, predict_world->last_frame, predict_world->last_frame->tick); + + P_DebugDrawFrame(local_frame); + } + + + + + + + // { + // i64 delay_ns = NsFromSeconds(100); + + // P_Frame *right_frame = &P_NilFrame; + // P_Frame *left_frame = &P_NilFrame; + + // for (P_Frame *tmp = + + + + + // P_Frame *right_frame = predict_world->last_frame; + // P_Frame *left_frame = predict_world->left_frame; + // } + + + + + + + + + + + + + ////////////////////////////// + //- Push test bullet particles + + // TODO: Not like this + + + + + for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) + { + if (bullet->is_bullet) + { + // FIXME: Truncate bullet trail at hit + Vec2 start = bullet->bullet_start; + Vec2 end = bullet->bullet_end; + + b32 skip = 0; + if (bullet->has_hit) + { + Vec2 hit_pos = bullet->hit_entry; + if (DotVec2(SubVec2(hit_pos, start), SubVec2(end, start)) < 0) + { + skip = 1; + } + // V_DrawPoint(MulXformV2(frame->xf.world_to_ui, start), Color_Red); + // V_DrawPoint(MulXformV2(frame->xf.world_to_ui, end), Color_Purple); + end = hit_pos; + } + + if (!skip) + { + f32 trail_len = Vec2Len(SubVec2(end, start)); + // f32 particles_count = MaxF32(trail_len * 10000, 1); + // f32 particles_count = MaxF32(trail_len * 512, 1); + // f32 particles_count = MaxF32(trail_len * 64, 1); + f32 particles_count = MaxF32(trail_len * 32, 1); + + + f32 angle = AngleFromVec2(PerpVec2(SubVec2(end, start))); + // f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start))); + + V_Emitter emitter = Zi; + { + // emitter.flags |= V_ParticleFlag_StainOnPrune; + // emitter.flags |= V_ParticleFlag_StainTrail; + + // emitter.lifetime = 1; + // emitter.lifetime_spread = 2; + + emitter.count = particles_count; + + // emitter.lifetime = 1; + // emitter.lifetime = 0.15; + emitter.lifetime = 0.075; + // emitter.lifetime = 0.05; + // emitter.lifetime = 0.04; + emitter.lifetime_spread = emitter.lifetime * 2; + + emitter.angle = angle; + // emitter.angle_spread = Tau / 4; + emitter.angle_spread = Tau / 4; + + emitter.start = start; + emitter.end = end; + + // emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1)); + + // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.8, 0.8, 0.25)); + emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1)); + // emitter.color_spread = LinearFromSrgb(VEC4(0, 0, 0, 0.2)); + + emitter.speed = 0; + emitter.speed_spread = 1; + + // emitter.speed = 1; + // emitter.speed_spread = 1; + + // emitter.velocity_falloff = 1; + // emitter.velocity_falloff_spread = 0; + } + V_PushParticles(emitter); + } + + + } + } + + + + + + // { + // PERSIST Vec2 start = {5, 5}; + // PERSIST Vec2 end = {3, -20}; + + + // b32 skip = 1; + // if (frame->held_buttons[Button_G]) + // { + // end = frame->world_cursor; + // if (!prev_frame->held_buttons[Button_G]) + // { + // start = end; + // } + // skip = 0; + // } + + // Vec2 vel = SubVec2(end, start); + + + // f32 trail_len = Vec2Len(SubVec2(end, start)); + // // f32 particles_count = MaxF32(trail_len * 10000, 1); + // // f32 particles_count = MaxF32(trail_len * 512, 1); + // // f32 particles_count = MaxF32(trail_len * 64, 1); + // f32 particles_count = MaxF32(trail_len * 32, 1); + + + // f32 angle = AngleFromVec2(PerpVec2(SubVec2(end, start))); + // // f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start))); + + // V_Emitter *emitter = V_PushEmitter(particles_count); + + // // emitter->flags |= V_ParticleFlag_StainOnPrune; + // // emitter->flags |= V_ParticleFlag_StainTrail; + + // // emitter->lifetime = 1; + // // emitter->lifetime_spread = 2; + + // emitter->lifetime = 0.25; + // emitter->lifetime_spread = emitter->lifetime * 2; + + // emitter->angle = angle; + // // emitter->angle_spread = Tau / 4; + // emitter->angle_spread = Tau / 4; + + // emitter->start = start; + // emitter->end = end; + + // // emitter->color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1)); + + // // emitter->color_lin = LinearFromSrgb(VEC4(0.8, 0.8, 0.8, 0.25)); + // emitter->color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1)); + // // emitter->color_spread = LinearFromSrgb(VEC4(0, 0, 0, 0.2)); + + // emitter->speed = 0; + // emitter->speed_spread = 1; + + // // emitter->speed = 1; + // // emitter->speed_spread = 1; + + // // emitter->velocity_falloff = 1; + // // emitter->velocity_falloff_spread = 0; + + // start = end; + // end = AddVec2(end, vel); + // } + + + + + + + + + + + + + + + + + + + + + + ////////////////////////////// + //- Push test blood particles + + // TODO: Not like this + + + + + + if (0) + { + for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) + { + if (bullet->is_bullet && bullet->has_hit) + { + // Vec2 bullet_start = bullet->start; + // Vec2 bullet_end = bullet->end; + + Vec2 hit_entry = bullet->hit_entry; + Vec2 hit_entry_normal = bullet->hit_entry_normal; + Vec2 bullet_vel = SubVec2(bullet->bullet_end, bullet->bullet_start); + + V_DrawLine(bullet->bullet_start, bullet->bullet_end, Color_Cyan); + + V_ParticleFlag flags = 0; + flags |= V_ParticleFlag_PruneWhenStill; + flags |= V_ParticleFlag_StainOnPrune; + if (TweakBool("Emitter stain trail", 1)) + { + flags |= V_ParticleFlag_StainTrail; + } + // f32 count = TweakFloat("Emitter count", 50, 0, 10000); + f32 count = TweakFloat("Emitter count", 20, 1, 1000); + f32 speed = TweakFloat("Emitter speed", 20, 0, 100); + f32 falloff = TweakFloat("Emitter falloff", 50, 0, 100); + f32 angle_spread = TweakFloat("Emitter angle spread", 0.1, 0, 1) * Tau; + + V_Emitter emitter = Zi; + { + emitter.count = count; + emitter.flags = flags; + + // Vec2 dir = hit_entry_normal; + Vec2 dir = NormVec2(NegVec2(bullet_vel)); + + emitter.start = hit_entry; + emitter.end = emitter.start; + + emitter.speed = speed; + emitter.speed_spread = speed * 2; + + emitter.velocity_falloff = falloff; + emitter.velocity_falloff_spread = falloff * 1.5; + + emitter.angle = AngleFromVec2(dir); + // emitter.angle_spread = Tau / 4; + emitter.angle_spread = angle_spread; + // emitter.angle_spread = Tau / 32; + + // emitter.color_lin = LinearFromSrgb(VEC4(0.5, 0.1, 0.1, 1)); + emitter.color_lin = LinearFromSrgb(VEC4(0.5, 0.1, 0.1, 0.5)); + + // emitter.color_spread = VEC4(0.1, 0, 0, 0); + emitter.color_spread = VEC4(0.1, 0, 0, 0.5); + + // emitter.color = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1)); + + // emitter.angle_spread = 1; + // emitter.angle_spread = 0.5; + // emitter.angle_spread = Tau; + } + V_PushParticles(emitter); + + // V_DrawPoint(victim_raycast.p, Color_Green); + // V_DrawLine(victim_raycast.p, AddVec2(victim_raycast.p, MulVec2(victim_raycast.normal, 0.5)), Color_White); + } + } + } + + + + + + + + + + // { + // for (P_Ent *firer = P_FirstEnt(local_frame); firer->valid; firer = P_NextEnt(firer)) + // { + // if (firer->fire_held) + // { + // Xform firer_xf = firer->xf; + // P_Shape firer_world_shape = P_MulXformShape(firer_xf, firer->local_shape); + + // Vec2 ray_start = firer_world_shape.centroid; + // Vec2 ray_dir = firer->look; + + // // TODO: Real raycast query + // P_Ent *closest_victim = &P_NilEnt; + // P_RaycastResult victim_raycast = Zi; + // { + // f32 closest_len_sq = Inf; + // for (P_Ent *victim = P_FirstEnt(local_frame); victim->valid; victim = P_NextEnt(victim)) + // { + // if (victim != firer) + // { + // Xform victim_xf = victim->xf; + // P_Shape victim_world_shape = P_MulXformShape(victim_xf, victim->local_shape); + + // P_RaycastResult raycast = P_RaycastShape(victim_world_shape, ray_start, ray_dir); + // if (raycast.is_intersecting) + // { + // f32 len_sq = Vec2LenSq(SubVec2(raycast.p, ray_start)); + // if (len_sq < closest_len_sq) + // { + // closest_len_sq = len_sq; + // closest_victim = victim; + // victim_raycast = raycast; + // } + // } + // } + // } + // } + + // if (closest_victim->valid) + // { + // V_ParticleFlag flags = 0; + // flags |= V_ParticleFlag_PruneWhenStill; + // flags |= V_ParticleFlag_StainOnPrune; + // if (TweakBool("Emitter stain trail", 1)) + // { + // flags |= V_ParticleFlag_StainTrail; + // } + // // f32 count = TweakFloat("Emitter count", 50, 0, 10000); + // f32 count = TweakFloat("Emitter count", 50, 1, 1000); + // f32 speed = TweakFloat("Emitter speed", 20, 0, 100); + // f32 falloff = TweakFloat("Emitter falloff", 50, 0, 100); + // f32 angle_spread = TweakFloat("Emitter angle spread", 0.1, 0, 1) * Tau; + + // V_Emitter *emitter = V_PushEmitter(count); + // emitter->flags = flags; + + // Vec2 dir = victim_raycast.normal; + // emitter->start = victim_raycast.p; + // emitter->end = emitter->start; + + // emitter->speed = speed; + // emitter->speed_spread = speed * 2; + + // emitter->velocity_falloff = falloff; + // emitter->velocity_falloff_spread = falloff * 1.5; + + // emitter->angle = AngleFromVec2(dir); + // // emitter->angle_spread = Tau / 4; + // emitter->angle_spread = angle_spread; + // // emitter->angle_spread = Tau / 32; + + // emitter->color_lin = LinearFromSrgb(VEC4(0.5, 0.1, 0.1, 1)); + // emitter->color_spread = VEC4(0.1, 0, 0, 0); + + // // emitter->color = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1)); + + // emitter->seed = RandU64FromState(&frame->rand); + // // emitter->angle_spread = 1; + // // emitter->angle_spread = 0.5; + // // emitter->angle_spread = Tau; + + // // V_DrawPoint(victim_raycast.p, Color_Green); + // // V_DrawLine(victim_raycast.p, AddVec2(victim_raycast.p, MulVec2(victim_raycast.normal, 0.5)), Color_White); + // } + + + // // for (P_QueryResult query = P_FirstRaycast(wrold, ray_start, ray_dir); query. + // // P_RaycastWorldResult hits = P_RaycastWorld(local_frame, ray_start, ray_dir) + // // { + // // } + // } + // } + // } + + ////////////////////////////// + //- Push test emitter + + if (frame->held_buttons[Button_F]) + { + V_Emitter emitter = Zi; + + Vec2 dir = frame->look; + emitter.start = frame->world_cursor; + emitter.end = emitter.start; + emitter.angle = AngleFromVec2(dir); + + // emitter.flags |= V_ParticleFlag_StainTrail; + + emitter.count = 128; + // emitter.count = 100; + emitter.speed = 10; + + emitter.color_lin = LinearFromSrgb(Color_Yellow); + + emitter.speed_spread = 1; + // emitter.angle_spread = 1; + // emitter.angle_spread = 0.5; + emitter.angle_spread = Tau / 4; + // emitter.angle_spread = Tau; + + V_PushParticles(emitter); + } + + ////////////////////////////// + //- Debug draw + + { + // Merge vis debug draws with sim debug draws + P_DebugDrawNode *first_debug_draw_node = first_sim_debug_draw_node; + P_DebugDrawNode *last_debug_draw_node = last_sim_debug_draw_node; + if (P_tl.first_debug_draw_node) + { + if (last_debug_draw_node) + { + last_debug_draw_node->next = P_tl.first_debug_draw_node; + } + else + { + first_debug_draw_node = P_tl.first_debug_draw_node; + } + last_debug_draw_node = P_tl.last_debug_draw_node; + } + + // Push draws + for (P_DebugDrawNode *n = first_debug_draw_node; n; n = n->next) + { + Vec4 color = Vec4FromU32(n->srgb32); + i32 detail = 24; + f32 radius = 5; + switch(n->kind) + { + case P_DebugDrawKind_Point: + { + Vec2 ui_p = MulXformV2(frame->xf.world_to_ui, n->point.p); + V_DrawPoint(ui_p, color); + } break; + + case P_DebugDrawKind_Line: + { + Vec2 ui_p0 = MulXformV2(frame->xf.world_to_ui, n->line.p0); + Vec2 ui_p1 = MulXformV2(frame->xf.world_to_ui, n->line.p1); + V_DrawLine(ui_p0, ui_p1, color); + } break; + + case P_DebugDrawKind_Rect: + { + Rng2 ui_rect = Zi; + ui_rect.p0 = MulXformV2(frame->xf.world_to_ui, n->rect.p0); + ui_rect.p1 = MulXformV2(frame->xf.world_to_ui, n->rect.p1); + V_DrawRect(ui_rect, color, V_DrawFlag_Line); + } break; + + case P_DebugDrawKind_Shape: + { + P_Shape ui_shape = P_MulXformShape(frame->xf.world_to_ui, n->shape); + V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line); + } break; + } + } + + // Reset vis debug draws + ResetArena(P_tl.debug_arena); + P_tl.first_debug_draw_node = 0; + P_tl.last_debug_draw_node = 0; + P_tl.debug_draw_nodes_count = 0; + } + ////////////////////////////// //- Build notifications @@ -2771,1208 +4000,6 @@ void V_TickForever(WaveLaneCtx *lane) } } - ////////////////////////////// - //- Try to connect to server - - frame->desired_sim_key = NET_KeyFromString(Lit("127.0.0.1"), Lit("22121")); - if (!NET_MatchKey(frame->sim_key, frame->desired_sim_key)) - { - i64 retry_rate_ns = NsFromSeconds(0.100); - // i64 retry_rate_ns = NsFromSeconds(0); - - b32 should_try = 0; - if (!NET_MatchKey(prev_frame->desired_sim_key, frame->desired_sim_key)) - { - should_try = 1; - } - if (V.connect_try_ns == 0 || frame->time_ns - V.connect_try_ns > retry_rate_ns) - { - should_try = 1; - } - - if (should_try) - { - P_MsgNode tmp_msg_node = Zi; - tmp_msg_node.msg.kind = P_MsgKind_Connect; - tmp_msg_node.msg.data = Lit("Bro"); - - P_MsgList tmp_msglist = Zi; - tmp_msglist.count = 1; - tmp_msglist.first = &tmp_msg_node; - tmp_msglist.last = &tmp_msg_node; - - BB_ResetWriter(&packer_bbw); - String packed = P_PackMessages(&packer_bbw, tmp_msglist); - - NET_Send(net_pipe, frame->desired_sim_key, packed, NET_SendFlag_Raw); - V.connect_try_ns = frame->time_ns; - } - } - - ////////////////////////////// - //- Pop messages from sim - - - - - - // FIXME: Reject messages if not from currently connected sim - - - P_MsgList in_msgs = Zi; - { - NET_MsgList net_msgs = NET_Swap(frame->arena, net_pipe); - for (NET_Msg *net_msg = net_msgs.first; net_msg; net_msg = net_msg->next) - { - NET_Key net_key = net_msg->sender; - - // String address_str = NET_StringFromKey(frame->arena, net_key); - // LogDebugF("Received message from server \"%F\"", FmtString(address_str)); - - if (NET_MatchKey(net_key, frame->desired_sim_key)) - { - frame->sim_key = net_key; - - // String address_str = NET_StringFromKey(frame->arena, net_key); - // LogDebugF("Received message from server \"%F\"", FmtString(address_str)); - - String packed = net_msg->data; - - P_MsgList server_msgs = P_UnpackMessages(frame->arena, packed, net_key); - if (server_msgs.first) - { - if (in_msgs.last) - { - in_msgs.last->next = server_msgs.first; - } - else - { - in_msgs.first = server_msgs.first; - } - server_msgs.first->prev = in_msgs.last; - in_msgs.last = server_msgs.last; - in_msgs.count += server_msgs.count; - } - } - - } - } - - ////////////////////////////// - //- Apply sim snapshots - - { - for (P_MsgNode *msg_node = in_msgs.first; msg_node; msg_node = msg_node->next) - { - P_Msg *msg = &msg_node->msg; - if (msg->kind == P_MsgKind_Raw) - { - BB_Buff bb = BB_BuffFromString(msg->data); - BB_Reader bbr = BB_ReaderFromBuff(&bb); - - //- Read header - BB_ReadBit(&bbr); // Raw - i64 fragment_idx = BB_ReadIV(&bbr); - i64 fragments_count = BB_ReadIV(&bbr); - i64 src_tick = BB_ReadIV(&bbr); - i64 dst_tick = BB_ReadIV(&bbr); - i64 time_ns = BB_ReadIV(&bbr); - u64 world_seed = BB_ReadUBits(&bbr, 64); - P_Key player_key = { .v = BB_ReadUBits(&bbr, 64) }; - i64 tmp_remote_ack = BB_ReadIV(&bbr); - - if (dst_tick > known_sim_tick) - { - known_sim_tick = dst_tick; - remote_ack_received_at_ns = frame->time_ns; - remote_ack = tmp_remote_ack; - } - - if (dst_tick > ready_sim_tick && fragments_count < P_MaxFrameSnapshotFragments) - { - P_Frame *src_frame = P_FrameFromTick(sim_world, src_tick); - if (src_frame->tick == src_tick) - { - V.player_key = player_key; - P_tl.local_player = player_key; - - P_Frame *dst_frame = P_FrameFromTick(sim_world, dst_tick); - if (P_IsFrameNil(dst_frame)) - { - dst_frame = P_PushFrame(sim_world, src_frame, dst_tick); - } - dst_frame->fragments_count = fragments_count; - dst_frame->time_ns = time_ns; - sim_world->seed = world_seed; - - if (fragment_idx < P_MaxFrameSnapshotFragments) - { - u64 *frag_bit_chunk = &dst_frame->received_fragment_bits[fragment_idx / 64]; - u64 frag_bit = (u64)1 << fragment_idx; - b32 is_new = !(*frag_bit_chunk & frag_bit); - if (is_new) - { - *frag_bit_chunk |= frag_bit; - dst_frame->received_fragments_count += 1; - - //- Read deltas - BB_ReadAlignToNextByte(&bbr); - - b32 done = 0; - while (!done && BB_NumBitsRemaining(&bbr) > 0) - { - // TODO: Delta compress - P_Ent *raw_ent = (P_Ent *)BB_ReadBytesRaw(&bbr, sizeof(P_Ent)); - if (raw_ent) - { - P_EntListNode tmp_ent_node = Zi; - CopyStruct(&tmp_ent_node.ent, raw_ent); - P_EntList ent_list = Zi; - ent_list.first = &tmp_ent_node; - ent_list.last = &tmp_ent_node; - ++ent_list.count; - P_SpawnEntsFromList(dst_frame, ent_list); - } - else - { - done = 1; - } - } - - // Mark snapshot as ready if assembled - if (dst_frame->received_fragments_count >= dst_frame->fragments_count) - { - ready_sim_tick = dst_tick; - } - } - } - } - } - - } - } - } - - ////////////////////////////// - //- Apply sim msgs - - { - for (P_MsgNode *msg_node = in_msgs.first; msg_node; msg_node = msg_node->next) - { - P_Msg *msg = &msg_node->msg; - - //- Chat - if (msg->kind == P_MsgKind_Chat) - { - String txt = msg->data; - V_PushNotif(txt); - } - - //- Tiles - if (msg->kind == P_MsgKind_Tiles && sim_world->tiles_hash != msg->tiles_hash) - { - // TODO: Remove this check. Allow non-whole-map tile ranges. - Rng2I32 range = msg->tile_range; - if (msg->data.len == P_TilesCount) - { - for (i32 tile_y = range.p0.y; tile_y < range.p1.y; ++tile_y) - { - i32 src_tile_y = tile_y - range.p0.y; - for (i32 tile_x = range.p0.x; tile_x < range.p1.x; ++tile_x) - { - i32 src_tile_x = tile_x - range.p0.x; - Vec2 src_tile_pos = VEC2(src_tile_x, src_tile_y); - i32 src_tile_idx = P_TileIdxFromTilePos(src_tile_pos); - u8 src_tile = msg->data.text[src_tile_idx]; - - Vec2 tile_pos = VEC2(tile_x, tile_y); - i32 tile_idx = P_TileIdxFromTilePos(tile_pos); - sim_world->tiles_hash = msg->tiles_hash; - sim_world->tiles[tile_idx] = src_tile; - } - } - } - } - } - } - - // TODO: Remove this - // TODO: Delete all frames except for prediction base & remote ack - P_ClearFrames(sim_world, I64Min, ready_sim_tick - SIM_TICKS_PER_SECOND); - // P_ClearFrames(sim_world, I64Min, sim_world->last_frame->tick - 1); - P_Frame *sim_frame = sim_world->last_frame; - - ////////////////////////////// - //- Compute movement & look - - if (frame->predict_to != prev_frame->predict_to) - { - Vec2 move = Zi; - { - if (frame->held_buttons[Button_A]) move.x -= 1; - if (frame->held_buttons[Button_D]) move.x += 1; - if (frame->held_buttons[Button_W]) move.y -= 1; - if (frame->held_buttons[Button_S]) move.y += 1; - } - move = ClampVec2Len(move, 1); - f32 fire_held = frame->held_buttons[Button_M1]; - f32 fire_presses = fire_held && !prev_frame->held_buttons[Button_M1]; - Vec2 look = Zi; - { - Vec2 center = P_WorldShapeFromEnt(local_guy).centroid; - look = SubVec2(frame->world_cursor, center); - } - if (frame->is_editing) - { - if (!frame->is_panning) - { - f32 edit_move_speed = 20.0 * MaxF32(frame->edit_camera_zoom, min_zoom); - frame->edit_camera_pos = AddVec2(frame->edit_camera_pos, MulVec2(move, edit_move_speed * frame->dt)); - } - - // FIXME: Remove this - frame->move = prev_frame->move; - frame->look = prev_frame->look; - frame->fire_held = prev_frame->fire_held; - frame->fire_presses = prev_frame->fire_presses; - - } - else - { - frame->move = move; - frame->look = look; - frame->fire_held = fire_held; - frame->fire_presses = fire_presses; - } - } - - ////////////////////////////// - //- Determine local simulation bounds - - // FIXME: Real ping - // f64 ping = 0.250; - f64 ping = 0; - i64 ping_ns = NsFromSeconds(ping); - - // frame->predict_to = sim_world->last_frame->tick + MaxF64(CeilF64(ping * SIM_TICKS_PER_SECOND), 1.0); - - frame->predict_to = ready_sim_tick + 10; - - ////////////////////////////// - //- Create player control - - if (frame->predict_to > prev_frame->predict_to) - { - i64 control_tick = frame->predict_to; - - P_Control control = Zi; - control.tick = control_tick; - control.orig_tick = control_tick; - control.move = frame->move; - control.look = frame->look; - control.fire_held = frame->fire_held; - control.fire_presses = frame->fire_presses; - - //- Fill controls buffer backwards - i64 max_fill_count = SIM_TICKS_PER_SECOND / 4; - for (i64 dst_tick = control_tick; dst_tick > MaxI64(prev_frame->predict_to, (dst_tick - max_local_controls)); --dst_tick) - { - P_Control *dst_control = &local_controls[dst_tick % max_local_controls]; - if (dst_tick > dst_control->orig_tick) - { - *dst_control = control; - dst_control->tick = dst_tick; - dst_control->orig_tick = control_tick; - } - else - { - break; - } - } - } - - ////////////////////////////// - //- Send player snapshots - - { - i64 snapshot_send_threshold_ns = NsFromSeconds(1.0) / SIM_TICKS_PER_SECOND; - if (prev_snapshot_sent_at_ns == 0 || (frame->time_ns - prev_snapshot_sent_at_ns) > snapshot_send_threshold_ns) - { - // FIXME: Base this on ping - i64 max_control_sends = SIM_TICKS_PER_SECOND; - - //- Pack control deltas - Struct(PackedDeltaNode) - { - PackedDeltaNode *next; - String packed; - }; - i64 total_delta_bytes = 0; - PackedDeltaNode *first_delta_node = 0; - PackedDeltaNode *last_delta_node = 0; - if (frame->predict_to >= ready_sim_tick) - { - i64 last_send_tick = frame->predict_to; - i64 first_send_tick = MaxI64(ready_sim_tick, remote_ack + 1); - first_send_tick = MaxI64(first_send_tick, last_send_tick - max_control_sends); - for (i64 send_tick = first_send_tick; send_tick <= last_send_tick; ++send_tick) - { - b32 should_transmit = 0; - BB_ResetWriter(&packer_bbw); - u64 delta_start = BB_GetNumBytesWritten(&packer_bbw); - { - // TODO: Delta compress - should_transmit = 1; - P_Control *control = &local_controls[send_tick % max_local_controls]; - BB_WriteBytes(&packer_bbw, StringFromStruct(control)); - } - u64 delta_end = BB_GetNumBytesWritten(&packer_bbw); - if (should_transmit) - { - String packed = Zi; - packed.text = BB_GetWrittenRaw(&packer_bbw) + delta_start; - packed.len = delta_end - delta_start; - { - PackedDeltaNode *delta_node = PushStruct(frame->arena, PackedDeltaNode); - delta_node->packed = PushString(frame->arena, packed); - SllQueuePush(first_delta_node, last_delta_node, delta_node); - total_delta_bytes += packed.len; - } - } - } - } - - //- Collect & send snapshots - { - u64 snapshot_cutoff_threshold = NET_PacketSize / 2; - PackedDeltaNode *delta_node = first_delta_node; - - u64 snapshot_start = 0; - b32 new_snapshot = 1; - b32 done = 0; - while (!done) - { - PackedDeltaNode *next_delta_node = delta_node ? delta_node->next : 0; - { - //- Init snapshot - if (new_snapshot) - { - new_snapshot = 0; - BB_ResetWriter(&packer_bbw); - snapshot_start = BB_GetNumBytesWritten(&packer_bbw); - BB_WriteBit(&packer_bbw, 1); // Raw - BB_WriteIV(&packer_bbw, ready_sim_tick); - } - - //- Append packed delta - BB_WriteAlignToNextByte(&packer_bbw); - if (delta_node) - { - BB_WriteBytes(&packer_bbw, delta_node->packed); - } - - //- Submit snapshot - { - if (!delta_node || !next_delta_node) - { - new_snapshot = 1; - done = 1; - } - u64 next_len = 0; - if (next_delta_node) - { - next_len = next_delta_node->packed.len; - } - u64 cur_snapshot_len = BB_GetNumBytesWritten(&packer_bbw); - if ((cur_snapshot_len - snapshot_start) + next_len >= snapshot_cutoff_threshold) - { - new_snapshot = 1; - } - } - if (new_snapshot) - { - u64 snapshot_end = BB_GetNumBytesWritten(&packer_bbw); - String snapshot = Zi; - snapshot.text = BB_GetWrittenRaw(&packer_bbw) + snapshot_start; - snapshot.len = snapshot_end - snapshot_start; - NET_Send(net_pipe, frame->sim_key, snapshot, NET_SendFlag_Raw); - - // Sanity check to catch whenever we end up packing too much information - // into a single raw snapshot, causing it to drop. This isn't necessarily - // an error, but signals we may have botched the packing code or need tighter - // compression - Assert(snapshot.len <= NET_PacketSize); - } - } - delta_node = next_delta_node; - } - } - - prev_snapshot_sent_at_ns = frame->time_ns; - } - } - - ////////////////////////////// - //- Predict - - - // TODO: Only predict when new sim snapshot is received - - - - - // P_Frame *predict_frame = &P_NilFrame; - // { - // if (predict_world->tiles_hash != sim_world->tiles_hash) - // { - // predict_world->tiles_hash = sim_world->tiles_hash; - // CopyStructs(predict_world->tiles, sim_world->tiles, P_TilesCount); - // } - // predict_world->seed = sim_world->seed; - - // P_ClearFrames(predict_world, I64Min, I64Max); - // predict_frame = P_PushFrame(predict_world, sim_world->last_frame, sim_world->last_frame->tick); - // P_DebugDrawFrame(predict_frame); - - // predict_frame = predict_world->last_frame; - // } - - - - - - - - - - - - - - - i64 first_predict_tick = 0; - i64 last_predict_tick = 0; - { - // FIXME: Not like this - i64 max_predict_ticks = SIM_TICKS_PER_SECOND; - last_predict_tick = frame->predict_to; - first_predict_tick = ready_sim_tick - 2; - first_predict_tick = MaxI64(first_predict_tick, last_predict_tick - max_predict_ticks); - } - - // Predict - P_Frame *predict_frame = predict_world->last_frame; - if (frame->predict_to != prev_frame->predict_to) - { - if (predict_world->tiles_hash != sim_world->tiles_hash) - { - predict_world->tiles_hash = sim_world->tiles_hash; - CopyStructs(predict_world->tiles, sim_world->tiles, P_TilesCount); - } - predict_world->seed = sim_world->seed; - - P_ClearFrames(predict_world, I64Min, first_predict_tick - 1); - predict_frame = P_PushFrame(predict_world, P_FrameFromTick(sim_world, first_predict_tick), first_predict_tick); - - for (i64 predict_tick = first_predict_tick + 1; predict_tick <= last_predict_tick; ++predict_tick) - { - predict_frame = P_PushFrame(predict_world, predict_world->last_frame, predict_tick); - P_Ent *predict_player = P_EntFromKey(predict_frame, V.player_key); - if (!P_IsEntNil(predict_player)) - { - P_Control *predict_control = &local_controls[predict_tick % max_local_controls]; - if (predict_control->tick == predict_tick) - { - predict_player->control = *predict_control; - } - } - // for (P_Ent *ent = P_FirstEnt(predict_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) - // { - // if (ent != predict_player) - // { - // ZeroStruct(&ent->control); - // } - // } - - b32 old_debug_draw = P_tl.debug_draw_enabled; - Vec4 old_debug_tint = P_tl.debug_tint; - { - P_tl.debug_draw_enabled = TweakBool("Debug draw intermediate prediction steps", 0); - P_tl.debug_tint = VEC4(0.8, 0, 0, 0.25); - P_StepFrame(predict_frame); - } - P_tl.debug_draw_enabled = old_debug_draw; - P_tl.debug_tint = old_debug_tint; - } - - predict_frame = predict_world->last_frame; - // P_DebugDrawFrame(predict_frame); - - // TODO: Extract information that occurred between first & last prediction, like bullet hits etc? - } - - - - - - - - - - - - // // Predict - // P_Frame *predict_frame = &P_NilFrame; - // { - // if (predict_world->tiles_hash != sim_world->tiles_hash) - // { - // predict_world->tiles_hash = sim_world->tiles_hash; - // CopyStructs(predict_world->tiles, sim_world->tiles, P_TilesCount); - // } - // predict_world->seed = sim_world->seed; - - - - // // P_ClearFrames(predict_world, I64Min, I64Max); - // // predict_frame = P_PushFrame(predict_world, sim_world->last_frame, sim_world->last_frame->tick); - - // // FIXME: Not like this - // i64 max_predict_ticks = SIM_TICKS_PER_SECOND; - - // i64 last_predict_tick = frame->predict_to; - // i64 first_predict_tick = sim_world->last_frame->tick; - // first_predict_tick = MaxI64(first_predict_tick, last_predict_tick - max_predict_ticks); - - // P_ClearFrames(predict_world, I64Min, first_predict_tick - 1); - // predict_frame = P_PushFrame(predict_world, sim_world->last_frame, first_predict_tick); - - // for (i64 predict_tick = first_predict_tick + 1; predict_tick <= last_predict_tick; ++predict_tick) - // { - // predict_frame = P_PushFrame(predict_world, predict_world->last_frame, predict_tick); - - // P_Ent *predict_player = P_EntFromKey(predict_frame, local_player->key); - // if (!P_IsEntNil(predict_player)) - // { - // P_Control *predict_control = &local_controls[predict_tick % max_local_controls]; - // if (predict_control->tick == predict_tick) - // { - // predict_player->control = *predict_control; - // } - // } - - // P_StepFrame(predict_frame); - // } - - // predict_frame = predict_world->last_frame; - // P_DebugDrawFrame(predict_frame); - - // // TODO: Extract information that occurred between first & last prediction, like bullet hits etc? - // } - - - - - - - - // ////////////////////////////// - // //- Update corrected world - - - - // P_Frame *correct_frame = &P_NilFrame; - // { - // if (correct_world->tiles_hash != predict_world->tiles_hash) - // { - // correct_world->tiles_hash = predict_world->tiles_hash; - // CopyStructs(correct_world->tiles, predict_world->tiles, P_TilesCount); - // tiles_dirty = 1; - // } - // correct_world->seed = predict_world->seed; - - // P_ClearFrames(correct_world, I64Min, I64Max); - // correct_frame = P_PushFrame(correct_world, predict_world->last_frame, predict_world->last_frame->tick); - // } - - - - - - // // P_Frame *correct_frame = correct_world->last_frame; - // // { - // // if (correct_world->tiles_hash != predict_world->tiles_hash) - // // { - // // correct_world->tiles_hash = predict_world->tiles_hash; - // // CopyStructs(correct_world->tiles, predict_world->tiles, P_TilesCount); - // // } - // // correct_world->seed = predict_world->seed; - - // // if (correct_world->last_frame->tick != predict_frame->tick) - // // { - // // correct_frame = P_PushFrame(correct_world, correct_world->last_frame, predict_frame->tick); - // // } - - // // // P_ClearFrames(correct_world, I64Min, correct_world->last_frame->tick - 1); - // // // P_Frame *prev_correct_frame = correct_world->last_frame; - - // // { - // // f32 correction_rate = 1; - // // // f32 correction_rate = 30 * frame->dt; - // // P_EntList spawn_ents = Zi; - // // for (P_Ent *predict_ent = P_FirstEnt(predict_frame); !P_IsEntNil(predict_ent); predict_ent = P_NextEnt(predict_ent)) - // // { - // // P_Ent *correct_ent = P_EntFromKey(correct_frame, predict_ent->key); - // // if (P_IsEntNil(correct_ent)) - // // { - // // correct_ent = P_PushTempEnt(frame->arena, &spawn_ents); - // // *correct_ent = *predict_ent; - // // } - // // else - // // { - // // // TODO: Unified blend logic between local world & correct world - // // correct_ent->xf = LerpXform(correct_ent->xf, predict_ent->xf, correction_rate); - // // correct_ent->solved_v = LerpVec2(correct_ent->solved_v, predict_ent->solved_v, correction_rate); - // // correct_ent->solved_w = LerpF32(correct_ent->solved_w, predict_ent->solved_w, correction_rate); - // // } - // // } - // // P_SpawnEntsFromList(correct_frame, spawn_ents); - // // } - - // // // Prune ents - // // { - // // i64 ents_to_prune_count = 0; - // // P_Ent **ents_to_prune = PushStructsNoZero(frame->arena, P_Ent *, correct_frame->ents_count); - // // for (P_Ent *ent = P_FirstEnt(correct_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) - // // { - // // if (ent->exists <= 0 || P_IsEntNil(P_EntFromKey(predict_frame, ent->key))) - // // { - // // ents_to_prune[ents_to_prune_count] = ent; - // // ents_to_prune_count += 1; - // // } - // // } - // // for (i64 prune_idx = 0; prune_idx < ents_to_prune_count; ++prune_idx) - // // { - // // P_Ent *ent = ents_to_prune[prune_idx]; - // // P_EntBin *bin = &correct_frame->ent_bins[ent->key.v % correct_frame->ent_bins_count]; - // // DllQueueRemoveNP(bin->first, bin->last, ent, next_in_bin, prev_in_bin); - // // DllQueueRemoveNPZ(&P_NilEnt, correct_frame->first_ent, correct_frame->last_ent, ent, next, prev); - // // correct_frame->ents_count -= 1; - // // SllStackPush(correct_world->first_free_ent, ent); - // // } - // // } - - - // // // P_Frame *prev_correct_frame = P_PushFrame(correct_world, predict_world->last_frame, predict_world->last_frame->tick); - - // // P_DebugDrawFrame(correct_frame); - // // } - - - - - - ////////////////////////////// - //- Update local world - - // TODO: Remove this - P_Frame *local_frame = &P_NilFrame; - { - if (local_world->tiles_hash != predict_world->tiles_hash) - { - local_world->tiles_hash = predict_world->tiles_hash; - CopyStructs(local_world->tiles, predict_world->tiles, P_TilesCount); - tiles_dirty = 1; - } - local_world->seed = predict_world->seed; - - P_ClearFrames(local_world, I64Min, I64Max); - local_frame = P_PushFrame(local_world, predict_world->last_frame, predict_world->last_frame->tick); - - P_DebugDrawFrame(local_frame); - } - - - - - - - // { - // i64 delay_ns = NsFromSeconds(100); - - // P_Frame *right_frame = &P_NilFrame; - // P_Frame *left_frame = &P_NilFrame; - - // for (P_Frame *tmp = - - - - - // P_Frame *right_frame = predict_world->last_frame; - // P_Frame *left_frame = predict_world->left_frame; - // } - - - - - - - - - - - - - ////////////////////////////// - //- Push test bullet particles - - // TODO: Not like this - - - - - for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) - { - if (bullet->is_bullet) - { - // FIXME: Truncate bullet trail at hit - Vec2 start = bullet->bullet_start; - Vec2 end = bullet->bullet_end; - - b32 skip = 0; - if (bullet->has_hit) - { - Vec2 hit_pos = bullet->hit_entry; - if (DotVec2(SubVec2(hit_pos, start), SubVec2(end, start)) < 0) - { - skip = 1; - } - // V_DrawPoint(MulXformV2(frame->xf.world_to_ui, start), Color_Red); - // V_DrawPoint(MulXformV2(frame->xf.world_to_ui, end), Color_Purple); - end = hit_pos; - } - - if (!skip) - { - f32 trail_len = Vec2Len(SubVec2(end, start)); - // f32 particles_count = MaxF32(trail_len * 10000, 1); - // f32 particles_count = MaxF32(trail_len * 512, 1); - // f32 particles_count = MaxF32(trail_len * 64, 1); - f32 particles_count = MaxF32(trail_len * 32, 1); - - - f32 angle = AngleFromVec2(PerpVec2(SubVec2(end, start))); - // f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start))); - - V_Emitter emitter = Zi; - { - // emitter.flags |= V_ParticleFlag_StainOnPrune; - // emitter.flags |= V_ParticleFlag_StainTrail; - - // emitter.lifetime = 1; - // emitter.lifetime_spread = 2; - - emitter.count = particles_count; - - // emitter.lifetime = 1; - // emitter.lifetime = 0.15; - emitter.lifetime = 0.075; - // emitter.lifetime = 0.05; - // emitter.lifetime = 0.04; - emitter.lifetime_spread = emitter.lifetime * 2; - - emitter.angle = angle; - // emitter.angle_spread = Tau / 4; - emitter.angle_spread = Tau / 4; - - emitter.start = start; - emitter.end = end; - - // emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1)); - - // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.8, 0.8, 0.25)); - emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1)); - // emitter.color_spread = LinearFromSrgb(VEC4(0, 0, 0, 0.2)); - - emitter.speed = 0; - emitter.speed_spread = 1; - - // emitter.speed = 1; - // emitter.speed_spread = 1; - - // emitter.velocity_falloff = 1; - // emitter.velocity_falloff_spread = 0; - } - V_PushParticles(emitter); - } - - - } - } - - - - - - // { - // PERSIST Vec2 start = {5, 5}; - // PERSIST Vec2 end = {3, -20}; - - - // b32 skip = 1; - // if (frame->held_buttons[Button_G]) - // { - // end = frame->world_cursor; - // if (!prev_frame->held_buttons[Button_G]) - // { - // start = end; - // } - // skip = 0; - // } - - // Vec2 vel = SubVec2(end, start); - - - // f32 trail_len = Vec2Len(SubVec2(end, start)); - // // f32 particles_count = MaxF32(trail_len * 10000, 1); - // // f32 particles_count = MaxF32(trail_len * 512, 1); - // // f32 particles_count = MaxF32(trail_len * 64, 1); - // f32 particles_count = MaxF32(trail_len * 32, 1); - - - // f32 angle = AngleFromVec2(PerpVec2(SubVec2(end, start))); - // // f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start))); - - // V_Emitter *emitter = V_PushEmitter(particles_count); - - // // emitter->flags |= V_ParticleFlag_StainOnPrune; - // // emitter->flags |= V_ParticleFlag_StainTrail; - - // // emitter->lifetime = 1; - // // emitter->lifetime_spread = 2; - - // emitter->lifetime = 0.25; - // emitter->lifetime_spread = emitter->lifetime * 2; - - // emitter->angle = angle; - // // emitter->angle_spread = Tau / 4; - // emitter->angle_spread = Tau / 4; - - // emitter->start = start; - // emitter->end = end; - - // // emitter->color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1)); - - // // emitter->color_lin = LinearFromSrgb(VEC4(0.8, 0.8, 0.8, 0.25)); - // emitter->color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1)); - // // emitter->color_spread = LinearFromSrgb(VEC4(0, 0, 0, 0.2)); - - // emitter->speed = 0; - // emitter->speed_spread = 1; - - // // emitter->speed = 1; - // // emitter->speed_spread = 1; - - // // emitter->velocity_falloff = 1; - // // emitter->velocity_falloff_spread = 0; - - // start = end; - // end = AddVec2(end, vel); - // } - - - - - - - - - - - - - - - - - - - - - - ////////////////////////////// - //- Push test blood particles - - // TODO: Not like this - - - - - - if (0) - { - for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) - { - if (bullet->is_bullet && bullet->has_hit) - { - // Vec2 bullet_start = bullet->start; - // Vec2 bullet_end = bullet->end; - - Vec2 hit_entry = bullet->hit_entry; - Vec2 hit_entry_normal = bullet->hit_entry_normal; - Vec2 bullet_vel = SubVec2(bullet->bullet_end, bullet->bullet_start); - - V_DrawLine(bullet->bullet_start, bullet->bullet_end, Color_Cyan); - - V_ParticleFlag flags = 0; - flags |= V_ParticleFlag_PruneWhenStill; - flags |= V_ParticleFlag_StainOnPrune; - if (TweakBool("Emitter stain trail", 1)) - { - flags |= V_ParticleFlag_StainTrail; - } - // f32 count = TweakFloat("Emitter count", 50, 0, 10000); - f32 count = TweakFloat("Emitter count", 20, 1, 1000); - f32 speed = TweakFloat("Emitter speed", 20, 0, 100); - f32 falloff = TweakFloat("Emitter falloff", 50, 0, 100); - f32 angle_spread = TweakFloat("Emitter angle spread", 0.1, 0, 1) * Tau; - - V_Emitter emitter = Zi; - { - emitter.count = count; - emitter.flags = flags; - - // Vec2 dir = hit_entry_normal; - Vec2 dir = NormVec2(NegVec2(bullet_vel)); - - emitter.start = hit_entry; - emitter.end = emitter.start; - - emitter.speed = speed; - emitter.speed_spread = speed * 2; - - emitter.velocity_falloff = falloff; - emitter.velocity_falloff_spread = falloff * 1.5; - - emitter.angle = AngleFromVec2(dir); - // emitter.angle_spread = Tau / 4; - emitter.angle_spread = angle_spread; - // emitter.angle_spread = Tau / 32; - - // emitter.color_lin = LinearFromSrgb(VEC4(0.5, 0.1, 0.1, 1)); - emitter.color_lin = LinearFromSrgb(VEC4(0.5, 0.1, 0.1, 0.5)); - - // emitter.color_spread = VEC4(0.1, 0, 0, 0); - emitter.color_spread = VEC4(0.1, 0, 0, 0.5); - - // emitter.color = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1)); - - // emitter.angle_spread = 1; - // emitter.angle_spread = 0.5; - // emitter.angle_spread = Tau; - } - V_PushParticles(emitter); - - // V_DrawPoint(victim_raycast.p, Color_Green); - // V_DrawLine(victim_raycast.p, AddVec2(victim_raycast.p, MulVec2(victim_raycast.normal, 0.5)), Color_White); - } - } - } - - - - - - - - - - // { - // for (P_Ent *firer = P_FirstEnt(local_frame); firer->valid; firer = P_NextEnt(firer)) - // { - // if (firer->fire_held) - // { - // Xform firer_xf = firer->xf; - // P_Shape firer_world_shape = P_MulXformShape(firer_xf, firer->local_shape); - - // Vec2 ray_start = firer_world_shape.centroid; - // Vec2 ray_dir = firer->look; - - // // TODO: Real raycast query - // P_Ent *closest_victim = &P_NilEnt; - // P_RaycastResult victim_raycast = Zi; - // { - // f32 closest_len_sq = Inf; - // for (P_Ent *victim = P_FirstEnt(local_frame); victim->valid; victim = P_NextEnt(victim)) - // { - // if (victim != firer) - // { - // Xform victim_xf = victim->xf; - // P_Shape victim_world_shape = P_MulXformShape(victim_xf, victim->local_shape); - - // P_RaycastResult raycast = P_RaycastShape(victim_world_shape, ray_start, ray_dir); - // if (raycast.is_intersecting) - // { - // f32 len_sq = Vec2LenSq(SubVec2(raycast.p, ray_start)); - // if (len_sq < closest_len_sq) - // { - // closest_len_sq = len_sq; - // closest_victim = victim; - // victim_raycast = raycast; - // } - // } - // } - // } - // } - - // if (closest_victim->valid) - // { - // V_ParticleFlag flags = 0; - // flags |= V_ParticleFlag_PruneWhenStill; - // flags |= V_ParticleFlag_StainOnPrune; - // if (TweakBool("Emitter stain trail", 1)) - // { - // flags |= V_ParticleFlag_StainTrail; - // } - // // f32 count = TweakFloat("Emitter count", 50, 0, 10000); - // f32 count = TweakFloat("Emitter count", 50, 1, 1000); - // f32 speed = TweakFloat("Emitter speed", 20, 0, 100); - // f32 falloff = TweakFloat("Emitter falloff", 50, 0, 100); - // f32 angle_spread = TweakFloat("Emitter angle spread", 0.1, 0, 1) * Tau; - - // V_Emitter *emitter = V_PushEmitter(count); - // emitter->flags = flags; - - // Vec2 dir = victim_raycast.normal; - // emitter->start = victim_raycast.p; - // emitter->end = emitter->start; - - // emitter->speed = speed; - // emitter->speed_spread = speed * 2; - - // emitter->velocity_falloff = falloff; - // emitter->velocity_falloff_spread = falloff * 1.5; - - // emitter->angle = AngleFromVec2(dir); - // // emitter->angle_spread = Tau / 4; - // emitter->angle_spread = angle_spread; - // // emitter->angle_spread = Tau / 32; - - // emitter->color_lin = LinearFromSrgb(VEC4(0.5, 0.1, 0.1, 1)); - // emitter->color_spread = VEC4(0.1, 0, 0, 0); - - // // emitter->color = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1)); - - // emitter->seed = RandU64FromState(&frame->rand); - // // emitter->angle_spread = 1; - // // emitter->angle_spread = 0.5; - // // emitter->angle_spread = Tau; - - // // V_DrawPoint(victim_raycast.p, Color_Green); - // // V_DrawLine(victim_raycast.p, AddVec2(victim_raycast.p, MulVec2(victim_raycast.normal, 0.5)), Color_White); - // } - - - // // for (P_QueryResult query = P_FirstRaycast(wrold, ray_start, ray_dir); query. - // // P_RaycastWorldResult hits = P_RaycastWorld(local_frame, ray_start, ray_dir) - // // { - // // } - // } - // } - // } - - ////////////////////////////// - //- Push test emitter - - if (frame->held_buttons[Button_F]) - { - V_Emitter emitter = Zi; - - Vec2 dir = frame->look; - emitter.start = frame->world_cursor; - emitter.end = emitter.start; - emitter.angle = AngleFromVec2(dir); - - // emitter.flags |= V_ParticleFlag_StainTrail; - - emitter.count = 128; - // emitter.count = 100; - emitter.speed = 10; - - emitter.color_lin = LinearFromSrgb(Color_Yellow); - - emitter.speed_spread = 1; - // emitter.angle_spread = 1; - // emitter.angle_spread = 0.5; - emitter.angle_spread = Tau / 4; - // emitter.angle_spread = Tau; - - V_PushParticles(emitter); - } - - ////////////////////////////// - //- Debug draw - - { - // Merge vis debug draws with sim debug draws - P_DebugDrawNode *first_debug_draw_node = first_sim_debug_draw_node; - P_DebugDrawNode *last_debug_draw_node = last_sim_debug_draw_node; - if (P_tl.first_debug_draw_node) - { - if (last_debug_draw_node) - { - last_debug_draw_node->next = P_tl.first_debug_draw_node; - } - else - { - first_debug_draw_node = P_tl.first_debug_draw_node; - } - last_debug_draw_node = P_tl.last_debug_draw_node; - } - - // Push draws - for (P_DebugDrawNode *n = first_debug_draw_node; n; n = n->next) - { - Vec4 color = Vec4FromU32(n->srgb32); - i32 detail = 24; - f32 radius = 5; - switch(n->kind) - { - case P_DebugDrawKind_Point: - { - Vec2 ui_p = MulXformV2(frame->xf.world_to_ui, n->point.p); - V_DrawPoint(ui_p, color); - } break; - - case P_DebugDrawKind_Line: - { - Vec2 ui_p0 = MulXformV2(frame->xf.world_to_ui, n->line.p0); - Vec2 ui_p1 = MulXformV2(frame->xf.world_to_ui, n->line.p1); - V_DrawLine(ui_p0, ui_p1, color); - } break; - - case P_DebugDrawKind_Rect: - { - Rng2 ui_rect = Zi; - ui_rect.p0 = MulXformV2(frame->xf.world_to_ui, n->rect.p0); - ui_rect.p1 = MulXformV2(frame->xf.world_to_ui, n->rect.p1); - V_DrawRect(ui_rect, color, V_DrawFlag_Line); - } break; - - case P_DebugDrawKind_Shape: - { - P_Shape ui_shape = P_MulXformShape(frame->xf.world_to_ui, n->shape); - V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line); - } break; - } - } - - // Reset vis debug draws - ResetArena(P_tl.debug_arena); - P_tl.first_debug_draw_node = 0; - P_tl.last_debug_draw_node = 0; - P_tl.debug_draw_nodes_count = 0; - } - ////////////////////////////// //- Render