FlightGear allows to define the frequency of position reports, but most users seem to leave it at a default setting, which is probably fine for users with strong CPUs, but not optimal for less capable hardware.
In addition to that, packets tend to arrive not evenly spaced but in bursts, which concentrates the processing load.
With the above in mind I wrote a patch to address this issue on the receiving end. The idea is simple: remember when the first position report from a given callsign was processed and ignore subsequent reports from that callsign for a configured amount of time. Then the cycle repeats. This is applied to position reports from all callsigns. Only position reports are moderated, chat messages etc. are processed as before.
The default behaviour of this code path is inactive - i.e. FlightGear acts as normal. To activate this defensive moderation of incoming position reports, a property /sim/multiplay/moderate-position-reports must be set to true and moderation interval (in seconds) must be increased from default 0.0 in /sim/multiplay/position-moderation-interval.
I tested intervals of 5 and 10 seconds.
The patch should apply cleanly to recent 'next' branch. If there is interest I can also make a patch for current 'stable'.
I tested this patch during our Wednesday multiplayer event and it worked as expected, that is, frame rate was comparable to flying in single player, with the (expected) side effect of other players' aircraft appear to 'jump' to a new position every now and then.
- Code: Select all
diff --git a/src/MultiPlayer/multiplaymgr.cxx b/src/MultiPlayer/multiplaymgr.cxx
index fcaa3c7..657c704 100644
--- a/src/MultiPlayer/multiplaymgr.cxx
+++ b/src/MultiPlayer/multiplaymgr.cxx
@@ -8,6 +8,7 @@
// Copyright (C) 2003 Airservices Australia
// Copyright (C) 2005 Oliver Schroeder
// Copyright (C) 2006 Mathias Froehlich
+// Copyright (C) 2021 Mariusz Matuszek (moderation of position reports)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
@@ -35,6 +36,8 @@
#include <errno.h>
#include <memory>
+#include <chrono>
+
#include <simgear/debug/logstream.hxx>
#include <simgear/math/sg_random.hxx>
#include <simgear/misc/sg_dir.hxx>
@@ -1059,6 +1062,10 @@ FGMultiplayMgr::FGMultiplayMgr()
pReplayState = fgGetNode("/sim/replay/replay-state", true);
pLogRawSpeedMultiplayer = fgGetNode("/sim/replay/log-raw-speed-multiplayer", true);
+ pMultiPlayModeratePositionReports = fgGetNode("/sim/multiplay/moderate-position-reports", true);
+ pMultiPlayPositionModerationInterval = fgGetNode("/sim/multiplay/position-moderation-interval", true);
+ fgSetBool("/sim/multiplay/moderate-position-reports", false);
+ fgSetDouble("/sim/multiplay/position-moderation-interval", 0.0);
} // FGMultiplayMgr::FGMultiplayMgr()
//////////////////////////////////////////////////////////////////////
@@ -1960,6 +1967,14 @@ FGMultiplayMgr::update(double dt)
/// Just for expiry
long stamp = SGTimeStamp::now().getSeconds();
+ // For incoming position report moderation
+ std::chrono::duration<double> time_span;
+ std::chrono::steady_clock::time_point time_now;
+ bool moderatePositionReports = pMultiPlayModeratePositionReports->getBoolValue();
+ double positionReportModerationInterval = pMultiPlayPositionModerationInterval->getDoubleValue();
+
+ if (positionReportModerationInterval < 0.0) positionReportModerationInterval = 0.0;
+
//////////////////////////////////////////////////
// Send if required
//////////////////////////////////////////////////
@@ -2023,7 +2038,22 @@ FGMultiplayMgr::update(double dt)
ProcessChatMsg(msgBuf, SenderAddress);
break;
case POS_DATA_ID:
- ProcessPosMsg(msgBuf, SenderAddress, stamp);
+ // cout << "+++++moderation: " << moderatePositionReports << ":" << positionReportModerationInterval << "\n";
+ if (moderatePositionReports) { // moderate frequent position updates
+ time_now = std::chrono::steady_clock::now();
+ time_span = std::chrono::duration_cast<std::chrono::duration<double>>(
+ time_now - lastPositionReportTime[MsgHdr->Callsign]
+ );
+ if (time_span.count() >= positionReportModerationInterval) {
+ //cout << "\n++" << MsgHdr->Callsign << ":" << time_span.count() << " " ; //position message processed
+ lastPositionReportTime[MsgHdr->Callsign] = time_now;
+ ProcessPosMsg(msgBuf, SenderAddress, stamp);
+ } else {
+ //cout << "--" << MsgHdr->Callsign << ":" << time_span.count() << " " ; // position message skipped
+ }
+ } else { // do not moderate
+ ProcessPosMsg(msgBuf, SenderAddress, stamp);
+ }
break;
case UNUSABLE_POS_DATA_ID:
case OLD_OLD_POS_DATA_ID:
diff --git a/src/MultiPlayer/multiplaymgr.hxx b/src/MultiPlayer/multiplaymgr.hxx
index e65417a..7505479 100644
--- a/src/MultiPlayer/multiplaymgr.hxx
+++ b/src/MultiPlayer/multiplaymgr.hxx
@@ -7,6 +7,7 @@
//
// Copyright (C) 2003 Airservices Australia
// Copyright (C) 2005 Oliver Schroeder
+// Copyright (C) 2021 Mariusz Matuszek (moderation of position reports)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
@@ -39,6 +40,9 @@ const int MAX_MP_PROTOCOL_VERSION = 2;
#include <vector>
#include <memory>
+#include <chrono>
+#include <unordered_map>
+
#include <simgear/compiler.h>
#include <simgear/props/props.hxx>
#include <simgear/io/raw_socket.hxx>
@@ -145,6 +149,9 @@ private:
SGPropertyNode *pMultiPlayTransmitPropertyBase;
SGPropertyNode *pReplayState;
SGPropertyNode *pLogRawSpeedMultiplayer;
+
+ SGPropertyNode *pMultiPlayModeratePositionReports;
+ SGPropertyNode *pMultiPlayPositionModerationInterval;
typedef std::map<unsigned int, const struct IdPropertyList*> PropertyDefinitionMap;
PropertyDefinitionMap mPropertyDefinition;
@@ -158,6 +165,10 @@ private:
std::deque<std::shared_ptr<std::vector<char>>> mRecordMessageQueue;
std::deque<std::shared_ptr<std::vector<char>>> mReplayMessageQueue;
+
+ // Map of last seen position reports
+ typedef unordered_map<string, std::chrono::steady_clock::time_point> LastSeenMap;
+ LastSeenMap lastPositionReportTime;
};
#endif