Browse Source

Fixed up to one second delay in starting UDP datagram sends.
Refactored SendMessage API in Connection & Network, allowing the user to specify message priority.
Split Network library internal messages into 3 priority categories.

Lasse Öörni 14 years ago
parent
commit
43671aa66f

+ 5 - 4
Docs/ScriptAPI.dox

@@ -185,6 +185,9 @@
 - uint DD_SOURCE
 - uint DD_TARGET
 - uint DD_SOURCE_AND_TARGET
+- uint NET_LOW_PRIORITY
+- uint NET_MEDIUM_PRIORITY
+- uint NET_HIGH_PRIORITY
 - uint DEBUGHUD_SHOW_NONE
 - uint DEBUGHUD_SHOW_STATS
 - uint DEBUGHUD_SHOW_MODE
@@ -3438,8 +3441,7 @@ Properties:<br>
 Connection
 
 Methods:<br>
-- void SendMessage(int, bool, bool, const VectorBuffer&)
-- void SendMessage(int, uint, bool, bool, const VectorBuffer&)
+- void SendMessage(int, bool, bool, const VectorBuffer&, uint arg4 = 0, uint arg5 = 0)
 - void SendRemoteEvent(const String&, bool, const VariantMap& arg2 = VariantMap ( ))
 - void SendRemoteEvent(Node@, const String&, bool, const VariantMap& arg3 = VariantMap ( ))
 - void Disconnect(int arg0 = 0)
@@ -3467,8 +3469,7 @@ Methods:<br>
 - void Disconnect(int arg0 = 0)
 - bool StartServer(uint16)
 - void StopServer()
-- void BroadcastMessage(int, bool, bool, const VectorBuffer&)
-- void BroadcastMessage(int, uint, bool, bool, const VectorBuffer&)
+- void BroadcastMessage(int, bool, bool, const VectorBuffer&, uint arg4 = 0, uint arg5 = 0)
 - void BroadcastRemoteEvent(const String&, bool, const VariantMap& arg2 = VariantMap ( ))
 - void BroadcastRemoteEvent(Scene@, const String&, bool, const VariantMap& arg3 = VariantMap ( ))
 - void BroadcastRemoteEvent(Node@, const String&, bool, const VariantMap& arg3 = VariantMap ( ))

+ 7 - 4
Engine/Engine/NetworkAPI.cpp

@@ -25,6 +25,7 @@
 #include "APITemplates.h"
 #include "Controls.h"
 #include "Network.h"
+#include "Protocol.h"
 
 static void ConstructControls(Controls* ptr)
 {
@@ -70,9 +71,12 @@ void SendRemoteNodeEvent(Node* receiver, const String& eventType, bool inOrder,
 
 static void RegisterConnection(asIScriptEngine* engine)
 {
+    engine->RegisterGlobalProperty("uint NET_LOW_PRIORITY", (void*)&NET_LOW_PRIORITY);
+    engine->RegisterGlobalProperty("uint NET_MEDIUM_PRIORITY", (void*)&NET_MEDIUM_PRIORITY);
+    engine->RegisterGlobalProperty("uint NET_HIGH_PRIORITY", (void*)&NET_HIGH_PRIORITY);
+    
     RegisterObject<Connection>(engine, "Connection");
-    engine->RegisterObjectMethod("Connection", "void SendMessage(int, bool, bool, const VectorBuffer&in)", asMETHODPR(Connection, SendMessage, (int, bool, bool, const VectorBuffer&), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Connection", "void SendMessage(int, uint, bool, bool, const VectorBuffer&in)", asMETHODPR(Connection, SendMessage, (int, unsigned, bool, bool, const VectorBuffer&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Connection", "void SendMessage(int, bool, bool, const VectorBuffer&in, uint priority = 0, uint contentID = 0)", asMETHODPR(Connection, SendMessage, (int, bool, bool, const VectorBuffer&, unsigned, unsigned), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Connection", "void SendRemoteEvent(const String&in, bool, const VariantMap&in eventData = VariantMap())", asFUNCTION(SendRemoteEvent), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Connection", "void SendRemoteEvent(Node@+, const String&in, bool, const VariantMap&in eventData = VariantMap())", asFUNCTION(SendRemoteNodeEvent), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Connection", "void Disconnect(int waitMSec = 0)", asMETHOD(Connection, Disconnect), asCALL_THISCALL);
@@ -163,8 +167,7 @@ void RegisterNetwork(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Network", "void Disconnect(int waitMSec = 0)", asMETHOD(Network, Disconnect), asCALL_THISCALL);
     engine->RegisterObjectMethod("Network", "bool StartServer(uint16)", asMETHOD(Network, StartServer), asCALL_THISCALL);
     engine->RegisterObjectMethod("Network", "void StopServer()", asMETHOD(Network, StopServer), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Network", "void BroadcastMessage(int, bool, bool, const VectorBuffer&in)", asMETHODPR(Network, BroadcastMessage, (int, bool, bool, const VectorBuffer&), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Network", "void BroadcastMessage(int, uint, bool, bool, const VectorBuffer&in)", asMETHODPR(Network, BroadcastMessage, (int, unsigned, bool, bool, const VectorBuffer&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Network", "void BroadcastMessage(int, bool, bool, const VectorBuffer&in, uint priority = 0, uint contentID = 0)", asMETHODPR(Network, BroadcastMessage, (int, bool, bool, const VectorBuffer&, unsigned, unsigned), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Network", "void BroadcastRemoteEvent(const String&in, bool, const VariantMap&in eventData = VariantMap())", asFUNCTION(NetworkBroadcastRemoteEvent), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Network", "void BroadcastRemoteEvent(Scene@+, const String&in, bool, const VariantMap&in eventData = VariantMap())", asFUNCTION(NetworkBroadcastRemoteSceneEvent), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Network", "void BroadcastRemoteEvent(Node@+, const String&in, bool, const VariantMap&in eventData = VariantMap())", asFUNCTION(NetworkBroadcastRemoteNodeEvent), asCALL_CDECL_OBJLAST);

+ 25 - 35
Engine/Network/Connection.cpp

@@ -69,22 +69,12 @@ Connection::~Connection()
     SetScene(0);
 }
 
-void Connection::SendMessage(int msgID, bool reliable, bool inOrder, const VectorBuffer& msg)
+void Connection::SendMessage(int msgID, bool reliable, bool inOrder, const VectorBuffer& msg, unsigned priority, unsigned contentID)
 {
-    SendMessage(msgID, 0, reliable, inOrder, msg.GetData(), msg.GetSize());
+    SendMessage(msgID, reliable, inOrder, msg.GetData(), msg.GetSize(), priority, contentID);
 }
 
-void Connection::SendMessage(int msgID, bool reliable, bool inOrder, const unsigned char* data, unsigned numBytes)
-{
-    SendMessage(msgID, 0, reliable, inOrder, data, numBytes);
-}
-
-void Connection::SendMessage(int msgID, unsigned contentID, bool reliable, bool inOrder, const VectorBuffer& msg)
-{
-    SendMessage(msgID, contentID, reliable, inOrder, msg.GetData(), msg.GetSize());
-}
-
-void Connection::SendMessage(int msgID, unsigned contentID, bool reliable, bool inOrder, const unsigned char* data, unsigned numBytes)
+void Connection::SendMessage(int msgID, bool reliable, bool inOrder, const unsigned char* data, unsigned numBytes, unsigned priority, unsigned contentID)
 {
     // Make sure not to use kNet internal message ID's
     if (msgID <= 0x4 || msgID >= 0x3ffffffe)
@@ -93,7 +83,7 @@ void Connection::SendMessage(int msgID, unsigned contentID, bool reliable, bool
         return;
     }
     
-    connection_->SendMessage(msgID, reliable, inOrder, DEFAULT_MSG_PRIORITY, contentID, (const char*)data, numBytes);
+    connection_->SendMessage(msgID, reliable, inOrder, priority, contentID, (const char*)data, numBytes);
 }
 
 void Connection::SendRemoteEvent(StringHash eventType, bool inOrder, const VariantMap& eventData)
@@ -179,7 +169,7 @@ void Connection::SetScene(Scene* newScene)
             msg_.WriteUInt(package->GetTotalSize());
             msg_.WriteUInt(package->GetChecksum());
         }
-        SendMessage(MSG_LOADSCENE, true, true, msg_);
+        SendMessage(MSG_LOADSCENE, true, true, msg_, NET_HIGH_PRIORITY);
     }
     else
     {
@@ -239,7 +229,7 @@ void Connection::SendServerUpdate()
             msg_.Clear();
             msg_.WriteVLE(current->first_);
             
-            SendMessage(MSG_REMOVENODE, true, true, msg_);
+            SendMessage(MSG_REMOVENODE, true, true, msg_, NET_HIGH_PRIORITY);
             sceneState_.Erase(current);
         }
     }
@@ -259,7 +249,7 @@ void Connection::SendClientUpdate()
     msg_.WriteFloat(controls_.yaw_);
     msg_.WriteFloat(controls_.pitch_);
     msg_.WriteVariantMap(controls_.extraData_);
-    SendMessage(MSG_CONTROLS, CONTROLS_CONTENT_ID, false, false, msg_);
+    SendMessage(MSG_CONTROLS, false, false, msg_, NET_MEDIUM_PRIORITY, CONTROLS_CONTENT_ID);
 }
 
 void Connection::SendQueuedRemoteEvents()
@@ -274,14 +264,14 @@ void Connection::SendQueuedRemoteEvents()
         {
             msg_.WriteStringHash(i->eventType_);
             msg_.WriteVariantMap(i->eventData_);
-            SendMessage(MSG_REMOTEEVENT, true, i->inOrder_, msg_);
+            SendMessage(MSG_REMOTEEVENT, true, i->inOrder_, msg_, NET_HIGH_PRIORITY);
         }
         else
         {
             msg_.WriteVLE(i->receiverID_);
             msg_.WriteStringHash(i->eventType_);
             msg_.WriteVariantMap(i->eventData_);
-            SendMessage(MSG_REMOTENODEEVENT, true, i->inOrder_, msg_);
+            SendMessage(MSG_REMOTENODEEVENT, true, i->inOrder_, msg_, NET_HIGH_PRIORITY);
         }
     }
     
@@ -691,7 +681,7 @@ void Connection::ProcessPackageDownload(int msgID, MemoryBuffer& msg)
                         msg_.WriteStringHash(nameHash);
                         msg_.WriteVLE(i);
                         msg_.Write(buffer, fragmentSize);
-                        SendMessage(MSG_PACKAGEDATA, true, false, msg_);
+                        SendMessage(MSG_PACKAGEDATA, true, false, msg_, NET_LOW_PRIORITY);
                     }
                     
                     return;
@@ -772,7 +762,7 @@ void Connection::ProcessPackageDownload(int msgID, MemoryBuffer& msg)
                     LOGINFO("Requesting package " + nextDownload.name_ + " from server");
                     msg_.Clear();
                     msg_.WriteString(nextDownload.name_);
-                    SendMessage(MSG_REQUESTPACKAGE, true, true, msg_);
+                    SendMessage(MSG_REQUESTPACKAGE, true, true, msg_, NET_HIGH_PRIORITY);
                     nextDownload.initiated_ = true;
                 }
             }
@@ -838,7 +828,7 @@ void Connection::ProcessSceneLoaded(int msgID, MemoryBuffer& msg)
     if (checksum != scene_->GetChecksum())
     {
         msg_.Clear();
-        SendMessage(MSG_SCENECHECKSUMERROR, true, true, msg_);
+        SendMessage(MSG_SCENECHECKSUMERROR, true, true, msg_, NET_HIGH_PRIORITY);
         OnSceneLoadFailed();
     }
     else
@@ -957,9 +947,9 @@ float Connection::GetDownloadProgress() const
 
 void Connection::HandleAsyncLoadFinished(StringHash eventType, VariantMap& eventData)
 {
-    VectorBuffer msg;
-    msg.WriteUInt(scene_->GetChecksum());
-    SendMessage(MSG_SCENELOADED, true, true, msg);
+    msg_.Clear();
+    msg_.WriteUInt(scene_->GetChecksum());
+    SendMessage(MSG_SCENELOADED, true, true, msg_, NET_HIGH_PRIORITY);
 }
 
 void Connection::ProcessNode(Node* node)
@@ -1023,7 +1013,7 @@ void Connection::ProcessNewNode(Node* node)
         component->WriteInitialDeltaUpdate(msg_, deltaUpdateBits_, componentState.attributes_);
     }
     
-    SendMessage(MSG_CREATENODE, true, true, msg_);
+    SendMessage(MSG_CREATENODE, true, true, msg_, NET_HIGH_PRIORITY);
 }
 
 void Connection::ProcessExistingNode(Node* node)
@@ -1066,7 +1056,7 @@ void Connection::ProcessExistingNode(Node* node)
             msg_.WriteVariant(j->second_);
         }
         
-        SendMessage(MSG_NODEDELTAUPDATE, true, true, msg_);
+        SendMessage(MSG_NODEDELTAUPDATE, true, true, msg_, NET_MEDIUM_PRIORITY);
     }
     
     // Send latestdata message if necessary
@@ -1077,7 +1067,7 @@ void Connection::ProcessExistingNode(Node* node)
         msg_.WriteVLE(node->GetID());
         node->WriteLatestDataUpdate(msg_, nodeState.attributes_);
         
-        SendMessage(MSG_NODELATESTDATA, node->GetID(), true, false, msg_);
+        SendMessage(MSG_NODELATESTDATA, true, false, msg_, NET_MEDIUM_PRIORITY, node->GetID());
     }
     
     // Check for new or changed components
@@ -1103,7 +1093,7 @@ void Connection::ProcessExistingNode(Node* node)
             msg_.WriteVLE(component->GetID());
             component->WriteInitialDeltaUpdate(msg_, deltaUpdateBits_, componentState.attributes_);
             
-            SendMessage(MSG_CREATECOMPONENT, true, true, msg_);
+            SendMessage(MSG_CREATECOMPONENT, true, true, msg_, NET_HIGH_PRIORITY);
         }
         else
         {
@@ -1121,7 +1111,7 @@ void Connection::ProcessExistingNode(Node* node)
                 msg_.WriteVLE(component->GetID());
                 component->WriteDeltaUpdate(msg_, deltaUpdateBits_, componentState.attributes_);
                 
-                SendMessage(MSG_COMPONENTDELTAUPDATE, true, true, msg_);
+                SendMessage(MSG_COMPONENTDELTAUPDATE, true, true, msg_, NET_MEDIUM_PRIORITY);
             }
             
             // Send latestdata message if necessary
@@ -1132,7 +1122,7 @@ void Connection::ProcessExistingNode(Node* node)
                 msg_.WriteVLE(component->GetID());
                 component->WriteLatestDataUpdate(msg_, componentState.attributes_);
                 
-                SendMessage(MSG_COMPONENTLATESTDATA, component->GetID(), true, false, msg_);
+                SendMessage(MSG_COMPONENTLATESTDATA, true, false, msg_, NET_MEDIUM_PRIORITY, component->GetID());
             }
         }
     }
@@ -1146,7 +1136,7 @@ void Connection::ProcessExistingNode(Node* node)
             msg_.Clear();
             msg_.WriteVLE(current->first_);
             
-            SendMessage(MSG_REMOVECOMPONENT, true, true, msg_);
+            SendMessage(MSG_REMOVECOMPONENT, true, true, msg_, NET_HIGH_PRIORITY);
             nodeState.components_.Erase(current);
         }
     }
@@ -1169,7 +1159,7 @@ void Connection::RequestPackage(const String& name, unsigned fileSize, unsigned
         LOGINFO("Requesting package " + name + " from server");
         msg_.Clear();
         msg_.WriteString(name);
-        SendMessage(MSG_REQUESTPACKAGE, true, true, msg_);
+        SendMessage(MSG_REQUESTPACKAGE, true, true, msg_, NET_HIGH_PRIORITY);
         download.initiated_ = true;
     }
 }
@@ -1178,7 +1168,7 @@ void Connection::SendPackageError(const String& name)
 {
     msg_.Clear();
     msg_.WriteStringHash(StringHash(name));
-    SendMessage(MSG_PACKAGEDATA, true, false, msg_);
+    SendMessage(MSG_PACKAGEDATA, true, false, msg_, NET_HIGH_PRIORITY);
 }
 
 void Connection::OnSceneLoadFailed()
@@ -1210,7 +1200,7 @@ void Connection::OnPackagesReady()
         // If filename is empty, can send the scene loaded reply immediately
         msg_.Clear();
         msg_.WriteUInt(scene_->GetChecksum());
-        SendMessage(MSG_SCENELOADED, true, true, msg_);
+        SendMessage(MSG_SCENELOADED, true, true, msg_, NET_HIGH_PRIORITY);
     }
     else
     {

+ 2 - 6
Engine/Network/Connection.h

@@ -87,13 +87,9 @@ public:
     ~Connection();
     
     /// Send a message
-    void SendMessage(int msgID, bool reliable, bool inOrder, const VectorBuffer& msg);
+    void SendMessage(int msgID, bool reliable, bool inOrder, const VectorBuffer& msg, unsigned priority = 0, unsigned contentID = 0);
     /// Send a message
-    void SendMessage(int msgID, bool reliable, bool inOrder, const unsigned char* data, unsigned numBytes);
-    /// Send a message with content ID
-    void SendMessage(int msgID, unsigned contentID, bool reliable, bool inOrder, const VectorBuffer& msg);
-    /// Send a message with content ID
-    void SendMessage(int msgID, unsigned contentID, bool reliable, bool inOrder, const unsigned char* data, unsigned numBytes);
+    void SendMessage(int msgID, bool reliable, bool inOrder, const unsigned char* data, unsigned numBytes, unsigned priority = 0, unsigned contentID = 0);
     /// Send a remote event
     void SendRemoteEvent(StringHash eventType, bool inOrder, const VariantMap& eventData = VariantMap());
     /// Send a remote node event

+ 5 - 15
Engine/Network/Network.cpp

@@ -258,22 +258,12 @@ void Network::StopServer()
     LOGINFO("Stopped server");
 }
 
-void Network::BroadcastMessage(int msgID, bool reliable, bool inOrder, const VectorBuffer& msg)
+void Network::BroadcastMessage(int msgID, bool reliable, bool inOrder, const VectorBuffer& msg, unsigned priority, unsigned contentID)
 {
-    BroadcastMessage(msgID, 0, reliable, inOrder, msg.GetData(), msg.GetSize());
+    BroadcastMessage(msgID, reliable, inOrder, msg.GetData(), msg.GetSize(), priority, contentID);
 }
 
-void Network::BroadcastMessage(int msgID, bool reliable, bool inOrder, const unsigned char* data, unsigned numBytes)
-{
-    BroadcastMessage(msgID, 0, reliable, inOrder, data, numBytes);
-}
-
-void Network::BroadcastMessage(int msgID, unsigned contentID, bool reliable, bool inOrder, const VectorBuffer& msg)
-{
-    BroadcastMessage(msgID, contentID, reliable, inOrder, msg.GetData(), msg.GetSize());
-}
-
-void Network::BroadcastMessage(int msgID, unsigned contentID, bool reliable, bool inOrder, const unsigned char* data, unsigned numBytes)
+void Network::BroadcastMessage(int msgID, bool reliable, bool inOrder, const unsigned char* data, unsigned numBytes, unsigned priority, unsigned contentID)
 {
    // Make sure not to use kNet internal message ID's
     if (msgID <= 0x4 || msgID >= 0x3ffffffe)
@@ -284,7 +274,7 @@ void Network::BroadcastMessage(int msgID, unsigned contentID, bool reliable, boo
     
     kNet::NetworkServer* server = network_->GetServer();
     if (server)
-        server->BroadcastMessage(msgID, reliable, inOrder, DEFAULT_MSG_PRIORITY, contentID, (const char*)data, numBytes);
+        server->BroadcastMessage(msgID, reliable, inOrder, priority, contentID, (const char*)data, numBytes);
     else
         LOGERROR("Server not running, can not broadcast messages");
 }
@@ -462,7 +452,7 @@ void Network::OnServerConnected()
     // Send the identity map now
     VectorBuffer msg;
     msg.WriteVariantMap(serverConnection_->GetIdentity());
-    serverConnection_->SendMessage(MSG_IDENTITY, true, true, msg);
+    serverConnection_->SendMessage(MSG_IDENTITY, true, true, msg, NET_HIGH_PRIORITY);
     
     SendEvent(E_SERVERCONNECTED);
 }

+ 2 - 6
Engine/Network/Network.h

@@ -61,14 +61,10 @@ public:
     bool StartServer(unsigned short port);
     /// Stop the server
     void StopServer();
-    /// Broadcast a message to all client connections
-    void BroadcastMessage(int msgID, bool reliable, bool inOrder, const VectorBuffer& msg);
-    /// Broadcast a message to all client connections
-    void BroadcastMessage(int msgID, bool reliable, bool inOrder, const unsigned char* data, unsigned numBytes);
     /// Broadcast a message with content ID to all client connections
-    void BroadcastMessage(int msgID, unsigned contentID, bool reliable, bool inOrder, const VectorBuffer& msg);
+    void BroadcastMessage(int msgID, bool reliable, bool inOrder, const VectorBuffer& msg, unsigned priority = 0, unsigned contentID = 0);
     /// Broadcast a message with content ID to all client connections
-    void BroadcastMessage(int msgID, unsigned contentID, bool reliable, bool inOrder, const unsigned char* data, unsigned numBytes);
+    void BroadcastMessage(int msgID, bool reliable, bool inOrder, const unsigned char* data, unsigned numBytes, unsigned priority = 0, unsigned contentID = 0);
     /// Broadcast a remote event to all client connections
     void BroadcastRemoteEvent(StringHash eventType, bool inOrder, const VariantMap& eventData = VariantMap());
     /// Broadcast a remote event to all client connections in the specific scene

+ 6 - 2
Engine/Network/Protocol.h

@@ -60,9 +60,13 @@ static const int MSG_REMOTEEVENT = 0x14;
 // Client->server and server->client: remote node event
 static const int MSG_REMOTENODEEVENT = 0x15;
 
-// Fixed message priority for kNet
-static const int DEFAULT_MSG_PRIORITY = 100;
 // Fixed content ID for client controls update
 static const unsigned CONTROLS_CONTENT_ID = 1;
 // Package file fragment size
 static const unsigned PACKAGE_FRAGMENT_SIZE = 1024;
+// Message high priority
+static const unsigned NET_HIGH_PRIORITY = 100;
+// Message medium priority
+static const unsigned NET_MEDIUM_PRIORITY = 50;
+// Message low priority (default)
+static const unsigned NET_LOW_PRIORITY = 0;

+ 0 - 2
ThirdParty/kNet/include/kNet/MessageConnection.h

@@ -557,8 +557,6 @@ protected:
 	/// Private ctor - MessageConnections are instantiated by Network and NetworkServer classes.
 	explicit MessageConnection(Network *owner, NetworkServer *ownerServer, Socket *socket, ConnectionState startingState);
 
-	virtual void Initialize() {} // [main thread]
-
 	virtual bool HandleMessage(packet_id_t /*packetID*/, u32 /*messageID*/, const char * /*data*/, size_t /*numBytes*/) { return false; } // [main thread]
 };
 

+ 0 - 2
ThirdParty/kNet/src/MessageConnection.cpp

@@ -107,8 +107,6 @@ bytesInTotal(0), bytesOutTotal(0)
 
 	eventMsgsOutAvailable = CreateNewEvent(EventWaitSignal);
 	assert(eventMsgsOutAvailable.IsValid());
-
-	Initialize();
 }
 
 MessageConnection::~MessageConnection()

+ 6 - 5
ThirdParty/kNet/src/UDPMessageConnection.cpp

@@ -68,6 +68,7 @@ receivedPacketIDs(64 * 1024), outboundPacketAckTrack(1024),
 previousReceivedPacketID(0), queuedInboundDatagrams(128)
 {
 	LOG(LogObjectAlloc, "Allocated UDPMessageConnection %p.", this);
+	Initialize();
 }
 
 UDPMessageConnection::~UDPMessageConnection()
@@ -661,13 +662,13 @@ unsigned long UDPMessageConnection::TimeUntilCanSendPacket() const
 {
 	tick_t now = Clock::Tick();
 
-	if (Clock::IsNewer(now, lastDatagramSendTime))
+	const tick_t datagramSendTickDelay = (tick_t)(Clock::TicksPerSec() / datagramSendRate);
+	tick_t nextDatagramSendTime = lastDatagramSendTime + datagramSendTickDelay;
+	
+	if (Clock::IsNewer(now, nextDatagramSendTime))
 		return 0;
 
-	if (Clock::IsNewer(lastDatagramSendTime, now + Clock::TicksPerSec()))
-		lastDatagramSendTime = now + Clock::TicksPerSec();
-
-	return (unsigned long)Clock::TimespanToMillisecondsF(now, lastDatagramSendTime);
+	return (unsigned long)Clock::TimespanToMillisecondsF(now, nextDatagramSendTime);
 }
 
 bool UDPMessageConnection::HaveReceivedPacketID(packet_id_t packetID) const