Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
44 changes: 35 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,13 @@ 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; }
void setShortGrouping(Bool shortGrouping); ///< Send at one frame while the run ahead is close to its floor.
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 +85,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 +115,17 @@ 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;

Bool m_shortGrouping;
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.
};
149 changes: 148 additions & 1 deletion 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 @@ -108,6 +112,7 @@ class ConnectionManager
UnsignedInt getPacketRouterSlot(); ///< Returns the current packet router's slot.
PlayerLeaveCode disconnectPlayer(Int slot); ///< Disconnect this player immediately. This should only be called by the disconnect manager.
#if defined(GENERALS_ONLINE)
void playerClosedGracefully(Int64 userID);
PlayerLeaveCode disconnectPlayer(int64_t userID); ///< Disconnect this player immediately. This should only be called by the disconnect manager.
#endif
void disconnectLocalPlayer(); ///< Does whatever is necessary to get TheNetwork to realize that it should be leaving the game now.
Expand Down Expand Up @@ -147,13 +152,18 @@ class ConnectionManager

void notifyOthersOfCurrentFrame(Int frame);
void sendFrameDataToPlayer(UnsignedInt playerID, UnsignedInt startingFrame);
void sendSingleFrameToPlayer(UnsignedInt playerID, UnsignedInt frame);
void sendSingleFrameToPlayer(UnsignedInt playerID, UnsignedInt frame, Bool ownOnly = FALSE);
void notifyOthersOfNewFrame(UnsignedInt frame);

UnsignedInt getNextPacketRouterSlot(UnsignedInt playerID); ///< returns the packet router player that comes after the given player.
Int getCurrentSenderSlot() const { return m_currentSenderSlot; }

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 +175,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 @@ -192,6 +206,11 @@ class ConnectionManager
Real getMaximumLatency(); ///< Returns the average of the two highest average latencies between players.

void requestFrameDataResend(Int playerID, UnsignedInt frame); ///< request of this player that he send the specified frame's data.
Int getSlotOfAddress(UnsignedInt addr, UnsignedShort port);
UnsignedShort generateImpersonatedCommandID(Int playerID, UnsignedInt frame);
#if !RETAIL_COMPATIBLE_NETWORKING
void askForOutPlayerFrame(Int slot, UnsignedInt frame);
#endif

// The connections are set up like the slot list. The connection corresponding to the local
// player's position in the slot list will be null. Connections corresponding to slots that
Expand All @@ -212,6 +231,14 @@ class ConnectionManager

NetCommandList *m_pendingCommands;
NetCommandList *m_relayedCommands;
Int m_currentSenderSlot; ///< The slot of the peer the packet being read came from, -1 when not known.
UnsignedShort m_lastSeenCommandID[MAX_SLOTS];
Bool m_haveLastSeenCommandID[MAX_SLOTS];
#if !RETAIL_COMPATIBLE_NETWORKING
Int m_outFrameAskedSlot;
UnsignedInt m_outFrameAskedFrame;
UnsignedInt m_outFrameAskedTime;
#endif

FrameMetrics m_frameMetrics;

Expand All @@ -226,6 +253,126 @@ 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];

Int m_runAheadLowerCount;
Int m_runAheadLowerPeak;
Bool isDuplicateCommand(NetCommandMsg *msg);

Int slotForUserID(Int64 userID) const;
Bool isPeerActive(Int slot) const;
void onDirectFrameAck(UnsignedInt frame, Int peerSlot);
std::map<UnsignedInt, UnsignedInt> m_frameAckedMasks; ///< Frame -> the peers that acked its frame info so far.
Bool isDirectSendActive();
Bool m_directSendActive;
time_t m_directSendCheckedAt;
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);
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;
Bool m_runAheadStillInitial; ///< No run ahead change has taken effect yet, the first decisions apply without the decrease hold.
Int m_pendingRunAhead; ///< Router: the run ahead of the last command it issued.
UnsignedInt m_pendingExecFrame; ///< Router: the execution frame of that command.
Int applyDecreaseHold(Int oldRunAhead, Int askedRunAhead, Int decreaseAfter);
public:
void noteRunAheadApplied(Int oldRunAhead, Int newRunAhead); ///< The run ahead command took effect on this frame.
private:

Bool m_shortGrouping; ///< The run ahead is within 2 frames of its floor, connections send at one frame
Int getRunAheadFloor() const;
time_t getEffectiveDecisionIntervalMs(Int runAhead) const; ///< The longer of the configured floor and 2 x the run ahead in ms
void updateShortGrouping();
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