diff --git a/Source/ACE.Server/Network/NetworkBundle.cs b/Source/ACE.Server/Network/NetworkBundle.cs index 6eb8f24e90..0695ab9213 100644 --- a/Source/ACE.Server/Network/NetworkBundle.cs +++ b/Source/ACE.Server/Network/NetworkBundle.cs @@ -9,7 +9,7 @@ internal class NetworkBundle private bool propChanged; public bool NeedsSending => propChanged || messages.Count > 0; - + public int MessageCount => messages.Count; public bool HasMoreMessages => messages.Count > 0; private Queue messages = new Queue(); diff --git a/Source/ACE.Server/Network/NetworkSession.cs b/Source/ACE.Server/Network/NetworkSession.cs index 415ca6319f..16803e5bc4 100644 --- a/Source/ACE.Server/Network/NetworkSession.cs +++ b/Source/ACE.Server/Network/NetworkSession.cs @@ -809,39 +809,52 @@ private void SendBundle(NetworkBundle bundle, GameMessageGroup group) { packetLog.DebugFormat("[{0}] Sending Bundle", session.LoggingIdentifier); - bool writeOptionalHeaders = true; - - List fragments = new List(); - // Pull all messages out and create MessageFragment objects - while (bundle.HasMoreMessages) - { - var message = bundle.Dequeue(); + var totalFragments = bundle.MessageCount; + var fragments = new MessageFragment[totalFragments]; + for (int i = 0; i < totalFragments; i++) + fragments[i] = new MessageFragment(bundle.Dequeue(), ConnectionData.FragmentSequence++); - var fragment = new MessageFragment(message, ConnectionData.FragmentSequence++); - fragments.Add(fragment); - } + packetLog.DebugFormat("[{0}] Bundle Fragment Count: {1}", session.LoggingIdentifier, totalFragments); - packetLog.DebugFormat("[{0}] Bundle Fragment Count: {1}", session.LoggingIdentifier, fragments.Count); + var hdrSize = PacketFragmentHeader.HeaderSize; + int processedCount = 0; + int lowerBoundIdx = 0; + int seekIdx = 0; + bool writeOptionalHeaders = true; // Loop through while we have fragements - while (fragments.Count > 0 || writeOptionalHeaders) + while (processedCount < totalFragments || writeOptionalHeaders) { + if (isReleased) + return; + ServerPacket packet = new ServerPacket(); PacketHeader packetHeader = packet.Header; - if (fragments.Count > 0) + if (processedCount < totalFragments) packetHeader.Flags |= PacketHeaderFlags.BlobFragments; if (bundle.EncryptedChecksum) packetHeader.Flags |= PacketHeaderFlags.EncryptedChecksum; - int availableSpace = ServerPacket.MaxPacketSize; + seekIdx = lowerBoundIdx; // Pull first message and see if it is a large one - var firstMessage = fragments.FirstOrDefault(); + MessageFragment firstMessage = null; + while (seekIdx < totalFragments) + { + firstMessage = fragments[seekIdx]; + if (firstMessage != null) + break; + seekIdx++; + lowerBoundIdx++; + } + if (firstMessage != null) { + int availableSpace = ServerPacket.MaxPacketSize; + // If a large message send only this one, filling the whole packet if (firstMessage.DataRemaining >= availableSpace) { @@ -850,7 +863,11 @@ private void SendBundle(NetworkBundle bundle, GameMessageGroup group) packet.Fragments.Add(spf); availableSpace -= spf.Length; if (firstMessage.DataRemaining <= 0) - fragments.Remove(firstMessage); + { + processedCount++; + lowerBoundIdx++; + fragments[seekIdx++] = null; + } } // Otherwise we'll write any optional headers and process any small messages that will fit else @@ -863,15 +880,20 @@ private void SendBundle(NetworkBundle bundle, GameMessageGroup group) availableSpace -= (int)packet.Data.Length; } - // Create a list to remove completed messages after iterator - List removeList = new List(); - - foreach (MessageFragment fragment in fragments) + while (seekIdx < totalFragments) { + var fragment = fragments[seekIdx]; + if (fragment == null) + { + seekIdx++; + continue; + } + bool fragmentSkipped = false; + // Is this a large fragment and does it have a tail that needs sending? - if (!fragment.TailSent && availableSpace >= fragment.TailSize) + if (availableSpace >= hdrSize && !fragment.TailSent && availableSpace >= fragment.TailSize) { packetLog.DebugFormat("[{0}] Sending tail fragment", session.LoggingIdentifier); ServerPacketFragment spf = fragment.GetTailFragment(); @@ -879,7 +901,7 @@ private void SendBundle(NetworkBundle bundle, GameMessageGroup group) availableSpace -= spf.Length; } // Otherwise will this message fit in the remaining space? - else if (availableSpace >= fragment.NextSize) + else if (availableSpace >= hdrSize && availableSpace >= fragment.NextSize) { packetLog.DebugFormat("[{0}] Sending small message", session.LoggingIdentifier); ServerPacketFragment spf = fragment.GetNextFragment(); @@ -891,28 +913,27 @@ private void SendBundle(NetworkBundle bundle, GameMessageGroup group) // If message is out of data, set to remove it if (fragment.DataRemaining <= 0) - removeList.Add(fragment); + { + processedCount++; + if (seekIdx == lowerBoundIdx) + lowerBoundIdx++; + fragments[seekIdx] = null; + } // UIQueue messages must go out in order. Otherwise, you might see an NPC's tells in an order that doesn't match their defined emotes. if (fragmentSkipped && group == GameMessageGroup.UIQueue) break; - } - // Remove all completed messages - fragments.RemoveAll(x => removeList.Contains(x)); + seekIdx++; + } } } // If no messages, write optional headers - else + else if (writeOptionalHeaders) { packetLog.DebugFormat("[{0}] No messages, just sending optional headers", session.LoggingIdentifier); - if (writeOptionalHeaders) - { - writeOptionalHeaders = false; - WriteOptionalHeaders(bundle, packet); - if (packet.Data != null) - availableSpace -= (int)packet.Data.Length; - } + writeOptionalHeaders = false; + WriteOptionalHeaders(bundle, packet); } EnqueueSend(packet); }