diff --git a/OpenMetaverse/AssetManager.cs b/OpenMetaverse/AssetManager.cs index 16a91975..04431f3f 100644 --- a/OpenMetaverse/AssetManager.cs +++ b/OpenMetaverse/AssetManager.cs @@ -422,8 +422,6 @@ namespace OpenMetaverse if (download.TimeSinceLastPacket > 5000) { - Logger.DebugLog("Image download refresh timer is re-requesting texture " + download.ID.ToString()); - // FIXME: This will probably break on baked textures and doesn't preserve the originally requested discardlevel download.TimeSinceLastPacket = 0; RequestImage(download.ID, ImageType.Normal, 1013010.0f, 0); @@ -652,8 +650,8 @@ namespace OpenMetaverse if (Single.IsNaN(percentComplete)) percentComplete = 0f; - Logger.DebugLog(String.Format("Updating priority on image transfer {0}, ({1:#.#}% complete", - imageID, percentComplete)); + Logger.DebugLog(String.Format("Updating priority on image transfer {0}, {1}% complete", + imageID, Math.Round(percentComplete, 2))); } // Build and send the request packet diff --git a/OpenMetaverse/BlockingQueue.cs b/OpenMetaverse/BlockingQueue.cs index 53c2c786..09bfd7be 100644 --- a/OpenMetaverse/BlockingQueue.cs +++ b/OpenMetaverse/BlockingQueue.cs @@ -25,6 +25,7 @@ */ using System; +using System.Collections.Generic; using System.Threading; using OpenMetaverse; @@ -34,28 +35,19 @@ namespace System.Collections /// Same as Queue except Dequeue function blocks until there is an object to return. /// Note: This class does not need to be synchronized /// - public class BlockingQueue : Queue + public class BlockingQueue : Queue { + private object SyncRoot; private bool open; /// /// Create new BlockingQueue. /// /// The System.Collections.ICollection to copy elements from - public BlockingQueue(ICollection col) + public BlockingQueue(IEnumerable col) : base(col) { - open = true; - } - - /// - /// Create new BlockingQueue. - /// - /// The initial number of elements that the queue can contain - /// The factor by which the capacity of the queue is expanded - public BlockingQueue(int capacity, float growFactor) - : base(capacity, growFactor) - { + SyncRoot = new object(); open = true; } @@ -66,6 +58,7 @@ namespace System.Collections public BlockingQueue(int capacity) : base(capacity) { + SyncRoot = new object(); open = true; } @@ -75,6 +68,7 @@ namespace System.Collections public BlockingQueue() : base() { + SyncRoot = new object(); open = true; } @@ -89,9 +83,9 @@ namespace System.Collections /// /// Remove all objects from the Queue. /// - public override void Clear() + public new void Clear() { - lock (base.SyncRoot) + lock (SyncRoot) { base.Clear(); } @@ -102,11 +96,11 @@ namespace System.Collections /// public void Close() { - lock (base.SyncRoot) + lock (SyncRoot) { open = false; base.Clear(); - Monitor.PulseAll(base.SyncRoot); // resume any waiting threads + Monitor.PulseAll(SyncRoot); // resume any waiting threads } } @@ -114,7 +108,7 @@ namespace System.Collections /// Removes and returns the object at the beginning of the Queue. /// /// Object in queue. - public override object Dequeue() + public new T Dequeue() { return Dequeue(Timeout.Infinite); } @@ -124,7 +118,7 @@ namespace System.Collections /// /// time to wait before returning /// Object in queue. - public NetworkManager.IncomingPacket Dequeue(TimeSpan timeout) + public T Dequeue(TimeSpan timeout) { return Dequeue(timeout.Milliseconds); } @@ -134,51 +128,54 @@ namespace System.Collections /// /// time to wait before returning (in milliseconds) /// Object in queue. - public NetworkManager.IncomingPacket Dequeue(int timeout) + public T Dequeue(int timeout) { - lock (base.SyncRoot) + lock (SyncRoot) { while (open && (base.Count == 0)) { - if (!Monitor.Wait(base.SyncRoot, timeout)) + if (!Monitor.Wait(SyncRoot, timeout)) throw new InvalidOperationException("Timeout"); } if (open) - return (NetworkManager.IncomingPacket)base.Dequeue(); + return base.Dequeue(); else throw new InvalidOperationException("Queue Closed"); } } - public bool Dequeue(int timeout, ref NetworkManager.IncomingPacket packet) + public bool Dequeue(int timeout, ref T obj) { - lock (base.SyncRoot) + lock (SyncRoot) { while (open && (base.Count == 0)) { - if (!Monitor.Wait(base.SyncRoot, timeout)) + if (!Monitor.Wait(SyncRoot, timeout)) return false; } if (open) { - packet = (NetworkManager.IncomingPacket)base.Dequeue(); + obj = base.Dequeue(); return true; } else + { + obj = default(T); return false; + } } } /// - /// Adds an object to the end of the Queue. + /// Adds an object to the end of the Queue /// /// Object to put in queue - public override void Enqueue(object obj) + public new void Enqueue(T obj) { - lock (base.SyncRoot) + lock (SyncRoot) { base.Enqueue(obj); - Monitor.Pulse(base.SyncRoot); + Monitor.Pulse(SyncRoot); } } @@ -187,7 +184,7 @@ namespace System.Collections /// public void Open() { - lock (base.SyncRoot) + lock (SyncRoot) { open = true; } diff --git a/OpenMetaverse/NetworkManager.cs b/OpenMetaverse/NetworkManager.cs index 763da5b5..a15a96d2 100644 --- a/OpenMetaverse/NetworkManager.cs +++ b/OpenMetaverse/NetworkManager.cs @@ -72,15 +72,42 @@ namespace OpenMetaverse #region Structs /// - /// Holds a simulator reference and a packet, these structs are put in - /// the packet inbox for decoding + /// Holds a simulator reference and a decoded packet, these structs are put in + /// the packet inbox for event handling /// public struct IncomingPacket { /// Reference to the simulator that this packet came from public Simulator Simulator; - /// The packet that needs to be processed + /// Packet that needs to be processed public Packet Packet; + + public IncomingPacket(Simulator simulator, Packet packet) + { + Simulator = simulator; + Packet = packet; + } + } + + /// + /// Holds a simulator reference and an encoded packet, these structs are put in + /// the packet outbox for sending + /// + public struct OutgoingPacket + { + /// Reference to the simulator this packet is destined for + public Simulator Simulator; + /// Packet that needs to be processed + public Packet Packet; + /// True if the sequence number needs to be set, otherwise false + public bool SetSequence; + + public OutgoingPacket(Simulator simulator, Packet packet, bool setSequence) + { + Simulator = simulator; + Packet = packet; + SetSequence = setSequence; + } } #endregion Structs @@ -212,6 +239,8 @@ namespace OpenMetaverse public bool Connected { get { return connected; } } /// Number of packets in the incoming queue public int InboxCount { get { return PacketInbox.Count; } } + /// Number of packets in the outgoing queue + public int OutboxCount { get { return PacketOutbox.Count; } } #endregion Properties @@ -223,7 +252,9 @@ namespace OpenMetaverse /// Handlers for incoming packets internal PacketEventDictionary PacketEvents; /// Incoming packets that are awaiting handling - internal BlockingQueue PacketInbox = new BlockingQueue(Settings.PACKET_INBOX_SIZE); + internal BlockingQueue PacketInbox = new BlockingQueue(Settings.PACKET_INBOX_SIZE); + /// Outgoing packets that are awaiting handling + internal BlockingQueue PacketOutbox = new BlockingQueue(Settings.PACKET_INBOX_SIZE); private GridClient Client; private Timer DisconnectTimer; @@ -328,31 +359,6 @@ namespace OpenMetaverse simulator.SendPacket(packet, true); } - /// - /// Send a raw byte array as a packet to the current simulator - /// - /// Byte array containing a packet - /// Whether to set the second, third, and fourth - /// bytes of the payload to the current sequence number - public void SendPacket(byte[] payload, bool setSequence) - { - if (CurrentSim != null) - CurrentSim.SendPacket(payload, setSequence); - } - - /// - /// Send a raw byte array as a packet to the specified simulator - /// - /// Byte array containing a packet - /// Simulator to send the packet to - /// Whether to set the second, third, and fourth - /// bytes of the payload to the current sequence number - public void SendPacket(byte[] payload, Simulator simulator, bool setSequence) - { - if (simulator != null) - simulator.SendPacket(payload, setSequence); - } - /// /// Connect to a simulator /// @@ -403,12 +409,17 @@ namespace OpenMetaverse // Mark that we are connecting/connected to the grid connected = true; - // restart the blocking queue in case of re-connect + // Open the queues in case this is a reconnect and they were shut down PacketInbox.Open(); + PacketOutbox.Open(); // Start the packet decoding thread - Thread decodeThread = new Thread(new ThreadStart(PacketHandler)); + Thread decodeThread = new Thread(new ThreadStart(IncomingPacketHandler)); decodeThread.Start(); + + // Start the packet sending thread + Thread sendThread = new Thread(new ThreadStart(OutgoingPacketHandler)); + sendThread.Start(); } // Fire the OnSimConnecting event @@ -605,6 +616,7 @@ namespace OpenMetaverse // Clear out all of the packets that never had time to process PacketInbox.Close(); + PacketOutbox.Close(); connected = false; @@ -649,7 +661,38 @@ namespace OpenMetaverse } } - private void PacketHandler() + private void OutgoingPacketHandler() + { + OutgoingPacket outgoingPacket = new OutgoingPacket(); + Simulator simulator = null; + Packet packet = null; + int now; + int lastPacketTime = Environment.TickCount; + + while (connected) + { + if (PacketOutbox.Dequeue(100, ref outgoingPacket)) + { + simulator = outgoingPacket.Simulator; + packet = outgoingPacket.Packet; + + // Very primitive rate limiting, keeps a fixed buffer of time between each packet + now = Environment.TickCount; + int ms = now - lastPacketTime; + + if (ms < 75) + { + Logger.DebugLog(String.Format("Rate limiting, last packet was {0}ms ago", ms)); + Thread.Sleep(75 - ms); + } + + lastPacketTime = now; + simulator.SendPacketUnqueued(packet, outgoingPacket.SetSequence); + } + } + } + + private void IncomingPacketHandler() { IncomingPacket incomingPacket = new IncomingPacket(); Packet packet = null; @@ -660,7 +703,7 @@ namespace OpenMetaverse // Reset packet to null for the check below packet = null; - if (PacketInbox.Dequeue(200, ref incomingPacket)) + if (PacketInbox.Dequeue(100, ref incomingPacket)) { packet = incomingPacket.Packet; simulator = incomingPacket.Simulator; diff --git a/OpenMetaverse/ParcelManager.cs b/OpenMetaverse/ParcelManager.cs index fdd115c5..1f1736a3 100644 --- a/OpenMetaverse/ParcelManager.cs +++ b/OpenMetaverse/ParcelManager.cs @@ -890,7 +890,8 @@ namespace OpenMetaverse Thread th = new Thread(delegate() { - int y, x; + int count = 0, y, x; + for (y = 0; y < 64; y++) { for (x = 0; x < 64; x++) @@ -903,11 +904,22 @@ namespace OpenMetaverse Client.Parcels.PropertiesRequest(simulator, (y + 1) * 4.0f, (x + 1) * 4.0f, y * 4.0f, x * 4.0f, 0, false); - // Pause after every request to avoid flooding the sim - System.Threading.Thread.Sleep(msDelay); + + // Simulators can be sliced up in 4x4 chunks, but even the smallest + // parcel is 16x16. There's no reason to send out 4096 packets. If + // we sleep for a little while here it is likely the response + // packet will come back and nearby parcel information is filled in + // so we can skip a lot of unnecessary sends + Thread.Sleep(100); + + ++count; } } } + + Logger.Log(String.Format( + "Requested full simulator parcel information. Sent {0} parcel requests. Current outgoing queue: {1}", + count, Client.Network.OutboxCount), Helpers.LogLevel.Info); }); th.Start(); diff --git a/OpenMetaverse/Simulator.cs b/OpenMetaverse/Simulator.cs index db7c236e..aeb9bd2c 100644 --- a/OpenMetaverse/Simulator.cs +++ b/OpenMetaverse/Simulator.cs @@ -572,16 +572,35 @@ namespace OpenMetaverse } } - - /// /// Sends a packet /// /// Packet to be sent - /// Increment sequence number? - /// + /// True to set the sequence number, false to + /// leave it as is + public void SendPacket(Packet packet, bool setSequence) + { + // Send ACK and logout packets directly, everything else goes through the queue + if (packet.Type == PacketType.PacketAck || + packet.Header.AppendedAcks || + packet.Type == PacketType.LogoutRequest) + { + SendPacketUnqueued(packet, setSequence); + } + else + { + Network.PacketOutbox.Enqueue(new NetworkManager.OutgoingPacket(this, packet, setSequence)); + } + + } - public void SendPacket(Packet packet, bool incrementSequence) + /// + /// Sends a packet directly to the simulator without queuing + /// + /// Packet to be sent + /// True to set the sequence number, false to + /// leave it as is + public void SendPacketUnqueued(Packet packet, bool setSequence) { byte[] buffer; int bytes; @@ -589,7 +608,7 @@ namespace OpenMetaverse // Keep track of when this packet was sent out packet.TickCount = Environment.TickCount; - if (incrementSequence) + if (setSequence) { // Reset to zero if we've hit the upper sequence number limit Interlocked.CompareExchange(ref Sequence, 0, Settings.MAX_SEQUENCE); @@ -693,7 +712,7 @@ namespace OpenMetaverse /// The packet payload /// Whether the second, third, and fourth bytes /// should be modified to the current stream sequence number - public void SendPacket(byte[] payload, bool setSequence) + public void SendPacketUnqueued(byte[] payload, bool setSequence) { try { @@ -730,16 +749,6 @@ namespace OpenMetaverse } } - /// - /// Send a prepared UDPPacketBuffer object as a packet - /// - /// The prepared packet structure to be sent out - public void SendPacket(UDPPacketBuffer buffer) - { - try { AsyncBeginSend(buffer); } - catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } - } - /// /// /// @@ -921,7 +930,6 @@ namespace OpenMetaverse /// /// Resend unacknowledged packets /// - /// private void ResendUnacked() { lock (NeedAck) @@ -932,7 +940,7 @@ namespace OpenMetaverse // Resend packets foreach (Packet packet in NeedAck.Values) { - if (now - packet.TickCount > Client.Settings.RESEND_TIMEOUT) + if (packet.TickCount != 0 && now - packet.TickCount > Client.Settings.RESEND_TIMEOUT) { if (packet.ResendCount < Client.Settings.MAX_RESEND_COUNT) { @@ -944,10 +952,10 @@ namespace OpenMetaverse packet.Header.Sequence, packet.GetType(), now - packet.TickCount), Client); } + packet.TickCount = 0; packet.Header.Resent = true; ++Stats.ResentPackets; ++packet.ResendCount; - packet.TickCount = now; SendPacket(packet, false); } diff --git a/Programs/examples/TestClient/Commands/Land/ParcelInfoCommand.cs b/Programs/examples/TestClient/Commands/Land/ParcelInfoCommand.cs index d8052c87..ccb64356 100644 --- a/Programs/examples/TestClient/Commands/Land/ParcelInfoCommand.cs +++ b/Programs/examples/TestClient/Commands/Land/ParcelInfoCommand.cs @@ -36,7 +36,7 @@ namespace OpenMetaverse.TestClient if (Client.Network.CurrentSim.IsParcelMapFull()) ParcelsDownloaded.Set(); - if (ParcelsDownloaded.WaitOne(20000, false) && Client.Network.Connected) + if (ParcelsDownloaded.WaitOne(30000, false) && Client.Network.Connected) { sb.AppendFormat("Downloaded {0} Parcels in {1} " + System.Environment.NewLine, Client.Network.CurrentSim.Parcels.Count, Client.Network.CurrentSim.Name);