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()); }