1+ #pragma optimize("", off) // TODO remove this, which is here just to get good debugging info.
2+
13#include " peer_networked_controller.h"
24
35#include " core/config/project_settings.h"
@@ -1372,7 +1374,7 @@ bool DollController::receive_inputs(const Vector<uint8_t> &p_data) {
13721374}
13731375
13741376bool is_doll_snap_A_older (const DollController::DollSnapshot &p_snap_a, const DollController::DollSnapshot &p_snap_b) {
1375- return p_snap_a.data . input_id < p_snap_b.data . input_id ;
1377+ return p_snap_a.doll_executed_input < p_snap_b.doll_executed_input ;
13761378}
13771379
13781380void DollController::on_rewind_frame_begin (FrameIndex p_frame_index, int p_rewinding_index, int p_rewinding_frame_count) {
@@ -1405,6 +1407,11 @@ int DollController::fetch_optimal_queued_inputs() const {
14051407
14061408bool DollController::fetch_next_input (double p_delta) {
14071409 if (queued_instant_to_process >= 0 ) {
1410+ if make_unlikely (queued_frame_index_to_process == FrameIndex::NONE) {
1411+ // This happens when the server didn't start to process this doll yet.
1412+ return false ;
1413+ }
1414+
14081415 // This offset is defined by the lag compensation algorithm inside the
14091416 // `on_snapshot_applied`, and is used to compensate the lag by
14101417 // getting rid or introduce inputs, during the recdonciliation (rewinding)
@@ -1595,15 +1602,19 @@ void DollController::copy_controlled_objects_snapshot(
15951602
15961603 DollSnapshot *snap;
15971604 {
1598- snap = find_snapshot_by_snapshot_id (r_snapshots, p_snapshot. input_id );
1599- if (!snap ) {
1605+ auto it = VecFunc::find (r_snapshots, DollSnapshot (doll_executed_input) );
1606+ if (it == r_snapshots. end () ) {
16001607 r_snapshots.push_back (DollSnapshot (FrameIndex::NONE));
16011608 snap = &r_snapshots.back ();
1602- snap->data .input_id = p_snapshot.input_id ;
1609+ snap->doll_executed_input = doll_executed_input;
1610+ } else {
1611+ snap = &*it;
16031612 }
16041613 }
16051614
1606- snap->doll_executed_input = doll_executed_input;
1615+ ASSERT_COND (snap->doll_executed_input == doll_executed_input);
1616+ snap->data .input_id = p_snapshot.input_id ;
1617+
16071618 // Extracts the data from the snapshot.
16081619 MapFunc::assign (snap->data .peers_frames_index , peer_controller->get_authority_peer (), doll_executed_input);
16091620
@@ -1719,56 +1730,169 @@ bool DollController::__pcr__fetch_recovery_info(
17191730void DollController::on_snapshot_applied (
17201731 const Snapshot &p_global_server_snapshot,
17211732 const int p_frame_count_to_rewind) {
1733+ #ifdef DEBUG_ENABLED
17221734 // The `DollController` is never created on the server, and the below
17231735 // assertion is always satisfied.
17241736 ASSERT_COND (peer_controller->scene_synchronizer ->is_client ());
1737+ ASSERT_COND (p_frame_count_to_rewind >= 0 );
1738+ #endif
1739+
1740+ if make_unlikely (!server_snapshots.empty () && server_snapshots.back ().doll_executed_input == FrameIndex::NONE) {
1741+ apply_snapshot_no_input_reconciliation (p_global_server_snapshot);
1742+ }
1743+
1744+ if make_likely (current_input_buffer_id != FrameIndex::NONE) {
1745+ if (p_frame_count_to_rewind == 0 ) {
1746+ apply_snapshot_instant_input_reconciliation (p_global_server_snapshot, p_frame_count_to_rewind);
1747+ } else {
1748+ apply_snapshot_rewinding_input_reconciliation (p_global_server_snapshot, p_frame_count_to_rewind);
1749+ }
1750+ }
1751+ }
17251752
1726- queued_frame_index_to_process = FrameIndex{ 0 };
1753+ void DollController::apply_snapshot_no_input_reconciliation (const Snapshot &p_global_server_snapshot) {
1754+ // Apply the latest received server snapshot right away since the doll is not
1755+ // yet still processing on the server.
1756+
1757+ ASSERT_COND (server_snapshots.back ().doll_executed_input == FrameIndex::NONE);
1758+
1759+ static_cast <ClientSynchronizer *>(peer_controller->scene_synchronizer ->get_synchronizer_internal ())->apply_snapshot (server_snapshots.back ().data , 0 , 0 , nullptr , true , true , true , true , true );
1760+ last_doll_compared_input = FrameIndex::NONE;
1761+ current_input_buffer_id = FrameIndex::NONE;
1762+ queued_frame_index_to_process = FrameIndex::NONE;
1763+ }
1764+
1765+ void DollController::apply_snapshot_instant_input_reconciliation (const Snapshot &p_global_server_snapshot, const int p_frame_count_to_rewind) {
1766+ // This function assume the "frame count to rewind" is always 0.
1767+ ASSERT_COND (p_frame_count_to_rewind == 0 );
17271768
17281769 const int input_count = frames_input.size ();
1770+ if make_unlikely (input_count == 0 ) {
1771+ // When there are not inputs to process, it's much better not to apply
1772+ // any snapshot.
1773+ // The reason is that at some point it will receive inputs, and then
1774+ // this algorithm will do much better job applying the snapshot and
1775+ // avoid jittering.
1776+ // NOTE: This logic is extremly important to avoid start discarding
1777+ // the inputs even before processing them, that could happen
1778+ // when the received server snapshot is ahead the received inputs.
1779+ return ;
1780+ }
17291781
1730- // 1. Ensure the input reconciliation algorithm can process, otherwise:
1731- if make_unlikely (input_count <= 0 || p_frame_count_to_rewind <= 0 ) {
1732- // - On the server, the doll was not processed so just apply the server snapshot.
1733- // - In case of no rewinding, it applies the most up to date server snapshot right away.
1734- ENSURE_MSG (!server_snapshots.empty (), " This should be impossible. The doll was unable to set the server snapshot since there are no server snapshots." );
1735- static_cast <ClientSynchronizer *>(peer_controller->scene_synchronizer ->get_synchronizer_internal ())->apply_snapshot (server_snapshots.back ().data , 0 , 0 , nullptr , true , true , true , true , true );
1736- last_doll_compared_input = server_snapshots.back ().doll_executed_input ;
1737- // Reset the current_input_buffer_id, to make sure the next Frame processed starts from here.
1782+ // 1. Fetch the optimal queued inputs (how many inputs should be queued based
1783+ // on the current connection).
1784+ const int optimal_queued_inputs = fetch_optimal_queued_inputs ();
1785+
1786+ if make_likely (frames_input.back ().id .id >= std::uint32_t (optimal_queued_inputs)) {
1787+ last_doll_compared_input = frames_input.back ().id - optimal_queued_inputs;
1788+ } else {
1789+ last_doll_compared_input = FrameIndex{ 0 };
1790+ }
1791+
1792+ // Search the snapshot to apply.
1793+ const DollSnapshot *snapshot_to_apply = nullptr ;
1794+ for (const DollSnapshot &snapshot : server_snapshots) {
1795+ if (snapshot.doll_executed_input <= last_doll_compared_input) {
1796+ snapshot_to_apply = &snapshot;
1797+ }
1798+ }
1799+
1800+ if (snapshot_to_apply) {
1801+ static_cast <ClientSynchronizer *>(peer_controller->scene_synchronizer ->get_synchronizer_internal ())->apply_snapshot (snapshot_to_apply->data , 0 , 0 , nullptr , true , true , true , true , true );
1802+ // Bring everything back to this point.
1803+ last_doll_compared_input = snapshot_to_apply->doll_executed_input ;
17381804 current_input_buffer_id = last_doll_compared_input;
1739- return ;
17401805 }
1806+ }
17411807
1742- // This function handles the reconciliation mechanism.
1743- // The reconciliation is implemented in this function because this is the
1744- // best moment to manipulate the processing (to consume or build the input
1745- // queue) and avoid to make it noticeable.
1808+ void DollController::apply_snapshot_rewinding_input_reconciliation (const Snapshot &p_global_server_snapshot, const int p_frame_count_to_rewind) {
1809+ // This function apply the snapshot and handles the reconciliation mechanism.
1810+ // over the rewinding.
1811+ // The input reconciliation performed during the rewinding is the best because
1812+ // allows to manipulate the timeline without causing too many rubberbanding.
17461813
1747- // 2. Fetch the optimal queued inputs (how many inputs should be queued based
1814+ // This function assume the "frame count to rewind" is never 0.
1815+ ASSERT_COND (p_frame_count_to_rewind > 0 );
1816+
1817+ // 1. Fetch the optimal queued inputs (how many inputs should be queued based
17481818 // on the current connection).
17491819 const int optimal_queued_inputs = fetch_optimal_queued_inputs ();
17501820
1751- // 3. Fetch the best input to start processing.
1752- const int optimal_input_count = p_frame_count_to_rewind + optimal_queued_inputs;
1821+ const int input_count = frames_input.size ();
1822+ const DollSnapshot *server_snapshot = nullptr ;
1823+ FrameIndex new_last_doll_compared_input;
1824+ if make_likely (input_count > 0 ) {
1825+ // 2. Fetch the best input to start processing.
1826+ const int optimal_input_count = p_frame_count_to_rewind + optimal_queued_inputs;
1827+
1828+ // The lag compensation algorithm offsets the available
1829+ // inputs so that the `input_count` equals to `optimal_queued_inputs`
1830+ // at the end of the reconcilation (rewinding) operation.
1831+
1832+ // 3. Fetch the best FrameInput to reset.
1833+ // Doesn't matter if we have or not the frame input. If not, the doll will
1834+ // just wait idle.
1835+ if make_likely (frames_input.back ().id .id >= std::uint32_t (optimal_input_count)) {
1836+ new_last_doll_compared_input = frames_input.back ().id - optimal_input_count;
1837+ } else {
1838+ new_last_doll_compared_input = FrameIndex{ 0 };
1839+ }
17531840
1754- // The lag compensation algorithm offsets the available
1755- // inputs so that the `input_count` equals to `optimal_queued_inputs`
1756- // at the end of the reconcilation (rewinding) operation.
1841+ // 4. Ensure there is a server snapshot at some point, in between the new
1842+ // rewinding process queue or return and wait untill there is a
1843+ // server snapshot.
1844+ bool server_snapshot_found = false ;
1845+ for (auto it = server_snapshots.rbegin (); it != server_snapshots.rend (); it++) {
1846+ if (it->doll_executed_input < (new_last_doll_compared_input + optimal_input_count)) {
1847+ if make_likely (it->doll_executed_input > new_last_doll_compared_input) {
1848+ // This is the most common case: The server snapshot is in between the rewinding.
1849+ // Nothing to do here.
1850+ } else if (it->doll_executed_input == new_last_doll_compared_input) {
1851+ // In this case the rewinding is still in between the rewinding
1852+ // though as an optimization we just assign the snapshot to apply
1853+ // to avoid searching it.
1854+ server_snapshot = &*it;
1855+ } else {
1856+ // In this case the server snapshot ISN'T part of the rewinding
1857+ // so it brings the rewinding back a bit, to ensure the server
1858+ // snapshot is applied.
1859+ new_last_doll_compared_input = it->doll_executed_input ;
1860+ server_snapshot = &*it;
1861+ }
1862+ server_snapshot_found = true ;
1863+ break ;
1864+ }
1865+ }
1866+
1867+ if (!server_snapshot_found) {
1868+ // Server snapshot not found: Set this to none to signal that this
1869+ // rewind should not be performed.
1870+ new_last_doll_compared_input = FrameIndex::NONE;
1871+ }
1872+ }
17571873
1758- // 4. Fetch the best FrameInput to reset.
1759- // Doesn't matter if we have or not the frame input. If not, the doll will
1760- // just wait idle.
1761- if make_likely (frames_input.back ().id .id >= std::uint32_t (optimal_input_count)) {
1762- last_doll_compared_input = frames_input.back ().id - optimal_input_count;
1874+ if make_unlikely (input_count == 0 || new_last_doll_compared_input == FrameIndex::NONE) {
1875+ // There are no inputs and in this case this function has to avoidhawk:
1876+ // - Advance on the timeline while rewinding (so it needs to set the timeline back).
1877+ // - Try to compensate so to give the inputs enough time to arrive.
1878+ const FrameIndex frames_to_travel = { std::uint32_t (p_frame_count_to_rewind + optimal_queued_inputs) };
1879+ if make_likely (current_input_buffer_id > frames_to_travel) {
1880+ last_doll_compared_input = current_input_buffer_id - frames_to_travel;
1881+ } else {
1882+ last_doll_compared_input = FrameIndex{ 0 };
1883+ }
17631884 } else {
1764- last_doll_compared_input = FrameIndex{ 0 } ;
1885+ last_doll_compared_input = new_last_doll_compared_input ;
17651886 }
17661887
17671888 // 5. Fetch frame index to start processing.
17681889 queued_frame_index_to_process = last_doll_compared_input + 1 ;
17691890
1770- if (!client_snapshots.empty ()) {
1771- // 6. Get the closest available snapshot, and apply it, no need to be
1891+ if make_unlikely (server_snapshot) {
1892+ // 6. Apply the server snapshot.
1893+ static_cast <ClientSynchronizer *>(peer_controller->scene_synchronizer ->get_synchronizer_internal ())->apply_snapshot (server_snapshots.back ().data , 0 , 0 , nullptr , true , true , true , true , true );
1894+ } else if make_likely (!client_snapshots.empty ()) {
1895+ // 7. Get the closest available snapshot, and apply it, no need to be
17721896 // precise here, since the process will apply the server snapshot
17731897 // when available.
17741898 int distance = std::numeric_limits<int >::max ();
@@ -1786,36 +1910,9 @@ void DollController::on_snapshot_applied(
17861910 }
17871911
17881912 if (best) {
1789- auto best_server_snap_it = NS::VecFunc::find (client_snapshots, best->doll_executed_input );
1790- if (best_server_snap_it != client_snapshots.end ()) {
1791- static_cast <ClientSynchronizer *>(peer_controller->scene_synchronizer ->get_synchronizer_internal ())->apply_snapshot (best_server_snap_it->data , 0 , 0 , nullptr , true , true , true , true , true );
1792- } else {
1793- static_cast <ClientSynchronizer *>(peer_controller->scene_synchronizer ->get_synchronizer_internal ())->apply_snapshot (best->data , 0 , 0 , nullptr , true , true , true , true , true );
1794- }
1795- }
1796- }
1797- }
1798-
1799- DollController::DollSnapshot *DollController::find_snapshot_by_snapshot_id (
1800- std::vector<DollSnapshot> &p_snapshots,
1801- FrameIndex p_index) const {
1802- for (DollSnapshot &snap : p_snapshots) {
1803- if (snap.data .input_id == p_index) {
1804- return &snap;
1805- }
1806- }
1807- return nullptr ;
1808- }
1809-
1810- const DollController::DollSnapshot *DollController::find_snapshot_by_snapshot_id (
1811- const std::vector<DollSnapshot> &p_snapshots,
1812- FrameIndex p_index) const {
1813- for (const DollSnapshot &snap : p_snapshots) {
1814- if (snap.data .input_id == p_index) {
1815- return &snap;
1913+ static_cast <ClientSynchronizer *>(peer_controller->scene_synchronizer ->get_synchronizer_internal ())->apply_snapshot (best->data , 0 , 0 , nullptr , true , true , true , true , true );
18161914 }
18171915 }
1818- return nullptr ;
18191916}
18201917
18211918NoNetController::NoNetController (PeerNetworkedController *p_peer_controller) :
0 commit comments