@@ -1345,24 +1345,36 @@ bool PlayerController::can_accept_new_inputs() const {
13451345DollController::DollController (PeerNetworkedController *p_peer_controller) :
13461346 RemotelyControlledController(p_peer_controller) {
13471347 event_handler_received_snapshot =
1348- peer_controller->scene_synchronizer ->event_received_snapshot .bind (std::bind (&DollController::received_snapshot, this , std::placeholders::_1));
1348+ peer_controller->scene_synchronizer ->event_received_server_snapshot .bind (std::bind (&DollController::on_received_server_snapshot, this , std::placeholders::_1));
1349+
1350+ event_handler_client_snapshot_updated =
1351+ peer_controller->scene_synchronizer ->event_snapshot_update_finished .bind (std::bind (&DollController::on_snapshot_update_finished, this , std::placeholders::_1));
13491352
13501353 event_handler_rewind_frame_begin =
13511354 peer_controller->scene_synchronizer ->event_rewind_frame_begin .bind (std::bind (&DollController::on_rewind_frame_begin, this , std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
13521355
13531356 event_handler_state_validated =
13541357 peer_controller->scene_synchronizer ->event_state_validated .bind (std::bind (&DollController::on_state_validated, this , std::placeholders::_1, std::placeholders::_2));
1358+
1359+ event_handler_state_validated =
1360+ peer_controller->scene_synchronizer ->event_snapshot_applied .bind (std::bind (&DollController::on_snapshot_applied, this , std::placeholders::_1));
13551361}
13561362
13571363DollController::~DollController () {
1358- peer_controller->scene_synchronizer ->event_received_snapshot .unbind (event_handler_received_snapshot);
1364+ peer_controller->scene_synchronizer ->event_received_server_snapshot .unbind (event_handler_received_snapshot);
13591365 event_handler_received_snapshot = NS::NullPHandler;
13601366
1367+ peer_controller->scene_synchronizer ->event_snapshot_update_finished .unbind (event_handler_client_snapshot_updated);
1368+ event_handler_client_snapshot_updated = NS::NullPHandler;
1369+
13611370 peer_controller->scene_synchronizer ->event_rewind_frame_begin .unbind (event_handler_rewind_frame_begin);
13621371 event_handler_rewind_frame_begin = NS::NullPHandler;
13631372
13641373 peer_controller->scene_synchronizer ->event_state_validated .unbind (event_handler_state_validated);
13651374 event_handler_state_validated = NS::NullPHandler;
1375+
1376+ peer_controller->scene_synchronizer ->event_snapshot_applied .unbind (event_handler_snapshot_applied);
1377+ event_handler_snapshot_applied = NS::NullPHandler;
13661378}
13671379
13681380bool DollController::receive_inputs (const Vector<uint8_t > &p_data) {
@@ -1421,46 +1433,7 @@ bool DollController::receive_inputs(const Vector<uint8_t> &p_data) {
14211433}
14221434
14231435bool is_doll_snap_A_older (const DollController::DollSnapshot &p_snap_a, const DollController::DollSnapshot &p_snap_b) {
1424- return p_snap_a.index < p_snap_b.index ;
1425- }
1426-
1427- void DollController::received_snapshot (const Snapshot &p_snapshot) {
1428- const std::vector<ObjectData *> *controlled_objects = peer_controller->scene_synchronizer ->get_peer_controlled_objects_data (peer_controller->get_authority_peer ());
1429- if (!controlled_objects) {
1430- // Nothing to store.
1431- return ;
1432- }
1433-
1434- if (last_checked_input != FrameIndex::NONE && last_checked_input >= p_snapshot.input_id ) {
1435- // Snapshot already checked, no need to store this.
1436- return ;
1437- }
1438-
1439- DollSnapshot *snap;
1440- if (VecFunc::has (snapshots, DollSnapshot (p_snapshot.input_id ))) {
1441- std::vector<DollSnapshot>::iterator it = VecFunc::find (snapshots, DollSnapshot (p_snapshot.input_id ));
1442- snap = &*it;
1443- } else {
1444- snapshots.push_back (DollSnapshot (p_snapshot.input_id ));
1445- snap = &snapshots.back ();
1446- }
1447-
1448- // This can't trigger because of the above check.
1449- ASSERT_COND (snap->index == p_snapshot.input_id );
1450-
1451- for (ObjectData *object_data : *controlled_objects) {
1452- const std::vector<NameAndVar> *vars = p_snapshot.get_object_vars (object_data->get_net_id ());
1453- ENSURE_CONTINUE_MSG (vars, " The snapshot didn't contain the object: " + object_data->get_net_id () + " . If this error spams for a long period (5/10 seconds), it's a bug." );
1454- std::map<ObjectNetId, std::vector<NameAndVar>>::iterator snap_object_vars = MapFunc::insert_if_new (snap->objects_vars , object_data->get_net_id (), std::vector<NameAndVar>());
1455- for (const NameAndVar &nav : *vars) {
1456- snap_object_vars->second .push_back (NameAndVar::make_copy (nav));
1457- }
1458- }
1459-
1460- std::sort (
1461- snapshots.begin (),
1462- snapshots.end (),
1463- is_doll_snap_A_older);
1436+ return p_snap_a.data .input_id < p_snap_b.data .input_id ;
14641437}
14651438
14661439void DollController::on_rewind_frame_begin (FrameIndex p_frame_index, int p_index, int p_count) {
@@ -1484,48 +1457,113 @@ void DollController::on_rewind_frame_begin(FrameIndex p_frame_index, int p_index
14841457 return ;
14851458}
14861459
1460+ int DollController::count_queued_inputs () const {
1461+ int queued_inputs_count = 0 ;
1462+ for (const FrameInput &frame : frames_input) {
1463+ if (frame.id > current_input_buffer_id) {
1464+ queued_inputs_count += 1 ;
1465+ }
1466+ }
1467+ return queued_inputs_count;
1468+ }
1469+
1470+ int DollController::fetch_optimal_queued_inputs () const {
1471+ // The optimal virtual delay is a number that refers to the amount of queued
1472+ // frames the DollController should try to have on each frame to avoid
1473+ // remaining without inputs.
1474+ // This delay should increase when the internet connection is bad (packet loss)
1475+ // and decrease otherwise, allowing the inputs more time to be received.
1476+ //
1477+ // TODO: At the moment this value is fixed to the min_frame_delay, but at some
1478+ // point we will want to change this value dynamically depending on packet loss.
1479+ return peer_controller->get_min_frames_delay ();
1480+ }
1481+
14871482bool DollController::fetch_next_input (double p_delta) {
14881483 if (queued_instant_to_process >= 0 ) {
1489- if (queued_instant_to_process >= int (frames_input.size ())) {
1490- return false ;
1491- } else {
1484+ if (queued_instant_to_process < int (frames_input.size ())) {
14921485 // The SceneSync is rewinding the scene, so let's find the
14931486 set_frame_input (frames_input[queued_instant_to_process], false );
14941487 return true ;
14951488 }
1489+ return false ;
1490+ }
14961491
1497- } else {
1498- if (current_input_buffer_id == FrameIndex::NONE) {
1499- if (frames_input.size () > 0 ) {
1500- // Anything, as first input is good.
1501- set_frame_input (frames_input.front (), true );
1502- return true ;
1503- } else {
1504- return false ;
1505- }
1492+ if make_unlikely (current_input_buffer_id == FrameIndex::NONE) {
1493+ if (frames_input.size () > 0 ) {
1494+ // Anything, as first input is good.
1495+ set_frame_input (frames_input.front (), true );
1496+ return true ;
1497+ }
1498+ return false ;
1499+ }
1500+
1501+ // -------------------------------------------------------- Lag compensation
1502+ // The following code performs a lag compensation check to ensure it never
1503+ // run out of inputs.
1504+
1505+ // This parameter is used to enstablish the percentage of the lag compensation
1506+ // algorithm to be activated.
1507+ // This percentage is increased linearly by the distance the queue_inputs_count is relative to optimal_queued_inputs.
1508+ // So, the bigger the distance is, the likely an action can happen.
1509+ const float lag_compensation_base_percentage = 0.3 ;
1510+
1511+ // 1. Count the queued inputs (the inputs that are not being processed yet).
1512+ const int queued_inputs_count = count_queued_inputs ();
1513+ // 2. Fetch the optimal queued inputs (how many inputs should be queued based
1514+ // on the current connection).
1515+ // NOTE: The `+1` is needed to ensure the `queued_inputs_count` tend toward
1516+ // optmal AFTER the input is consumed, that is about to happen just after
1517+ // this function is executed.
1518+ const int optimal_queued_inputs = fetch_optimal_queued_inputs () + 1 ;
1519+
1520+ // The likeliness is used to scale the `lag_compensation_percentage`
1521+ // to make it likely something to happen.
1522+ const float factor = std::abs (float (optimal_queued_inputs) - float (queued_inputs_count)) / float (optimal_queued_inputs);
1523+
1524+ const float lag_compensation_percentage = lag_compensation_base_percentage * factor;
1525+
1526+ // 3. Based on the the virtual delay and queue_inputs_count it fetches the next input id.
1527+ FrameIndex next_input_id = current_input_buffer_id + 1 ;
1528+ const float r = float (rand ()) / float (RAND_MAX);
1529+ if (r < lag_compensation_percentage) {
1530+ // Lag compensation activated.
1531+ if (queued_inputs_count > optimal_queued_inputs) {
1532+ // It has more queued inputs than the established optimal.
1533+ // so let's skip 1 input so we can tend toward the optimal queued inputs.
1534+ next_input_id += 1 ;
1535+ } else if (queued_inputs_count == optimal_queued_inputs) {
1536+ // It has the exact number of queued inputs.
1537+ // Nothing to do.
15061538 } else {
1507- const FrameIndex next_input_id = current_input_buffer_id + 1 ;
1508- // Loop the snapshots.
1509- for (size_t i = 0 ; i < frames_input.size (); ++i) {
1510- // Take any NEXT snapshot. Eventually the rewind will fix this.
1511- // NOTE: the snapshots are sorted.
1512- if (frames_input[i].id >= next_input_id) {
1513- set_frame_input (frames_input[i], false );
1514- return true ;
1515- }
1516- }
1517- if (frames_input.size () > 0 ) {
1518- set_frame_input (frames_input.back (), false );
1519- // true anyway, don't stop the processing, just use the input.
1520- return true ;
1521- }
1539+ // It has less queued input than the established optimal,
1540+ // so let's use the old input for an extra frame, so we can
1541+ // build the queue again.
1542+ next_input_id -= 1 ;
15221543 }
15231544 }
1545+
1546+ // -------------------------------------------------------- Search the input
1547+ for (size_t i = 0 ; i < frames_input.size (); ++i) {
1548+ if (frames_input[i].id >= next_input_id) {
1549+ set_frame_input (frames_input[i], false );
1550+ return true ;
1551+ }
1552+ }
1553+
1554+ if (frames_input.size () > 0 ) {
1555+ set_frame_input (frames_input.back (), false );
1556+ // It was impossible to find the input, so just pick the oldest one:
1557+ // it's better than to stop the processing.
1558+ return true ;
1559+ }
1560+
15241561 return false ;
15251562}
15261563
15271564void DollController::process (double p_delta) {
15281565 notify_frame_checked (peer_controller->scene_synchronizer ->client_get_last_checked_frame_index ());
1566+
15291567 const bool is_new_input = fetch_next_input (p_delta);
15301568
15311569 if (is_new_input) {
@@ -1565,21 +1603,143 @@ void DollController::notify_frame_checked(FrameIndex p_frame_index) {
15651603 last_checked_input = p_frame_index;
15661604}
15671605
1606+ void DollController::on_received_server_snapshot (const Snapshot &p_snapshot) {
1607+ if (last_checked_input != FrameIndex::NONE && last_checked_input >= p_snapshot.input_id ) {
1608+ // Snapshot already checked, no need to store this.
1609+ return ;
1610+ }
1611+
1612+ copy_controlled_objects_snapshot (p_snapshot, p_snapshot.input_id , server_snapshots);
1613+ }
1614+
1615+ void DollController::on_snapshot_update_finished (const Snapshot &p_snapshot) {
1616+ copy_controlled_objects_snapshot (p_snapshot, current_input_buffer_id, client_snapshots);
1617+ }
1618+
1619+ void DollController::copy_controlled_objects_snapshot (
1620+ const Snapshot &p_snapshot,
1621+ FrameIndex p_doll_executed_input,
1622+ std::vector<DollSnapshot> &r_snapshots) {
1623+ const std::vector<ObjectData *> *controlled_objects = peer_controller->scene_synchronizer ->get_peer_controlled_objects_data (peer_controller->get_authority_peer ());
1624+ if (!controlled_objects || controlled_objects->size () <= 0 ) {
1625+ // Nothing to store.
1626+ return ;
1627+ }
1628+
1629+ DollSnapshot *snap;
1630+ {
1631+ std::vector<DollSnapshot>::iterator it = VecFunc::find (r_snapshots, DollSnapshot (p_snapshot.input_id ));
1632+ if (r_snapshots.end () != it) {
1633+ snap = &*it;
1634+ } else {
1635+ r_snapshots.push_back (DollSnapshot (p_snapshot.input_id ));
1636+ snap = &r_snapshots.back ();
1637+ }
1638+ }
1639+
1640+ // This can't trigger because of the above check.
1641+ ASSERT_COND (snap->data .input_id == p_snapshot.input_id );
1642+ snap->doll_executed_input = p_doll_executed_input;
1643+
1644+ // Find the biggest ID to initialize the snapshot.
1645+ {
1646+ ObjectNetId biggest_id = { 0 };
1647+ for (ObjectData *object_data : *controlled_objects) {
1648+ if (object_data->get_net_id () > biggest_id) {
1649+ biggest_id = object_data->get_net_id ();
1650+ }
1651+ }
1652+ snap->data .object_vars .resize (biggest_id.id + 1 );
1653+ }
1654+
1655+ // Now store the vars info.
1656+ for (ObjectData *object_data : *controlled_objects) {
1657+ if (!VecFunc::has (p_snapshot.simulated_objects , object_data->get_net_id ())) {
1658+ // This object was not simulated.
1659+ continue ;
1660+ }
1661+
1662+ const std::vector<NameAndVar> *vars = p_snapshot.get_object_vars (object_data->get_net_id ());
1663+ ENSURE_CONTINUE_MSG (vars, " The snapshot didn't contain the object: " + object_data->get_net_id () + " . If this error spams for a long period (5/10 seconds), it's a bug." );
1664+
1665+ snap->data .simulated_objects .push_back (object_data->get_net_id ());
1666+
1667+ for (const NameAndVar &nav : *vars) {
1668+ snap->data .object_vars [object_data->get_net_id ().id ].push_back (NameAndVar::make_copy (nav));
1669+ }
1670+ }
1671+
1672+ // This array must be always sorted to ensure the snapshots order.
1673+ std::sort (
1674+ r_snapshots.begin (),
1675+ r_snapshots.end (),
1676+ is_doll_snap_A_older);
1677+ }
1678+
15681679bool DollController::__pcr__fetch_recovery_info (
15691680 FrameIndex p_checking_frame_index,
1681+ Snapshot *r_no_rewind_recover,
1682+ // The frames to process afterward.
15701683 int p_predicted_frames,
15711684 std::vector<std::string> *r_differences_info
15721685#ifdef DEBUG_ENABLED
15731686 ,
15741687 std::vector<ObjectNetId> *r_different_node_data
15751688#endif
15761689) const {
1577- // TODO please implement this.
1578- return true ;
1690+
1691+ // 1. Fetch the server snapshot.
1692+ // The server snapshot can be fetched, normally, using the index.
1693+ std::vector<DollSnapshot>::const_iterator server_snap_it = VecFunc::find (server_snapshots, DollSnapshot (p_checking_frame_index));
1694+ // The server snapshot was not found, this is impossible because we store
1695+ // all the snapshots.
1696+ ENSURE_V_MSG (server_snapshots.end () != server_snap_it, false , " Doll fetch recovery info failed because the snapshot was not found, though this should be impossible to trigger. checking_frame_index: " + p_checking_frame_index);
1697+
1698+ const DollSnapshot *server_snapshot = &*server_snap_it;
1699+ ENSURE_V (server_snapshot, false );
1700+
1701+ // 2. Now fetch the client snapshot.
1702+ // Since the doll is following a a different timeline, we need to fetch the
1703+ // client frame by checking the `doll_executed_index` instead.
1704+ const DollSnapshot *client_snapshot = nullptr ;
1705+ for (const DollSnapshot &snapshot : client_snapshots) {
1706+ if (snapshot.doll_executed_input == p_checking_frame_index) {
1707+ client_snapshot = &snapshot;
1708+ }
1709+ }
1710+ ENSURE_V_MSG (client_snapshot, false , " Doll fetch recovery info failed because the client snapshot was not found. checking_frame: " + p_checking_frame_index)
1711+
1712+ // Now just compare the two snapshots.
1713+ return Snapshot::compare (
1714+ *peer_controller->scene_synchronizer ,
1715+ server_snapshot->data ,
1716+ client_snapshot->data ,
1717+ -1 ,
1718+ r_no_rewind_recover,
1719+ r_differences_info
1720+ #ifdef DEBUG_ENABLED
1721+ ,
1722+ r_different_node_data
1723+ #endif
1724+ );
15791725}
15801726
1581- void DollController::notify_applied_snapshot (FrameIndex frame_index, ObjectNetId p_id) {
1582- ASSERT_NO_ENTRY (); // TODO implement or remove this function if not implemented.
1727+ void DollController::on_snapshot_applied (const Snapshot &p_snapshot) {
1728+ // Since this doll is executing on a different timeline, we can't apply
1729+ // the received snapshot.
1730+ // 1. Search which input was executed by this doll when this snapshot was executed.
1731+ FrameIndex doll_executed_input;
1732+ {
1733+ const auto client_snap_it = VecFunc::find (client_snapshots, DollSnapshot (p_snapshot.input_id ));
1734+ 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: " + p_snapshot.input_id );
1735+
1736+ doll_executed_input = client_snap_it->doll_executed_input ;
1737+ }
1738+
1739+ // Now it has the executed input so, fetch the snapshot to apply.
1740+ // 2. Fetch the server snapshot.
1741+ const auto server_snap_it = VecFunc::find (server_snapshots, DollSnapshot (doll_executed_input));
1742+ if (server_snap_it ==)
15831743}
15841744
15851745NoNetController::NoNetController (PeerNetworkedController *p_peer_controller) :
0 commit comments