浏览代码

Added SphereCast function to PhysicsWorld.
Applied kNet bugfix.
Restored custom kNet UDP flow control mechanism.

Lasse Öörni 13 年之前
父节点
当前提交
d8eb85b9b4

+ 3 - 3
Bin/Data/Scripts/NinjaSnowWar.as

@@ -824,9 +824,9 @@ void UpdateCamera()
     // Collide camera ray with static objects (collision mask 2)
     // Collide camera ray with static objects (collision mask 2)
     Vector3 rayDir = (maxDist - minDist).Normalized();
     Vector3 rayDir = (maxDist - minDist).Normalized();
     float rayDistance = cameraMaxDist - cameraMinDist + cameraSafetyDist;
     float rayDistance = cameraMaxDist - cameraMinDist + cameraSafetyDist;
-    Array<PhysicsRaycastResult>@ result = gameScene.physicsWorld.Raycast(Ray(minDist, rayDir), rayDistance, 2);
-    if (result.length > 0)
-        rayDistance = Min(rayDistance, result[0].distance - cameraSafetyDist);
+    PhysicsRaycastResult result = gameScene.physicsWorld.RaycastSingle(Ray(minDist, rayDir), rayDistance, 2);
+    if (result.body !is null)
+        rayDistance = Min(rayDistance, result.distance - cameraSafetyDist);
 
 
     gameCameraNode.position = minDist + rayDir * rayDistance;
     gameCameraNode.position = minDist + rayDir * rayDistance;
     gameCameraNode.rotation = dir;
     gameCameraNode.rotation = dir;

+ 1 - 1
Docs/ScriptAPI.dox

@@ -2309,7 +2309,6 @@ Properties:<br>
 - bool hardwareDepthSupport (readonly)
 - bool hardwareDepthSupport (readonly)
 - bool hardwareShadowSupport (readonly)
 - bool hardwareShadowSupport (readonly)
 - bool hiresShadowSupport (readonly)
 - bool hiresShadowSupport (readonly)
-- bool compressedTextureSupport (readonly)
 - bool forceSM2
 - bool forceSM2
 - IntVector2[]@ resolutions (readonly)
 - IntVector2[]@ resolutions (readonly)
 - int[]@ multiSampleLevels (readonly)
 - int[]@ multiSampleLevels (readonly)
@@ -4086,6 +4085,7 @@ Methods:<br>
 - void UpdateCollisions()
 - void UpdateCollisions()
 - PhysicsRaycastResult[]@ Raycast(const Ray&, float arg1 = M_INFINITY, uint arg2 = 0xffff)
 - PhysicsRaycastResult[]@ Raycast(const Ray&, float arg1 = M_INFINITY, uint arg2 = 0xffff)
 - PhysicsRaycastResult RaycastSingle(const Ray&, float arg1 = M_INFINITY, uint arg2 = 0xffff)
 - PhysicsRaycastResult RaycastSingle(const Ray&, float arg1 = M_INFINITY, uint arg2 = 0xffff)
+- PhysicsRaycastResult SphereCast(const Ray&, float, float arg2 = M_INFINITY, uint arg3 = 0xffff)
 - void DrawDebugGeometry(bool)
 - void DrawDebugGeometry(bool)
 
 
 Properties:<br>
 Properties:<br>

+ 22 - 14
Engine/Engine/PhysicsAPI.cpp

@@ -55,20 +55,6 @@ static RigidBody* PhysicsRaycastResultGetRigidBody(PhysicsRaycastResult* ptr)
     return ptr->body_;
     return ptr->body_;
 }
 }
 
 
-static CScriptArray* PhysicsWorldRaycast(const Ray& ray, float maxDistance, unsigned collisionMask, PhysicsWorld* ptr)
-{
-    PODVector<PhysicsRaycastResult> result;
-    ptr->Raycast(result, ray, maxDistance, collisionMask);
-    return VectorToArray<PhysicsRaycastResult>(result, "Array<PhysicsRaycastResult>");
-}
-
-static PhysicsRaycastResult PhysicsWorldRaycastSingle(const Ray& ray, float maxDistance, unsigned collisionMask, PhysicsWorld* ptr)
-{
-    PhysicsRaycastResult result;
-    ptr->RaycastSingle(result, ray, maxDistance, collisionMask);
-    return result;
-}
-
 static void RegisterCollisionShape(asIScriptEngine* engine)
 static void RegisterCollisionShape(asIScriptEngine* engine)
 {
 {
     engine->RegisterEnum("ShapeType");
     engine->RegisterEnum("ShapeType");
@@ -197,6 +183,27 @@ static void RegisterJoint(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Joint", "JointType get_jointType() const", asMETHOD(Joint, GetJointType), asCALL_THISCALL);
     engine->RegisterObjectMethod("Joint", "JointType get_jointType() const", asMETHOD(Joint, GetJointType), asCALL_THISCALL);
 }
 }
 
 
+static CScriptArray* PhysicsWorldRaycast(const Ray& ray, float maxDistance, unsigned collisionMask, PhysicsWorld* ptr)
+{
+    PODVector<PhysicsRaycastResult> result;
+    ptr->Raycast(result, ray, maxDistance, collisionMask);
+    return VectorToArray<PhysicsRaycastResult>(result, "Array<PhysicsRaycastResult>");
+}
+
+static PhysicsRaycastResult PhysicsWorldRaycastSingle(const Ray& ray, float maxDistance, unsigned collisionMask, PhysicsWorld* ptr)
+{
+    PhysicsRaycastResult result;
+    ptr->RaycastSingle(result, ray, maxDistance, collisionMask);
+    return result;
+}
+
+static PhysicsRaycastResult PhysicsWorldSphereCast(const Ray& ray, float radius, float maxDistance, unsigned collisionMask, PhysicsWorld* ptr)
+{
+    PhysicsRaycastResult result;
+    ptr->SphereCast(result, ray, radius, maxDistance, collisionMask);
+    return result;
+}
+
 static void RegisterPhysicsWorld(asIScriptEngine* engine)
 static void RegisterPhysicsWorld(asIScriptEngine* engine)
 {
 {
     engine->RegisterObjectType("PhysicsRaycastResult", sizeof(PhysicsRaycastResult), asOBJ_VALUE | asOBJ_APP_CLASS_C);
     engine->RegisterObjectType("PhysicsRaycastResult", sizeof(PhysicsRaycastResult), asOBJ_VALUE | asOBJ_APP_CLASS_C);
@@ -213,6 +220,7 @@ static void RegisterPhysicsWorld(asIScriptEngine* engine)
     engine->RegisterObjectMethod("PhysicsWorld", "void UpdateCollisions()", asMETHOD(PhysicsWorld, UpdateCollisions), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "void UpdateCollisions()", asMETHOD(PhysicsWorld, UpdateCollisions), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "Array<PhysicsRaycastResult>@ Raycast(const Ray&in, float maxDistance = M_INFINITY, uint collisionMask = 0xffff)", asFUNCTION(PhysicsWorldRaycast), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("PhysicsWorld", "Array<PhysicsRaycastResult>@ Raycast(const Ray&in, float maxDistance = M_INFINITY, uint collisionMask = 0xffff)", asFUNCTION(PhysicsWorldRaycast), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("PhysicsWorld", "PhysicsRaycastResult RaycastSingle(const Ray&in, float maxDistance = M_INFINITY, uint collisionMask = 0xffff)", asFUNCTION(PhysicsWorldRaycastSingle), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("PhysicsWorld", "PhysicsRaycastResult RaycastSingle(const Ray&in, float maxDistance = M_INFINITY, uint collisionMask = 0xffff)", asFUNCTION(PhysicsWorldRaycastSingle), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("PhysicsWorld", "PhysicsRaycastResult SphereCast(const Ray&in, float, float maxDistance = M_INFINITY, uint collisionMask = 0xffff)", asFUNCTION(PhysicsWorldSphereCast), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("PhysicsWorld", "void DrawDebugGeometry(bool)", asMETHOD(PhysicsWorld, DrawDebugGeometry), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "void DrawDebugGeometry(bool)", asMETHOD(PhysicsWorld, DrawDebugGeometry), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "void set_gravity(Vector3)", asMETHOD(PhysicsWorld, SetGravity), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "void set_gravity(Vector3)", asMETHOD(PhysicsWorld, SetGravity), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "Vector3 get_gravity() const", asMETHOD(PhysicsWorld, GetGravity), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "Vector3 get_gravity() const", asMETHOD(PhysicsWorld, GetGravity), asCALL_THISCALL);

+ 33 - 2
Engine/Physics/PhysicsWorld.cpp

@@ -40,6 +40,7 @@
 
 
 #include <BulletCollision/BroadphaseCollision/btDbvtBroadphase.h>
 #include <BulletCollision/BroadphaseCollision/btDbvtBroadphase.h>
 #include <BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h>
 #include <BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h>
+#include <BulletCollision/CollisionShapes/btSphereShape.h>
 #include <BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h>
 #include <BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h>
 #include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
 #include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
 
 
@@ -195,7 +196,8 @@ void PhysicsWorld::Raycast(PODVector<PhysicsRaycastResult>& result, const Ray& r
 {
 {
     PROFILE(PhysicsRaycast);
     PROFILE(PhysicsRaycast);
     
     
-    btCollisionWorld::AllHitsRayResultCallback rayCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ + maxDistance * ray.direction_));
+    btCollisionWorld::AllHitsRayResultCallback rayCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ +
+        maxDistance * ray.direction_));
     rayCallback.m_collisionFilterGroup = (short)0xffff;
     rayCallback.m_collisionFilterGroup = (short)0xffff;
     rayCallback.m_collisionFilterMask = collisionMask;
     rayCallback.m_collisionFilterMask = collisionMask;
     
     
@@ -218,7 +220,8 @@ void PhysicsWorld::RaycastSingle(PhysicsRaycastResult& result, const Ray& ray, f
 {
 {
     PROFILE(PhysicsRaycastSingle);
     PROFILE(PhysicsRaycastSingle);
     
     
-    btCollisionWorld::ClosestRayResultCallback rayCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ + maxDistance * ray.direction_));
+    btCollisionWorld::ClosestRayResultCallback rayCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ +
+        maxDistance * ray.direction_));
     rayCallback.m_collisionFilterGroup = (short)0xffff;
     rayCallback.m_collisionFilterGroup = (short)0xffff;
     rayCallback.m_collisionFilterMask = collisionMask;
     rayCallback.m_collisionFilterMask = collisionMask;
     
     
@@ -240,6 +243,34 @@ void PhysicsWorld::RaycastSingle(PhysicsRaycastResult& result, const Ray& ray, f
     }
     }
 }
 }
 
 
+void PhysicsWorld::SphereCast(PhysicsRaycastResult& result, const Ray& ray, float radius, float maxDistance, unsigned collisionMask)
+{
+    btSphereShape shape(radius);
+    
+    btCollisionWorld::ClosestConvexResultCallback convexCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ +
+        maxDistance * ray.direction_));
+    convexCallback.m_collisionFilterGroup = (short)0xffff;
+    convexCallback.m_collisionFilterMask = collisionMask;
+    
+    world_->convexSweepTest(&shape, btTransform(btQuaternion::getIdentity(), convexCallback.m_convexFromWorld),
+        btTransform(btQuaternion::getIdentity(), convexCallback.m_convexToWorld), convexCallback);
+    
+    if (convexCallback.hasHit())
+    {
+        result.body_ = static_cast<RigidBody*>(convexCallback.m_hitCollisionObject->getUserPointer());
+        result.position_ = ToVector3(convexCallback.m_hitPointWorld);
+        result.normal_ = ToVector3(convexCallback.m_hitNormalWorld);
+        result.distance_ = (result.position_ - ray.origin_).Length();
+    }
+    else
+    {
+        result.body_ = 0;
+        result.position_ = Vector3::ZERO;
+        result.normal_ = Vector3::ZERO;
+        result.distance_ = M_INFINITY;
+    }
+}
+
 Vector3 PhysicsWorld::GetGravity() const
 Vector3 PhysicsWorld::GetGravity() const
 {
 {
     return ToVector3(world_->getGravity());
     return ToVector3(world_->getGravity());

+ 5 - 3
Engine/Physics/PhysicsWorld.h

@@ -125,13 +125,15 @@ public:
     void SetMaxNetworkAngularVelocity(float velocity);
     void SetMaxNetworkAngularVelocity(float velocity);
     /// %Set simulation step time accumulator.
     /// %Set simulation step time accumulator.
     void SetTimeAccumulator(float time);
     void SetTimeAccumulator(float time);
-    /// Perform a physics world raycast.
+    /// Perform a physics world raycast and return all hits.
     void Raycast(PODVector<PhysicsRaycastResult>& result, const Ray& ray, float maxDistance, unsigned collisionMask =
     void Raycast(PODVector<PhysicsRaycastResult>& result, const Ray& ray, float maxDistance, unsigned collisionMask =
         M_MAX_UNSIGNED);
         M_MAX_UNSIGNED);
-    /// Perform a physics world raycast and return the closest result.
+    /// Perform a physics world raycast and return the closest hit.
     void RaycastSingle(PhysicsRaycastResult& result, const Ray& ray, float maxDistance, unsigned collisionMask =
     void RaycastSingle(PhysicsRaycastResult& result, const Ray& ray, float maxDistance, unsigned collisionMask =
         M_MAX_UNSIGNED);
         M_MAX_UNSIGNED);
-    
+    /// Perform a physics world swept sphere test and return the closest hit.
+    void SphereCast(PhysicsRaycastResult& result, const Ray& ray, float radius, float maxDistance, unsigned collisionMask =
+        M_MAX_UNSIGNED);
     /// Return gravity.
     /// Return gravity.
     Vector3 GetGravity() const;
     Vector3 GetGravity() const;
     /// Return whether interpolation between simulation steps is enabled.
     /// Return whether interpolation between simulation steps is enabled.

+ 3 - 15
ThirdParty/kNet/include/kNet/UDPMessageConnection.h

@@ -16,6 +16,8 @@
 /** @file UDPMessageConnection.h
 /** @file UDPMessageConnection.h
 	@brief The UDPMessageConnection class. */
 	@brief The UDPMessageConnection class. */
 
 
+// Modified by Lasse Öörni for Urho3D
+
 #include "MessageConnection.h"
 #include "MessageConnection.h"
 #include "SequentialIntegerSet.h"
 #include "SequentialIntegerSet.h"
 #include "Array.h"
 #include "Array.h"
@@ -66,8 +68,6 @@ public:
 	UDPMessageConnection(Network *owner, NetworkServer *ownerServer, Socket *socket, ConnectionState startingState);
 	UDPMessageConnection(Network *owner, NetworkServer *ownerServer, Socket *socket, ConnectionState startingState);
 	~UDPMessageConnection();
 	~UDPMessageConnection();
 
 
-	void SetDatagramInFlowRatePerSecond(int newDatagramReceiveRate, bool internalCall);
-
 	float RetransmissionTimeout() const { return retransmissionTimeout; }
 	float RetransmissionTimeout() const { return retransmissionTimeout; }
 
 
 	float DatagramSendRate() const { return datagramSendRate; }
 	float DatagramSendRate() const { return datagramSendRate; }
@@ -99,10 +99,6 @@ private:
 	/// @param bytesRead [out] Returns the total number of bytes containes in the datagrams that were read.
 	/// @param bytesRead [out] Returns the total number of bytes containes in the datagrams that were read.
 	SocketReadResult UDPReadSocket(size_t &bytesRead); // [worker thread]
 	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 UpdateRTOCounterOnPacketAck(float rtt); // [worker thread]
 	void UpdateRTOCounterOnPacketLoss(); // [worker thread]
 	void UpdateRTOCounterOnPacketLoss(); // [worker thread]
 
 
@@ -153,8 +149,8 @@ private:
 
 
 	/// The flow control algorithm:
 	/// The flow control algorithm:
 	float datagramSendRate; ///< The number of datagrams/second to send.
 	float datagramSendRate; ///< The number of datagrams/second to send.
-
 	float lowestDatagramSendRateOnPacketLoss;
 	float lowestDatagramSendRateOnPacketLoss;
+	int slowModeDelay; ///< Go into slow increase mode for some time on receiving loss
 
 
 	// These variables correspond to RFC2988, http://tools.ietf.org/html/rfc2988 , section 2.
 	// 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".
 	bool rttCleared; ///< If true, smoothedRTT and rttVariation do not contain meaningful values, but "are clear".
@@ -243,14 +239,6 @@ private:
 	// Contains a list of all messages we've received that we need to Ack at some point.
 	// Contains a list of all messages we've received that we need to Ack at some point.
 	PacketAckTrackMap inboundPacketAckTrack;
 	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.
 	/// Contains the reliable message numbers of all reliable messages we've received.
 	/// Used to detect and discard duplicate messages we've received.
 	/// Used to detect and discard duplicate messages we've received.
 	std::set<unsigned long> receivedReliableMessages;
 	std::set<unsigned long> receivedReliableMessages;

+ 6 - 1
ThirdParty/kNet/src/NetworkServer.cpp

@@ -321,7 +321,12 @@ bool NetworkServer::ProcessNewUDPConnectionAttempt(Socket *listenSocket, const E
 	{
 	{
 		PolledTimer timer;
 		PolledTimer timer;
 		Lockable<ConnectionMap>::LockType clientsLock = clients.Acquire();
 		Lockable<ConnectionMap>::LockType clientsLock = clients.Acquire();
-		(*clientsLock)[endPoint] = connection;
+		if (clientsLock->find(endPoint) == clientsLock->end())
+			(*clientsLock)[endPoint] = connection;
+		else
+			LOG(LogError, "NetworkServer::ProcessNewUDPConnectionAttempt: Trying to overwrite an old connection with a new one! Discarding connection attempt datagram!",
+				timer.MSecsElapsed());
+
 
 
 		LOG(LogWaits, "NetworkServer::ProcessNewUDPConnectionAttempt: Accessing the connection list took %f msecs.",
 		LOG(LogWaits, "NetworkServer::ProcessNewUDPConnectionAttempt: Accessing the connection list took %f msecs.",
 			timer.MSecsElapsed());
 			timer.MSecsElapsed());

+ 62 - 127
ThirdParty/kNet/src/UDPMessageConnection.cpp

@@ -16,6 +16,8 @@
 	@brief Implements the UDP-specific code of MessageConnection.
 	@brief Implements the UDP-specific code of MessageConnection.
 	\todo Flow control currently disabled since testing out the performance of UDT. */
 	\todo Flow control currently disabled since testing out the performance of UDT. */
 
 
+// Modified by Lasse Öörni for Urho3D
+
 #include <cmath>
 #include <cmath>
 #include <cstdio>
 #include <cstdio>
 #include <sstream>
 #include <sstream>
@@ -42,26 +44,27 @@ using namespace std;
 namespace kNet
 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,
 /// 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)
 /// acking will be performed earlier. (milliseconds)
 static const float maxAckDelay = 33.f; // (1/30th of a second)
 static const float maxAckDelay = 33.f; // (1/30th of a second)
-/// The time counter after which an unacked reliable message will be resent. (UDP only)
-static const float timeOutMilliseconds = 2000.f;//750.f;
 /// The maximum number of datagrams to read in from the socket at one go - after this reads will be throttled
 /// The maximum number of datagrams to read in from the socket at one go - after this reads will be throttled
 /// to give time for data sending as well.
 /// to give time for data sending as well.
 static const int cMaxDatagramsToReadInOneFrame = 2048;
 static const int cMaxDatagramsToReadInOneFrame = 2048;
 
 
 static const u32 cMaxUDPMessageFragmentSize = 470;
 static const u32 cMaxUDPMessageFragmentSize = 470;
 
 
+/// Minimum retransmission timeout value (milliseconds)
+static const float minRTOTimeoutValue = 500.f;
+/// Maximum retransmission timeout value (milliseconds)
+static const float maxRTOTimeoutValue = 5000.f;
+
 UDPMessageConnection::UDPMessageConnection(Network *owner, NetworkServer *ownerServer, Socket *socket, ConnectionState startingState)
 UDPMessageConnection::UDPMessageConnection(Network *owner, NetworkServer *ownerServer, Socket *socket, ConnectionState startingState)
 :MessageConnection(owner, ownerServer, socket, startingState),
 :MessageConnection(owner, ownerServer, socket, startingState),
-retransmissionTimeout(3.f), numAcksLastFrame(0), numLossesLastFrame(0), smoothedRTT(3.f), rttVariation(0.f), rttCleared(true), // Set RTT initial values as per RFC 2988.
+retransmissionTimeout(1000.f), numAcksLastFrame(0), numLossesLastFrame(0), smoothedRTT(1000.f), rttVariation(0.f), rttCleared(true), // Set RTT initial values as per RFC 2988.
 lastReceivedInOrderPacketID(0), 
 lastReceivedInOrderPacketID(0), 
 lastSentInOrderPacketID(0), datagramPacketIDCounter(1),
 lastSentInOrderPacketID(0), datagramPacketIDCounter(1),
-packetLossRate(0.f), packetLossCount(0.f), datagramOutRatePerSecond(initialDatagramRatePerSecond), 
-datagramInRatePerSecond(initialDatagramRatePerSecond),
-datagramSendRate(70),
+packetLossRate(0.f), packetLossCount(0.f), 
+datagramSendRate(50.f), lowestDatagramSendRateOnPacketLoss(50.f), slowModeDelay(0),
 receivedPacketIDs(64 * 1024), outboundPacketAckTrack(1024),
 receivedPacketIDs(64 * 1024), outboundPacketAckTrack(1024),
 previousReceivedPacketID(0), queuedInboundDatagrams(128)
 previousReceivedPacketID(0), queuedInboundDatagrams(128)
 {
 {
@@ -255,42 +258,68 @@ void UDPMessageConnection::HandleFlowControl()
 	AssertInWorkerThreadContext();
 	AssertInWorkerThreadContext();
 
 
 	// In packets/second.
 	// In packets/second.
-	const float totalEstimatedBandwidth = 50; ///\todo Make this estimation dynamic as in UDT or similar.
-	const float additiveIncreaseAggressiveness = 5e-2f;
+	const float minBandwidthOnLoss = 10.f;
+	const float minBandwidth = 50.f;
+	const float maxBandwidth = 10000.f;
+	const int framesPerSec = 10;
+	const int maxSlowModeDelay = 10 * framesPerSec;
 
 
-	const tick_t frameLength = Clock::TicksPerSec() / 100; // in ticks
-	// Additively increase the outbound send rate.
-	unsigned long numFrames = (unsigned long)(Clock::TicksInBetween(Clock::Tick(), lastFrameTime) / frameLength);
-	if (/*numAcksLastFrame > 0 &&*/ numFrames > 0)
+	const tick_t frameLength = Clock::TicksPerSec() / framesPerSec; // in ticks
+	const tick_t now = Clock::Tick();
+
+	unsigned long numFrames = (unsigned long)(Clock::TicksInBetween(now, lastFrameTime) / frameLength);
+	if (numFrames > 0)
 	{
 	{
-		if (numFrames >= 100)
-			numFrames = 100;
+		if (numFrames >= framesPerSec)
+			numFrames = framesPerSec;
+
+		int numUnacked = NumOutboundUnackedDatagrams();
 
 
-		if (numLossesLastFrame > 5) // Do not respond to a random single packet losses.
+		// Reduce sendrate on significant loss
+		if (numLossesLastFrame > 2)
 		{
 		{
 			float oldRate = datagramSendRate;
 			float oldRate = datagramSendRate;
-			datagramSendRate = min(datagramSendRate, max(1.f, lowestDatagramSendRateOnPacketLoss * 0.9f)); // Multiplicative decreases.
-//			datagramSendRate = max(1.f, datagramSendRate * 0.9f); // Multiplicative decreases.
+			datagramSendRate = min(datagramSendRate, max(minBandwidthOnLoss, lowestDatagramSendRateOnPacketLoss * 0.9f)); // Multiplicative decreases.
+			std::cout << "Loss!" << std::endl;
 			LOG(LogVerbose, "Received %d losses. datagramSendRate backed to %.2f from %.2f", (int)numLossesLastFrame, datagramSendRate, oldRate);
 			LOG(LogVerbose, "Received %d losses. datagramSendRate backed to %.2f from %.2f", (int)numLossesLastFrame, datagramSendRate, oldRate);
 		}
 		}
-		else // Additive increases.
+		else
 		{
 		{
-			float increment = min((float)numFrames * additiveIncreaseAggressiveness * (totalEstimatedBandwidth - datagramSendRate), 1.f);
-			datagramSendRate += increment;
-			datagramSendRate = min(datagramSendRate, totalEstimatedBandwidth);
-			lowestDatagramSendRateOnPacketLoss = datagramSendRate;
-//			LOG(LogVerbose, "Incremented sendRate by %.2f to %.2f", increment, datagramSendRate);
+			// Check if more or less bandwidth is needed
+			///\todo Very simple logic for now, can be improved
+			bool needMore = outboundQueue.Size() > 10;
+			bool needLess = outboundQueue.Size() == 0;
+			float maxRTT = max(rtt, smoothedRTT);
+
+			// Need more: increase sendrate. Factor in RTT and acks
+			if (needMore && numLossesLastFrame == 0)
+			{
+				float delta = (50.f + 2.f * numAcksLastFrame) / maxRTT;
+				if (slowModeDelay > 0)
+					delta *= 0.2f;
+				datagramSendRate = min(datagramSendRate + delta, maxBandwidth);
+				lowestDatagramSendRateOnPacketLoss = datagramSendRate;
+			}
+			// Need less: decrease sendrate if not already at minimum
+			else if (needLess && datagramSendRate > minBandwidth)
+				datagramSendRate = max(datagramSendRate * 0.98f, minBandwidth);
+
+			// Whenever slow mode or slight loss is occurring and RTT is more than the minimum RTO value, back off slowly
+			// This is to ensure we do not stay "balanced" in a state where slight loss occurs constantly due to sending too much
+			if ((numLossesLastFrame > 0 || slowModeDelay > 0) && maxRTT > minRTOTimeoutValue && datagramSendRate > minBandwidth)
+				datagramSendRate = max(datagramSendRate * 0.999f, minBandwidth);
 		}
 		}
+
+		// Update the slow mode timer
+		if (numLossesLastFrame > 1)
+			slowModeDelay = min(slowModeDelay + numLossesLastFrame * framesPerSec, maxSlowModeDelay);
+		else if (slowModeDelay > 0)
+			--slowModeDelay;
+
 		numAcksLastFrame = 0;
 		numAcksLastFrame = 0;
 		numLossesLastFrame = 0;
 		numLossesLastFrame = 0;
-		if (numFrames < 100)
-			lastFrameTime += numFrames * frameLength;
-		else
-			lastFrameTime = Clock::Tick();
+		lastFrameTime = now;
 	}
 	}
-
-	// Do a fixed flow control for testing.
-	datagramSendRate = 1000; ///\todo Remove.
 }
 }
 
 
 void UDPMessageConnection::SendOutPackets()
 void UDPMessageConnection::SendOutPackets()
@@ -952,31 +981,6 @@ void UDPMessageConnection::SendDisconnectAckMessage()
 	LOG(LogInfo, "UDPMessageConnection::SendDisconnectAckMessage: Sent DisconnectAck.");
 	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)
 int UDPMessageConnection::BiasedBinarySearchFindPacketIndex(PacketAckTrackQueue &queue, int packetID)
 {
 {
 	///\bug Make this all packetID wrap-around -aware.
 	///\bug Make this all packetID wrap-around -aware.
@@ -1049,9 +1053,6 @@ void UDPMessageConnection::FreeOutboundPacketAckTrack(packet_id_t packetID)
 	outboundPacketAckTrack.EraseItemAt(itemIndex);
 	outboundPacketAckTrack.EraseItemAt(itemIndex);
 }
 }
 
 
-static const float minRTOTimeoutValue = 1000.f;
-static const float maxRTOTimeoutValue = 5000.f;
-
 /// Adjusts the retransmission timer values as per RFC 2988.
 /// Adjusts the retransmission timer values as per RFC 2988.
 /// @param rtt The round trip time that was measured on the packet that was just acked.
 /// @param rtt The round trip time that was measured on the packet that was just acked.
 void UDPMessageConnection::UpdateRTOCounterOnPacketAck(float rtt)
 void UDPMessageConnection::UpdateRTOCounterOnPacketAck(float rtt)
@@ -1076,20 +1077,9 @@ void UDPMessageConnection::UpdateRTOCounterOnPacketAck(float rtt)
 	}
 	}
 	// We add this much constant delay to all RTO timers to avoid too optimistic RTO values
 	// We add this much constant delay to all RTO timers to avoid too optimistic RTO values
 	// in excellent conditions (localhost, LAN).
 	// in excellent conditions (localhost, LAN).
-	const float safetyThresholdAdd = 1.f;
 	const float safetyThresholdMul = 2.f;
 	const float safetyThresholdMul = 2.f;
 
 
-//	retransmissionTimeout = min(maxRTOTimeoutValue, max(minRTOTimeoutValue, safetyThresholdAdd + safetyThresholdMul * (smoothedRTT + rttVariation)));
-	retransmissionTimeout = min(maxRTOTimeoutValue, max(minRTOTimeoutValue, safetyThresholdAdd + safetyThresholdMul * (smoothedRTT + rttVariation)));
-
-///	const float maxDatagramSendRate = 3000.f;
-	// Update data send rate.
-//	++datagramOutRatePerSecond; // Additive increases.
-//	datagramSendRate = datagramSendRate + 1.f; // Increase by one datagram/successfully sent packet.
-//	datagramSendRate = min(datagramSendRate + 1.f, maxDatagramSendRate); // Increase by one datagram/successfully sent packet.
-
-//	LOG(LogVerbose, "Packet ack event: RTO: %.3f sec., srtt: %.3f sec., rttvar: %.3f sec. datagramSendRate: %.2f", 
-//		retransmissionTimeout, smoothedRTT, rttVariation, datagramSendRate);
+	retransmissionTimeout = min(maxRTOTimeoutValue, max(minRTOTimeoutValue, safetyThresholdMul * (smoothedRTT + rttVariation)));
 }
 }
 
 
 void UDPMessageConnection::UpdateRTOCounterOnPacketLoss()
 void UDPMessageConnection::UpdateRTOCounterOnPacketLoss()
@@ -1098,14 +1088,10 @@ void UDPMessageConnection::UpdateRTOCounterOnPacketLoss()
 
 
 	using namespace std;
 	using namespace std;
 
 
-	retransmissionTimeout = smoothedRTT = min(maxRTOTimeoutValue, max(minRTOTimeoutValue, smoothedRTT * 2.f));
+	// retransmissionTimeout = smoothedRTT = min(maxRTOTimeoutValue, max(minRTOTimeoutValue, smoothedRTT * 2.f));
 	// The variation just gives bogus values, so clear it altogether.
 	// The variation just gives bogus values, so clear it altogether.
 	rttVariation = 0.f;
 	rttVariation = 0.f;
 
 
-	// Multiplicative decreases.
-//	datagramOutRatePerSecond = max(1, datagramOutRatePerSecond / 2);
-//	datagramSendRate = max(1.f, datagramSendRate * 0.9f); // At least send one packet/second.
-
 	++numLossesLastFrame;
 	++numLossesLastFrame;
 
 
 //	LOG(LogVerbose, "Packet loss event: RTO: %.3f sec. datagramSendRate: %.2f", retransmissionTimeout, datagramSendRate);
 //	LOG(LogVerbose, "Packet loss event: RTO: %.3f sec. datagramSendRate: %.2f", retransmissionTimeout, datagramSendRate);
@@ -1206,32 +1192,6 @@ void UDPMessageConnection::HandleDisconnectAckMessage()
 	connectionState = ConnectionClosed;
 	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()
 void UDPMessageConnection::ComputePacketLoss()
 {
 {
 	AssertInWorkerThreadContext();
 	AssertInWorkerThreadContext();
@@ -1291,28 +1251,6 @@ void AppendU16ToVector(std::vector<char> &data, unsigned long value)
 	data.insert(data.end(), (const char *)&value, (const char *)&value + 2);
 	data.insert(data.end(), (const char *)&value, (const char *)&value + 2);
 }
 }
 
 
-void UDPMessageConnection::SetDatagramInFlowRatePerSecond(int newDatagramReceiveRate, bool internalCall)
-{/*
-	if (newDatagramReceiveRate == datagramInRatePerSecond) // No need to set it multiple times.
-		return;
-
-	if (newDatagramReceiveRate < 5 || newDatagramReceiveRate > 10 * 1024)
-	{
-		LOG(LogError, "Tried to set invalid UDP receive rate %d packets/sec! Ignored.", newDatagramReceiveRate);
-		return;
-	}
-	
-	datagramInRatePerSecond = newDatagramReceiveRate;
-
-	NetworkMessage *msg = StartNewMessage(MsgIdFlowControlRequest);
-	AppendU16ToVector(msg->data, newDatagramReceiveRate);
-	msg->priority = NetworkMessage::cMaxPriority - 1;
-#ifdef KNET_NETWORK_PROFILING
-	msg->profilerName = "FlowControlRequest (3)";
-#endif
-	EndAndQueueMessage(msg, 2, internalCall);*/
-}
-
 bool UDPMessageConnection::HandleMessage(packet_id_t packetID, message_id_t messageID, const char *data, size_t numBytes)
 bool UDPMessageConnection::HandleMessage(packet_id_t packetID, message_id_t messageID, const char *data, size_t numBytes)
 {
 {
 	AssertInWorkerThreadContext();
 	AssertInWorkerThreadContext();
@@ -1323,9 +1261,6 @@ bool UDPMessageConnection::HandleMessage(packet_id_t packetID, message_id_t mess
 	case MsgIdPingReply:
 	case MsgIdPingReply:
 		return false; // We don't do anything with these messages, the MessageConnection base class handles these.
 		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:
 	case MsgIdPacketAck:
 		HandlePacketAckMessage(data, numBytes);
 		HandlePacketAckMessage(data, numBytes);
 		return true;
 		return true;