Skip to content

Commit f9b0989

Browse files
committed
Implementing the doll processing.
- Implemented the doll snapshot storing - Implemented the doll snapshot comparison. - WIP character processing.
1 parent 0db3be5 commit f9b0989

6 files changed

Lines changed: 273 additions & 100 deletions

File tree

core/peer_networked_controller.cpp

Lines changed: 233 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,24 +1345,36 @@ bool PlayerController::can_accept_new_inputs() const {
13451345
DollController::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

13571363
DollController::~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

13681380
bool 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

14231435
bool 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

14661439
void 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+
14871482
bool 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

15271564
void 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+
15681679
bool 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

15851745
NoNetController::NoNetController(PeerNetworkedController *p_peer_controller) :

0 commit comments

Comments
 (0)