2
0
Эх сурвалжийг харах

In absence of packet loss, use dynamic send rate increase/decrease based on utilization.
Removed unused UDPMessageConnection code.

Lasse Öörni 14 жил өмнө
parent
commit
dca6687166

+ 2 - 2
Docs/Reference.dox

@@ -232,7 +232,7 @@ This requires a successfully loaded ScriptFile resource, whose \ref ScriptFile::
 ScriptFile* file = GetSubsystem<ResourceCache>()->GetResource<ScriptFile>("Scripts/MyScript.as");
 
 VariantVector parameters;
-parameters.push_back(Variant(100)); // Add an int parameter
+parameters.Push(Variant(100)); // Add an int parameter
 file->Execute("void MyFunction(int)", parameters); // Execute
 \endcode
 
@@ -269,7 +269,7 @@ Subscribing to \ref Events "events" in script behaves differently depending on w
 
 The script object's active/inactive state can be controlled through the \ref ScriptInstance::SetActive "SetActive()" function. When inactive, the scripted update methods or event handlers will not be called. This can be used to reduce CPU load in a large or densely populated scene.
 
-There are shortcut methods on the script side for creating and accessing a node's script object: node.CreateScriptObject() and node.GetScriptObject(). These are not actual Node member functions on the C++ side. CreateScriptObject() takes the script file name (or alternatively, a ScriptFile object handle) and class name as parameters and creates a ScriptInstance component automatically, then creates the script object. For example:
+There are shortcut methods on the script side for creating and accessing a node's script object: node.CreateScriptObject() and node.GetScriptObject() (alternatively, if the node has only one ScriptInstance, and a specific class is not needed, the node's scriptObject property can also be used.) These are not actual Node member functions on the C++ side. CreateScriptObject() takes the script file name (or alternatively, a ScriptFile object handle) and class name as parameters and creates a ScriptInstance component automatically, then creates the script object. For example:
 
 \code
 ScriptObject@ object = node.CreateScriptObject("Scripts/MyClass.as", "MyClass");

+ 2 - 2
Engine/Network/Network.h

@@ -79,7 +79,7 @@ public:
     void UnregisterRemoteEvent(StringHash eventType);
     /// Unregister all remote events. This results in all being allowed
     void UnregisterAllRemoteEvents();
-    /// %Set the package download cache path
+    /// %Set the package download cache directory
     void SetPackageCacheDir(const String& path);
     
     /// Return network update FPS
@@ -94,7 +94,7 @@ public:
     bool IsServerRunning() const;
     /// Return whether a remote event is allowed to be sent and received. If no events are registered, all are allowed
     bool CheckRemoteEvent(StringHash eventType) const;
-    /// Return the package download cache path
+    /// Return the package download cache directory
     const String& GetPackageCacheDir() const { return packageCacheDir_; }
     
     /// Process incoming messages from connections. Called by HandleBeginFrame

+ 4 - 12
ThirdParty/kNet/include/kNet/UDPMessageConnection.h

@@ -101,10 +101,6 @@ private:
 	/// @param bytesRead [out] Returns the total number of bytes containes in the datagrams that were read.
 	SocketReadResult UDPReadSocket(size_t &bytesRead); // [worker thread]
 
-	// Congestion control and data rate management:
-	void PerformFlowControl(); // [worker thread]
-	void HandleFlowControlRequestMessage(const char *data, size_t numBytes); // [worker thread]
-
 	void UpdateRTOCounterOnPacketAck(float rtt); // [worker thread]
 	void UpdateRTOCounterOnPacketLoss(); // [worker thread]
 
@@ -158,6 +154,9 @@ private:
 
 	float lowestDatagramSendRateOnPacketLoss;
 
+	/// The currently achieved send rate for purposes flow control
+	float actualDatagramSendRate;
+
 	// These variables correspond to RFC2988, http://tools.ietf.org/html/rfc2988 , section 2.
 	bool rttCleared; ///< If true, smoothedRTT and rttVariation do not contain meaningful values, but "are clear".
 	float smoothedRTT;
@@ -222,6 +221,7 @@ private:
 
 	/// Used to perform flow control on outbound UDP messages.
 	mutable tick_t lastDatagramSendTime; ///\todo. No mutable. Rename to nextDatagramSendTime.
+	mutable tick_t lastActualDatagramSendTime;
 
 	/// Connection control update timer.
 	PolledTimer udpUpdateTimer;
@@ -245,14 +245,6 @@ private:
 	// Contains a list of all messages we've received that we need to Ack at some point.
 	PacketAckTrackMap inboundPacketAckTrack;
 
-	/// The number of UDP packets to send out per second.
-	int datagramOutRatePerSecond;
-
-	/// The number of UDP packets to receive per second. Of course the local end of the
-	/// connection cannot directly control this, but it uses the FlowControlRequest
-	/// packet to send it to the other party.
-	int datagramInRatePerSecond;
-
 	/// Contains the reliable message numbers of all reliable messages we've received.
 	/// Used to detect and discard duplicate messages we've received.
 	Set<unsigned long> receivedReliableMessages;

+ 30 - 68
ThirdParty/kNet/src/UDPMessageConnection.cpp

@@ -44,7 +44,6 @@ using namespace std;
 namespace kNet
 {
 
-static const int initialDatagramRatePerSecond = 30;
 /// The maximum time to wait before acking a packet. If there are enough packets to ack for a full ack message,
 /// acking will be performed earlier. (milliseconds)
 static const float maxAckDelay = 33.f; // (1/30th of a second)
@@ -61,9 +60,8 @@ UDPMessageConnection::UDPMessageConnection(Network *owner, NetworkServer *ownerS
 retransmissionTimeout(3.f), numAcksLastFrame(0), numLossesLastFrame(0), smoothedRTT(3.f), rttVariation(0.f), rttCleared(true), // Set RTT initial values as per RFC 2988.
 lastReceivedInOrderPacketID(0), 
 lastSentInOrderPacketID(0), datagramPacketIDCounter(1),
-packetLossRate(0.f), packetLossCount(0.f), datagramOutRatePerSecond(initialDatagramRatePerSecond), 
-datagramInRatePerSecond(initialDatagramRatePerSecond),
-datagramSendRate(10),
+packetLossRate(0.f), packetLossCount(0.f),
+datagramSendRate(100.f), actualDatagramSendRate(0.f),
 receivedPacketIDs(64 * 1024), outboundPacketAckTrack(1024),
 previousReceivedPacketID(0), queuedInboundDatagrams(128)
 {
@@ -158,10 +156,9 @@ void UDPMessageConnection::Initialize()
 	smoothedRTT = 3.f;
 	rttVariation = 0.f;
 
-	datagramSendRate = 70.f; // At start, send one datagram per second.
 	lastFrameTime = Clock::Tick();
-
 	lastDatagramSendTime = Clock::Tick();
+	lastActualDatagramSendTime = Clock::Tick();
 }
 
 void UDPMessageConnection::PerformPacketAckSends()
@@ -269,11 +266,12 @@ void UDPMessageConnection::HandleFlowControl()
 	AssertInWorkerThreadContext();
 
 	// In packets/second.
-	const float totalEstimatedBandwidth = 1000; ///\todo Make this estimation dynamic as in UDT or similar.
-	const float additiveIncreaseAggressiveness = 0.1f;
+	const float minBandwidth = 25.f;
+	const float maxBandwidth = 2000.f;
+	const float additiveIncreaseAggressiveness = 5.0f;
 
 	const tick_t frameLength = Clock::TicksPerSec() / 100; // in ticks
-	// Additively increase the outbound send rate.
+	// If no losses, additively increase or decrease the outbound send rate based on demand
 	unsigned long numFrames = (unsigned long)(Clock::TicksInBetween(Clock::Tick(), lastFrameTime) / frameLength);
 	if (/*numAcksLastFrame > 0 &&*/ numFrames > 0)
 	{
@@ -287,13 +285,23 @@ void UDPMessageConnection::HandleFlowControl()
 //			datagramSendRate = max(1.f, datagramSendRate * 0.9f); // Multiplicative decreases.
 			LOG(LogVerbose, "Received %d losses. datagramSendRate backed to %.2f from %.2f", (int)numLossesLastFrame, datagramSendRate, oldRate);
 		}
-		else // Additive increases.
+		else // Change send rate based on on utilization
 		{
-			float increment = min((float)numFrames * additiveIncreaseAggressiveness * (totalEstimatedBandwidth - datagramSendRate), 1.f);
-			datagramSendRate += increment;
-			datagramSendRate = min(datagramSendRate, totalEstimatedBandwidth);
+			// If no packets have been sent for a while, drop the actual send rate toward zero (as it is otherwise updated only when packets are sent)
+			const tick_t now = Clock::Tick();
+			if (now - lastDatagramSendTime > Clock::TicksPerSec())
+				actualDatagramSendRate = 0.75f * actualDatagramSendRate;
+
+			float utilization = actualDatagramSendRate / datagramSendRate;
+			float delta = numFrames * additiveIncreaseAggressiveness;
+
+			if (utilization > 0.75f)
+				datagramSendRate = min(datagramSendRate + delta, maxBandwidth);
+			else if (utilization < 0.25f)
+				datagramSendRate = max(datagramSendRate - delta, minBandwidth);
+
+			//printf("Numframes %d Packets %f Util %f Sendrate %f\n", (int)numFrames, actualDatagramSendRate, utilization, datagramSendRate);
 			lowestDatagramSendRateOnPacketLoss = datagramSendRate;
-//			LOG(LogVerbose, "Incremented sendRate by %.2f to %.2f", increment, datagramSendRate);
 		}
 		numAcksLastFrame = 0;
 		numLossesLastFrame = 0;
@@ -899,6 +907,8 @@ bool UDPMessageConnection::CanSendOutNewDatagram() const
 
 void UDPMessageConnection::NewDatagramSent()
 {
+	tick_t oldLastDatagramSendTime = lastDatagramSendTime;
+
 	const tick_t datagramSendTickDelay = (tick_t)(Clock::TicksPerSec() / datagramSendRate);
 	const tick_t now = Clock::Tick();
 
@@ -906,6 +916,12 @@ void UDPMessageConnection::NewDatagramSent()
 		lastDatagramSendTime += datagramSendTickDelay;
 	else
 		lastDatagramSendTime = now;
+
+	// Calculate running smoothed send rate for flow control utilization determination
+	tick_t datagramInterval = now - lastActualDatagramSendTime;
+	if (datagramInterval > 0)
+		actualDatagramSendRate = min(0.75f * actualDatagramSendRate + 0.25f * (Clock::TicksPerSec() / datagramInterval), datagramSendRate);
+	lastActualDatagramSendTime = now;
 }
 
 void UDPMessageConnection::SendDisconnectMessage(bool isInternal)
@@ -938,31 +954,6 @@ void UDPMessageConnection::SendDisconnectAckMessage()
 	LOG(LogInfo, "UDPMessageConnection::SendDisconnectAckMessage: Sent DisconnectAck.");
 }
 
-void UDPMessageConnection::HandleFlowControlRequestMessage(const char *data, size_t numBytes)
-{
-	AssertInWorkerThreadContext();
-	/*
-	if (numBytes != 2)
-	{
-		LOG(LogError, "Malformed FlowControlRequest message received! Size was %d bytes, expected 2 bytes!", (int)numBytes);
-		return;
-	}
-
-	const u16 minOutboundRate = 5;
-	const u16 maxOutboundRate = 10 * 1024;
-	u16 newOutboundRate = *reinterpret_cast<const u16*>(data);
-	if (newOutboundRate < minOutboundRate || newOutboundRate > maxOutboundRate)
-	{
-		LOG(LogError, "Invalid FlowControlRequest rate %d packets/sec received! Ignored. Valid range (%d, %d)", (int)newOutboundRate,
-			(int)minOutboundRate, (int)maxOutboundRate);
-		return;
-	}
-
-//	LOG(LogVerbose, "Received FlowControl message. Adjusting OutRate from %d to %d msgs/sec.", (int)datagramOutRatePerSecond, (int)newOutboundRate);
-
-	datagramOutRatePerSecond = newOutboundRate;*/
-}
-
 int UDPMessageConnection::BiasedBinarySearchFindPacketIndex(PacketAckTrackQueue &queue, int packetID)
 {
 	///\bug Make this all packetID wrap-around -aware.
@@ -1192,32 +1183,6 @@ void UDPMessageConnection::HandleDisconnectAckMessage()
 	connectionState = ConnectionClosed;
 }
 
-void UDPMessageConnection::PerformFlowControl()
-{
-	AssertInWorkerThreadContext();
-
-	/*
-	// The manual flow control only applies to UDP connections.
-	if (socket->TransportLayer() == SocketOverTCP)
-		return;
-
-	const float maxAllowedPacketLossRate = 0.f;
-	if (GetPacketLossRate() > maxAllowedPacketLossRate)
-	{
-		float newInboundRate = PacketsInPerSec() * (1.f - GetPacketLossRate());
-//		LOG(LogVerbose, "Packet loss rate: %.2f. Adjusting InRate from %d to %d!", GetPacketLossRate(), datagramInRatePerSecond, (int)newInboundRate);
-		SetDatagramInFlowRatePerSecond((int)newInboundRate, true);
-	}
-	else if (PacketsInPerSec() >= (float)datagramInRatePerSecond / 2)
-	{
-		const int flowRateIncr = 50;
-//		LOG(LogVerbose, "Have received %.2f packets in/sec with loss rate of %.2f. Increasing InRate from %d to %d.",
-//			PacketsInPerSec(), GetPacketLossRate(), datagramInRatePerSecond, datagramInRatePerSecond + flowRateIncr);
-		SetDatagramInFlowRatePerSecond(datagramInRatePerSecond + flowRateIncr, true);
-	}
-	*/
-}
-
 void UDPMessageConnection::ComputePacketLoss()
 {
 	AssertInWorkerThreadContext();
@@ -1309,9 +1274,6 @@ bool UDPMessageConnection::HandleMessage(packet_id_t packetID, u32 messageID, co
 	case MsgIdPingReply:
 		return false; // We don't do anything with these messages, the MessageConnection base class handles these.
 
-	case MsgIdFlowControlRequest:
-		HandleFlowControlRequestMessage(data, numBytes);
-		return true;
 	case MsgIdPacketAck:
 		HandlePacketAckMessage(data, numBytes);
 		return true;