diff --git a/Lidgren.Network/NetNatIntroduction.cs b/Lidgren.Network/NetNatIntroduction.cs index d239c39..e59067a 100644 --- a/Lidgren.Network/NetNatIntroduction.cs +++ b/Lidgren.Network/NetNatIntroduction.cs @@ -18,24 +18,24 @@ namespace Lidgren.Network string token) { // send message to client - NetOutgoingMessage msg = CreateMessage(10 + token.Length + 1); - msg.m_messageType = NetMessageType.NatIntroduction; - msg.Write((byte)0); - msg.Write(hostInternal); - msg.Write(hostExternal); - msg.Write(token); - Interlocked.Increment(ref msg.m_recyclingCount); - m_unsentUnconnectedMessages.Enqueue(new NetTuple(clientExternal, msg)); + NetOutgoingMessage um = CreateMessage(10 + token.Length + 1); + um.m_messageType = NetMessageType.NatIntroduction; + um.Write((byte)0); + um.Write(hostInternal); + um.Write(hostExternal); + um.Write(token); + Interlocked.Increment(ref um.m_recyclingCount); + m_unsentUnconnectedMessages.Enqueue(new NetTuple(clientExternal, um)); // send message to host - msg = CreateMessage(10 + token.Length + 1); - msg.m_messageType = NetMessageType.NatIntroduction; - msg.Write((byte)1); - msg.Write(clientInternal); - msg.Write(clientExternal); - msg.Write(token); - Interlocked.Increment(ref msg.m_recyclingCount); - m_unsentUnconnectedMessages.Enqueue(new NetTuple(hostExternal, msg)); + um = CreateMessage(10 + token.Length + 1); + um.m_messageType = NetMessageType.NatIntroduction; + um.Write((byte)1); + um.Write(clientInternal); + um.Write(clientExternal); + um.Write(token); + Interlocked.Increment(ref um.m_recyclingCount); + m_unsentUnconnectedMessages.Enqueue(new NetTuple(hostExternal, um)); } /// diff --git a/Lidgren.Network/NetOutgoingMessage.cs b/Lidgren.Network/NetOutgoingMessage.cs index 532247a..0bd4f98 100644 --- a/Lidgren.Network/NetOutgoingMessage.cs +++ b/Lidgren.Network/NetOutgoingMessage.cs @@ -30,7 +30,14 @@ namespace Lidgren.Network { internal NetMessageType m_messageType; internal bool m_isSent; - internal int m_recyclingCount; // when this reaches zero the message is ready to be recycled + + // Recycling count is: + // * incremented for each recipient on send + // * incremented, when reliable, in SenderChannel.ExecuteSend() + // * decremented (both reliable and unreliable) in NetConnection.QueueSendMessage() + // * decremented, when reliable, in SenderChannel.DestoreMessage() + // ... when it reaches zero it can be recycled + internal int m_recyclingCount; internal int m_fragmentGroup; // which group of fragments ths belongs to internal int m_fragmentGroupTotalBits; // total number of bits in this group diff --git a/Lidgren.Network/NetPeer.Discovery.cs b/Lidgren.Network/NetPeer.Discovery.cs index 7a420a6..7786732 100644 --- a/Lidgren.Network/NetPeer.Discovery.cs +++ b/Lidgren.Network/NetPeer.Discovery.cs @@ -11,10 +11,10 @@ namespace Lidgren.Network /// public void DiscoverLocalPeers(int serverPort) { - NetOutgoingMessage om = CreateMessage(0); - om.m_messageType = NetMessageType.Discovery; - Interlocked.Increment(ref om.m_recyclingCount); - m_unsentUnconnectedMessages.Enqueue(new NetTuple(new IPEndPoint(IPAddress.Broadcast, serverPort), om)); + NetOutgoingMessage um = CreateMessage(0); + um.m_messageType = NetMessageType.Discovery; + Interlocked.Increment(ref um.m_recyclingCount); + m_unsentUnconnectedMessages.Enqueue(new NetTuple(new IPEndPoint(IPAddress.Broadcast, serverPort), um)); } /// diff --git a/Lidgren.Network/NetPeer.MessagePools.cs b/Lidgren.Network/NetPeer.MessagePools.cs index 0798fe6..ee1fb37 100644 --- a/Lidgren.Network/NetPeer.MessagePools.cs +++ b/Lidgren.Network/NetPeer.MessagePools.cs @@ -158,10 +158,10 @@ namespace Lidgren.Network /// public void Recycle(NetIncomingMessage msg) { - if (m_incomingMessagesPool == null) + if (m_incomingMessagesPool == null || msg == null) return; - NetException.Assert(m_incomingMessagesPool.Contains(msg) == false, "Recyling already recycled message! Thread race?"); + NetException.Assert(m_incomingMessagesPool.Contains(msg) == false, "Recyling already recycled incoming message! Thread race?"); byte[] storage = msg.m_data; msg.m_data = null; @@ -188,7 +188,7 @@ namespace Lidgren.Network if (m_outgoingMessagesPool == null) return; #if DEBUG - NetException.Assert(m_outgoingMessagesPool.Contains(msg) == false, "Recyling already recycled message! Thread race?"); + NetException.Assert(m_outgoingMessagesPool.Contains(msg) == false, "Recyling already recycled outgoing message! Thread race?"); if (msg.m_recyclingCount != 0) LogWarning("Wrong recycling count! should be zero; found " + msg.m_recyclingCount); #endif diff --git a/Lidgren.Network/NetPeer.Send.cs b/Lidgren.Network/NetPeer.Send.cs index 80b868f..f803b1a 100644 --- a/Lidgren.Network/NetPeer.Send.cs +++ b/Lidgren.Network/NetPeer.Send.cs @@ -209,31 +209,35 @@ namespace Lidgren.Network /// /// Send a message to this exact same netpeer (loopback) /// - public void SendUnconnectedToSelf(NetOutgoingMessage msg) + public void SendUnconnectedToSelf(NetOutgoingMessage om) { - if (msg == null) + if (om == null) throw new ArgumentNullException("msg"); - if (msg.m_isSent) + if (om.m_isSent) throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently"); - msg.m_messageType = NetMessageType.Unconnected; - msg.m_isSent = true; + om.m_messageType = NetMessageType.Unconnected; + om.m_isSent = true; if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData) == false) { - Interlocked.Decrement(ref msg.m_recyclingCount); + Interlocked.Decrement(ref om.m_recyclingCount); return; // dropping unconnected message since it's not enabled for receiving } - NetIncomingMessage om = CreateIncomingMessage(NetIncomingMessageType.UnconnectedData, msg.LengthBytes); - om.Write(msg); - om.m_isFragment = false; - om.m_receiveTime = NetTime.Now; - om.m_senderConnection = null; - om.m_senderEndPoint = m_socket.LocalEndPoint as IPEndPoint; - NetException.Assert(om.m_bitLength == msg.LengthBits); + // convert outgoing to incoming + NetIncomingMessage im = CreateIncomingMessage(NetIncomingMessageType.UnconnectedData, om.LengthBytes); + im.Write(om); + im.m_isFragment = false; + im.m_receiveTime = NetTime.Now; + im.m_senderConnection = null; + im.m_senderEndPoint = m_socket.LocalEndPoint as IPEndPoint; + NetException.Assert(im.m_bitLength == om.LengthBits); - ReleaseMessage(om); + // recycle outgoing message + Recycle(om); + + ReleaseMessage(im); } } } diff --git a/Lidgren.Network/NetPeer.cs b/Lidgren.Network/NetPeer.cs index 7d8088e..69ae45b 100644 --- a/Lidgren.Network/NetPeer.cs +++ b/Lidgren.Network/NetPeer.cs @@ -225,7 +225,7 @@ namespace Lidgren.Network return added; } - // send message immediately + // send message immediately and recycle it internal void SendLibrary(NetOutgoingMessage msg, IPEndPoint recipient) { VerifyNetworkThread(); @@ -234,6 +234,10 @@ namespace Lidgren.Network bool connReset; int len = msg.Encode(m_sendBuffer, 0, 0); SendPacket(len, recipient, 1, out connReset); + + // no reliability, no multiple recipients - we can just recycle this message immediately + msg.m_recyclingCount = 0; + Recycle(msg); } /// diff --git a/Lidgren.Network/NetReliableSenderChannel.cs b/Lidgren.Network/NetReliableSenderChannel.cs index 1f9a13a..eac8bfe 100644 --- a/Lidgren.Network/NetReliableSenderChannel.cs +++ b/Lidgren.Network/NetReliableSenderChannel.cs @@ -118,6 +118,10 @@ namespace Lidgren.Network int seqNr = m_sendStart; m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers; + // must increment recycle count here, since it's decremented in QueueSendMessage and we want to keep it for the future in case or resends + // we will decrement once more in DestoreMessage for final recycling + Interlocked.Increment(ref message.m_recyclingCount); + m_connection.QueueSendMessage(message, seqNr); int storeIndex = seqNr % m_windowSize; @@ -134,6 +138,9 @@ namespace Lidgren.Network private void DestoreMessage(int storeIndex) { NetOutgoingMessage storedMessage = m_storedMessages[storeIndex].Message; + + // on each destore; reduce recyclingcount so that when all instances are destored, the outgoing message can be recycled + Interlocked.Decrement(ref storedMessage.m_recyclingCount); #if DEBUG if (storedMessage == null) throw new NetException("m_storedMessages[" + storeIndex + "].Message is null; sent " + m_storedMessages[storeIndex].NumSent + " times, last time " + (NetTime.Now - m_storedMessages[storeIndex].LastSent) + " seconds ago"); diff --git a/Samples/LibraryTestSamples/ManySample/ManyServer/Program.cs b/Samples/LibraryTestSamples/ManySample/ManyServer/Program.cs index 6ee6f2f..f8cdede 100644 --- a/Samples/LibraryTestSamples/ManySample/ManyServer/Program.cs +++ b/Samples/LibraryTestSamples/ManySample/ManyServer/Program.cs @@ -73,7 +73,13 @@ namespace ManyServer var conns = Server.Connections; // resend to ONE random connection - Server.SendMessage(outMsg, conns[NetRandom.Instance.Next(0, conns.Count)], NetDeliveryMethod.ReliableOrdered, 0); + //Server.SendMessage(outMsg, conns[NetRandom.Instance.Next(0, conns.Count)], NetDeliveryMethod.ReliableOrdered, 0); + + List rec = new List(); + rec.AddRange(conns); + rec.Remove(inc.SenderConnection); + if (rec.Count > 0) + Server.SendMessage(outMsg, rec, NetDeliveryMethod.ReliableOrdered, 0); break; } } diff --git a/Samples/LibraryTestSamples/UnconnectedSample/UnconnectedSample/Program.cs b/Samples/LibraryTestSamples/UnconnectedSample/UnconnectedSample/Program.cs index de487c7..45751cb 100644 --- a/Samples/LibraryTestSamples/UnconnectedSample/UnconnectedSample/Program.cs +++ b/Samples/LibraryTestSamples/UnconnectedSample/UnconnectedSample/Program.cs @@ -49,7 +49,6 @@ namespace UnconnectedSample break; case NetIncomingMessageType.UnconnectedData: MainForm.richTextBox1.AppendText("Received from " + im.SenderEndPoint + ": " + im.ReadString() + Environment.NewLine); - Peer.Recycle(im); break; } Peer.Recycle(im);