Skip to content

Commit 249e157

Browse files
committed
Implementing the new logic to perform the lag compensation during the reconciliation. Still WIP.
1 parent c8c4429 commit 249e157

5 files changed

Lines changed: 95 additions & 49 deletions

File tree

core/peer_networked_controller.cpp

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,7 +1295,7 @@ DollController::DollController(PeerNetworkedController *p_peer_controller) :
12951295
peer_controller->scene_synchronizer->event_state_validated.bind(std::bind(&DollController::on_state_validated, this, std::placeholders::_1, std::placeholders::_2));
12961296

12971297
event_handler_snapshot_applied =
1298-
peer_controller->scene_synchronizer->event_snapshot_applied.bind(std::bind(&DollController::on_snapshot_applied, this, std::placeholders::_1));
1298+
peer_controller->scene_synchronizer->event_snapshot_applied.bind(std::bind(&DollController::on_snapshot_applied, this, std::placeholders::_1, std::placeholders::_2));
12991299
}
13001300

13011301
DollController::~DollController() {
@@ -1395,16 +1395,6 @@ void DollController::on_rewind_frame_begin(FrameIndex p_frame_index, int p_index
13951395
return;
13961396
}
13971397

1398-
int DollController::count_queued_inputs() const {
1399-
int queued_inputs_count = 0;
1400-
for (const FrameInput &frame : frames_input) {
1401-
if (frame.id > current_input_buffer_id) {
1402-
queued_inputs_count += 1;
1403-
}
1404-
}
1405-
return queued_inputs_count;
1406-
}
1407-
14081398
int DollController::fetch_optimal_queued_inputs() const {
14091399
// The optimal virtual delay is a number that refers to the amount of queued
14101400
// frames the DollController should try to have on each frame to avoid
@@ -1485,7 +1475,7 @@ void DollController::process(double p_delta) {
14851475
auto server_snap_it = VecFunc::find(server_snapshots, DollSnapshot(frame_index - 1));
14861476
if (server_snap_it != server_snapshots.end()) {
14871477
// The snapshot was found, so apply it.
1488-
static_cast<ClientSynchronizer *>(peer_controller->scene_synchronizer->get_synchronizer_internal())->apply_snapshot(server_snap_it->data, 0, nullptr, true, true, true, true, true);
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);
14891479
}
14901480
}
14911481
}
@@ -1650,8 +1640,6 @@ void DollController::copy_controlled_objects_snapshot(
16501640
bool DollController::__pcr__fetch_recovery_info(
16511641
FrameIndex p_checking_frame_index,
16521642
Snapshot *r_no_rewind_recover,
1653-
// The frames to process afterward.
1654-
int p_predicted_frames,
16551643
std::vector<std::string> *r_differences_info
16561644
#ifdef DEBUG_ENABLED
16571645
,
@@ -1692,17 +1680,65 @@ bool DollController::__pcr__fetch_recovery_info(
16921680
);
16931681
}
16941682

1695-
void DollController::on_snapshot_applied(const Snapshot &p_global_server_snapshot) {
1696-
// This function can't run on the server.
1697-
ENSURE(peer_controller->scene_synchronizer->is_client());
1683+
void DollController::on_snapshot_applied(
1684+
const Snapshot &p_global_server_snapshot,
1685+
const int p_frame_count_to_rewind) {
1686+
// The `DollController` is never created on the server, and the below
1687+
// assertion is always satisfied.
1688+
ASSERT_COND(peer_controller->scene_synchronizer->is_client());
1689+
1690+
// This function handles the reconciliation mechanism.
1691+
// The reconciliation is implemented in this function because this is the
1692+
// best moment to manipulate the processing (to consume or build the input
1693+
// queue) and avoid to make it noticeable.
1694+
1695+
// 1. Fetch the optimal queued inputs (how many inputs should be queued based
1696+
// on the current connection).
1697+
const int optimal_queued_inputs = fetch_optimal_queued_inputs();
1698+
1699+
// 2. Get the input count.
1700+
const int input_count = frames_input.size();
1701+
1702+
// 3. Fetch the best input to start processing.
1703+
// TODO consider to scale this dynamically to slowly catchup with the server, as too drastic change may result in a less stable simulation.
1704+
const int optimal_input_count = p_frame_count_to_rewind + optimal_queued_inputs;
1705+
1706+
// The lag compensation algorithm offsets the available
1707+
// inputs so that the `input_count` equals to `optimal_queued_inputs`
1708+
// at the end of the reconcilation (rewinding) operation.
1709+
1710+
if (input_count < optimal_input_count) {
1711+
// 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.
1716+
const int missing_inputs = optimal_input_count - input_count;
1717+
} else {
1718+
// It has more inputs than the optimal input count defined.
1719+
// In this case the offset is negative, meaning it throws away all the
1720+
// extra inputs to recatch the server.
1721+
const int extra_inputs = input_count - optimal_input_count;
1722+
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;
1726+
}
16981727

1699-
const FrameIndex doll_frame_index = MapFunc::at(p_global_server_snapshot.peers_frames_index, peer_controller->get_authority_peer(), FrameIndex::NONE);
1728+
const FrameIndex doll_frame_index =
1729+
MapFunc::at(
1730+
p_global_server_snapshot.peers_frames_index,
1731+
peer_controller->get_authority_peer(),
1732+
FrameIndex::NONE);
17001733

17011734
if (doll_frame_index == FrameIndex::NONE) {
17021735
// On the server, the doll was not executed so just apply the server snapshot.
1703-
const auto server_snap_it = VecFunc::find(server_snapshots, DollSnapshot(FrameIndex::NONE));
1736+
const auto server_snap_it =
1737+
VecFunc::find(
1738+
server_snapshots,
1739+
DollSnapshot(FrameIndex::NONE));
17041740
ENSURE_MSG(server_snap_it != server_snapshots.end(), "The doll was unable to set the NO-CONTROLLER snapshot because it was unable to find it in the server_snapshots array.");
1705-
static_cast<ClientSynchronizer *>(peer_controller->scene_synchronizer->get_synchronizer_internal())->apply_snapshot(server_snap_it->data, 0, nullptr, true, true, true, true, true);
1741+
static_cast<ClientSynchronizer *>(peer_controller->scene_synchronizer->get_synchronizer_internal())->apply_snapshot(server_snap_it->data, 0, 0, nullptr, true, true, true, true, true);
17061742

17071743
} else {
17081744
// At this point it's necessary to mention that due to the fact the doll is
@@ -1714,7 +1750,7 @@ void DollController::on_snapshot_applied(const Snapshot &p_global_server_snapsho
17141750
const auto client_snap_it = VecFunc::find(client_snapshots, DollSnapshot(doll_frame_index));
17151751
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);
17161752

1717-
static_cast<ClientSynchronizer *>(peer_controller->scene_synchronizer->get_synchronizer_internal())->apply_snapshot(client_snap_it->data, 0, nullptr, true, true, true, true, true);
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);
17181754
}
17191755
}
17201756

core/peer_networked_controller.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,6 @@ struct DollController final : public RemotelyControlledController {
399399

400400
virtual bool receive_inputs(const Vector<uint8_t> &p_data) override;
401401
void on_rewind_frame_begin(FrameIndex p_input_id, int p_index, int p_count);
402-
int count_queued_inputs() const;
403402
int fetch_optimal_queued_inputs() const;
404403
virtual bool fetch_next_input(double p_delta) override;
405404
virtual void process(double p_delta) override;
@@ -415,15 +414,14 @@ struct DollController final : public RemotelyControlledController {
415414
bool __pcr__fetch_recovery_info(
416415
FrameIndex p_checking_frame_index,
417416
Snapshot *r_no_rewind_recover,
418-
int p_predicted_frames,
419417
std::vector<std::string> *r_differences_info
420418
#ifdef DEBUG_ENABLED
421419
,
422420
std::vector<ObjectNetId> *r_different_node_data
423421
#endif
424422
) const;
425423

426-
void on_snapshot_applied(const Snapshot &p_snapshot);
424+
void on_snapshot_applied(const Snapshot &p_snapshot, const int p_frame_count_to_rewind);
427425

428426
DollSnapshot *find_snapshot_by_snapshot_id(std::vector<DollSnapshot> &p_snapshots, FrameIndex p_index) const;
429427
const DollSnapshot *find_snapshot_by_snapshot_id(const std::vector<DollSnapshot> &p_snapshots, FrameIndex p_index) const;

scene_synchronizer.cpp

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2644,7 +2644,7 @@ void ClientSynchronizer::process_received_server_state() {
26442644
// The server last received snapshot is a no input snapshot. Just assume it's the most up-to-date.
26452645
SceneSynchronizerDebugger::singleton()->debug_print(&scene_synchronizer->get_network_interface(), "The client received a \"no input\" snapshot, so the client is setting it right away assuming is the most updated one.", true);
26462646

2647-
apply_snapshot(*last_received_server_snapshot, NetEventFlag::SYNC_RECOVER, nullptr);
2647+
apply_snapshot(*last_received_server_snapshot, NetEventFlag::SYNC_RECOVER, 0, nullptr);
26482648
last_received_server_snapshot.reset();
26492649
return;
26502650
}
@@ -2726,7 +2726,9 @@ void ClientSynchronizer::process_received_server_state() {
27262726
scene_synchronizer->get_network_interface().get_owner_name());
27272727

27282728
// Sync.
2729-
__pcr__sync__rewind();
2729+
__pcr__sync__rewind(
2730+
last_checked_input,
2731+
*inner_player_controller);
27302732

27312733
// Rewind.
27322734
__pcr__rewind(
@@ -2774,15 +2776,12 @@ bool ClientSynchronizer::__pcr__fetch_recovery_info(
27742776
);
27752777

27762778
if (is_equal) {
2777-
const int frames_count_after_input_id = p_local_player_controller.count_frames_after(p_input_id);
2778-
27792779
// The snapshots are equals, make sure the dolls doesn't need to be reconciled.
27802780
for (const auto &[peer, data] : scene_synchronizer->peer_data) {
27812781
if (data.get_controller() && data.get_controller()->is_doll_controller()) {
27822782
const bool is_doll_state_valid = data.get_controller()->get_doll_controller()->__pcr__fetch_recovery_info(
27832783
p_input_id,
27842784
&r_no_rewind_recover,
2785-
frames_count_after_input_id,
27862785
scene_synchronizer->debug_rewindings_enabled ? &differences_info : nullptr
27872786
#ifdef DEBUG_ENABLED
27882787
,
@@ -2854,17 +2853,22 @@ bool ClientSynchronizer::__pcr__fetch_recovery_info(
28542853
return !is_equal;
28552854
}
28562855

2857-
void ClientSynchronizer::__pcr__sync__rewind() {
2856+
void ClientSynchronizer::__pcr__sync__rewind(
2857+
FrameIndex p_last_checked_input_id,
2858+
const PlayerController &p_local_player_controller) {
28582859
NS_PROFILE
28592860
// Apply the server snapshot so to go back in time till that moment,
28602861
// so to be able to correctly reply the movements.
28612862

2863+
const int frame_count_after_input_id = p_local_player_controller.count_frames_after(p_last_checked_input_id);
2864+
28622865
std::vector<std::string> applied_data_info;
28632866

28642867
const NS::Snapshot &server_snapshot = *last_received_server_snapshot;
28652868
apply_snapshot(
28662869
server_snapshot,
28672870
NetEventFlag::SYNC_RECOVER | NetEventFlag::SYNC_RESET,
2871+
frame_count_after_input_id,
28682872
scene_synchronizer->debug_rewindings_enabled ? &applied_data_info : nullptr);
28692873

28702874
if (applied_data_info.size() > 0) {
@@ -2950,6 +2954,7 @@ void ClientSynchronizer::__pcr__sync__no_rewind(const NS::Snapshot &p_no_rewind_
29502954
apply_snapshot(
29512955
p_no_rewind_recover,
29522956
NetEventFlag::SYNC_RECOVER,
2957+
0,
29532958
scene_synchronizer->debug_rewindings_enabled ? &applied_data_info : nullptr,
29542959
// ALWAYS skips custom data because partial snapshots don't contain custom_data.
29552960
true);
@@ -2986,6 +2991,7 @@ void ClientSynchronizer::process_paused_controller_recovery() {
29862991
apply_snapshot(
29872992
*last_received_server_snapshot,
29882993
NetEventFlag::SYNC_RECOVER,
2994+
0,
29892995
&applied_data_info);
29902996

29912997
last_received_server_snapshot.reset();
@@ -3738,6 +3744,7 @@ void ClientSynchronizer::update_simulated_objects_list(const std::vector<ObjectN
37383744
void ClientSynchronizer::apply_snapshot(
37393745
const NS::Snapshot &p_snapshot,
37403746
const int p_flag,
3747+
const int p_frame_count_to_rewind,
37413748
std::vector<std::string> *r_applied_data_info,
37423749
const bool p_skip_custom_data,
37433750
const bool p_skip_simulated_objects_update,
@@ -3847,7 +3854,7 @@ void ClientSynchronizer::apply_snapshot(
38473854
}
38483855

38493856
if (!p_skip_snapshot_applied_event_broadcast) {
3850-
scene_synchronizer->event_snapshot_applied.broadcast(p_snapshot);
3857+
scene_synchronizer->event_snapshot_applied.broadcast(p_snapshot, p_frame_count_to_rewind);
38513858
}
38523859

38533860
if (!p_skip_change_event) {

scene_synchronizer.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ class SceneSynchronizerBase {
226226
/// the data read through the get functions, at the moment of the event.
227227
/// So, you can assume the snapshot contains the result of the last executed input.
228228
Processor<const Snapshot & /*p_snapshot*/> event_snapshot_update_finished;
229-
Processor<const Snapshot & /*p_snapshot*/> event_snapshot_applied;
229+
Processor<const Snapshot & /*p_snapshot*/, int /*p_frame_count_to_rewind*/> event_snapshot_applied;
230230
Processor<const Snapshot & /*p_received_snapshot*/> event_received_server_snapshot;
231231
Processor<FrameIndex, int /*p_index*/, int /*p_count*/> event_rewind_frame_begin;
232232
Processor<FrameIndex, ObjectHandle /*p_app_object_handle*/, const std::vector<std::string> & /*p_var_names*/, const std::vector<VarData> & /*p_client_values*/, const std::vector<VarData> & /*p_server_values*/> event_desync_detected_with_info;
@@ -816,7 +816,7 @@ class ClientSynchronizer final : public Synchronizer {
816816
const struct PlayerController &p_local_player_controller,
817817
Snapshot &r_no_rewind_recover);
818818

819-
void __pcr__sync__rewind();
819+
void __pcr__sync__rewind(FrameIndex p_last_checked_input_id, const PlayerController &p_local_player_controller);
820820

821821
void __pcr__rewind(
822822
const FrameIndex p_checkable_frame_index,
@@ -847,6 +847,8 @@ class ClientSynchronizer final : public Synchronizer {
847847
void apply_snapshot(
848848
const Snapshot &p_snapshot,
849849
const int p_flag,
850+
// The frames rewinded just after this function.
851+
const int p_frame_count_to_rewind,
850852
std::vector<std::string> *r_applied_data_info,
851853
const bool p_skip_custom_data = false,
852854
const bool p_skip_simulated_objects_update = false,

tests/test_doll_simulation.cpp

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -86,22 +86,25 @@ class TDSControlledObject : public NS::LocalSceneObject {
8686
}
8787

8888
// TODO remove this, is here just for debug.
89-
if (authoritative_peer_id != 2) {
90-
return;
91-
}
92-
NS::PeerNetworkedController *controller = scene_owner->scene_sync->get_controller_for_peer(authoritative_peer_id);
93-
NS::FrameIndex fi = controller->get_current_frame_index();
94-
std::string frame_info;
95-
frame_info += "FrameIndex: " + fi;
96-
frame_info += " initial X: " + std::to_string(current.data.vec.x) + " Y: " + std::to_string(current.data.vec.y);
97-
frame_info += " input: " + std::string(advance_or_turn ? "advance" : "turn");
98-
99-
if (controller->is_doll_controller()) {
100-
NS::SceneSynchronizerBase::__print_line("Doll controller " + frame_info);
101-
} else if (controller->is_player_controller()) {
102-
NS::SceneSynchronizerBase::__print_line("Player controller " + frame_info);
103-
} else if (controller->is_server_controller()) {
104-
NS::SceneSynchronizerBase::__print_line("Server controller " + frame_info);
89+
const bool debug_procesing = false;
90+
if (!debug_procesing) {
91+
if (authoritative_peer_id != 2) {
92+
return;
93+
}
94+
NS::PeerNetworkedController *controller = scene_owner->scene_sync->get_controller_for_peer(authoritative_peer_id);
95+
NS::FrameIndex fi = controller->get_current_frame_index();
96+
std::string frame_info;
97+
frame_info += "FrameIndex: " + fi;
98+
frame_info += " initial X: " + std::to_string(current.data.vec.x) + " Y: " + std::to_string(current.data.vec.y);
99+
frame_info += " input: " + std::string(advance_or_turn ? "advance" : "turn");
100+
101+
if (controller->is_doll_controller()) {
102+
NS::SceneSynchronizerBase::__print_line("Doll controller " + frame_info);
103+
} else if (controller->is_player_controller()) {
104+
NS::SceneSynchronizerBase::__print_line("Player controller " + frame_info);
105+
} else if (controller->is_server_controller()) {
106+
NS::SceneSynchronizerBase::__print_line("Server controller " + frame_info);
107+
}
105108
}
106109
}
107110

0 commit comments

Comments
 (0)