diff --git a/AllSamples.sln b/AllSamples.sln
index d321a5a..5272eb8 100644
--- a/AllSamples.sln
+++ b/AllSamples.sln
@@ -25,6 +25,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BarebonesServer", "Samples\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BarebonesClient", "Samples\BarebonesClient\BarebonesClient.csproj", "{BC0CBAEE-70FE-4B1E-A2FA-BCC731F1E48F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManyServer", "Samples\ManyServer\ManyServer.csproj", "{44EFFA4A-C7FC-4C45-B84D-F13A391EF4E7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManyClients", "Samples\ManyClients\ManyClients.csproj", "{A41772F5-F20F-408D-ABD1-5D1C144853C6}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -75,6 +79,14 @@ Global
{BC0CBAEE-70FE-4B1E-A2FA-BCC731F1E48F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BC0CBAEE-70FE-4B1E-A2FA-BCC731F1E48F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BC0CBAEE-70FE-4B1E-A2FA-BCC731F1E48F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {44EFFA4A-C7FC-4C45-B84D-F13A391EF4E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {44EFFA4A-C7FC-4C45-B84D-F13A391EF4E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {44EFFA4A-C7FC-4C45-B84D-F13A391EF4E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {44EFFA4A-C7FC-4C45-B84D-F13A391EF4E7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A41772F5-F20F-408D-ABD1-5D1C144853C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A41772F5-F20F-408D-ABD1-5D1C144853C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A41772F5-F20F-408D-ABD1-5D1C144853C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A41772F5-F20F-408D-ABD1-5D1C144853C6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -89,5 +101,7 @@ Global
{0B4B02BB-0F43-4466-A369-0682281AF60E} = {DA6697E7-4DD4-45EF-90A7-2FC265855019}
{438173C5-8E95-4AE1-AAAB-5C1009F05302} = {DA6697E7-4DD4-45EF-90A7-2FC265855019}
{BC0CBAEE-70FE-4B1E-A2FA-BCC731F1E48F} = {DA6697E7-4DD4-45EF-90A7-2FC265855019}
+ {44EFFA4A-C7FC-4C45-B84D-F13A391EF4E7} = {DA6697E7-4DD4-45EF-90A7-2FC265855019}
+ {A41772F5-F20F-408D-ABD1-5D1C144853C6} = {DA6697E7-4DD4-45EF-90A7-2FC265855019}
EndGlobalSection
EndGlobal
diff --git a/Lidgren.Network/Lidgren.Network.csproj b/Lidgren.Network/Lidgren.Network.csproj
index df56de4..8df4f04 100644
--- a/Lidgren.Network/Lidgren.Network.csproj
+++ b/Lidgren.Network/Lidgren.Network.csproj
@@ -74,6 +74,7 @@
+
diff --git a/Lidgren.Network/NetConnection.Handshake.cs b/Lidgren.Network/NetConnection.Handshake.cs
index 8906cc2..9558150 100644
--- a/Lidgren.Network/NetConnection.Handshake.cs
+++ b/Lidgren.Network/NetConnection.Handshake.cs
@@ -84,7 +84,6 @@ namespace Lidgren.Network
int len = 2 + m_peerConfiguration.AppIdentifier.Length + 8 + 4 + (m_approvalMessage == null ? 0 : m_approvalMessage.LengthBytes);
NetOutgoingMessage om = m_owner.CreateMessage(len);
- om.m_type = NetMessageType.Library;
om.m_libType = NetMessageLibraryType.Connect;
om.Write(m_peerConfiguration.AppIdentifier);
om.Write(m_owner.m_uniqueIdentifier);
@@ -100,10 +99,9 @@ namespace Lidgren.Network
}
m_owner.LogVerbose("Sending Connect");
- double now = NetTime.Now;
- m_owner.SendImmediately(now, this, om);
+ m_owner.SendLibraryImmediately(om, m_remoteEndpoint);
- m_connectInitationTime = now;
+ m_connectInitationTime = NetTime.Now;
SetStatus(NetConnectionStatus.Connecting, "Connecting");
return;
@@ -114,12 +112,11 @@ namespace Lidgren.Network
double now = NetTime.Now;
NetOutgoingMessage reply = m_owner.CreateMessage(4);
- reply.m_type = NetMessageType.Library;
reply.m_libType = NetMessageLibraryType.ConnectResponse;
reply.Write((float)now);
m_owner.LogVerbose("Sending LibraryConnectResponse");
- m_owner.SendImmediately(now, this, reply);
+ m_owner.SendLibraryImmediately(reply, m_remoteEndpoint);
}
internal void SendConnectionEstablished()
@@ -127,12 +124,11 @@ namespace Lidgren.Network
double now = NetTime.Now;
NetOutgoingMessage ce = m_owner.CreateMessage(4);
- ce.m_type = NetMessageType.Library;
ce.m_libType = NetMessageLibraryType.ConnectionEstablished;
ce.Write((float)now);
m_owner.LogVerbose("Sending LibraryConnectionEstablished");
- m_owner.SendImmediately(now, this, ce);
+ m_owner.SendLibraryImmediately(ce, m_remoteEndpoint);
}
internal void ExecuteDisconnect(bool sendByeMessage)
@@ -145,7 +141,7 @@ namespace Lidgren.Network
if (sendByeMessage)
{
NetOutgoingMessage om = m_owner.CreateLibraryMessage(NetMessageLibraryType.Disconnect, m_disconnectByeMessage);
- EnqueueOutgoingMessage(om);
+ SendLibrary(om);
}
m_owner.LogVerbose("Executing Disconnect(" + m_disconnectByeMessage + ")");
@@ -163,12 +159,7 @@ namespace Lidgren.Network
m_owner.LogVerbose("Finishing Disconnect(" + m_disconnectByeMessage + ")");
// release some held memory
- if (m_storedMessages != null)
- {
- foreach (var dict in m_storedMessages)
- if (dict != null)
- dict.Clear();
- }
+ m_unackedSends.Clear();
m_acknowledgesToSend.Clear();
SetStatus(NetConnectionStatus.Disconnected, m_disconnectByeMessage);
diff --git a/Lidgren.Network/NetConnection.Latency.cs b/Lidgren.Network/NetConnection.Latency.cs
index a8d180b..52e142a 100644
--- a/Lidgren.Network/NetConnection.Latency.cs
+++ b/Lidgren.Network/NetConnection.Latency.cs
@@ -32,7 +32,7 @@ namespace Lidgren.Network
private byte m_lastSentPingNumber;
private double m_lastPingSendTime;
private double m_nextPing;
- private double m_lastSendRespondedTo;
+ internal double m_lastSendRespondedTo;
// remote + diff = local
// diff = local - remote
@@ -62,14 +62,12 @@ namespace Lidgren.Network
double now = NetTime.Now;
// send pong
- NetOutgoingMessage pong = m_owner.CreateMessage(1);
- pong.m_type = NetMessageType.Library;
+ NetOutgoingMessage pong = m_owner.CreateMessage(1 + 8);
pong.m_libType = NetMessageLibraryType.Pong;
pong.Write((byte)pingNumber);
pong.Write(now);
- // send immediately
- m_owner.SendImmediately(now, this, pong);
+ m_owner.SendLibraryImmediately(pong, m_remoteEndpoint);
}
internal void HandleIncomingPong(double receiveNow, byte pingNumber, double remoteNetTime)
@@ -122,9 +120,8 @@ namespace Lidgren.Network
{
// send keepalive thru regular channels to give acks something to piggyback on
NetOutgoingMessage pig = m_owner.CreateMessage(1);
- pig.m_type = NetMessageType.Library;
pig.m_libType = NetMessageLibraryType.KeepAlive;
- EnqueueOutgoingMessage(pig);
+ SendLibrary(pig);
m_nextForceAckTime = now + m_peerConfiguration.m_maxAckDelayTime;
}
@@ -141,17 +138,17 @@ namespace Lidgren.Network
//
// send ping
//
+ now = NetTime.Now; // need exact number
m_lastSentPingNumber++;
+ m_lastPingSendTime = now;
+ m_nextPing = now + m_owner.Configuration.m_pingFrequency;
NetOutgoingMessage ping = m_owner.CreateMessage(1);
- ping.m_type = NetMessageType.Library;
ping.m_libType = NetMessageLibraryType.Ping;
ping.Write((byte)m_lastSentPingNumber);
- m_owner.SendImmediately(now, this, ping);
+ m_owner.SendLibraryImmediately(ping, m_remoteEndpoint);
- m_lastPingSendTime = NetTime.Now; // need exact number
- m_nextPing = now + m_owner.Configuration.m_pingFrequency;
}
}
}
diff --git a/Lidgren.Network/NetConnection.Reliability.cs b/Lidgren.Network/NetConnection.Reliability.cs
index 9985486..64b1b4a 100644
--- a/Lidgren.Network/NetConnection.Reliability.cs
+++ b/Lidgren.Network/NetConnection.Reliability.cs
@@ -25,11 +25,13 @@ namespace Lidgren.Network
{
public partial class NetConnection
{
- private ushort[] m_nextSendSequenceNumber;
+ private int[] m_nextSendSequenceNumber;
private ushort[] m_lastReceivedSequenced;
- internal readonly Dictionary[] m_storedMessages = new Dictionary[NetConstants.NumReliableChannels];
- internal readonly Dictionary[] m_inverseStored = new Dictionary[NetConstants.NumReliableChannels];
+ internal readonly List m_unackedSends = new List();
+
+ //internal readonly Dictionary[] m_storedMessages = new Dictionary[NetConstants.NumReliableChannels];
+ //internal readonly Dictionary[] m_inverseStored = new Dictionary[NetConstants.NumReliableChannels];
internal readonly NetBitVector m_storedMessagesNotEmpty = new NetBitVector(NetConstants.NumReliableChannels);
private readonly ushort[] m_nextExpectedReliableSequence = new ushort[NetConstants.NumReliableChannels];
@@ -41,14 +43,7 @@ namespace Lidgren.Network
public int GetStoredMessagesCount()
{
- int retval = 0;
- for (int i = 0; i < m_storedMessages.Length; i++)
- {
- var list = m_storedMessages[i];
- if (list != null)
- retval += list.Count;
- }
- return retval;
+ return m_unackedSends.Count;
}
public int GetWithheldMessagesCount()
@@ -66,16 +61,17 @@ namespace Lidgren.Network
private void InitializeReliability()
{
int num = ((int)NetMessageType.UserReliableOrdered + NetConstants.NetChannelsPerDeliveryMethod) - (int)NetMessageType.UserSequenced;
- m_nextSendSequenceNumber = new ushort[num];
+ m_nextSendSequenceNumber = new int[num];
+ for(int i=0;i= baseTimes.Length)
- {
- // no more resends! We failed!
- int reliableSlot = (int)msg.m_type - (int)NetMessageType.UserReliableUnordered;
- m_storedMessages[reliableSlot].Remove(seqNr);
- m_owner.LogWarning("Failed to deliver reliable message " + msg + " (seqNr " + seqNr + ")");
-
- Disconnect("Failed to deliver reliable message!");
-
- return; // bye
- }
-
- m_owner.LogVerbose("Resending " + msg + " (seqNr " + seqNr + ")");
-
- Interlocked.Increment(ref msg.m_inQueueCount);
- m_unsentMessages.EnqueueFirst(msg);
-
- msg.m_lastSentTime = now;
-
- // schedule next resend
- float[] multiplers = m_peerConfiguration.m_resendRTTMultiplier;
- msg.m_nextResendTime = now + baseTimes[numSends] + (m_averageRoundtripTime * multiplers[numSends]);
- }
+ */
private void HandleIncomingAcks(int ptr, int payloadByteLength)
{
@@ -205,6 +173,25 @@ namespace Lidgren.Network
NetMessageType tp = (NetMessageType)buffer[ptr++];
//m_owner.LogDebug("Got ack for " + tp + "|" + seqNr);
+ foreach(NetSending send in m_unackedSends)
+ {
+ if (send.MessageType == tp && send.SequenceNumber == seqNr)
+ {
+ // found it
+ m_lastSendRespondedTo = NetTime.Now; // TODO: calculate from send.NextResend and send.NumSends
+ int unfin = send.Message.m_numUnfinishedSendings;
+ send.Message.m_numUnfinishedSendings = unfin - 1;
+ if (unfin <= 1)
+ m_owner.Recycle(send.Message); // every sent has been acked; free the message
+
+ m_unackedSends.Remove(send);
+
+ // TODO: recycle send
+ break;
+ }
+ }
+
+ /*
// remove stored message
int reliableSlot = (int)tp - (int)NetMessageType.UserReliableUnordered;
@@ -220,6 +207,9 @@ namespace Lidgren.Network
dict.Remove(seqNr);
m_inverseStored[reliableSlot].Remove(om);
+ if (dict.Count < 1)
+ m_storedMessagesNotEmpty[reliableSlot] = false;
+
Interlocked.Decrement(ref om.m_inQueueCount);
NetException.Assert(om.m_lastSentTime != 0);
@@ -230,6 +220,7 @@ namespace Lidgren.Network
if (om.m_inQueueCount < 1)
m_owner.Recycle(om);
}
+ */
// TODO: receipt handling
}
@@ -251,6 +242,10 @@ namespace Lidgren.Network
received[(nextExpected + (NetConstants.NumSequenceNumbers / 2)) % NetConstants.NumSequenceNumbers] = false; // reset for next pass
nextExpected = (nextExpected + 1) % NetConstants.NumSequenceNumbers;
+ //
+ // Release withheld messages
+ //
+
while (received[nextExpected] == true)
{
// it seems we've already received the next expected reliable sequence number
diff --git a/Lidgren.Network/NetConnection.cs b/Lidgren.Network/NetConnection.cs
index 5ab0116..c0ebeaa 100644
--- a/Lidgren.Network/NetConnection.cs
+++ b/Lidgren.Network/NetConnection.cs
@@ -30,10 +30,10 @@ namespace Lidgren.Network
{
private static readonly NetFragmentationInfo s_genericFragmentationInfo = new NetFragmentationInfo();
- private readonly NetPeer m_owner;
+ internal readonly NetPeer m_owner;
internal readonly IPEndPoint m_remoteEndpoint;
internal double m_lastHeardFrom;
- internal NetQueue m_unsentMessages;
+ internal readonly NetQueue m_unsentMessages;
internal NetConnectionStatus m_status;
private NetConnectionStatus m_visibleStatus;
private double m_lastSentUnsentMessages;
@@ -91,10 +91,10 @@ namespace Lidgren.Network
m_owner = owner;
m_peerConfiguration = m_owner.m_configuration;
m_remoteEndpoint = remoteEndpoint;
- m_unsentMessages = new NetQueue(16);
m_fragmentGroups = new Dictionary();
m_status = NetConnectionStatus.None;
m_visibleStatus = NetConnectionStatus.None;
+ m_unsentMessages = new NetQueue(8);
double now = NetTime.Now;
m_nextPing = now + 5.0f;
@@ -143,21 +143,41 @@ namespace Lidgren.Network
}
// queue resends
+ foreach (NetSending send in m_unackedSends)
+ {
+ if (now > send.NextResend)
+ {
+ m_unsentMessages.EnqueueFirst(send);
+ send.SetNextResend(this);
+ }
+ }
+
+ /*
if (!m_storedMessagesNotEmpty.IsEmpty())
{
int first = m_storedMessagesNotEmpty.GetFirstSetIndex();
+
+#if DEBUG
+ // slow slow verification
+ for (int i = 0; i < first; i++)
+ if (m_storedMessages[i] != null && m_storedMessages[i].Count > 0)
+ throw new NetException("m_storedMessagesNotEmpty mismatch; first is " + first + " but actual first is " + i);
+ if (m_storedMessages[first] == null || m_storedMessages[first].Count < 1)
+ throw new NetException("m_storedMessagesNotEmpty failure; first is " + first + ", but that entry is empty!");
+#endif
for (int i = first; i < m_storedMessages.Length; i++)
{
if (m_storedMessagesNotEmpty.Get(i))
{
Dictionary dict = m_storedMessages[i];
+ RestartCheck:
foreach (ushort seqNr in m_storedMessages[i].Keys)
{
NetOutgoingMessage om = dict[seqNr];
if (now >= om.m_nextResendTime)
{
Resend(now, seqNr, om);
- break; // need to break out here; collection may have been modified
+ goto RestartCheck; // need to break out here; collection may have been modified
}
}
}
@@ -169,6 +189,7 @@ namespace Lidgren.Network
#endif
}
}
+ */
}
// send unsent messages; high priority first
@@ -199,13 +220,14 @@ namespace Lidgren.Network
if (m_throttleDebt >= throttleThreshold)
break;
- NetOutgoingMessage msg = m_unsentMessages.TryDequeue();
- if (msg == null)
+ NetSending send = m_unsentMessages.TryDequeue();
+ if (send == null)
continue;
- Interlocked.Decrement(ref msg.m_inQueueCount);
+ send.NumSends++;
+
+ NetOutgoingMessage msg = send.Message;
int msgPayloadLength = msg.LengthBytes;
- msg.m_lastSentTime = now;
if (ptr > 0)
{
@@ -233,9 +255,27 @@ namespace Lidgren.Network
// encode message
//
- ptr = msg.Encode(now, buffer, ptr, this);
+ if (send.FragmentGroupId > 0)
+ ptr = msg.EncodeFragmented(buffer, ptr, send, mtu);
+ else
+ ptr = msg.EncodeUnfragmented(buffer, ptr, send.MessageType, send.SequenceNumber);
numIncludedMessages++;
+ if (send.MessageType >= NetMessageType.UserReliableUnordered)
+ {
+ // store for reliability
+ if (send.NumSends == 0)
+ m_unackedSends.Add(send);
+ }
+ else
+ {
+ // unreliable message; recycle if all sendings done
+ int unfin = msg.m_numUnfinishedSendings;
+ msg.m_numUnfinishedSendings = unfin - 1;
+ if (unfin <= 1)
+ m_owner.Recycle(msg);
+ }
+
// room to piggyback some acks?
if (m_acknowledgesToSend.Count > 0)
{
@@ -250,14 +290,11 @@ namespace Lidgren.Network
}
}
- if (msg.m_type == NetMessageType.Library && msg.m_libType == NetMessageLibraryType.Disconnect)
+ if (send.MessageType == NetMessageType.Library && msg.m_libType == NetMessageLibraryType.Disconnect)
{
FinishDisconnect();
break;
}
-
- if (msg.m_inQueueCount < 1)
- m_owner.Recycle(msg);
}
if (ptr > 0)
@@ -340,7 +377,7 @@ namespace Lidgren.Network
{
// Expected sequence number
AcceptMessage(mtp, isFragment, channelSequenceNumber, ptr, payloadLengthBits);
-
+
ExpectedReliableSequenceArrived(reliableSlot, isFragment);
return;
}
@@ -368,13 +405,12 @@ namespace Lidgren.Network
}
// It's an early reliable message
- recList[channelSequenceNumber] = true;
-
m_owner.LogVerbose("Received early reliable message: " + channelSequenceNumber);
//
// It's not a duplicate; mark as received. Release if it's unordered, else withhold
//
+ recList[channelSequenceNumber] = true;
if (ndm == NetDeliveryMethod.ReliableUnordered)
{
@@ -563,43 +599,58 @@ namespace Lidgren.Network
return;
}
- public void SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method)
+ internal void SendLibrary(NetOutgoingMessage msg)
{
- if (msg.IsSent)
- throw new NetException("Message has already been sent!");
- msg.m_type = (NetMessageType)method;
- EnqueueOutgoingMessage(msg);
+ NetException.Assert(msg.m_libType != NetMessageLibraryType.Error);
+
+ NetSending send = new NetSending(msg, NetMessageType.Library, 0);
+
+ msg.m_wasSent = true;
+ msg.m_numUnfinishedSendings++;
+ m_unsentMessages.Enqueue(send);
}
- public void SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel)
+ public void SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method)
{
+ SendMessage(msg, method, 0);
+ }
+
+ public bool SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel)
+ {
+ NetException.Assert(msg.m_libType == NetMessageLibraryType.Error, "Use SendLibrary() instead!");
+
if (msg.IsSent)
throw new NetException("Message has already been sent!");
NetException.Assert(sequenceChannel >= 0 && sequenceChannel < NetConstants.NetChannelsPerDeliveryMethod, "Sequence channel must be between 0 and NetConstants.NetChannelsPerDeliveryMethod (" + NetConstants.NetChannelsPerDeliveryMethod + ")");
- msg.m_type = (NetMessageType)((int)method + sequenceChannel);
- EnqueueOutgoingMessage(msg);
+ if (m_owner == null)
+ return false; // we've been disposed
+
+ msg.m_wasSent = true;
+
+ NetMessageType tp = (NetMessageType)((int)method + sequenceChannel);
+ return EnqueueSendMessage(msg, tp);
}
- // called by user and network thread
- internal void EnqueueOutgoingMessage(NetOutgoingMessage msg)
- {
- if (m_owner == null)
- return; // we've been disposed
-
+ internal bool EnqueueSendMessage(NetOutgoingMessage msg, NetMessageType tp)
+ {
int msgLen = msg.LengthBytes;
int mtu = m_owner.m_configuration.m_maximumTransmissionUnit;
if (msgLen <= mtu)
{
- Interlocked.Increment(ref msg.m_inQueueCount);
- m_unsentMessages.Enqueue(msg);
- return;
+ NetSending send = new NetSending(msg, tp, GetSendSequenceNumber(tp));
+ msg.m_numUnfinishedSendings++;
+
+ send.SetNextResend(this);
+
+ m_unsentMessages.Enqueue(send);
+ return true;
}
#if DEBUG
- if ((int)msg.m_type < (int)NetMessageType.UserReliableUnordered)
+ if (tp < NetMessageType.UserReliableUnordered)
{
// unreliable
m_owner.LogWarning("Sending more than MTU (currently " + mtu + ") bytes unreliably is not recommended!");
@@ -609,6 +660,7 @@ namespace Lidgren.Network
// message must be fragmented
int fgi = Interlocked.Increment(ref m_nextFragmentGroupId);
+ // TODO: loop group id?
int numFragments = (msgLen + mtu - 1) / mtu;
@@ -616,16 +668,16 @@ namespace Lidgren.Network
{
int flen = (i == numFragments - 1 ? (msgLen - (mtu * (numFragments - 1))) : mtu);
- NetOutgoingMessage fm = m_owner.CreateMessage(flen);
- fm.m_fragmentGroupId = fgi;
- fm.m_fragmentNumber = i;
- fm.m_fragmentTotalCount = numFragments;
-
- fm.Write(msg.m_data, mtu * i, flen);
- fm.m_type = msg.m_type;
- Interlocked.Increment(ref fm.m_inQueueCount);
- m_unsentMessages.Enqueue(fm);
+ NetSending fs = new NetSending(msg, tp, GetSendSequenceNumber(tp));
+ fs.FragmentGroupId = fgi;
+ fs.FragmentNumber = i;
+ fs.FragmentTotalCount = numFragments;
+ msg.m_numUnfinishedSendings++;
+ m_unsentMessages.Enqueue(fs);
+ fs.SetNextResend(this);
}
+
+ return true;
}
public void Disconnect(string byeMessage)
@@ -640,26 +692,13 @@ namespace Lidgren.Network
// loosen up throttling
m_throttleDebt = -m_owner.m_configuration.m_throttlePeakBytes;
- // shorten resend times
- for (int i = 0; i < m_storedMessages.Length; i++)
- {
- Dictionary dict = m_storedMessages[i];
- if (dict != null)
- {
- try
- {
- foreach (NetOutgoingMessage om in dict.Values)
- om.m_nextResendTime = (om.m_nextResendTime * 0.8) - 0.05;
- }
- catch (InvalidOperationException)
- {
- // ok, collection was modified, never mind then - it was worth a shot
- }
- }
- }
+ // instantly resend all unacked
+ double now = NetTime.Now;
+ foreach(NetSending send in m_unackedSends)
+ send.NextResend = now;
NetOutgoingMessage bye = m_owner.CreateLibraryMessage(NetMessageLibraryType.Disconnect, byeMessage);
- EnqueueOutgoingMessage(bye);
+ SendLibrary(bye);
}
public void Approve()
diff --git a/Lidgren.Network/NetConnectionStatistics.cs b/Lidgren.Network/NetConnectionStatistics.cs
index 76b363b..a5159ed 100644
--- a/Lidgren.Network/NetConnectionStatistics.cs
+++ b/Lidgren.Network/NetConnectionStatistics.cs
@@ -72,6 +72,20 @@ namespace Lidgren.Network
///
public int ReceivedBytes { get { return m_receivedBytes; } }
+ public double LastSendRespondedTo { get { return m_connection.m_lastSendRespondedTo; } }
+
+ public int MostSends
+ {
+ get
+ {
+ int most = 0;
+ foreach (var a in m_connection.m_unackedSends)
+ if (a.NumSends > most)
+ most = a.NumSends;
+ return most;
+ }
+ }
+
[Conditional("DEBUG")]
internal void PacketSent(int numBytes, int numMessages)
{
diff --git a/Lidgren.Network/NetNatIntroduction.cs b/Lidgren.Network/NetNatIntroduction.cs
index d54b06a..40ef758 100644
--- a/Lidgren.Network/NetNatIntroduction.cs
+++ b/Lidgren.Network/NetNatIntroduction.cs
@@ -18,21 +18,23 @@ namespace Lidgren.Network
{
// send message to client
NetOutgoingMessage msg = CreateMessage(10 + token.Length + 1);
+ msg.m_libType = NetMessageLibraryType.NatIntroduction;
msg.Write(false);
msg.WritePadBits();
msg.Write(hostInternal);
msg.Write(hostExternal);
msg.Write(token);
- SendUnconnectedLibraryMessage(msg, NetMessageLibraryType.NatIntroduction, clientExternal);
+ SendUnconnectedLibrary(msg, clientExternal);
// send message to host
msg = CreateMessage(10 + token.Length + 1);
+ msg.m_libType = NetMessageLibraryType.NatIntroduction;
msg.Write(true);
msg.WritePadBits();
msg.Write(clientInternal);
msg.Write(clientExternal);
msg.Write(token);
- SendUnconnectedLibraryMessage(msg, NetMessageLibraryType.NatIntroduction, hostExternal);
+ SendUnconnectedLibrary(msg, hostExternal);
}
///
@@ -60,15 +62,17 @@ namespace Lidgren.Network
// send internal punch
punch = CreateMessage(1);
+ punch.m_libType = NetMessageLibraryType.NatPunchMessage;
punch.Write(hostByte);
punch.Write(token);
- SendUnconnectedLibraryMessage(punch, NetMessageLibraryType.NatPunchMessage, remoteInternal);
+ SendUnconnectedLibrary(punch, remoteInternal);
// send external punch
punch = CreateMessage(1);
+ punch.m_libType = NetMessageLibraryType.NatPunchMessage;
punch.Write(hostByte);
punch.Write(token);
- SendUnconnectedLibraryMessage(punch, NetMessageLibraryType.NatPunchMessage, remoteExternal);
+ SendUnconnectedLibrary(punch, remoteExternal);
}
///
diff --git a/Lidgren.Network/NetOutgoingMessage.cs b/Lidgren.Network/NetOutgoingMessage.cs
index 9a3fe69..7f04126 100644
--- a/Lidgren.Network/NetOutgoingMessage.cs
+++ b/Lidgren.Network/NetOutgoingMessage.cs
@@ -28,40 +28,32 @@ namespace Lidgren.Network
public sealed partial class NetOutgoingMessage
{
// reference count before message can be recycled
- internal int m_inQueueCount;
+ internal int m_numUnfinishedSendings;
- internal NetMessageType m_type;
- internal NetMessageLibraryType m_libType;
+ internal bool m_wasSent; // true is SendMessage() public method has been called
+ internal NetMessageLibraryType m_libType = NetMessageLibraryType.Error;
- internal IPEndPoint m_unconnectedRecipient;
-
- internal double m_lastSentTime; // when was this message sent last?
- internal double m_nextResendTime; // when to resend this message the next time
- internal int m_numSends; // the number of times this message has been sent/resent
-
- internal int m_fragmentGroupId;
- internal int m_fragmentNumber;
- internal int m_fragmentTotalCount;
+ //internal int m_fragmentGroupId;
+ //internal int m_fragmentNumber;
+ //internal int m_fragmentTotalCount;
///
/// Returns true if this message has been passed to SendMessage() already
///
- public bool IsSent { get { return m_numSends > 0; } }
+ public bool IsSent { get { return m_wasSent; } }
internal NetOutgoingMessage()
{
- Reset();
}
internal void Reset()
{
- NetException.Assert(m_inQueueCount == 0, "Ouch! Resetting NetOutgoingMessage still in some queue!");
+ NetException.Assert(m_numUnfinishedSendings == 0, "Ouch! Resetting NetOutgoingMessage still in some queue!");
+ NetException.Assert(m_wasSent == true, "Ouch! Resetting unsent message!");
m_bitLength = 0;
- m_type = NetMessageType.Error;
- m_inQueueCount = 0;
- m_numSends = 0;
- m_fragmentGroupId = -1;
+ m_libType = NetMessageLibraryType.Error;
+ m_wasSent = false;
}
internal static int EncodeAcksMessage(byte[] buffer, int ptr, NetConnection conn, int maxBytesPayload)
@@ -98,27 +90,18 @@ namespace Lidgren.Network
return ptr;
}
- // encode and store for resending (if conn != null and message is reliable)
- internal int Encode(double now, byte[] buffer, int ptr, NetConnection conn)
+ internal int EncodeUnfragmented(byte[] buffer, int ptr, NetMessageType tp, ushort sequenceNumber)
{
// message type
- buffer[ptr++] = (byte)((int)m_type | (m_fragmentGroupId == -1 ? 0 : 128));
+ buffer[ptr++] = (byte)tp; // | (m_fragmentGroupId == -1 ? 0 : 128));
- if (m_type == NetMessageType.Library)
- buffer[ptr++] =(byte)m_libType;
+ if (tp == NetMessageType.Library)
+ buffer[ptr++] = (byte)m_libType;
// channel sequence number
- if (m_type >= NetMessageType.UserSequenced)
+ if (tp >= NetMessageType.UserSequenced)
{
- if (conn == null)
- throw new NetException("Trying to encode NetMessageType " + m_type + " to unconnected endpoint!");
-
- ushort seqNr;
- if (m_type < NetMessageType.UserReliableUnordered)
- seqNr = conn.GetSendSequenceNumber(m_type); // "disposable" sequence number
- else
- seqNr = conn.StoreReliableMessage(now, this);
-
+ ushort seqNr = (ushort)sequenceNumber;
buffer[ptr++] = (byte)seqNr;
buffer[ptr++] = (byte)(seqNr >> 8);
}
@@ -140,17 +123,6 @@ namespace Lidgren.Network
throw new NetException("Packet content too large; 4095 bytes maximum");
}
- // fragmentation info
- if (m_fragmentGroupId != -1)
- {
- buffer[ptr++] = (byte)m_fragmentGroupId;
- buffer[ptr++] = (byte)(m_fragmentGroupId >> 8);
- buffer[ptr++] = (byte)m_fragmentTotalCount;
- buffer[ptr++] = (byte)(m_fragmentTotalCount >> 8);
- buffer[ptr++] = (byte)m_fragmentNumber;
- buffer[ptr++] = (byte)(m_fragmentNumber >> 8);
- }
-
// payload
if (payloadBitsLength > 0)
{
@@ -161,7 +133,62 @@ namespace Lidgren.Network
ptr += payloadBytesLength;
}
- m_numSends++;
+ return ptr;
+ }
+
+ internal int EncodeFragmented(byte[] buffer, int ptr, NetSending send, int mtu)
+ {
+ NetException.Assert(send.MessageType != NetMessageType.Library, "Library messages cant be fragmented");
+
+ // message type
+ buffer[ptr++] = (byte)((int)send.MessageType | 128);
+
+ // channel sequence number
+ if (send.MessageType >= NetMessageType.UserSequenced)
+ {
+ ushort seqNr = (ushort)send.SequenceNumber;
+ buffer[ptr++] = (byte)seqNr;
+ buffer[ptr++] = (byte)(seqNr >> 8);
+ }
+
+ // calculate fragment payload length
+ mtu -= NetConstants.FragmentHeaderSize; // size of fragmentation info
+ int thisFragmentLength = (send.FragmentNumber == send.FragmentTotalCount - 1 ? (send.Message.LengthBytes - (mtu * (send.FragmentTotalCount - 1))) : mtu);
+
+ int payloadBitsLength = thisFragmentLength * 8;
+ if (payloadBitsLength < 127)
+ {
+ buffer[ptr++] = (byte)payloadBitsLength;
+ }
+ else if (payloadBitsLength < 32768)
+ {
+ buffer[ptr++] = (byte)((payloadBitsLength & 127) | 128);
+ buffer[ptr++] = (byte)(payloadBitsLength >> 7);
+ }
+ else
+ {
+ throw new NetException("Packet content too large; 4095 bytes maximum");
+ }
+
+ // fragmentation info
+ buffer[ptr++] = (byte)send.FragmentGroupId;
+ buffer[ptr++] = (byte)(send.FragmentGroupId >> 8);
+ buffer[ptr++] = (byte)send.FragmentTotalCount;
+ buffer[ptr++] = (byte)(send.FragmentTotalCount >> 8);
+ buffer[ptr++] = (byte)send.FragmentNumber;
+ buffer[ptr++] = (byte)(send.FragmentNumber >> 8);
+
+ // payload
+ if (payloadBitsLength > 0)
+ {
+ // zero out last byte
+ buffer[ptr + thisFragmentLength] = 0;
+
+ int offset = (mtu * send.FragmentNumber);
+
+ Buffer.BlockCopy(m_data, offset, buffer, ptr, thisFragmentLength);
+ ptr += thisFragmentLength;
+ }
return ptr;
}
@@ -184,18 +211,7 @@ namespace Lidgren.Network
public override string ToString()
{
- StringBuilder bdr = new StringBuilder();
- bdr.Append("[NetOutgoingMessage ");
- bdr.Append(m_type.ToString());
- if (m_type == NetMessageType.Library)
- {
- bdr.Append('|');
- bdr.Append(m_libType.ToString());
- }
- bdr.Append(" sent ");
- bdr.Append(m_numSends);
- bdr.Append(" times]");
- return bdr.ToString();
+ return "[NetOutgoingMessage " + LengthBytes + " bytes]";
}
}
}
diff --git a/Lidgren.Network/NetPeer.ConnectionApproval.cs b/Lidgren.Network/NetPeer.ConnectionApproval.cs
index 1e7d9a4..f5160b9 100644
--- a/Lidgren.Network/NetPeer.ConnectionApproval.cs
+++ b/Lidgren.Network/NetPeer.ConnectionApproval.cs
@@ -75,7 +75,7 @@ namespace Lidgren.Network
case PendingConnectionStatus.Denied:
// send disconnected
NetOutgoingMessage bye = CreateLibraryMessage(NetMessageLibraryType.Disconnect, conn.m_pendingDenialReason);
- EnqueueUnconnectedMessage(bye, conn.m_remoteEndpoint);
+ SendUnconnectedLibrary(bye, conn.m_remoteEndpoint);
m_pendingConnections.Remove(conn);
return;
}
diff --git a/Lidgren.Network/NetPeer.Discovery.cs b/Lidgren.Network/NetPeer.Discovery.cs
index 3b80a2d..fa48165 100644
--- a/Lidgren.Network/NetPeer.Discovery.cs
+++ b/Lidgren.Network/NetPeer.Discovery.cs
@@ -11,7 +11,8 @@ namespace Lidgren.Network
public void DiscoverLocalPeers(int serverPort)
{
NetOutgoingMessage om = CreateMessage(0);
- SendUnconnectedLibraryMessage(om, NetMessageLibraryType.Discovery, new IPEndPoint(IPAddress.Broadcast, serverPort));
+ om.m_libType = NetMessageLibraryType.Discovery;
+ SendUnconnectedLibrary(om, new IPEndPoint(IPAddress.Broadcast, serverPort));
}
///
@@ -31,7 +32,8 @@ namespace Lidgren.Network
public bool DiscoverKnownPeer(IPEndPoint endpoint)
{
NetOutgoingMessage om = CreateMessage(0);
- SendUnconnectedLibraryMessage(om, NetMessageLibraryType.Discovery, endpoint);
+ om.m_libType = NetMessageLibraryType.Discovery;
+ SendUnconnectedLibrary(om, endpoint);
return true;
}
}
diff --git a/Lidgren.Network/NetPeer.Internal.cs b/Lidgren.Network/NetPeer.Internal.cs
index a128087..32116f7 100644
--- a/Lidgren.Network/NetPeer.Internal.cs
+++ b/Lidgren.Network/NetPeer.Internal.cs
@@ -34,8 +34,8 @@ namespace Lidgren.Network
private int m_listenPort;
private AutoResetEvent m_messageReceivedEvent = new AutoResetEvent(false);
- private readonly NetQueue m_releasedIncomingMessages = new NetQueue(16);
- private readonly NetQueue m_unsentUnconnectedMessage = new NetQueue(4);
+ private readonly NetQueue m_releasedIncomingMessages = new NetQueue(8);
+ private readonly NetQueue m_unsentUnconnectedMessage = new NetQueue(2);
///
/// Signalling event which can be waited on to determine when a message is queued for reading.
@@ -115,9 +115,19 @@ namespace Lidgren.Network
m_listenPort = boundEp.Port;
- long first = (pa == null ? (long)this.GetHashCode() : (long)pa.GetHashCode());
- long second = (long)((long)boundEp.GetHashCode() << 32);
- m_uniqueIdentifier = first ^ second;
+ int first = (pa == null ? this.GetHashCode() : pa.GetHashCode());
+ int second = boundEp.GetHashCode();
+
+ byte[] raw = new byte[8];
+ raw[0] = (byte)first;
+ raw[1] = (byte)(first << 8);
+ raw[2] = (byte)(first << 16);
+ raw[3] = (byte)(first << 24);
+ raw[4] = (byte)second;
+ raw[5] = (byte)(second << 8);
+ raw[6] = (byte)(second << 16);
+ raw[7] = (byte)(second << 24);
+ m_uniqueIdentifier = BitConverter.ToInt64(NetSha.Hash(raw), 0);
m_receiveBuffer = new byte[m_configuration.ReceiveBufferSize];
m_sendBuffer = new byte[m_configuration.SendBufferSize];
@@ -222,25 +232,23 @@ namespace Lidgren.Network
}
// send unconnected sends
- NetOutgoingMessage um;
- while ((um = m_unsentUnconnectedMessage.TryDequeue()) != null)
+ NetSending uncSend;
+ while ((uncSend = m_unsentUnconnectedMessage.TryDequeue()) != null)
{
- IPEndPoint recipient = um.m_unconnectedRecipient;
-
//
// TODO: use throttling here
//
- int ptr = um.Encode(now, m_sendBuffer, 0, null);
+ int ptr = uncSend.Message.EncodeUnfragmented(m_sendBuffer, 0, uncSend.MessageType, uncSend.SequenceNumber);
bool connectionReset = false;
- if (recipient.Address.Equals(IPAddress.Broadcast))
+ if (uncSend.Recipient.Address.Equals(IPAddress.Broadcast))
{
// send using broadcast
try
{
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
- SendPacket(ptr, recipient, 1, out connectionReset);
+ SendPacket(ptr, uncSend.Recipient, 1, out connectionReset);
}
finally
{
@@ -250,11 +258,16 @@ namespace Lidgren.Network
else
{
// send normally
- SendPacket(ptr, recipient, 1, out connectionReset);
+ SendPacket(ptr, uncSend.Recipient, 1, out connectionReset);
}
if (connectionReset)
LogWarning(NetConstants.ConnResetMessage);
+
+ int unfin = uncSend.Message.m_numUnfinishedSendings;
+ uncSend.Message.m_numUnfinishedSendings = unfin - 1;
+ if (unfin <= 1)
+ Recycle(uncSend.Message);
}
// check if we need to reduce the recycled pool
@@ -487,7 +500,7 @@ namespace Lidgren.Network
LogWarning("Connect received with wrong appidentifier (need '" + m_configuration.AppIdentifier + "' found '" + appIdent + "') from " + senderEndpoint);
NetOutgoingMessage bye = CreateLibraryMessage(NetMessageLibraryType.Disconnect, "Wrong app identifier!");
- SendUnconnectedLibraryMessage(bye, NetMessageLibraryType.Disconnect, senderEndpoint);
+ SendUnconnectedLibrary(bye, senderEndpoint);
break;
}
@@ -557,17 +570,30 @@ namespace Lidgren.Network
private void HandleServerFull(IPEndPoint connecter)
{
- const string rejectMessage = "Server is full!";
+ const string rejectMessage = "Server is full!"; // TODO: put in configuration
NetOutgoingMessage reply = CreateLibraryMessage(NetMessageLibraryType.Disconnect, rejectMessage);
- EnqueueUnconnectedMessage(reply, connecter);
+ SendLibraryImmediately(reply, connecter);
}
// called by user and network thread
private void EnqueueUnconnectedMessage(NetOutgoingMessage msg, IPEndPoint recipient)
{
- msg.m_unconnectedRecipient = recipient;
- Interlocked.Increment(ref msg.m_inQueueCount);
- m_unsentUnconnectedMessage.Enqueue(msg);
+ NetSending send = new NetSending(msg, NetMessageType.UserUnreliable, 0);
+ send.Recipient = recipient;
+
+ msg.m_numUnfinishedSendings++;
+ m_unsentUnconnectedMessage.Enqueue(send);
+ }
+
+ // called by user and network thread
+ private void SendUnconnectedLibrary(NetOutgoingMessage msg, IPEndPoint recipient)
+ {
+ msg.m_wasSent = true;
+ NetSending send = new NetSending(msg, NetMessageType.Library, 0);
+ send.Recipient = recipient;
+
+ msg.m_numUnfinishedSendings++;
+ m_unsentUnconnectedMessage.Enqueue(send);
}
internal static NetDeliveryMethod GetDeliveryMethod(NetMessageType mtp)
@@ -583,21 +609,18 @@ namespace Lidgren.Network
return NetDeliveryMethod.Unreliable;
}
- internal void SendImmediately(double now, NetConnection conn, NetOutgoingMessage msg)
+ internal void SendLibraryImmediately(NetOutgoingMessage msg, IPEndPoint destination)
{
- NetException.Assert(msg.m_type == NetMessageType.Library, "SendImmediately can only send library (non-reliable) messages");
-
- msg.m_inQueueCount = 1;
- int len = msg.Encode(now, m_sendBuffer, 0, conn);
- Interlocked.Decrement(ref msg.m_inQueueCount);
+ msg.m_wasSent = true;
+ int len = msg.EncodeUnfragmented(m_sendBuffer, 0, NetMessageType.Library, 0);
bool connectionReset;
- SendPacket(len, conn.m_remoteEndpoint, 1, out connectionReset);
+ SendPacket(len, destination, 1, out connectionReset);
+
+ // TODO: handle connectionReset
Recycle(msg);
-
- if (connectionReset)
- LogWarning("Connection was reset; remote host is not listening");
}
+
}
}
\ No newline at end of file
diff --git a/Lidgren.Network/NetPeer.Recycling.cs b/Lidgren.Network/NetPeer.Recycling.cs
index 5b2ca36..f622bdf 100644
--- a/Lidgren.Network/NetPeer.Recycling.cs
+++ b/Lidgren.Network/NetPeer.Recycling.cs
@@ -96,7 +96,6 @@ namespace Lidgren.Network
internal NetOutgoingMessage CreateLibraryMessage(NetMessageLibraryType tp, string content)
{
NetOutgoingMessage retval = CreateMessage(1 + (content == null ? 0 : content.Length));
- retval.m_type = NetMessageType.Library;
retval.m_libType = tp;
retval.Write((content == null ? "" : content));
return retval;
@@ -139,22 +138,20 @@ namespace Lidgren.Network
{
foreach (NetConnection conn in m_connections)
{
- if (conn.m_unsentMessages.Contains(msg))
- throw new NetException("Ouch! Recycling unsent message!");
-
- for(int i=0;i
- /// Send a message to a number of existing connections
+ /// Send a message to a number of existing connections; returns true if all recipients were sent the message
///
/// Delivery channel (0-31)
- public bool SendMessage(NetOutgoingMessage msg, IEnumerable recipients, NetDeliveryMethod deliveryMethod, int channel)
+ public bool SendMessage(NetOutgoingMessage msg, IEnumerable recipients, NetDeliveryMethod deliveryMethod, int sequenceChannel)
{
if (msg.IsSent)
throw new NetException("Message has already been sent!");
- if (channel < 0 || channel > NetConstants.NetChannelsPerDeliveryMethod)
+ if (sequenceChannel < 0 || sequenceChannel > NetConstants.NetChannelsPerDeliveryMethod)
throw new NetException("Channel must be between 0 and " + (NetConstants.NetChannelsPerDeliveryMethod - 1));
- if (channel != 0 && (deliveryMethod == NetDeliveryMethod.Unreliable || deliveryMethod == NetDeliveryMethod.ReliableUnordered))
+ if (sequenceChannel != 0 && (deliveryMethod == NetDeliveryMethod.Unreliable || deliveryMethod == NetDeliveryMethod.ReliableUnordered))
throw new NetException("Channel must be 0 for Unreliable and ReliableUnordered");
if (m_status != NetPeerStatus.Running)
return false;
- msg.m_type = (NetMessageType)((int)deliveryMethod + channel);
+ msg.m_wasSent = true;
+ NetMessageType tp = (NetMessageType)((int)deliveryMethod + sequenceChannel);
+
+ bool all = true;
foreach (NetConnection conn in recipients)
- conn.EnqueueOutgoingMessage(msg);
+ {
+ if (!conn.EnqueueSendMessage(msg, tp))
+ all = false;
+ }
- return true;
+ return all;
}
///
@@ -282,7 +284,6 @@ namespace Lidgren.Network
if (adr == null)
throw new NetException("Failed to resolve " + host);
- msg.m_type = NetMessageType.UserUnreliable; // sortof not applicable
EnqueueUnconnectedMessage(msg, new IPEndPoint(adr, port));
}
@@ -293,16 +294,6 @@ namespace Lidgren.Network
{
if (msg.IsSent)
throw new NetException("Message has already been sent!");
- msg.m_type = NetMessageType.UserUnreliable; // sortof not applicable
- EnqueueUnconnectedMessage(msg, recipient);
- }
-
- internal void SendUnconnectedLibraryMessage(NetOutgoingMessage msg, NetMessageLibraryType libType, IPEndPoint recipient)
- {
- if (msg.IsSent)
- throw new NetException("Message has already been sent!");
- msg.m_type = NetMessageType.Library;
- msg.m_libType = libType;
EnqueueUnconnectedMessage(msg, recipient);
}
@@ -313,9 +304,8 @@ namespace Lidgren.Network
{
if (msg.IsSent)
throw new NetException("Message has already been sent!");
- msg.m_type = NetMessageType.UserUnreliable; // sortof not applicable
- foreach (IPEndPoint ipe in recipients)
- EnqueueUnconnectedMessage(msg, ipe);
+ foreach (IPEndPoint rec in recipients)
+ EnqueueUnconnectedMessage(msg, rec);
}
///
@@ -327,9 +317,9 @@ namespace Lidgren.Network
msg = CreateMessage(0);
if (msg.IsSent)
throw new NetException("Message has already been sent!");
- msg.m_type = NetMessageType.Library;
+
msg.m_libType = NetMessageLibraryType.DiscoveryResponse;
- EnqueueUnconnectedMessage(msg, recipient);
+ SendUnconnectedLibrary(msg, recipient);
}
///
diff --git a/Lidgren.Network/NetPeerConfiguration.cs b/Lidgren.Network/NetPeerConfiguration.cs
index 4459cc4..38aceb2 100644
--- a/Lidgren.Network/NetPeerConfiguration.cs
+++ b/Lidgren.Network/NetPeerConfiguration.cs
@@ -52,8 +52,6 @@ namespace Lidgren.Network
internal float m_pingFrequency;
// reliability
- internal float[] m_resendRTTMultiplier;
- internal float[] m_resendBaseTime;
internal float m_maxAckDelayTime;
// bad network simulation
@@ -77,7 +75,7 @@ namespace Lidgren.Network
m_sendBufferSize = 131071;
m_keepAliveDelay = 4.0f;
m_connectionTimeout = 25;
- m_maximumConnections = 8;
+ m_maximumConnections = 16;
m_defaultOutgoingMessageCapacity = 8;
m_pingFrequency = 6.0f;
m_throttleBytesPerSecond = 1024 * 512;
@@ -96,37 +94,6 @@ namespace Lidgren.Network
// default disabled types
m_disabledTypes = NetIncomingMessageType.ConnectionApproval | NetIncomingMessageType.UnconnectedData | NetIncomingMessageType.VerboseDebugMessage;
- // reliability
- m_resendRTTMultiplier = new float[]
- {
- 1.1f,
- 2.25f,
- 3.5f,
- 4.0f,
- 4.0f,
- 4.0f,
- 4.0f,
- 4.0f,
- 4.0f,
- 6.0f,
- 6.0f
- };
-
- m_resendBaseTime = new float[]
- {
- 0.025f, // just processing time + ack delay wait time
- 0.05f, // just processing time + ack delay wait time
- 0.2f, // 0.16 delay since last resend
- 0.5f, // 0.3 delay
- 1.5f, // 1.0 delay
- 3.0f, // 1.5 delay
- 5.0f, // 2.0 delay
- 7.5f, // 2.5 delay
- 12.5f, // 5.0 delay
- 17.5f, // 5.0 delay
- 25.0f // 7.5 delay, obi wan you're my only hope
- };
-
// Maximum transmission unit
// The aim is for a max full packet to be 1440 bytes (30 x 48 bytes, lower than 1468)
// 20 bytes ip header
diff --git a/Lidgren.Network/NetQueue.cs b/Lidgren.Network/NetQueue.cs
index 37fd79f..3049990 100644
--- a/Lidgren.Network/NetQueue.cs
+++ b/Lidgren.Network/NetQueue.cs
@@ -63,12 +63,12 @@ namespace Lidgren.Network
public void Enqueue(T item)
{
#if DEBUG
- if (typeof(T) == typeof(NetOutgoingMessage))
+ if (typeof(T) == typeof(NetSending))
{
- NetOutgoingMessage om = item as NetOutgoingMessage;
+ NetSending om = item as NetSending;
if (om != null)
- if (om.m_type == NetMessageType.Error)
- throw new NetException("Enqueuing error message!");
+ if (om.MessageType == NetMessageType.Error)
+ throw new NetException("Enqueuing NetSending with MessageType.Error!");
}
#endif
lock (m_lock)
@@ -153,6 +153,19 @@ namespace Lidgren.Network
}
}
+ public T TryPeek(int offset)
+ {
+ if (m_size == 0)
+ return default(T);
+
+ lock (m_lock)
+ {
+ if (m_size == 0)
+ return default(T);
+ return m_items[(m_head + offset) % m_items.Length];
+ }
+ }
+
public bool Contains(T item)
{
lock (m_lock)
diff --git a/Lidgren.Network/NetSending.cs b/Lidgren.Network/NetSending.cs
new file mode 100644
index 0000000..2936860
--- /dev/null
+++ b/Lidgren.Network/NetSending.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Diagnostics;
+
+namespace Lidgren.Network
+{
+ [DebuggerDisplay("MessageType={MessageType} SequenceNumber={SequenceNumber} NumSends={NumSends}")]
+ internal sealed class NetSending
+ {
+ public NetOutgoingMessage Message;
+ public IPEndPoint Recipient;
+ public NetMessageType MessageType;
+ public ushort SequenceNumber;
+ public double NextResend;
+ public int NumSends; // how many times has this sending been sent
+
+ public int FragmentGroupId;
+ public int FragmentNumber;
+ public int FragmentTotalCount;
+
+ public NetSending(NetOutgoingMessage msg, NetMessageType tp, ushort sequenceNumber)
+ {
+ Message = msg;
+ MessageType = tp;
+ SequenceNumber = sequenceNumber;
+ }
+
+ internal void SetNextResend(NetConnection conn)
+ {
+ float baseDelay;
+ switch(NumSends)
+ {
+ case 0: baseDelay = 0.025f; break;
+ case 1: baseDelay = 0.05f; break;
+ case 2: baseDelay = 0.15f; break;
+ case 3: baseDelay = 0.3f; break;
+ default:
+ baseDelay = (float)(NumSends - 3); // 4: 1 second, 5: 2 seconds, 6: 3 seconds etc
+ break;
+ }
+
+ float rttMultiplier = 1.15f + (0.15f * NumSends);
+
+ float totalDelay = baseDelay + (conn.AverageRoundtripTime * rttMultiplier);
+
+ NextResend = NetTime.Now + totalDelay;
+ }
+ }
+}
diff --git a/Samples/SamplesCommon/NetPeerSettingsWindow.cs b/Samples/SamplesCommon/NetPeerSettingsWindow.cs
index 4646015..92dfb09 100644
--- a/Samples/SamplesCommon/NetPeerSettingsWindow.cs
+++ b/Samples/SamplesCommon/NetPeerSettingsWindow.cs
@@ -66,6 +66,8 @@ namespace SamplesCommon
NetConnection conn = Peer.Connections[0];
bdr.AppendLine("Connection 0:");
bdr.AppendLine("Average RTT: " + ((int)(conn.AverageRoundtripTime * 1000.0f)) + " ms");
+ bdr.AppendLine("Last response: " + (int)(NetTime.Now - conn.Statistics.LastSendRespondedTo) + "s ago");
+ bdr.AppendLine("Most sends: " + conn.Statistics.MostSends);
bdr.Append(conn.Statistics.ToString());
}