From 746ef6913b51b260c55b6e70479e46d5a3792a79 Mon Sep 17 00:00:00 2001 From: jacob Date: Sun, 18 Jan 2026 18:14:46 -0600 Subject: [PATCH] client -> server control snapshots --- src/gpu/gpu_dx12/gpu_dx12_core.c | 7 +- src/net/net_win32/net_win32.c | 1 - src/pp/pp.c | 102 ++++++++----- src/pp/pp.h | 51 +------ src/pp/pp_sim/pp_sim_core.c | 176 +++++++++++++++++++--- src/pp/pp_sim/pp_sim_core.h | 7 +- src/pp/pp_vis/pp_vis_core.c | 245 +++++++++++++++++++++++++------ src/pp/pp_vis/pp_vis_gpu.g | 8 +- src/pp/pp_vis/pp_vis_gpu.gh | 8 +- src/ui/ui_gpu.g | 4 +- 10 files changed, 452 insertions(+), 157 deletions(-) diff --git a/src/gpu/gpu_dx12/gpu_dx12_core.c b/src/gpu/gpu_dx12/gpu_dx12_core.c index dcbb6b25..f4e93d96 100644 --- a/src/gpu/gpu_dx12/gpu_dx12_core.c +++ b/src/gpu/gpu_dx12/gpu_dx12_core.c @@ -442,7 +442,12 @@ D3D12_BARRIER_LAYOUT G_D12_BarrierLayoutFromLayout(G_Layout layout) [G_Layout_ComputeQueue_CopyRead] = D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_SOURCE, [G_Layout_ComputeQueue_CopyWrite] = D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_DEST, }; - return translate[layout]; + D3D12_BARRIER_LAYOUT result = D3D12_BARRIER_LAYOUT_UNDEFINED; + if (layout >= 0 && layout < countof(translate)) + { + result = translate[layout]; + } + return result; }; //////////////////////////////////////////////////////////// diff --git a/src/net/net_win32/net_win32.c b/src/net/net_win32/net_win32.c index db1c4e9f..16b404c1 100644 --- a/src/net/net_win32/net_win32.c +++ b/src/net/net_win32/net_win32.c @@ -103,7 +103,6 @@ u32 NET_W32_ChecksumFromPacketString(String str) // Skip the first 4 bytes of packet since it contains the checksum itself str.text += 4; str.len -= 4; - // https://en.wikipedia.org/wiki/Adler-32 u32 a = 1; u32 b = 0; diff --git a/src/pp/pp.c b/src/pp/pp.c index 471b3453..918f778c 100644 --- a/src/pp/pp.c +++ b/src/pp/pp.c @@ -1464,54 +1464,80 @@ void P_StepFrame(P_Frame *frame) // } ////////////////////////////// - //- Integrate control forces + //- Update guy controls from player controls - for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) + for (P_Ent *guy = P_FirstEnt(frame); !P_IsEntNil(guy); guy = P_NextEnt(guy)) { - // Xform xf = ent->xf; - // Xform desired_xf = xf; - // if (!IsVec2Zero(ent->control.look)) - // { - // desired_xf = XformWithWorldRotation(xf, AngleFromVec2(ent->control.look)); - // } - // f32 move_speed = TweakFloat("Guy move speed", 6.5, 0, 20); - // desired_xf.og = AddVec2(xf.og, MulVec2(ent->control.move, move_speed * sim_dt)); - - // Vec2 pos_diff = SubVec2(desired_xf.og, xf.og); - // f32 angle_diff = UnwindAngleF32(RotationFromXform(desired_xf) - RotationFromXform(xf)); - - // ent->solved_v = pos_diff; - // ent->solved_w = angle_diff; - + if (guy->is_guy) { - // f32 damp_vel = damp_force * sim_dt; - - if (Vec2Len(ent->solved_v) > 0.001) - { - f32 damp_force = TweakFloat("Guy damp force", 50, 0, 100); - Vec2 damp = MulVec2(NegVec2(ent->solved_v), damp_force * sim_dt); - ent->solved_v = AddVec2(ent->solved_v, damp); - } - else - { - ent->solved_v = VEC2(0, 0); - } - + ZeroStruct(&guy->control); } - + } + for (P_Ent *player = P_FirstEnt(frame); !P_IsEntNil(player); player = P_NextEnt(player)) + { + if (player->is_player) { - f32 move_force = TweakFloat("Guy move force", 400, 0, 400); - f32 max_speed = TweakFloat("Guy max speed", 10, 0, 20); + P_Ent *guy = P_EntFromKey(frame, player->guy); + if (!P_IsEntNil(guy)) + { + guy->control = player->control; + } + } + } - Vec2 new_velocity = ent->solved_v; - new_velocity = AddVec2(new_velocity, MulVec2(ent->control.move, move_force * sim_dt)); + ////////////////////////////// + //- Integrate guy control forces - // if (Vec2Len(new_velocity) > max_speed) + for (P_Ent *guy = P_FirstEnt(frame); !P_IsEntNil(guy); guy = P_NextEnt(guy)) + { + if (guy->is_guy) + { + P_Control control = guy->control; + + // Xform xf = guy->xf; + // Xform desired_xf = xf; + // if (!IsVec2Zero(control.look)) // { - // new_velocity = Vec2WithLen(new_velocity, max_speed); + // desired_xf = XformWithWorldRotation(xf, AngleFromVec2(control.look)); // } + // f32 move_speed = TweakFloat("Guy move speed", 6.5, 0, 20); + // desired_xf.og = AddVec2(xf.og, MulVec2(control.move, move_speed * sim_dt)); - ent->solved_v = new_velocity; + // Vec2 pos_diff = SubVec2(desired_xf.og, xf.og); + // f32 angle_diff = UnwindAngleF32(RotationFromXform(desired_xf) - RotationFromXform(xf)); + + // guy->solved_v = pos_diff; + // guy->solved_w = angle_diff; + + { + // f32 damp_vel = damp_force * sim_dt; + + if (Vec2Len(guy->solved_v) > 0.001) + { + f32 damp_force = TweakFloat("Guy damp force", 50, 0, 100); + Vec2 damp = MulVec2(NegVec2(guy->solved_v), damp_force * sim_dt); + guy->solved_v = AddVec2(guy->solved_v, damp); + } + else + { + guy->solved_v = VEC2(0, 0); + } + } + + { + f32 move_force = TweakFloat("Guy move force", 400, 0, 400); + f32 max_speed = TweakFloat("Guy max speed", 10, 0, 20); + + Vec2 new_velocity = guy->solved_v; + new_velocity = AddVec2(new_velocity, MulVec2(control.move, move_force * sim_dt)); + + // if (Vec2Len(new_velocity) > max_speed) + // { + // new_velocity = Vec2WithLen(new_velocity, max_speed); + // } + + guy->solved_v = new_velocity; + } } } diff --git a/src/pp/pp.h b/src/pp/pp.h index ae8f1f06..6f216a31 100644 --- a/src/pp/pp.h +++ b/src/pp/pp.h @@ -74,6 +74,9 @@ Struct(P_DebugDrawNode) Struct(P_Control) { + i64 tick; + i64 orig_tick; // Will differ from tick if this control was propagated from the control of another tick + Vec2 move; Vec2 look; f32 fire_held; @@ -119,6 +122,8 @@ Struct(P_Ent) Vec2 hit_entry; Vec2 hit_entry_normal; + //- Player / Guy + P_Control control; //- Player @@ -253,52 +258,6 @@ Struct(P_World) u8 *tiles; }; -//////////////////////////////////////////////////////////// -//~ Sim snapshot types - -Enum(P_SimDeltaKind) -{ - P_SimDeltaKind_None, - P_SimDeltaKind_RawEnt, -}; - -Struct(P_SimDelta) -{ - P_SimDeltaKind kind; - P_Ent ent; -}; - -Struct(P_SimDeltaNode) -{ - P_SimDeltaNode *next; - P_SimDelta delta; -}; - -Struct(P_SimSnapshot) -{ - u64 world_seed; - i64 src_tick; - i64 tick; - i64 time_ns; - - i64 deltas_count; - P_SimDeltaNode *first_delta_node; - P_SimDeltaNode *last_delta_node; -}; - -//////////////////////////////////////////////////////////// -//~ Player snapshot types - -Struct(P_PlayerSnapshot) -{ - i64 tick; - - Vec2 move; - Vec2 look; - b32 fire_held; - i32 fire_presses; -}; - //////////////////////////////////////////////////////////// //~ Message types diff --git a/src/pp/pp_sim/pp_sim_core.c b/src/pp/pp_sim/pp_sim_core.c index d0c08849..7661cf11 100644 --- a/src/pp/pp_sim/pp_sim_core.c +++ b/src/pp/pp_sim/pp_sim_core.c @@ -202,26 +202,35 @@ void S_TickForever(WaveLaneCtx *lane) if (S_IsClientNil(client)) { String address_str = NET_StringFromKey(frame_arena, net_key); - u64 hash = NET_HashFromKey(net_key); - S_ClientBin *bin = &S.client_bins[hash % S.client_bins_count]; String orig_src_name = msg->data; String src_name = TrimWhitespace(orig_src_name); - if (src_name.len < P_MinPlayerNameLen) { - src_name = Lit("Player"); + if (src_name.len < P_MinPlayerNameLen) + { + src_name = Lit("Player"); + } + src_name.len = MinI64(src_name.len, P_MaxPlayerNameLen); } - src_name.len = MinI64(src_name.len, P_MaxPlayerNameLen); // FIXME: Freelist client client = PushStruct(perm, S_Client); - client->hash = hash; - client->net_key = net_key; - client->name_len = MinI64(countof(client->name_text), src_name.len); - CopyBytes(client->name_text, src_name.text, client->name_len); + { + u64 hash = NET_HashFromKey(net_key); + S_ClientBin *bin = &S.client_bins[hash % S.client_bins_count]; + + client->hash = hash; + client->net_key = net_key; + client->name_len = MinI64(countof(client->name_text), src_name.len); + CopyBytes(client->name_text, src_name.text, client->name_len); + + client->max_controls = SIM_MAX_PING * SIM_TICKS_PER_SECOND; + client->controls = PushStructs(perm, P_Control, client->max_controls); + + DllQueuePushNPZ(&S_NilClient, S.first_client, S.last_client, client, next, prev); + DllQueuePushNP(bin->first, bin->last, client, next_in_bin, prev_in_bin); + } - DllQueuePushNPZ(&S_NilClient, S.first_client, S.last_client, client, next, prev); - DllQueuePushNP(bin->first, bin->last, client, next_in_bin, prev_in_bin); // Acknowledge player connection { @@ -258,15 +267,15 @@ void S_TickForever(WaveLaneCtx *lane) P_EntList new_players = Zi; for (S_Client *client = S.first_client; !S_IsClientNil(client); client = client->next) { - if (P_IsKeyNil(client->player_key)) + if (P_IsKeyNil(client->player)) { - client->player_key = P_RandKey(); + client->player = P_RandKey(); } - P_Ent *player = P_EntFromKey(world_frame, client->player_key); + P_Ent *player = P_EntFromKey(world_frame, client->player); if (P_IsEntNil(player)) { player = P_PushTempEnt(frame_arena, &new_players); - player->key = client->player_key; + player->key = client->player; } player->is_player = 1; // Update name @@ -307,6 +316,136 @@ void S_TickForever(WaveLaneCtx *lane) P_SpawnEntsFromList(world_frame, new_guys); } + ////////////////////////////// + //- Read client 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) + { + S_Client *client = S_ClientFromNetKey(msg->src); + if (!S_IsClientNil(client)) + { + BB_Buff bb = BB_BuffFromString(msg->data); + BB_Reader bbr = BB_ReaderFromBuff(&bb); + + //- Read header + BB_ReadBit(&bbr); // Raw + i64 remote_ack = BB_ReadIV(&bbr); + + //- Read control + b32 done = 0; + while (!done) + { + // TODO: Delta compress + P_Control *raw_control = (P_Control *)BB_ReadBytesRaw(&bbr, sizeof(P_Control)); + if (raw_control) + { + i64 control_tick = raw_control->tick; + P_Control *control = &client->controls[control_tick % client->max_controls]; + { + *control = *raw_control; + } + + //- Propagate control + // + // In case future snapshots aren't received, the last-received control should be used, so we copy this control forward here + // + // TODO: Not like this + i64 max_propagate_count = SIM_TICKS_PER_SECOND / 4; + { + // Propagate forwards + for (i64 prop_tick = control_tick; prop_tick < prop_tick + max_propagate_count; ++prop_tick) + { + P_Control *prop_control = &client->controls[prop_tick % client->max_controls]; + if (prop_tick > prop_control->orig_tick) + { + *prop_control = *control; + prop_control->tick = prop_tick; + prop_control->orig_tick = control_tick; + } + else + { + break; + } + } + // Propagate backwards + for (i64 prop_tick = control_tick - 1; prop_tick > MaxI64(prop_tick - max_propagate_count, 0); --prop_tick) + { + P_Control *prop_control = &client->controls[prop_tick % client->max_controls]; + if (prop_tick > prop_control->orig_tick) + { + *prop_control = *control; + prop_control->tick = prop_tick; + prop_control->orig_tick = control_tick; + } + else + { + break; + } + } + } + + //- Update ack + if (control->tick < world_frame->tick) + { + // If the control is outdated, go ahead and just ack it + if (control->tick > client->ack) + { + client->ack = control->tick; + } + } + else + { + // Acknowledge the highest contiguously received control tick starting with the current frame + i64 max_ack_count = SIM_TICKS_PER_SECOND * SIM_MAX_PING; + i64 ack_tick = client->ack; + for (i64 check_ack_tick = world_frame->tick; check_ack_tick < world_frame->tick + max_ack_count; ++check_ack_tick) + { + P_Control *ack_control = &client->controls[check_ack_tick % client->max_controls]; + if (ack_control->orig_tick == check_ack_tick) + { + ack_tick = check_ack_tick; + } + else + { + break; + } + } + client->ack = ack_tick; + } + } + else + { + done = 1; + } + } + } + } + } + + ////////////////////////////// + //- Apply client controls + + for (S_Client *client = S.first_client; !S_IsClientNil(client); client = client->next) + { + P_Control *control = &client->controls[world_frame->tick % client->max_controls]; + + P_Ent *player = P_EntFromKey(world_frame, client->player); + if (!P_IsEntNil(player)) + { + player->control = *control; + } + } + + + + + + + + ////////////////////////////// //- Process connection messages @@ -703,7 +842,7 @@ void S_TickForever(WaveLaneCtx *lane) BB_ResetWriter(&packer_bbw); u64 delta_start = BB_GetNumBytesWritten(&packer_bbw); { - // TODO: Compress + // TODO: Delta compress should_transmit = 1; BB_WriteBytes(&packer_bbw, StringFromStruct(ent)); } @@ -722,7 +861,7 @@ void S_TickForever(WaveLaneCtx *lane) } } - // Collect deltas into snapshots + //- Collect & send snapshots { u64 max_snapshot_size = NET_PacketSize / 2; PackedDeltaNode *delta_node = first_delta_node; @@ -745,7 +884,8 @@ void S_TickForever(WaveLaneCtx *lane) BB_WriteIV(&packer_bbw, world_frame->tick); BB_WriteIV(&packer_bbw, world_frame->time_ns); BB_WriteUBits(&packer_bbw, world->seed, 64); - BB_WriteUBits(&packer_bbw, client->player_key.v, 64); + BB_WriteUBits(&packer_bbw, client->player.v, 64); + BB_WriteIV(&packer_bbw, client->ack); } //- Append packed delta diff --git a/src/pp/pp_sim/pp_sim_core.h b/src/pp/pp_sim/pp_sim_core.h index 335b254d..68cace82 100644 --- a/src/pp/pp_sim/pp_sim_core.h +++ b/src/pp/pp_sim/pp_sim_core.h @@ -12,11 +12,16 @@ Struct(S_Client) NET_Key net_key; P_MsgList out_msgs; - P_Key player_key; + P_Key player; i32 name_len; u8 name_text[P_MaxPlayerNameLen]; + i64 max_controls; + P_Control *controls; + + 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 b1747037..3462429f 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -360,7 +360,15 @@ void V_TickForever(WaveLaneCtx *lane) P_World *sim_world = P_AcquireWorld(); P_World *predict_world = P_AcquireWorld(); - P_World *blend_world = P_AcquireWorld(); + P_World *local_world = P_AcquireWorld(); + + i64 max_local_controls = SIM_MAX_PING * SIM_TICKS_PER_SECOND; + P_Control *local_controls = PushStructs(perm, P_Control, max_local_controls); + + i64 known_sim_tick = 0; + i64 remote_ack_received_at_ns = 0; + i64 remote_ack = 0; + i64 prev_snapshot_sent_at_ns = 0; Vec2I32 tiles_dims = VEC2I32(P_TilesPitch, P_TilesPitch); Vec2I32 cells_dims = VEC2I32(V_CellsPerMeter * P_WorldPitch, V_CellsPerMeter * P_WorldPitch); @@ -819,8 +827,8 @@ void V_TickForever(WaveLaneCtx *lane) Vec2 look_ratio = Zi; look_ratio.y = 0.5; look_ratio.x = look_ratio.y / (16.0 / 9.0); - P_Ent *player = P_EntFromKey(blend_world->last_frame, V.player_key); - P_Ent *guy = P_EntFromKey(blend_world->last_frame, player->guy); + P_Ent *player = P_EntFromKey(local_world->last_frame, V.player_key); + P_Ent *guy = P_EntFromKey(local_world->last_frame, player->guy); Vec2 guy_center = P_WorldShapeFromEnt(guy).centroid; Vec2 ui_center = MulVec2(frame->ui_dims, 0.5); Vec2 look = MulXformBasisV2(prev_frame->xf.ui_to_world, SubVec2(ui_frame->cursor_pos, ui_center)); @@ -929,11 +937,6 @@ void V_TickForever(WaveLaneCtx *lane) frame->is_selecting = 1; // frame->equipped_tile = P_TileKind_Floor; } - else if (m2_held) - { - frame->is_selecting = 1; - // frame->equipped_tile = P_TileKind_Empty; - } if (frame->is_selecting && prev_frame->is_selecting) { @@ -975,13 +978,13 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Query entities - P_Ent *local_player = P_EntFromKey(blend_world->last_frame, V.player_key); - P_Ent *local_guy = P_EntFromKey(blend_world->last_frame, local_player->guy); + P_Ent *local_player = P_EntFromKey(local_world->last_frame, V.player_key); + P_Ent *local_guy = P_EntFromKey(local_world->last_frame, local_player->guy); P_Ent *hovered_ent = &P_NilEnt; { // TODO: Real world query P_Shape cursor_shape = P_ShapeFromDesc(.count = 1, .points = { frame->world_cursor }); - for (P_Ent *ent = P_FirstEnt(blend_world->last_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) + for (P_Ent *ent = P_FirstEnt(local_world->last_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) { P_Shape ent_shape = P_WorldShapeFromEnt(ent); b32 is_hovered = P_CollisionResultFromShapes(ent_shape, cursor_shape).collision_points_count > 0; @@ -2320,9 +2323,9 @@ void V_TickForever(WaveLaneCtx *lane) UI_BuildLabelF("Predicted world tick: %F", FmtSint(predict_world->last_frame->tick)); UI_BuildLabelF("Predicted world entities: %F", FmtSint(predict_world->last_frame->ents_count)); UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); - UI_BuildLabelF("Blended world seed: 0x%F", FmtHex(blend_world->seed)); - UI_BuildLabelF("Blended world tick: %F", FmtSint(blend_world->last_frame->tick)); - UI_BuildLabelF("Blended world entities: %F", FmtSint(blend_world->last_frame->ents_count)); + UI_BuildLabelF("Local world seed: 0x%F", FmtHex(local_world->seed)); + UI_BuildLabelF("Local world tick: %F", FmtSint(local_world->last_frame->tick)); + UI_BuildLabelF("Local world entities: %F", FmtSint(local_world->last_frame->ents_count)); } UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); { @@ -2703,21 +2706,19 @@ void V_TickForever(WaveLaneCtx *lane) frame->desired_sim_key = NET_KeyFromString(Lit("127.0.0.1"), Lit("22121")); if (!NET_MatchKey(frame->sim_key, frame->desired_sim_key)) { - i64 now_ns = TimeNs(); - // i64 retry_rate_ns = NsFromSeconds(0.100); - i64 retry_rate_ns = NsFromSeconds(0); + 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 || now_ns - V.connect_try_ns > retry_rate_ns) + if (V.connect_try_ns == 0 || frame->time_ns - V.connect_try_ns > retry_rate_ns) { should_try = 1; } - // if (should_try && now_ns > NsFromSeconds(1.5)) if (should_try) { P_MsgNode tmp_msg_node = Zi; @@ -2733,7 +2734,7 @@ void V_TickForever(WaveLaneCtx *lane) String packed = P_PackMessages(&packer_bbw, tmp_msglist); NET_Send(net_pipe, frame->desired_sim_key, packed, NET_SendFlag_Raw); - V.connect_try_ns = now_ns; + V.connect_try_ns = frame->time_ns; } } @@ -2793,9 +2794,6 @@ void V_TickForever(WaveLaneCtx *lane) for (P_MsgNode *msg_node = in_msgs.first; msg_node; msg_node = msg_node->next) { P_Msg *msg = &msg_node->msg; - - - //- Snapshot if (msg->kind == P_MsgKind_Raw) { BB_Buff bb = BB_BuffFromString(msg->data); @@ -2808,6 +2806,14 @@ void V_TickForever(WaveLaneCtx *lane) 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; + } P_Frame *src_frame = P_FrameFromTick(sim_world, src_tick); if (src_frame->tick == src_tick) @@ -2828,11 +2834,12 @@ void V_TickForever(WaveLaneCtx *lane) b32 done = 0; while (!done && BB_NumBitsRemaining(&bbr) > 0) { - P_Ent *src_ent = (P_Ent *)BB_ReadBytesRaw(&bbr, sizeof(P_Ent)); - if (src_ent) + // 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, src_ent); + CopyStruct(&tmp_ent_node.ent, raw_ent); P_EntList ent_list = Zi; ent_list.first = &tmp_ent_node; ent_list.last = &tmp_ent_node; @@ -2933,14 +2940,168 @@ void V_TickForever(WaveLaneCtx *lane) } ////////////////////////////// - //- Push player snapshots + //- 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 = sim_world->last_frame->tick + MaxF64(CeilF64(ping * SIM_TICKS_PER_SECOND), 1.0); + + frame->predict_to = known_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 >= known_sim_tick) + { + i64 last_send_tick = frame->predict_to; + i64 first_send_tick = MaxI64(known_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 max_snapshot_size = 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, known_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 >= max_snapshot_size) + { + 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; + + // FIXME: Send raw snapshots + // NET_Send(net_pipe, frame->sim_key, snapshot, NET_SendFlag_Raw); + NET_Send(net_pipe, frame->sim_key, snapshot, NET_SendFlag_None); + } + } + delta_node = next_delta_node; + } + } + + prev_snapshot_sent_at_ns = frame->time_ns; + } + } + // if (frame->predict_to != prev_frame->predict_to) // { // { @@ -3031,21 +3192,21 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// - //- Update blended world + //- Update local world // TODO: Remove this - P_Frame *blend_frame = &P_NilFrame; + P_Frame *local_frame = &P_NilFrame; { - if (blend_world->tiles_hash != predict_world->tiles_hash) + if (local_world->tiles_hash != predict_world->tiles_hash) { - blend_world->tiles_hash = predict_world->tiles_hash; - CopyStructs(blend_world->tiles, predict_world->tiles, P_TilesCount); + local_world->tiles_hash = predict_world->tiles_hash; + CopyStructs(local_world->tiles, predict_world->tiles, P_TilesCount); tiles_dirty = 1; } - blend_world->seed = predict_world->seed; + local_world->seed = predict_world->seed; - P_ClearFrames(blend_world, I64Min, I64Max); - blend_frame = P_PushFrame(blend_world, predict_world->last_frame, predict_world->last_frame->tick); + P_ClearFrames(local_world, I64Min, I64Max); + local_frame = P_PushFrame(local_world, predict_world->last_frame, predict_world->last_frame->tick); } @@ -3087,7 +3248,7 @@ void V_TickForever(WaveLaneCtx *lane) - for (P_Ent *bullet = P_FirstEnt(blend_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) + for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) { if (bullet->is_bullet) { @@ -3267,7 +3428,7 @@ void V_TickForever(WaveLaneCtx *lane) if (0) { - for (P_Ent *bullet = P_FirstEnt(blend_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) + for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) { if (bullet->is_bullet && bullet->has_hit) { @@ -3344,7 +3505,7 @@ void V_TickForever(WaveLaneCtx *lane) // { - // for (P_Ent *firer = P_FirstEnt(blend_frame); firer->valid; firer = P_NextEnt(firer)) + // for (P_Ent *firer = P_FirstEnt(local_frame); firer->valid; firer = P_NextEnt(firer)) // { // if (firer->fire_held) // { @@ -3359,7 +3520,7 @@ void V_TickForever(WaveLaneCtx *lane) // P_RaycastResult victim_raycast = Zi; // { // f32 closest_len_sq = Inf; - // for (P_Ent *victim = P_FirstEnt(blend_frame); victim->valid; victim = P_NextEnt(victim)) + // for (P_Ent *victim = P_FirstEnt(local_frame); victim->valid; victim = P_NextEnt(victim)) // { // if (victim != firer) // { @@ -3430,7 +3591,7 @@ void V_TickForever(WaveLaneCtx *lane) // // for (P_QueryResult query = P_FirstRaycast(wrold, ray_start, ray_dir); query. - // // P_RaycastWorldResult hits = P_RaycastWorld(blend_frame, ray_start, ray_dir) + // // P_RaycastWorldResult hits = P_RaycastWorld(local_frame, ray_start, ray_dir) // // { // // } // } @@ -3639,7 +3800,7 @@ void V_TickForever(WaveLaneCtx *lane) G_CopyCpuToTexture( frame->cl, gpu_tiles, VEC3I32(0, 0, 0), - blend_world->tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1), + local_world->tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1), RNG3I32(VEC3I32(0, 0, 0), VEC3I32(tiles_dims.x, tiles_dims.y, 1)) ); G_DumbMemoryLayoutSync(frame->cl, gpu_tiles, G_Layout_DirectQueue_ShaderRead); diff --git a/src/pp/pp_vis/pp_vis_gpu.g b/src/pp/pp_vis/pp_vis_gpu.g index 477206c1..42288187 100644 --- a/src/pp/pp_vis/pp_vis_gpu.g +++ b/src/pp/pp_vis/pp_vis_gpu.g @@ -63,7 +63,7 @@ ComputeShader(V_ClearParticlesCS, 64) } //////////////////////////////////////////////////////////// -//~ Backdrop shader +//~ Backdrop shaders ComputeShader2D(V_BackdropCS, 8, 8) { @@ -270,7 +270,7 @@ ComputeShader2D(V_BackdropCS, 8, 8) } //////////////////////////////////////////////////////////// -//~ Quad shader +//~ Quad shaders ////////////////////////////// //- Vertex shader @@ -461,7 +461,7 @@ ComputeShader(V_SimParticlesCS, 64) } //////////////////////////////////////////////////////////// -//~ Shape shader +//~ Shape shaders ////////////////////////////// //- Vertex shader @@ -493,7 +493,7 @@ PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input) } //////////////////////////////////////////////////////////// -//~ Overlay shader +//~ Overlay shaders ////////////////////////////// //- Vertex shader diff --git a/src/pp/pp_vis/pp_vis_gpu.gh b/src/pp/pp_vis/pp_vis_gpu.gh index 7d112c30..4a159f00 100644 --- a/src/pp/pp_vis/pp_vis_gpu.gh +++ b/src/pp/pp_vis/pp_vis_gpu.gh @@ -53,10 +53,10 @@ Vec4 V_DryColor(Vec4 color, f32 dryness); ComputeShader2D(V_ClearCellsCS, 8, 8); ComputeShader(V_ClearParticlesCS, 64); -//- Backdrop shader +//- Backdrop shaders ComputeShader2D(V_BackdropCS, 8, 8); -//- Quad shader +//- Quad shaders VertexShader(V_DQuadVS, V_DQuadPSInput); PixelShader(V_DQuadPS, V_DQuadPSOutput, V_DQuadPSInput input); @@ -64,10 +64,10 @@ PixelShader(V_DQuadPS, V_DQuadPSOutput, V_DQuadPSInput input); ComputeShader(V_EmitParticlesCS, 64); ComputeShader(V_SimParticlesCS, 64); -//- Shape shader +//- Shape shaders VertexShader(V_DVertVS, V_DVertPSInput); PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input); -//- Overlay shader +//- Overlay shaders VertexShader(V_OverlayVS, V_OverlayPSInput); PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input); diff --git a/src/ui/ui_gpu.g b/src/ui/ui_gpu.g index d3b4ab4e..539ffe51 100644 --- a/src/ui/ui_gpu.g +++ b/src/ui/ui_gpu.g @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////// -//~ Rect shader +//~ Rect shaders ////////////////////////////// //- Vertex shader @@ -115,7 +115,7 @@ PixelShader(UI_DRectPS, UI_DRectPSOutput, UI_DRectPSInput input) } //////////////////////////////////////////////////////////// -//~ Blit shader +//~ Blit shaders ////////////////////////////// //- Vertex shader