@@ -1005,16 +1005,17 @@ FrameIndex PlayerController::get_stored_frame_index(int p_i) const {
10051005 }
10061006}
10071007
1008- void PlayerController::on_rewind_frame_begin (FrameIndex p_frame_index, int p_index, int p_count) {
1008+ void PlayerController::on_rewind_frame_begin (FrameIndex p_frame_index, int p_rewinding_index, int p_rewinding_frame_count) {
1009+ NS_PROFILE
10091010 if (!peer_controller->can_simulate ()) {
10101011 return ;
10111012 }
10121013
1013- if (p_index >= 0 && p_index < int (frames_input.size ())) {
1014- queued_instant_to_process = p_index ;
1014+ if (p_rewinding_index >= 0 && p_rewinding_index < int (frames_input.size ())) {
1015+ queued_instant_to_process = p_rewinding_index ;
10151016#ifdef DEBUG_ENABLED
10161017 // IMPOSSIBLE to trigger - without bugs.
1017- ASSERT_COND (frames_input[p_index ].id == p_frame_index);
1018+ ASSERT_COND (frames_input[p_rewinding_index ].id == p_frame_index);
10181019#endif
10191020 } else {
10201021 queued_instant_to_process = -1 ;
@@ -1374,7 +1375,9 @@ bool is_doll_snap_A_older(const DollController::DollSnapshot &p_snap_a, const Do
13741375 return p_snap_a.data .input_id < p_snap_b.data .input_id ;
13751376}
13761377
1377- void DollController::on_rewind_frame_begin (FrameIndex p_frame_index, int p_index, int p_count) {
1378+ void DollController::on_rewind_frame_begin (FrameIndex p_frame_index, int p_rewinding_index, int p_rewinding_frame_count) {
1379+ NS_PROFILE
1380+
13781381 if (!peer_controller->can_simulate ()) {
13791382 return ;
13801383 }
@@ -1383,16 +1386,9 @@ void DollController::on_rewind_frame_begin(FrameIndex p_frame_index, int p_index
13831386 return ;
13841387 }
13851388
1386- for (size_t i = 0 ; i < frames_input.size (); ++i) {
1387- if (frames_input[i].id == p_frame_index) {
1388- queued_instant_to_process = i;
1389- return ;
1390- }
1391- }
1392-
1393- SceneSynchronizerDebugger::singleton ()->print (WARNING, " DollController was uable to find the input: " + std::string (p_frame_index) + " maybe it was never received?" , " CONTROLLER-" + std::to_string (peer_controller->authority_peer ));
1394- queued_instant_to_process = frames_input.size ();
1395- return ;
1389+ // Just set the rewinding frame count, the fetch_next_input will
1390+ // validate it anyway.
1391+ queued_instant_to_process = p_rewinding_index;
13961392}
13971393
13981394int DollController::fetch_optimal_queued_inputs () const {
@@ -1409,12 +1405,19 @@ int DollController::fetch_optimal_queued_inputs() const {
14091405
14101406bool DollController::fetch_next_input (double p_delta) {
14111407 if (queued_instant_to_process >= 0 ) {
1412- if (queued_instant_to_process < int (frames_input.size ())) {
1413- // The SceneSync is rewinding the scene, so let's find the
1414- set_frame_input (frames_input[queued_instant_to_process], false );
1408+ // This offset is defined by the lag compensation algorithm inside the
1409+ // `on_snapshot_applied`, and is used to compensate the lag by
1410+ // getting rid or introduce inputs, during the recdonciliation (rewinding)
1411+ // phase.
1412+ const int instant_to_process = queued_instant_to_process - queued_instant_offset;
1413+ if (instant_to_process >= 0 ) {
1414+ ENSURE_V_MSG (instant_to_process < int (frames_input.size ()), false , " The doll lag compensation failed. The offsetted instant_to_process is never supposed to overflow the frames_input. Something didn't work inside the `on_snapshot_applied`." );
1415+ set_frame_input (frames_input[instant_to_process], false );
14151416 return true ;
1417+ } else {
1418+ // The doll character is compensating for missing inputs, so return false for now.
1419+ return false ;
14161420 }
1417- return false ;
14181421 }
14191422
14201423 if make_unlikely (current_input_buffer_id == FrameIndex::NONE) {
@@ -1426,7 +1429,7 @@ bool DollController::fetch_next_input(double p_delta) {
14261429 return false ;
14271430 }
14281431
1429- FrameIndex next_input_id = current_input_buffer_id + 1 ;
1432+ const FrameIndex next_input_id = current_input_buffer_id + 1 ;
14301433
14311434 // -------------------------------------------------------- Search the input
14321435 int closest_frame_index = -1 ;
@@ -1451,9 +1454,18 @@ bool DollController::fetch_next_input(double p_delta) {
14511454 }
14521455 }
14531456
1457+ if (!peer_controller->scene_synchronizer ->get_settings ().lag_compensation .doll_allow_guess_input_when_missing ) {
1458+ // It was not possible to find the input, and the doll is not allowed to guess,
1459+ // so just return false.
1460+ return false ;
1461+ }
1462+
14541463 if (closest_frame_index > 0 ) {
1455- // It was impossible to find the input, so just pick the closest one.
1456- set_frame_input (frames_input[closest_frame_index], false );
1464+ // It was impossible to find the input, so just pick the closest one and
1465+ // assume it's the one we are executing.
1466+ FrameInput guessed_fi = frames_input[closest_frame_index];
1467+ guessed_fi.id = next_input_id;
1468+ set_frame_input (guessed_fi, false );
14571469 return true ;
14581470 } else {
14591471 // The input is not set and there is no suitable one.
@@ -1462,29 +1474,25 @@ bool DollController::fetch_next_input(double p_delta) {
14621474}
14631475
14641476void DollController::process (double p_delta) {
1465- if (queued_instant_to_process >= 0 && queued_instant_to_process < int (frames_input.size ())) {
1466- // Rewinding in progress.
1467- // On the doll the rewind's processing takes care to apply the server
1468- // snapshot if it's found.
1469- // This operation is done here, because the doll process on a different
1470- // timeline than the one processed by the client.
1471- const FrameIndex frame_index = frames_input[queued_instant_to_process].id ;
1472- // 1. Skil the first frame as there is nomthing before it.
1473- if (frame_index > FrameIndex{ 0 }) {
1474- // 2. Try fetching the previous server snapshot.
1475- auto server_snap_it = VecFunc::find (server_snapshots, DollSnapshot (frame_index - 1 ));
1476- if (server_snap_it != server_snapshots.end ()) {
1477- // The snapshot was found, so apply it.
1478- static_cast <ClientSynchronizer *>(peer_controller->scene_synchronizer ->get_synchronizer_internal ())->apply_snapshot (server_snap_it->data , 0 , 0 , nullptr , true , true , true , true , true );
1479- }
1480- }
1481- }
1482-
14831477 notify_frame_checked (peer_controller->scene_synchronizer ->client_get_last_checked_frame_index ());
14841478
14851479 const bool is_new_input = fetch_next_input (p_delta);
14861480
14871481 if (is_new_input) {
1482+ if (queued_instant_to_process >= 0 ) {
1483+ // The rewinding is in progress.
1484+ // On the doll the rewind's processing takes care to apply the server
1485+ // snapshot if it's found.
1486+ // This operation is done here, because the doll process on a different
1487+ // timeline than the one processed by the client.
1488+ // 1. Try fetching the previous server snapshot.
1489+ auto server_snap_it = VecFunc::find (server_snapshots, DollSnapshot (current_input_buffer_id - 1 ));
1490+ if (server_snap_it != server_snapshots.end ()) {
1491+ // 2. The snapshot was found, so apply it.
1492+ static_cast <ClientSynchronizer *>(peer_controller->scene_synchronizer ->get_synchronizer_internal ())->apply_snapshot (server_snap_it->data , 0 , 0 , nullptr , true , true , true , true , true );
1493+ }
1494+ }
1495+
14881496 SceneSynchronizerDebugger::singleton ()->print (INFO, " Doll process index: " + std::string (current_input_buffer_id), " CONTROLLER-" + std::to_string (peer_controller->authority_peer ));
14891497
14901498 peer_controller->get_inputs_buffer_mut ().begin_read ();
@@ -1698,6 +1706,7 @@ void DollController::on_snapshot_applied(
16981706
16991707 // 2. Get the input count.
17001708 const int input_count = frames_input.size ();
1709+ ENSURE_MSG (input_count > 0 , " This function is not supposed to work with 0 inputs. Report this bug." )
17011710
17021711 // 3. Fetch the best input to start processing.
17031712 // TODO consider to scale this dynamically to slowly catchup with the server, as too drastic change may result in a less stable simulation.
@@ -1707,24 +1716,27 @@ void DollController::on_snapshot_applied(
17071716 // inputs so that the `input_count` equals to `optimal_queued_inputs`
17081717 // at the end of the reconcilation (rewinding) operation.
17091718
1719+ // 4. Compensate the lag.
17101720 if (input_count < optimal_input_count) {
17111721 // It has less inputs than the optimal input count defined.
1712- // In this case the offset, mention above, is going to be positive.
1713- // It will offset the reconciliation, by the delta difference
1714- // `optimal_input_count - input_count`, with frames where the object are
1715- // not procesed.
1722+ // In this case the offset, mention above, is going to be positive;
1723+ // making sure the available frames_input are used at the end of the processing
1724+ // so the `optimal_queued_inputs` is left at the endo of the rewinding.
17161725 const int missing_inputs = optimal_input_count - input_count;
1726+
1727+ queued_instant_offset = missing_inputs;
17171728 } else {
17181729 // It has more inputs than the optimal input count defined.
17191730 // In this case the offset is negative, meaning it throws away all the
17201731 // extra inputs to recatch the server.
1721- const int extra_inputs = input_count - optimal_input_count;
17221732
1723- current_input_buffer_id += extra_inputs;
1724-
1725- I need to find a way to define the `current_input_buffer_id` based on the extra inputs and the available frames_input;
1733+ // The following variable is the index pointing to the frame it want to
1734+ // start processing.
1735+ const int index_to_frame_to_keep = input_count - optimal_input_count;
1736+ queued_instant_offset = -index_to_frame_to_keep;
17261737 }
17271738
1739+ // 5. Get the executed frame index on the server side.
17281740 const FrameIndex doll_frame_index =
17291741 MapFunc::at (
17301742 p_global_server_snapshot.peers_frames_index ,
@@ -1741,16 +1753,53 @@ void DollController::on_snapshot_applied(
17411753 static_cast <ClientSynchronizer *>(peer_controller->scene_synchronizer ->get_synchronizer_internal ())->apply_snapshot (server_snap_it->data , 0 , 0 , nullptr , true , true , true , true , true );
17421754
17431755 } else {
1744- // At this point it's necessary to mention that due to the fact the doll is
1745- // processing on a different timeline, we may not have the server snapshot
1746- // to reconcile the doll with the server right away.
1747- // For this reason, this function apply the client snapshot, and leaves to
1748- // Rewinding's process function the task to apply the server snapshot.
1756+ // At this point it's necessary to mention that the doll is procesing
1757+ // on a different timeline, compared to the one executed on the server;
1758+ // For this reason, this function applies the client snapshot, and leaves to
1759+ // Rewinding's process function the task to apply the server snapshot
1760+ // when it's the time to do it.
1761+
1762+ // 6. Fetch the best FrameInput.
1763+ FrameIndex index;
1764+ if (queued_instant_offset > 0 ) {
1765+ // Missing inputs, so we need to give room to the rewinding and apply
1766+ // the available frames_input at the end of the rewinding.
1767+ if make_likely (frames_input[0 ].id .id > std::uint32_t (queued_instant_offset)) {
1768+ // In this case the available ID is big enough and it's enough
1769+ // subtracting the missing amount to find the best index to apply.
1770+ index = index - queued_instant_offset;
1771+ } else {
1772+ // In this case the available ID is not big enough, so just assume
1773+ // we start from 0.
1774+ index = { 0 };
1775+ }
1776+ } else {
1777+ // More inputs than needed.
1778+ index = frames_input[-queued_instant_offset].id ;
1779+ }
17491780
1750- const auto client_snap_it = VecFunc::find (client_snapshots, DollSnapshot (doll_frame_index));
1751- ENSURE_MSG (client_snap_it != client_snapshots.end (), " The doll was unable to set the snapshot because it was unable to find the client snapshot with ID: " + doll_frame_index);
1781+ if (!client_snapshots.empty ()) {
1782+ // 7. Get the closest available snapshot, and apply it, no need to be
1783+ // precise here, since the process will apply the server snapshot
1784+ // when available.
1785+ int distance = std::numeric_limits<int >::max ();
1786+ DollSnapshot *best = &client_snapshots.front ();
1787+ for (auto &snap : client_snapshots) {
1788+ const int delta = std::abs (std::int64_t (best->doll_executed_input .id ) - std::int64_t (snap.doll_executed_input .id ));
1789+ if (delta < distance) {
1790+ best = &snap;
1791+ distance = delta;
1792+ } else {
1793+ // Since the snapshots are sorted, it can interrupt the
1794+ // processing right after the distance start increasing.
1795+ break ;
1796+ }
1797+ }
17521798
1753- static_cast <ClientSynchronizer *>(peer_controller->scene_synchronizer ->get_synchronizer_internal ())->apply_snapshot (client_snap_it->data , 0 , 0 , nullptr , true , true , true , true , true );
1799+ if (best) {
1800+ static_cast <ClientSynchronizer *>(peer_controller->scene_synchronizer ->get_synchronizer_internal ())->apply_snapshot (best->data , 0 , 0 , nullptr , true , true , true , true , true );
1801+ }
1802+ }
17541803 }
17551804}
17561805
0 commit comments