Skip to content
2 changes: 2 additions & 0 deletions Core/GameEngine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@ set(GAMEENGINE_SRC
# Include/GameLogic/WeaponStatus.h
Include/GameNetwork/Connection.h
Include/GameNetwork/ConnectionManager.h
Include/GameNetwork/ConnectionStats.h
Include/GameNetwork/DisconnectManager.h
Include/GameNetwork/DownloadManager.h
Include/GameNetwork/FileTransfer.h
Expand Down Expand Up @@ -1099,6 +1100,7 @@ set(GAMEENGINE_SRC
Source/GameLogic/System/RankInfo.cpp
Source/GameNetwork/Connection.cpp
Source/GameNetwork/ConnectionManager.cpp
Source/GameNetwork/ConnectionStats.cpp
Source/GameNetwork/DisconnectManager.cpp
Source/GameNetwork/DownloadManager.cpp
Source/GameNetwork/FileTransfer.cpp
Expand Down
42 changes: 33 additions & 9 deletions Core/GameEngine/Include/GameNetwork/Connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,9 @@
#include "GameNetwork/User.h"
#include "GameNetwork/Transport.h"
#include "GameNetwork/NetPacket.h"

#include "GameNetwork/ConnectionStats.h"
#if defined(GENERALS_ONLINE)
#define CONNECTION_LATENCY_HISTORY_LENGTH 10
#else
#define CONNECTION_LATENCY_HISTORY_LENGTH 200
#include "GameNetwork/NetworkSettings.h"
#endif

class Connection : public MemoryPoolObject
Expand All @@ -66,6 +64,12 @@ class Connection : public MemoryPoolObject
void setUser(User *user);
User *getUser();
void setFrameGrouping(time_t frameGrouping);
#if defined(GENERALS_ONLINE)
void setNetworkSettings(const NetworkSettings &settings, Bool unreliableTransport) { m_networkSettings = settings; m_unreliableTransport = unreliableTransport; }
#endif
void setSlot(Int slot) { m_slot = slot; }
Int getRedundancyDepth() const { return m_redundancyDepth; }
time_t getFrameGrouping() const { return m_frameGrouping; } ///< The send interval in use, shorter than the configured one while redundancy is on.

void sendNetCommandMsg(NetCommandMsg *msg, UnsignedByte relay);

Expand All @@ -80,12 +84,27 @@ class Connection : public MemoryPoolObject
void setQuitting();
Bool isQuitting() { return m_isQuitting; }

const ConnectionStats& getStats() const { return m_stats; }

#if defined(RTS_DEBUG)
void debugPrintCommands();
#endif

protected:
void doRetryMetrics();
#if defined(GENERALS_ONLINE)
NetworkSettings m_networkSettings;
Bool m_unreliableTransport;
#endif

void resetRedundancy();
void updateRedundancy(time_t curtime, Bool &active, time_t &window, Int &depth);
Bool hasLossEvidence(); ///< A packet was lost in either direction lately, or nothing says otherwise
Bool getOutgoingLoss(float &lossPercent, Int &longestRun); ///< FALSE when the peer sent no packet feedback
void noteLossEvent(time_t now, Bool burst);
void setRedundancyDepth(Int depth, Int cap, time_t now);
Bool isQueueBuilding(time_t now); ///< Round trip at 2x its low while packets are lost: a full queue
void applyFrameGrouping();
void addRedundantCommands(NetPacket *packet, time_t curtime, time_t window, Int depth);

Bool m_isQuitting;
UnsignedInt m_quitTime;
Expand All @@ -95,11 +114,16 @@ class Connection : public MemoryPoolObject

NetCommandList *m_netCommandList;
time_t m_retryTime; ///< The time between sending retry packets for this connection. Time is in milliseconds.
Real m_averageLatency; ///< The average time between sending a command and receiving an ACK.
Real m_latencies[CONNECTION_LATENCY_HISTORY_LENGTH]; ///< List of the last 100 latencies.
ConnectionStats m_stats; ///< Latency, jitter and loss of this connection.

Int m_redundancyDepth; ///< How many later packets each un-acked command is repeated in, 0 on a clean connection.
Int m_redundancyCap; ///< The deepest depth, from the longest burst to repair and the send interval.
time_t m_lastLossEventTime; ///< What the decay counts from.
time_t m_lastDepthChangeTime; ///< A burst needs a round trip to show whether the new depth helped.
Bool m_queueSuspected; ///< A full queue on the path, redundancy steps down instead of up.
Int m_slot;

time_t m_configuredFrameGrouping;
time_t m_frameGrouping; ///< The minimum time between packet sends.
time_t m_lastTimeSent; ///< The time of the last packet send.
Int m_numRetries; ///< The number of retries for the last second.
time_t m_retryMetricsTime; ///< The start time of the current retry metrics thing.
};
115 changes: 115 additions & 0 deletions Core/GameEngine/Include/GameNetwork/ConnectionManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
#include "GameNetwork/FrameMetrics.h"
#include "GameNetwork/NetworkDefs.h"
#include "GameNetwork/DisconnectManager.h"
#if defined(GENERALS_ONLINE)
#include "GameNetwork/NetworkSettings.h"
#endif

class GameInfo;
class NetCommandWrapperList;
Expand All @@ -56,6 +59,7 @@ class ConnectionManager
virtual void update(Bool isInGame); ///< Service the Connections being managed by this instance.

#if defined(GENERALS_ONLINE)
const NetworkSettings &getNetworkSettings() const { return m_networkSettings; }
void SeedLatencyData(int highestLatency)
{
for (int i = 0; i < MAX_SLOTS; ++i) {
Expand Down Expand Up @@ -154,6 +158,10 @@ class ConnectionManager

Int getAverageFPS();
Int getSlotAverageFPS(Int slot);
#if defined(GENERALS_ONLINE)
void noteStalling(Bool stalling, UnsignedInt frame); ///< Called every network update with whether the game is waiting for commands.
#endif
const ConnectionStats* getConnectionStats(Int slot) const; ///< Returns nullptr when there is no connection for the slot.

#if defined(RTS_DEBUG)
void debugPrintConnectionCommands();
Expand All @@ -165,6 +173,10 @@ class ConnectionManager
Int getPingsReceived();

private:
#if defined(GENERALS_ONLINE)
NetworkSettings m_networkSettings;
#endif

void doRelay();
void doKeepAlive();
void sendRemoteCommand(NetCommandRef *msg);
Expand Down Expand Up @@ -212,6 +224,9 @@ class ConnectionManager

NetCommandList *m_pendingCommands;
NetCommandList *m_relayedCommands;
#if !RETAIL_COMPATIBLE_NETWORKING
Int m_currentSenderSlot; ///< The slot of the peer the packet being read came from, -1 when not known.
#endif

FrameMetrics m_frameMetrics;

Expand All @@ -226,6 +241,106 @@ class ConnectionManager
UnsignedInt m_smallestPacketArrivalCushion;
Bool m_didSelfSlug;

#if defined(GENERALS_ONLINE)
// the last command ids seen per sender, so a redundant copy of a command that is not synchronized is dropped
enum { RECENT_ID_COUNT = 256 };
struct RecentCommandIds
{
Bool valid;
UnsignedShort highest;
Bool seen[RECENT_ID_COUNT];
};
RecentCommandIds m_recentIds[MAX_SLOTS];

Bool isDuplicateCommand(NetCommandMsg *msg);

Int slotForUserID(Int64 userID) const;
UnsignedInt m_formerRouterMask; ///< Slots that were the packet router in this match, their last packets can still be on the way.
Bool isCommandFromSender(NetCommandMsg *msg, Int senderSlot); ///< A command must come from the player it names, unless the packet router relays it
Bool noteSecurityDrop();
Bool m_loggedSenderUnavailable;
Int m_securityDrops;
void resetRecentCommandIds();

// Frames with commands and chat that a peer passes on for another player need that player's signature.
// Every peer keeps the signatures it gets as long as the frames, so whoever holds a frame holds its proof.
enum { SIGNATURE_BYTES = 64, SIGNATURE_WAIT_MS = 3000, HELD_CHAT_COUNT = 8 };
struct FrameSignature
{
Bool used;
UnsignedInt frame;
Bool relayed; ///< a command or the frame info arrived from a peer other than the author
Bool haveSignature;
Bool verified;
Int relayedBy;
time_t waitingSince; ///< when the frame was complete without a signature, 0 when not waiting
UnsignedByte signature[SIGNATURE_BYTES];
};
struct HeldChat
{
NetChatCommandMsg *msg;
Int relayedBy;
time_t since;
};
struct ChatSignature
{
Bool used;
UnsignedInt author;
UnsignedShort commandID;
time_t since;
UnsignedByte signature[SIGNATURE_BYTES];
};
enum { RELAYED_WRAPPER_COUNT = 8 };
struct RelayedWrapper
{
Bool used;
UnsignedShort wrappedCommandID;
Int relayedBy;
};
std::vector<FrameSignature> m_frameSignatures[MAX_SLOTS]; ///< Indexed like the frame data, by frame modulo FRAME_DATA_LENGTH.
HeldChat m_heldChats[HELD_CHAT_COUNT];
ChatSignature m_chatSignatures[HELD_CHAT_COUNT];
RelayedWrapper m_relayedWrappers[MAX_SLOTS][RELAYED_WRAPPER_COUNT];
Int m_nextRelayedWrapper[MAX_SLOTS];
Int64 m_signerUserID[MAX_SLOTS]; ///< The user id per slot, kept after the player leaves to verify its last frames.
UnsignedInt m_lastFrameInfoSent; ///< The last frame our frame info went out for, our data up to it is final.
void clearSignatureState(Bool releaseChats);
void requestPassedOnFrameAgain(Int slot, UnsignedInt frame, Int passedOnBy);
FrameSignature *findFrameSignature(Int slot, UnsignedInt frame);
FrameSignature &frameSignatureFor(Int slot, UnsignedInt frame);
Int64 signerUserID(Int slot);
void noteFramePassedOn(Int author, UnsignedInt frame, Int relayedBy, Bool changesFrame);
void noteWrapperPassedOn(NetCommandMsg *wrapper, Int relayedBy);
Int wrapperPassedOnBy(NetCommandMsg *msg);
Bool buildSignedFrame(Int slot, UnsignedInt frame, std::vector<UnsignedByte> &out);
Bool buildSignedCommand(Int slot, NetCommandMsg *msg, std::vector<UnsignedByte> &out);
void signLocalFrame(NetFrameCommandMsg *frameInfo);
void signLocalChat(NetChatCommandMsg *msg);
Bool buildSignedLeave(Int slot, std::vector<UnsignedByte> &out);
public:
Bool signLeaveNotice(NetDisconnectPlayerCommandMsg *msg); ///< The leaving player signs its leave notice.
Bool verifyLeaveNotice(NetDisconnectPlayerCommandMsg *msg); ///< The notice carries the signature of the player it says left.
private:
void attachStoredSignature(Int author, NetFrameCommandMsg *frameInfo);
Bool isPassedOnFrameAuthentic(Int slot, UnsignedInt frame);
void storeFrameSignature(Int author, UnsignedInt frame, const UnsignedByte *signature, Bool fromAuthor);
void processSignatures(NetSignaturesCommandMsg *msg);
Bool holdPassedOnChat(NetChatCommandMsg *msg);
void releaseHeldChat(Int index, const UnsignedByte *signature);
void expireHeldChats(time_t now);

ArrivalCushionStats m_cushionStats[MAX_SLOTS];
StallStats m_stallStats;
Bool m_stallActive;
time_t m_stallStartTime;
UnsignedInt m_stallMissingMask;
Int m_lastKnownRunAhead;
Int m_frameGroupingMs;
public:
Bool getOverlayInfo(Int slot, NetworkOverlayInfo &info); ///< Fills what the overlay and the connection rating need about one slot.
protected:
#endif

// -----------------------------------------------------------------------------
FileCommandMap s_fileCommandMap;
FileMaskMap s_fileRecipientMaskMap;
Expand Down
Loading
Loading