aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs6
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs455
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs117
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs365
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs19
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs148
6 files changed, 564 insertions, 546 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs
index 32a4ad4..31f9580 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs
@@ -34,16 +34,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
34{ 34{
35 public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes); 35 public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes);
36 public delegate void PacketDrop(Packet pack, Object id); 36 public delegate void PacketDrop(Packet pack, Object id);
37 public delegate void QueueEmpty(ThrottleOutPacketType queue);
37 public delegate bool SynchronizeClientHandler(IScene scene, Packet packet, UUID agentID, ThrottleOutPacketType throttlePacketType); 38 public delegate bool SynchronizeClientHandler(IScene scene, Packet packet, UUID agentID, ThrottleOutPacketType throttlePacketType);
38 39
39 /// <summary> 40 /// <summary>
40 /// Interface to a class that handles all the activity involved with maintaining the client circuit (handling acks, 41 /// Interface to a class that handles all the activity involved with maintaining the client circuit (handling acks,
41 /// resends, pings, etc.) 42 /// resends, pings, etc.)
42 /// </summary> 43 /// </summary>
43 public interface ILLPacketHandler 44 public interface ILLPacketHandler : IDisposable
44 { 45 {
45 event PacketStats OnPacketStats; 46 event PacketStats OnPacketStats;
46 event PacketDrop OnPacketDrop; 47 event PacketDrop OnPacketDrop;
48 event QueueEmpty OnQueueEmpty;
47 SynchronizeClientHandler SynchronizeClient { set; } 49 SynchronizeClientHandler SynchronizeClient { set; }
48 50
49 int PacketsReceived { get; } 51 int PacketsReceived { get; }
@@ -70,12 +72,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
70 void OutPacket(Packet NewPack, 72 void OutPacket(Packet NewPack,
71 ThrottleOutPacketType throttlePacketType, Object id); 73 ThrottleOutPacketType throttlePacketType, Object id);
72 LLPacketQueue PacketQueue { get; } 74 LLPacketQueue PacketQueue { get; }
73 void Stop();
74 void Flush(); 75 void Flush();
75 void Clear(); 76 void Clear();
76 ClientInfo GetClientInfo(); 77 ClientInfo GetClientInfo();
77 void SetClientInfo(ClientInfo info); 78 void SetClientInfo(ClientInfo info);
78 void AddImportantPacket(PacketType type); 79 void AddImportantPacket(PacketType type);
79 void RemoveImportantPacket(PacketType type); 80 void RemoveImportantPacket(PacketType type);
81 int GetQueueCount(ThrottleOutPacketType queue);
80 } 82 }
81} 83}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
index 638c765..5f549b5 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
@@ -38,40 +38,37 @@ using System.Reflection;
38namespace OpenSim.Region.ClientStack.LindenUDP 38namespace OpenSim.Region.ClientStack.LindenUDP
39{ 39{
40 /// <summary> 40 /// <summary>
41 /// We use this class to store image data and associated request data and attributes 41 /// Stores information about a current texture download and a reference to the texture asset
42 /// </summary> 42 /// </summary>
43 public class J2KImage 43 public class J2KImage
44 { 44 {
45 private const int IMAGE_PACKET_SIZE = 1000;
46 private const int FIRST_PACKET_SIZE = 600;
47
45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 48 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46 49
47 public double m_designatedPriorityKey; 50 public uint m_lastSequence;
48 public double m_requestedPriority = 0.0d; 51 public float m_requestedPriority;
49 public uint m_lastSequence = 0;
50 public uint m_requestedPacketNumber; 52 public uint m_requestedPacketNumber;
51 public sbyte m_requestedDiscardLevel; 53 public sbyte m_requestedDiscardLevel;
52 public UUID m_requestedUUID; 54 public UUID m_requestedUUID;
53 public IJ2KDecoder m_j2kDecodeModule; 55 public IJ2KDecoder m_j2kDecodeModule;
54 public IAssetService m_assetCache; 56 public IAssetService m_assetCache;
55 public OpenJPEG.J2KLayerInfo[] Layers = new OpenJPEG.J2KLayerInfo[0]; 57 public OpenJPEG.J2KLayerInfo[] m_layers;
56 public AssetBase m_MissingSubstitute = null; 58 public bool m_decoded;
57 public bool m_decoded = false; 59 public bool m_hasasset;
58 public bool m_completedSendAtCurrentDiscardLevel; 60 public C5.IPriorityQueueHandle<J2KImage> m_priorityQueueHandle;
59 61
60 private sbyte m_discardLevel=-1;
61 private uint m_packetNumber; 62 private uint m_packetNumber;
62 private bool m_decoderequested = false; 63 private bool m_decoderequested;
63 private bool m_hasasset = false; 64 private bool m_asset_requested;
64 private bool m_asset_requested = false; 65 private bool m_sentinfo;
65 private bool m_sentinfo = false; 66 private uint m_stopPacket;
66 private uint m_stopPacket = 0; 67 private AssetBase m_asset;
67 private const int cImagePacketSize = 1000; 68 private int m_assetDataLength;
68 private const int cFirstPacketSize = 600; 69 private LLImageManager m_imageManager;
69 private AssetBase m_asset = null; 70
70 private LLImageManager m_image; 71 #region Properties
71 public J2KImage(LLImageManager image)
72 {
73 m_image = image;
74 }
75 72
76 public uint m_pPacketNumber 73 public uint m_pPacketNumber
77 { 74 {
@@ -84,10 +81,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
84 81
85 public byte[] Data 82 public byte[] Data
86 { 83 {
87 get 84 get
88 { 85 {
89 if (m_asset != null) 86 if (m_asset != null)
90 return m_asset.Data; 87 return m_asset.Data;
91 else 88 else
92 return null; 89 return null;
93 } 90 }
@@ -97,9 +94,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
97 { 94 {
98 if (!m_decoded) 95 if (!m_decoded)
99 return 0; 96 return 0;
97
100 try 98 try
101 { 99 {
102 return (ushort)(((m_asset.Data.Length - cFirstPacketSize + cImagePacketSize - 1) / cImagePacketSize) + 1); 100 return (ushort)(((m_assetDataLength - FIRST_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1);
103 } 101 }
104 catch (Exception) 102 catch (Exception)
105 { 103 {
@@ -110,119 +108,154 @@ namespace OpenSim.Region.ClientStack.LindenUDP
110 } 108 }
111 } 109 }
112 110
113 public void J2KDecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers) 111 #endregion Properties
114 {
115 m_image.m_outstandingtextures++;
116 Layers = layers;
117 m_decoded = true;
118 RunUpdate();
119 }
120 112
121 public void AssetDataCallback(UUID AssetID, AssetBase asset) 113 public J2KImage(LLImageManager imageManager)
122 { 114 {
123 m_hasasset = true; 115 m_imageManager = imageManager;
124 if (asset == null || asset.Data == null)
125 {
126 m_asset = m_MissingSubstitute;
127 }
128 else
129 {
130 m_asset = asset;
131 }
132 RunUpdate();
133 } 116 }
134 117
135 protected void AssetReceived(string id, Object sender, AssetBase asset) 118 public bool SendPackets(LLClientView client, int maxpack)
136 { 119 {
137 UUID assetID = UUID.Zero; 120 if (m_packetNumber <= m_stopPacket)
138 if (asset != null) 121 {
139 assetID = asset.FullID; 122 bool SendMore = true;
140 123 if (!m_sentinfo || (m_packetNumber == 0))
141 AssetDataCallback(assetID, asset); 124 {
142 125 if (SendFirstPacket(client))
143 } 126 {
127 SendMore = false;
128 }
129 m_sentinfo = true;
130 m_packetNumber++;
131 }
132 // bool ignoreStop = false;
133 if (m_packetNumber < 2)
134 {
135 m_packetNumber = 2;
136 }
144 137
145 private int GetPacketForBytePosition(int bytePosition) 138 int count = 0;
146 { 139 while (SendMore && count < maxpack && m_packetNumber <= m_stopPacket)
147 return ((bytePosition - cFirstPacketSize + cImagePacketSize - 1) / cImagePacketSize) + 1; 140 {
148 } 141 count++;
142 SendMore = SendPacket(client);
143 m_packetNumber++;
144 }
149 145
150 public int LastPacketSize() 146 if (m_packetNumber > m_stopPacket)
151 { 147 return true;
152 if (m_packetNumber == 1)
153 return m_asset.Data.Length;
154 int lastsize = (m_asset.Data.Length - cFirstPacketSize) % cImagePacketSize;
155 //If the last packet size is zero, it's really cImagePacketSize, it sits on the boundary
156 if (lastsize == 0)
157 {
158 lastsize = cImagePacketSize;
159 } 148 }
160 return lastsize;
161 }
162
163 public int CurrentBytePosition()
164 {
165 if (m_packetNumber == 0)
166 return 0;
167 if (m_packetNumber == 1)
168 return cFirstPacketSize;
169 149
170 int result = cFirstPacketSize + ((int)m_packetNumber - 2) * cImagePacketSize; 150 return false;
171 if (result < 0)
172 {
173 result = cFirstPacketSize;
174 }
175 return result;
176 } 151 }
177 152
178 public bool SendFirstPacket(LLClientView client) 153 public void RunUpdate()
179 { 154 {
180 // this means we don't have 155 //This is where we decide what we need to update
181 if (Data == null) 156 //and assign the real discardLevel and packetNumber
182 { 157 //assuming of course that the connected client might be bonkers
183 client.SendImageNotFound(m_requestedUUID); 158
184 m_log.WarnFormat("[TEXTURE]: Got null Data element on a asset {0}.. and the missing image Data property is al", m_requestedUUID); 159 if (!m_hasasset)
185 return true;
186 }
187 // Do we have less then 1 packet's worth of data?
188 else if (m_asset.Data.Length <= cFirstPacketSize)
189 { 160 {
190 // Send only 1 packet 161 if (!m_asset_requested)
191 client.SendImageFirstPart(1, m_requestedUUID, (uint)m_asset.Data.Length, m_asset.Data, 2); 162 {
192 m_stopPacket = 0; 163 m_asset_requested = true;
193 return true; 164 m_assetCache.Get(m_requestedUUID.ToString(), this, AssetReceived);
165 }
194 } 166 }
195 else 167 else
196 { 168 {
197 byte[] firstImageData = new byte[cFirstPacketSize]; 169 if (!m_decoded)
198 try 170 {
199 { 171 //We need to decode the requested image first
200 Buffer.BlockCopy(m_asset.Data, 0, firstImageData, 0, (int)cFirstPacketSize); 172 if (!m_decoderequested)
201 client.SendImageFirstPart(TexturePacketCount(), m_requestedUUID, (uint)m_asset.Data.Length, firstImageData, 2); 173 {
174 //Request decode
175 m_decoderequested = true;
176 // Do we have a jpeg decoder?
177 if (m_j2kDecodeModule != null)
178 {
179 if (Data == null)
180 {
181 J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]);
182 }
183 else
184 {
185 // Send it off to the jpeg decoder
186 m_j2kDecodeModule.BeginDecode(m_requestedUUID, Data, J2KDecodedCallback);
187 }
188
189 }
190 else
191 {
192 J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]);
193 }
194 }
202 } 195 }
203 catch (Exception) 196 else
204 { 197 {
205 m_log.Error("Texture block copy failed. Possibly out of memory?"); 198 // Check for missing image asset data
206 return true; 199 if (m_asset == null || m_asset.Data == null)
200 {
201 // FIXME:
202 m_packetNumber = m_stopPacket;
203 return;
204 }
205
206 if (m_requestedDiscardLevel >= 0 || m_stopPacket == 0)
207 {
208 int maxDiscardLevel = Math.Max(0, m_layers.Length - 1);
209
210 // Treat initial texture downloads with a DiscardLevel of -1 a request for the highest DiscardLevel
211 if (m_requestedDiscardLevel < 0 && m_stopPacket == 0)
212 m_requestedDiscardLevel = (sbyte)maxDiscardLevel;
213
214 // Clamp at the highest discard level
215 m_requestedDiscardLevel = (sbyte)Math.Min(m_requestedDiscardLevel, maxDiscardLevel);
216
217 //Calculate the m_stopPacket
218 if (m_layers.Length > 0)
219 {
220 m_stopPacket = (uint)GetPacketForBytePosition(m_layers[(m_layers.Length - 1) - m_requestedDiscardLevel].End);
221 //I don't know why, but the viewer seems to expect the final packet if the file
222 //is just one packet bigger.
223 if (TexturePacketCount() == m_stopPacket + 1)
224 {
225 m_stopPacket = TexturePacketCount();
226 }
227 }
228 else
229 {
230 m_stopPacket = TexturePacketCount();
231 }
232
233 m_packetNumber = m_requestedPacketNumber;
234 }
235
236 if (m_imageManager.Client.PacketHandler.GetQueueCount(ThrottleOutPacketType.Texture) == 0)
237 {
238 //m_log.Debug("No textures queued, sending one packet to kickstart it");
239 SendPacket(m_imageManager.Client);
240 }
207 } 241 }
208 } 242 }
209 return false;
210 } 243 }
211 244
212 private bool SendPacket(LLClientView client) 245 private bool SendPacket(LLClientView client)
213 { 246 {
214 bool complete = false; 247 bool complete = false;
215 int imagePacketSize = ((int)m_packetNumber == (TexturePacketCount())) ? LastPacketSize() : cImagePacketSize; 248 int imagePacketSize = ((int)m_packetNumber == (TexturePacketCount())) ? LastPacketSize() : IMAGE_PACKET_SIZE;
216 249
217 try 250 try
218 { 251 {
219 if ((CurrentBytePosition() + cImagePacketSize) > m_asset.Data.Length) 252 if ((CurrentBytePosition() + IMAGE_PACKET_SIZE) > m_assetDataLength)
220 { 253 {
221 imagePacketSize = LastPacketSize(); 254 imagePacketSize = LastPacketSize();
222 complete=true; 255 complete = true;
223 if ((CurrentBytePosition() + imagePacketSize) > m_asset.Data.Length) 256 if ((CurrentBytePosition() + imagePacketSize) > m_assetDataLength)
224 { 257 {
225 imagePacketSize = m_asset.Data.Length - CurrentBytePosition(); 258 imagePacketSize = m_assetDataLength - CurrentBytePosition();
226 complete = true; 259 complete = true;
227 } 260 }
228 } 261 }
@@ -244,7 +277,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
244 } 277 }
245 278
246 //Send the packet 279 //Send the packet
247 client.SendImageNextPart((ushort)(m_packetNumber-1), m_requestedUUID, imageData); 280 client.SendImageNextPart((ushort)(m_packetNumber - 1), m_requestedUUID, imageData);
248 } 281 }
249 if (complete) 282 if (complete)
250 { 283 {
@@ -260,143 +293,115 @@ namespace OpenSim.Region.ClientStack.LindenUDP
260 return false; 293 return false;
261 } 294 }
262 } 295 }
263 public bool SendPackets(LLClientView client, int maxpack) 296
297 private int GetPacketForBytePosition(int bytePosition)
264 { 298 {
299 return ((bytePosition - FIRST_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1;
300 }
265 301
266 if (!m_completedSendAtCurrentDiscardLevel) 302 private int LastPacketSize()
303 {
304 if (m_packetNumber == 1)
305 return m_assetDataLength;
306 int lastsize = (m_assetDataLength - FIRST_PACKET_SIZE) % IMAGE_PACKET_SIZE;
307 //If the last packet size is zero, it's really cImagePacketSize, it sits on the boundary
308 if (lastsize == 0)
267 { 309 {
268 if (m_packetNumber <= m_stopPacket) 310 lastsize = IMAGE_PACKET_SIZE;
269 { 311 }
270 bool SendMore = true; 312 return lastsize;
271 if (!m_sentinfo || (m_packetNumber == 0)) 313 }
272 {
273 if (SendFirstPacket(client))
274 {
275 SendMore = false;
276 }
277 m_sentinfo = true;
278 m_packetNumber++;
279 }
280 // bool ignoreStop = false;
281 if (m_packetNumber < 2)
282 {
283 m_packetNumber = 2;
284 }
285 314
286 int count = 0; 315 private int CurrentBytePosition()
287 while (SendMore && count < maxpack && m_packetNumber <= m_stopPacket) 316 {
288 { 317 if (m_packetNumber == 0)
289 count++; 318 return 0;
290 SendMore = SendPacket(client); 319 if (m_packetNumber == 1)
291 m_packetNumber++; 320 return FIRST_PACKET_SIZE;
292 }
293 321
294 if (m_packetNumber > m_stopPacket) 322 int result = FIRST_PACKET_SIZE + ((int)m_packetNumber - 2) * IMAGE_PACKET_SIZE;
295 { 323 if (result < 0)
296 return true; 324 {
297 } 325 result = FIRST_PACKET_SIZE;
326 }
327 return result;
328 }
329
330 private bool SendFirstPacket(LLClientView client)
331 {
332 // this means we don't have
333 if (Data == null)
334 {
335 client.SendImageNotFound(m_requestedUUID);
336 m_log.WarnFormat("[TEXTURE]: Got null Data element on a asset {0}.. and the missing image Data property is also null", m_requestedUUID);
337 return true;
338 }
339 // Do we have less then 1 packet's worth of data?
340 else if (m_assetDataLength <= FIRST_PACKET_SIZE)
341 {
342 // Send only 1 packet
343 client.SendImageFirstPart(1, m_requestedUUID, (uint)m_assetDataLength, m_asset.Data, 2);
344 m_stopPacket = 0;
345 return true;
346 }
347 else
348 {
349 byte[] firstImageData = new byte[FIRST_PACKET_SIZE];
350 try
351 {
352 Buffer.BlockCopy(m_asset.Data, 0, firstImageData, 0, (int)FIRST_PACKET_SIZE);
353 client.SendImageFirstPart(TexturePacketCount(), m_requestedUUID, (uint)m_assetDataLength, firstImageData, 2);
354 }
355 catch (Exception)
356 {
357 m_log.Error("Texture block copy failed. Possibly out of memory?");
358 return true;
298 } 359 }
299 } 360 }
300 return false; 361 return false;
301 } 362 }
302 363
303 public void RunUpdate() 364 private void J2KDecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers)
304 { 365 {
305 //This is where we decide what we need to update 366 m_layers = layers;
306 //and assign the real discardLevel and packetNumber 367 m_decoded = true;
307 //assuming of course that the connected client might be bonkers 368 RunUpdate();
369 }
308 370
309 if (!m_hasasset) 371 private void AssetDataCallback(UUID AssetID, AssetBase asset)
310 { 372 {
373 m_hasasset = true;
311 374
312 if (!m_asset_requested) 375 if (asset == null || asset.Data == null)
376 {
377 if (m_imageManager.MissingImage != null)
313 { 378 {
314 m_asset_requested = true; 379 m_asset = m_imageManager.MissingImage;
315 m_assetCache.Get(m_requestedUUID.ToString(), this, AssetReceived); 380 m_assetDataLength = m_asset.Data.Length;
316 381 }
382 else
383 {
384 m_asset = null;
385 m_decoded = true;
317 } 386 }
318
319 } 387 }
320 else 388 else
321 { 389 {
390 m_asset = asset;
391 m_assetDataLength = m_asset.Data.Length;
392 }
322 393
394 RunUpdate();
395 }
323 396
324 if (!m_decoded) 397 private void AssetReceived(string id, Object sender, AssetBase asset)
325 { 398 {
326 //We need to decode the requested image first 399 UUID assetID = UUID.Zero;
327 if (!m_decoderequested) 400 if (asset != null)
328 { 401 assetID = asset.FullID;
329 //Request decode
330 m_decoderequested = true;
331 // Do we have a jpeg decoder?
332 if (m_j2kDecodeModule != null)
333 {
334 if (Data == null)
335 {
336 J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]);
337 }
338 // Send it off to the jpeg decoder
339 m_j2kDecodeModule.decode(m_requestedUUID, Data, J2KDecodedCallback);
340
341 }
342 else
343 {
344 J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]);
345 }
346 }
347
348 }
349 else
350 {
351 //discardLevel of -1 means just update the priority
352 if (m_requestedDiscardLevel != -1)
353 {
354 //Evaluate the discard level
355 //First, is it positive?
356 if (m_requestedDiscardLevel >= 0)
357 {
358 if (m_requestedDiscardLevel > Layers.Length - 1)
359 {
360 m_discardLevel = (sbyte)(Layers.Length - 1);
361 }
362 else
363 {
364 m_discardLevel = m_requestedDiscardLevel;
365 }
366 402
367 //Calculate the m_stopPacket 403 AssetDataCallback(assetID, asset);
368 if (Layers.Length > 0)
369 {
370 m_stopPacket = (uint)GetPacketForBytePosition(Layers[(Layers.Length - 1) - m_discardLevel].End);
371 //I don't know why, but the viewer seems to expect the final packet if the file
372 //is just one packet bigger.
373 if (TexturePacketCount() == m_stopPacket + 1)
374 {
375 m_stopPacket = TexturePacketCount();
376 }
377 }
378 else
379 {
380 m_stopPacket = TexturePacketCount();
381 }
382 //Don't reset packet number unless we're waiting or it's ahead of us
383 if (m_completedSendAtCurrentDiscardLevel || m_requestedPacketNumber>m_packetNumber)
384 {
385 m_packetNumber = m_requestedPacketNumber;
386 }
387 404
388 if (m_packetNumber <= m_stopPacket)
389 {
390 m_completedSendAtCurrentDiscardLevel = false;
391 }
392 }
393 }
394 else
395 {
396 m_packetNumber = m_stopPacket;
397 }
398 }
399 }
400 } 405 }
401 } 406 }
402} 407}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 88ace6a..3b43771 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -81,8 +81,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
81 private List<ObjectUpdatePacket.ObjectDataBlock> m_primFullUpdates = 81 private List<ObjectUpdatePacket.ObjectDataBlock> m_primFullUpdates =
82 new List<ObjectUpdatePacket.ObjectDataBlock>(); 82 new List<ObjectUpdatePacket.ObjectDataBlock>();
83 83
84 private Timer m_textureRequestTimer;
85
86 private bool m_clientBlocked; 84 private bool m_clientBlocked;
87 85
88 private int m_probesWithNoIngressPackets; 86 private int m_probesWithNoIngressPackets;
@@ -143,9 +141,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
143 protected int m_primTerseUpdateRate = 10; 141 protected int m_primTerseUpdateRate = 10;
144 protected int m_primFullUpdateRate = 14; 142 protected int m_primFullUpdateRate = 14;
145 143
146 protected int m_textureRequestRate = 100; 144 protected int m_textureSendLimit = 20;
147 protected int m_textureSendLimit = 10; 145 protected int m_textureDataLimit = 10;
148 protected int m_textureDataLimit = 5;
149 146
150 protected int m_packetMTU = 1400; 147 protected int m_packetMTU = 1400;
151 148
@@ -534,6 +531,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
534 m_PacketHandler = new LLPacketHandler(this, m_networkServer, userSettings); 531 m_PacketHandler = new LLPacketHandler(this, m_networkServer, userSettings);
535 m_PacketHandler.SynchronizeClient = SynchronizeClient; 532 m_PacketHandler.SynchronizeClient = SynchronizeClient;
536 m_PacketHandler.OnPacketStats += PopulateStats; 533 m_PacketHandler.OnPacketStats += PopulateStats;
534 m_PacketHandler.OnQueueEmpty += HandleQueueEmpty;
537 535
538 if (scene.Config != null) 536 if (scene.Config != null)
539 { 537 {
@@ -555,9 +553,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
555 m_primFullUpdateRate = clientConfig.GetInt("FullUpdateRate", 553 m_primFullUpdateRate = clientConfig.GetInt("FullUpdateRate",
556 m_primFullUpdateRate); 554 m_primFullUpdateRate);
557 555
558 m_textureRequestRate = clientConfig.GetInt("TextureRequestRate",
559 m_textureRequestRate);
560
561 m_textureSendLimit = clientConfig.GetInt("TextureSendLimit", 556 m_textureSendLimit = clientConfig.GetInt("TextureSendLimit",
562 m_textureSendLimit); 557 m_textureSendLimit);
563 558
@@ -607,9 +602,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
607 if (m_primFullUpdateTimer.Enabled) 602 if (m_primFullUpdateTimer.Enabled)
608 lock (m_primFullUpdateTimer) 603 lock (m_primFullUpdateTimer)
609 m_primFullUpdateTimer.Stop(); 604 m_primFullUpdateTimer.Stop();
610 if (m_textureRequestTimer.Enabled)
611 lock (m_textureRequestTimer)
612 m_textureRequestTimer.Stop();
613 605
614 // This is just to give the client a reasonable chance of 606 // This is just to give the client a reasonable chance of
615 // flushing out all it's packets. There should probably 607 // flushing out all it's packets. There should probably
@@ -633,6 +625,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
633 // of the client thread regardless of where Close() is called. 625 // of the client thread regardless of where Close() is called.
634 KillEndDone(); 626 KillEndDone();
635 } 627 }
628
629 Terminate();
636 } 630 }
637 631
638 /// <summary> 632 /// <summary>
@@ -704,10 +698,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
704 if (m_primFullUpdateTimer.Enabled) 698 if (m_primFullUpdateTimer.Enabled)
705 lock (m_primFullUpdateTimer) 699 lock (m_primFullUpdateTimer)
706 m_primFullUpdateTimer.Stop(); 700 m_primFullUpdateTimer.Stop();
707
708 if (m_textureRequestTimer.Enabled)
709 lock (m_textureRequestTimer)
710 m_textureRequestTimer.Stop();
711 } 701 }
712 702
713 public void Restart() 703 public void Restart()
@@ -730,23 +720,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP
730 m_primFullUpdateTimer = new Timer(m_primFullUpdateRate); 720 m_primFullUpdateTimer = new Timer(m_primFullUpdateRate);
731 m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates); 721 m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates);
732 m_primFullUpdateTimer.AutoReset = false; 722 m_primFullUpdateTimer.AutoReset = false;
733
734 m_textureRequestTimer = new Timer(m_textureRequestRate);
735 m_textureRequestTimer.Elapsed += new ElapsedEventHandler(ProcessTextureRequests);
736 m_textureRequestTimer.AutoReset = false;
737
738 } 723 }
739 724
740 public void Terminate() 725 private void Terminate()
741 { 726 {
727 IsActive = false;
728
729 m_clientPingTimer.Close();
730 m_avatarTerseUpdateTimer.Close();
731 m_primTerseUpdateTimer.Close();
732 m_primFullUpdateTimer.Close();
733
742 m_PacketHandler.OnPacketStats -= PopulateStats; 734 m_PacketHandler.OnPacketStats -= PopulateStats;
743 m_PacketHandler.Stop(); 735 m_PacketHandler.Dispose();
744 736
745 // wait for thread stoped 737 // wait for thread stoped
746 m_clientThread.Join(); 738 // m_clientThread.Join();
747 739
748 // delete circuit code 740 // delete circuit code
749 m_networkServer.CloseClient(this); 741 //m_networkServer.CloseClient(this);
750 } 742 }
751 743
752 #endregion 744 #endregion
@@ -876,6 +868,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
876 while (IsActive) 868 while (IsActive)
877 { 869 {
878 LLQueItem nextPacket = m_PacketHandler.PacketQueue.Dequeue(); 870 LLQueItem nextPacket = m_PacketHandler.PacketQueue.Dequeue();
871
872 if (nextPacket == null) {
873 m_log.DebugFormat("[CLIENT]: PacketQueue return null LLQueItem");
874 continue;
875 }
879 876
880 if (nextPacket.Incoming) 877 if (nextPacket.Incoming)
881 { 878 {
@@ -967,10 +964,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
967 m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates); 964 m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates);
968 m_primFullUpdateTimer.AutoReset = false; 965 m_primFullUpdateTimer.AutoReset = false;
969 966
970 m_textureRequestTimer = new Timer(m_textureRequestRate);
971 m_textureRequestTimer.Elapsed += new ElapsedEventHandler(ProcessTextureRequests);
972 m_textureRequestTimer.AutoReset = false;
973
974 m_scene.AddNewClient(this); 967 m_scene.AddNewClient(this);
975 968
976 RefreshGroupMembership(); 969 RefreshGroupMembership();
@@ -1042,26 +1035,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1042 } 1035 }
1043 } 1036 }
1044 1037
1045 protected virtual void TextureRequestHandler()
1046 {
1047 m_log.DebugFormat("[TRH] Thread started");
1048 while (m_imageManager != null)
1049 {
1050 try
1051 {
1052 while (m_imageManager != null)
1053 {
1054 }
1055 }
1056 catch (Exception e)
1057 {
1058 m_log.WarnFormat("[TRH] Exception in handler loop: {0}", e.Message);
1059 m_log.Debug(e);
1060 }
1061 }
1062 m_log.DebugFormat("[TRH] Thread terminated");
1063 }
1064
1065 # endregion 1038 # endregion
1066 1039
1067 // Previously ClientView.API partial class 1040 // Previously ClientView.API partial class
@@ -3161,22 +3134,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3161 } 3134 }
3162 } 3135 }
3163 3136
3164 // Unlike the other timers, this one is only started after 3137 void HandleQueueEmpty(ThrottleOutPacketType queue)
3165 // the first request is seen.
3166
3167 void ProcessTextureRequests(object sender, ElapsedEventArgs e)
3168 { 3138 {
3169 if (m_imageManager != null) 3139 switch (queue)
3170 { 3140 {
3171 if (m_imageManager.ProcessImageQueue(m_textureSendLimit, 3141 case ThrottleOutPacketType.Texture:
3172 m_textureDataLimit)) 3142 ProcessTextureRequests();
3173 { 3143 break;
3174 lock (m_textureRequestTimer)
3175 m_textureRequestTimer.Start();
3176 }
3177 } 3144 }
3178 } 3145 }
3179 3146
3147 void ProcessTextureRequests()
3148 {
3149 if (m_imageManager != null)
3150 m_imageManager.ProcessImageQueue(m_textureSendLimit, m_textureDataLimit);
3151 }
3152
3180 void ProcessPrimFullUpdates(object sender, ElapsedEventArgs e) 3153 void ProcessPrimFullUpdates(object sender, ElapsedEventArgs e)
3181 { 3154 {
3182 lock (m_primFullUpdates) 3155 lock (m_primFullUpdates)
@@ -5275,13 +5248,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
5275 // for the client session anyway, in order to protect ourselves against bad code in plugins 5248 // for the client session anyway, in order to protect ourselves against bad code in plugins
5276 try 5249 try
5277 { 5250 {
5278 List<byte> visualparams = new List<byte>(); 5251 byte[] visualparams = new byte[appear.VisualParam.Length];
5279 foreach (AgentSetAppearancePacket.VisualParamBlock x in appear.VisualParam) 5252 for (int i = 0; i < appear.VisualParam.Length; i++)
5280 { 5253 visualparams[i] = appear.VisualParam[i].ParamValue;
5281 visualparams.Add(x.ParamValue);
5282 }
5283 5254
5284 handlerSetAppearance(appear.ObjectData.TextureEntry, visualparams); 5255 Primitive.TextureEntry te = null;
5256 if (appear.ObjectData.TextureEntry.Length > 1)
5257 te = new Primitive.TextureEntry(appear.ObjectData.TextureEntry, 0, appear.ObjectData.TextureEntry.Length);
5258
5259 handlerSetAppearance(te, visualparams);
5285 } 5260 }
5286 catch (Exception e) 5261 catch (Exception e)
5287 { 5262 {
@@ -6624,8 +6599,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
6624 if (m_imageManager != null) 6599 if (m_imageManager != null)
6625 { 6600 {
6626 m_imageManager.EnqueueReq(args); 6601 m_imageManager.EnqueueReq(args);
6627 lock (m_textureRequestTimer)
6628 m_textureRequestTimer.Start();
6629 } 6602 }
6630 } 6603 }
6631 } 6604 }
@@ -11026,5 +10999,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11026 } 10999 }
11027 11000
11028 #endregion 11001 #endregion
11002
11003 public void SendRebakeAvatarTextures(UUID textureID)
11004 {
11005 RebakeAvatarTexturesPacket pack =
11006 (RebakeAvatarTexturesPacket)PacketPool.Instance.GetPacket(PacketType.RebakeAvatarTextures);
11007
11008 pack.TextureData = new RebakeAvatarTexturesPacket.TextureDataBlock();
11009 pack.TextureData.TextureID = textureID;
11010 OutPacket(pack, ThrottleOutPacketType.Task);
11011 }
11029 } 11012 }
11030} 11013}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
index 295a5e6..a484fdf 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
@@ -27,58 +27,60 @@
27 27
28using System; 28using System;
29using System.Threading; 29using System.Threading;
30using System.Collections;
30using System.Collections.Generic; 31using System.Collections.Generic;
32using System.Reflection;
31using OpenMetaverse; 33using OpenMetaverse;
32using OpenMetaverse.Imaging; 34using OpenMetaverse.Imaging;
33using OpenSim.Framework; 35using OpenSim.Framework;
34using OpenSim.Region.Framework.Interfaces; 36using OpenSim.Region.Framework.Interfaces;
35using OpenSim.Services.Interfaces; 37using OpenSim.Services.Interfaces;
36using log4net; 38using log4net;
37using System.Reflection;
38 39
39namespace OpenSim.Region.ClientStack.LindenUDP 40namespace OpenSim.Region.ClientStack.LindenUDP
40{ 41{
41
42 public class LLImageManager 42 public class LLImageManager
43 { 43 {
44 44 private sealed class J2KImageComparer : IComparer<J2KImage>
45 //Public interfaces: 45 {
46 //Constructor - (LLClientView, IAssetCache, IJ2KDecoder); 46 public int Compare(J2KImage x, J2KImage y)
47 //void EnqueueReq - (TextureRequestArgs) 47 {
48 //ProcessImageQueue 48 return x.m_requestedPriority.CompareTo(y.m_requestedPriority);
49 //Close 49 }
50 }
51
50 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 52 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51 private bool m_shuttingdown = false; 53 private bool m_shuttingdown = false;
52 private long m_lastloopprocessed = 0; 54 private long m_lastloopprocessed = 0;
55 private AssetBase m_missingImage = null;
53 56
54 private LLClientView m_client; //Client we're assigned to 57 private LLClientView m_client; //Client we're assigned to
55 private IAssetService m_assetCache; //Asset Cache 58 private IAssetService m_assetCache; //Asset Cache
56 private IJ2KDecoder m_j2kDecodeModule; //Our J2K module 59 private IJ2KDecoder m_j2kDecodeModule; //Our J2K module
60 private C5.IntervalHeap<J2KImage> m_priorityQueue = new C5.IntervalHeap<J2KImage>(10, new J2KImageComparer());
57 61
58 private readonly AssetBase m_missingsubstitute; //Sustitute for bad decodes
59 private Dictionary<UUID,J2KImage> m_imagestore; // Our main image storage dictionary
60 private SortedList<double,UUID> m_priorities; // For fast image lookup based on priority
61 private Dictionary<int, int> m_priorityresolver; //Enabling super fast assignment of images with the same priorities
62
63 private const double doubleMinimum = .0000001;
64
65 public int m_outstandingtextures = 0;
66 //Constructor
67 public LLImageManager(LLClientView client, IAssetService pAssetCache, IJ2KDecoder pJ2kDecodeModule) 62 public LLImageManager(LLClientView client, IAssetService pAssetCache, IJ2KDecoder pJ2kDecodeModule)
68 { 63 {
69
70 m_imagestore = new Dictionary<UUID,J2KImage>();
71 m_priorities = new SortedList<double,UUID>();
72 m_priorityresolver = new Dictionary<int, int>();
73 m_client = client; 64 m_client = client;
74 m_assetCache = pAssetCache; 65 m_assetCache = pAssetCache;
75 if (pAssetCache != null) 66 if (pAssetCache != null)
76 m_missingsubstitute = pAssetCache.Get("5748decc-f629-461c-9a36-a35a221fe21f"); 67 m_missingImage = pAssetCache.Get("5748decc-f629-461c-9a36-a35a221fe21f");
77 else 68 else
78 m_log.Error("[ClientView] - couldn't set missing image, all manner of things will probably break"); 69 m_log.Error("[ClientView] - couldn't set missing image asset, falling back to missing image packet. This is known to crash the client");
70
79 m_j2kDecodeModule = pJ2kDecodeModule; 71 m_j2kDecodeModule = pJ2kDecodeModule;
80 } 72 }
81 73
74 public LLClientView Client
75 {
76 get { return m_client; }
77 }
78
79 public AssetBase MissingImage
80 {
81 get { return m_missingImage; }
82 }
83
82 public void EnqueueReq(TextureRequestArgs newRequest) 84 public void EnqueueReq(TextureRequestArgs newRequest)
83 { 85 {
84 //newRequest is the properties of our new texture fetch request. 86 //newRequest is the properties of our new texture fetch request.
@@ -88,234 +90,203 @@ namespace OpenSim.Region.ClientStack.LindenUDP
88 //Make sure we're not shutting down.. 90 //Make sure we're not shutting down..
89 if (!m_shuttingdown) 91 if (!m_shuttingdown)
90 { 92 {
93 J2KImage imgrequest;
91 94
92 //Do we already know about this UUID? 95 // Do a linear search for this texture download
93 if (m_imagestore.ContainsKey(newRequest.RequestedAssetID)) 96 lock (m_priorityQueue)
94 { 97 m_priorityQueue.Find(delegate(J2KImage img) { return img.m_requestedUUID == newRequest.RequestedAssetID; }, out imgrequest);
95 //Check the packet sequence to make sure this isn't older than
96 //one we've already received
97
98 J2KImage imgrequest = m_imagestore[newRequest.RequestedAssetID];
99
100 // This is the inbound request sequence number. We can ignore
101 // "old" ones.
102 98
103 if (newRequest.requestSequence > imgrequest.m_lastSequence) 99 if (imgrequest != null)
100 {
101 if (newRequest.DiscardLevel == -1 && newRequest.Priority == 0f)
104 { 102 {
103 //m_log.Debug("[TEX]: (CAN) ID=" + newRequest.RequestedAssetID);
105 104
106 imgrequest.m_lastSequence = newRequest.requestSequence; 105 try
107
108 //Check the priority
109
110 double priority = imgrequest.m_requestedPriority;
111 if (priority != newRequest.Priority)
112 { 106 {
113 //Remove the old priority 107 lock (m_priorityQueue)
114 m_priorities.Remove(imgrequest.m_designatedPriorityKey); 108 m_priorityQueue.Delete(imgrequest.m_priorityQueueHandle);
115 //Assign a new unique priority
116 imgrequest.m_requestedPriority = newRequest.Priority;
117 imgrequest.m_designatedPriorityKey = AssignPriority(newRequest.RequestedAssetID, newRequest.Priority);
118 } 109 }
110 catch (Exception) { }
111 }
112 else
113 {
114 //m_log.DebugFormat("[TEX]: (UPD) ID={0}: D={1}, S={2}, P={3}",
115 // newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority);
119 116
120 //Update the requested discard level 117 //Check the packet sequence to make sure this isn't older than
121 imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel; 118 //one we've already received
119 if (newRequest.requestSequence > imgrequest.m_lastSequence)
120 {
121 //Update the sequence number of the last RequestImage packet
122 imgrequest.m_lastSequence = newRequest.requestSequence;
122 123
123 //Update the requested packet number 124 //Update the requested discard level
124 imgrequest.m_requestedPacketNumber = newRequest.PacketNumber; 125 imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel;
125 126
126 //Check if this will create an outstanding texture request 127 //Update the requested packet number
127 bool activated = imgrequest.m_completedSendAtCurrentDiscardLevel; 128 imgrequest.m_requestedPacketNumber = newRequest.PacketNumber;
128 //Run an update
129 129
130 imgrequest.RunUpdate(); 130 //Update the requested priority
131 imgrequest.m_requestedPriority = newRequest.Priority;
132 try
133 {
134 lock (m_priorityQueue)
135 m_priorityQueue.Replace(imgrequest.m_priorityQueueHandle, imgrequest);
136 }
137 catch (Exception)
138 {
139 imgrequest.m_priorityQueueHandle = null;
140 lock (m_priorityQueue)
141 m_priorityQueue.Add(ref imgrequest.m_priorityQueueHandle, imgrequest);
142 }
131 143
132 if (activated && !imgrequest.m_completedSendAtCurrentDiscardLevel && imgrequest.m_decoded) 144 //Run an update
133 { 145 imgrequest.RunUpdate();
134 Interlocked.Increment(ref m_outstandingtextures);
135 } 146 }
136 } 147 }
137 } 148 }
138 else 149 else
139 { 150 {
140 J2KImage imgrequest = new J2KImage(this); 151 if (newRequest.DiscardLevel == -1 && newRequest.Priority == 0f)
141 152 {
142 //Assign our missing substitute 153 //m_log.DebugFormat("[TEX]: (IGN) ID={0}: D={1}, S={2}, P={3}",
143 imgrequest.m_MissingSubstitute = m_missingsubstitute; 154 // newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority);
155 }
156 else
157 {
158 //m_log.DebugFormat("[TEX]: (NEW) ID={0}: D={1}, S={2}, P={3}",
159 // newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority);
144 160
145 //Assign our decoder module 161 imgrequest = new J2KImage(this);
146 imgrequest.m_j2kDecodeModule = m_j2kDecodeModule;
147 162
148 //Assign our asset cache module 163 //Assign our decoder module
149 imgrequest.m_assetCache = m_assetCache; 164 imgrequest.m_j2kDecodeModule = m_j2kDecodeModule;
150 165
151 //Assign a priority based on our request 166 //Assign our asset cache module
152 imgrequest.m_designatedPriorityKey = AssignPriority(newRequest.RequestedAssetID, newRequest.Priority); 167 imgrequest.m_assetCache = m_assetCache;
153 168
154 //Assign the requested discard level 169 //Assign the requested discard level
155 imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel; 170 imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel;
156 171
157 //Assign the requested packet number 172 //Assign the requested packet number
158 imgrequest.m_requestedPacketNumber = newRequest.PacketNumber; 173 imgrequest.m_requestedPacketNumber = newRequest.PacketNumber;
159 174
160 //Assign the requested priority 175 //Assign the requested priority
161 imgrequest.m_requestedPriority = newRequest.Priority; 176 imgrequest.m_requestedPriority = newRequest.Priority;
162 177
163 //Assign the asset uuid 178 //Assign the asset uuid
164 imgrequest.m_requestedUUID = newRequest.RequestedAssetID; 179 imgrequest.m_requestedUUID = newRequest.RequestedAssetID;
165 180
166 m_imagestore.Add(imgrequest.m_requestedUUID, imgrequest); 181 //Assign the requested priority
182 imgrequest.m_requestedPriority = newRequest.Priority;
167 183
168 //Run an update 184 //Add this download to the priority queue
169 imgrequest.RunUpdate(); 185 lock (m_priorityQueue)
186 m_priorityQueue.Add(ref imgrequest.m_priorityQueueHandle, imgrequest);
170 187
188 //Run an update
189 imgrequest.RunUpdate();
190 }
171 } 191 }
172 } 192 }
173 } 193 }
174 194
175 private double AssignPriority(UUID pAssetID, double pPriority) 195 public bool ProcessImageQueue(int count, int maxpack)
176 { 196 {
177 197 lock (this)
178 //First, find out if we can just assign directly
179 if (m_priorityresolver.ContainsKey((int)pPriority) == false)
180 { 198 {
181 m_priorities.Add((double)((int)pPriority), pAssetID); 199 //count is the number of textures we want to process in one go.
182 m_priorityresolver.Add((int)pPriority, 0); 200 //As part of this class re-write, that number will probably rise
183 return (double)((int)pPriority); 201 //since we're processing in a more efficient manner.
184 }
185 else
186 {
187 //Use the hash lookup goodness of a secondary dictionary to find a free slot
188 double mFreePriority = ((int)pPriority) + (doubleMinimum * (m_priorityresolver[(int)pPriority] + 1));
189 m_priorities[mFreePriority] = pAssetID;
190 m_priorityresolver[(int)pPriority]++;
191 return mFreePriority;
192 }
193 202
203 // this can happen during Close()
204 if (m_client == null)
205 return false;
194 206
207 int numCollected = 0;
195 208
196 } 209 //Calculate our threshold
210 int threshold;
211 if (m_lastloopprocessed == 0)
212 {
213 if (m_client.PacketHandler == null || m_client.PacketHandler.PacketQueue == null || m_client.PacketHandler.PacketQueue.TextureThrottle == null)
214 return false;
215 //This is decent for a semi fast machine, but we'll calculate it more accurately based on time below
216 threshold = m_client.PacketHandler.PacketQueue.TextureThrottle.Current / 6300;
217 m_lastloopprocessed = DateTime.Now.Ticks;
218 }
219 else
220 {
221 double throttleseconds = ((double)DateTime.Now.Ticks - (double)m_lastloopprocessed) / (double)TimeSpan.TicksPerSecond;
222 throttleseconds = throttleseconds * m_client.PacketHandler.PacketQueue.TextureThrottle.Current;
197 223
198 public bool ProcessImageQueue(int count, int maxpack) 224 //Average of 1000 bytes per packet
199 { 225 throttleseconds = throttleseconds / 1000;
226
227 //Safe-zone multiplier of 2.0
228 threshold = (int)(throttleseconds * 2.0);
229 m_lastloopprocessed = DateTime.Now.Ticks;
230
231 }
232
233 if (m_client.PacketHandler == null)
234 return false;
200 235
201 // this can happen during Close() 236 if (m_client.PacketHandler.PacketQueue == null)
202 if (m_client == null) 237 return false;
203 return false; 238
204 239 if (threshold < 10)
205 //Count is the number of textures we want to process in one go. 240 threshold = 10;
206 //As part of this class re-write, that number will probably rise 241
207 //since we're processing in a more efficient manner. 242 //Uncomment this to see what the texture stack is doing
208 243 //m_log.Debug("Queue: " + m_client.PacketHandler.PacketQueue.getQueueCount(ThrottleOutPacketType.Texture).ToString() + " Threshold: " + threshold.ToString() + " outstanding: " + m_outstandingtextures.ToString());
209 int numCollected = 0; 244 if (true) //m_client.PacketHandler.PacketQueue.GetQueueCount(ThrottleOutPacketType.Texture) < threshold)
210
211 //Calculate our threshold
212 int threshold;
213 if (m_lastloopprocessed == 0)
214 {
215 if (m_client.PacketHandler == null || m_client.PacketHandler.PacketQueue == null || m_client.PacketHandler.PacketQueue.TextureThrottle == null)
216 return false;
217 //This is decent for a semi fast machine, but we'll calculate it more accurately based on time below
218 threshold = m_client.PacketHandler.PacketQueue.TextureThrottle.Current / 6300;
219 m_lastloopprocessed = DateTime.Now.Ticks;
220 }
221 else
222 {
223 double throttleseconds = ((double)DateTime.Now.Ticks - (double)m_lastloopprocessed) / (double)TimeSpan.TicksPerSecond;
224 throttleseconds = throttleseconds * m_client.PacketHandler.PacketQueue.TextureThrottle.Current;
225
226 //Average of 1000 bytes per packet
227 throttleseconds = throttleseconds / 1000;
228
229 //Safe-zone multiplier of 2.0
230 threshold = (int)(throttleseconds * 2.0);
231 m_lastloopprocessed = DateTime.Now.Ticks;
232
233 }
234
235 if (threshold < 10)
236 {
237 threshold = 10;
238 }
239
240 if (m_client.PacketHandler == null)
241 return false;
242
243 if (m_client.PacketHandler.PacketQueue == null)
244 return false;
245
246 //First of all make sure our packet queue isn't above our threshold
247
248 //Uncomment this to see what the texture stack is doing
249 //m_log.Debug("Queue: " + m_client.PacketHandler.PacketQueue.TextureOutgoingPacketQueueCount.ToString() + " Threshold: " + threshold.ToString() + " outstanding: " + m_outstandingtextures.ToString());
250 if (m_client.PacketHandler.PacketQueue.TextureOutgoingPacketQueueCount < threshold && m_outstandingtextures > 0)
251 {
252 bool justreset = false;
253
254 for (int x = m_priorities.Count - 1; x > -1; x--)
255 { 245 {
256 246 while (m_priorityQueue.Count > 0)
257 J2KImage imagereq = m_imagestore[m_priorities.Values[x]];
258 if (imagereq.m_decoded == true && !imagereq.m_completedSendAtCurrentDiscardLevel)
259 { 247 {
260 numCollected++; 248 J2KImage imagereq = null;
261 //SendPackets will send up to ten packets per cycle 249 lock (m_priorityQueue)
262 if (imagereq.SendPackets(m_client, maxpack)) 250 imagereq = m_priorityQueue.FindMax();
251
252 if (imagereq.m_decoded == true)
263 { 253 {
264 //Send complete 254 // we need to test this here now that we are dropping assets
265 if (!imagereq.m_completedSendAtCurrentDiscardLevel) 255 if (!imagereq.m_hasasset)
266 { 256 {
267 imagereq.m_completedSendAtCurrentDiscardLevel = true; 257 m_log.WarnFormat("[LLIMAGE MANAGER]: Re-requesting the image asset {0}", imagereq.m_requestedUUID);
268 Interlocked.Decrement(ref m_outstandingtextures); 258 imagereq.RunUpdate();
269 //Re-assign priority to bottom 259 continue;
270 //Remove the old priority 260 }
271 m_priorities.Remove(imagereq.m_designatedPriorityKey); 261
272 int lowest; 262 ++numCollected;
273 if (m_priorities.Count > 0) 263
274 { 264 //SendPackets will send up to ten packets per cycle
275 lowest = (int)m_priorities.Keys[0]; 265 if (imagereq.SendPackets(m_client, maxpack))
276 lowest--; 266 {
277 } 267 // Send complete. Destroy any knowledge of this transfer
278 else 268 try
279 { 269 {
280 lowest = -10000; 270 lock (m_priorityQueue)
281 } 271 m_priorityQueue.Delete(imagereq.m_priorityQueueHandle);
282 m_priorities.Add((double)lowest, imagereq.m_requestedUUID);
283 imagereq.m_designatedPriorityKey = (double)lowest;
284 if (m_priorityresolver.ContainsKey((int)lowest))
285 {
286 m_priorityresolver[(int)lowest]++;
287 }
288 else
289 {
290 m_priorityresolver.Add((int)lowest, 0);
291 } 272 }
273 catch (Exception) { }
292 } 274 }
293 } 275 }
276
294 if (numCollected == count) 277 if (numCollected == count)
295 {
296 break; 278 break;
297 }
298 }
299 if (numCollected == count || m_outstandingtextures == 0)
300 break;
301 if (numCollected % m_outstandingtextures == 0 && !justreset)
302 {
303 //We've gotten as much as we can from the stack,
304 //reset to the top so that we can send MOAR DATA (nomnomnom)!
305 x = m_priorities.Count - 1;
306
307 justreset = true; //prevents us from getting stuck in a loop
308 } 279 }
309 } 280 }
310 }
311 281
312 return m_outstandingtextures != 0; 282 return m_priorityQueue.Count > 0;
283 }
313 } 284 }
314 285
315 //Faux destructor 286 //Faux destructor
316 public void Close() 287 public void Close()
317 { 288 {
318 289
319 m_shuttingdown = true; 290 m_shuttingdown = true;
320 m_j2kDecodeModule = null; 291 m_j2kDecodeModule = null;
321 m_assetCache = null; 292 m_assetCache = null;
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
index eaf8f60..e98a360 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
@@ -129,6 +129,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
129 // 129 //
130 public event PacketStats OnPacketStats; 130 public event PacketStats OnPacketStats;
131 public event PacketDrop OnPacketDrop; 131 public event PacketDrop OnPacketDrop;
132 public event QueueEmpty OnQueueEmpty;
132 133
133 134
134 //private SynchronizeClientHandler m_SynchronizeClient = null; 135 //private SynchronizeClientHandler m_SynchronizeClient = null;
@@ -172,13 +173,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
172 173
173 m_PacketQueue = new LLPacketQueue(client.AgentId, userSettings); 174 m_PacketQueue = new LLPacketQueue(client.AgentId, userSettings);
174 175
176 m_PacketQueue.OnQueueEmpty += TriggerOnQueueEmpty;
177
175 m_AckTimer.Elapsed += AckTimerElapsed; 178 m_AckTimer.Elapsed += AckTimerElapsed;
176 m_AckTimer.Start(); 179 m_AckTimer.Start();
177 } 180 }
178 181
179 public void Stop() 182 public void Dispose()
180 { 183 {
181 m_AckTimer.Stop(); 184 m_AckTimer.Stop();
185 m_AckTimer.Close();
182 186
183 m_PacketQueue.Enqueue(null); 187 m_PacketQueue.Enqueue(null);
184 m_PacketQueue.Close(); 188 m_PacketQueue.Close();
@@ -768,6 +772,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
768 handlerPacketDrop(packet, id); 772 handlerPacketDrop(packet, id);
769 } 773 }
770 774
775 private void TriggerOnQueueEmpty(ThrottleOutPacketType queue)
776 {
777 QueueEmpty handlerQueueEmpty = OnQueueEmpty;
778
779 if (handlerQueueEmpty != null)
780 handlerQueueEmpty(queue);
781 }
782
771 // Convert the packet to bytes and stuff it onto the send queue 783 // Convert the packet to bytes and stuff it onto the send queue
772 // 784 //
773 public void ProcessOutPacket(LLQueItem item) 785 public void ProcessOutPacket(LLQueItem item)
@@ -849,5 +861,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
849 m_PacketQueue.Close(); 861 m_PacketQueue.Close();
850 Thread.CurrentThread.Abort(); 862 Thread.CurrentThread.Abort();
851 } 863 }
864
865 public int GetQueueCount(ThrottleOutPacketType queue)
866 {
867 return m_PacketQueue.GetQueueCount(queue);
868 }
852 } 869 }
853} 870}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
index c427870..3eed2e0 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
@@ -39,7 +39,7 @@ using Timer=System.Timers.Timer;
39 39
40namespace OpenSim.Region.ClientStack.LindenUDP 40namespace OpenSim.Region.ClientStack.LindenUDP
41{ 41{
42 public class LLPacketQueue : IPullStatsProvider 42 public class LLPacketQueue : IPullStatsProvider, IDisposable
43 { 43 {
44 private static readonly ILog m_log 44 private static readonly ILog m_log
45 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 45 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
@@ -62,6 +62,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
62 private Queue<LLQueItem> TextureOutgoingPacketQueue; 62 private Queue<LLQueItem> TextureOutgoingPacketQueue;
63 private Queue<LLQueItem> AssetOutgoingPacketQueue; 63 private Queue<LLQueItem> AssetOutgoingPacketQueue;
64 64
65 private List<ThrottleOutPacketType> Empty = new List<ThrottleOutPacketType>();
66 // m_log.Info("[THROTTLE]: Entering Throttle");
65 // private Dictionary<uint, uint> PendingAcks = new Dictionary<uint, uint>(); 67 // private Dictionary<uint, uint> PendingAcks = new Dictionary<uint, uint>();
66 // private Dictionary<uint, Packet> NeedAck = new Dictionary<uint, Packet>(); 68 // private Dictionary<uint, Packet> NeedAck = new Dictionary<uint, Packet>();
67 69
@@ -85,26 +87,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
85 87
86 private Dictionary<uint,int> contents = new Dictionary<uint, int>(); 88 private Dictionary<uint,int> contents = new Dictionary<uint, int>();
87 89
88 /// <summary>
89 /// The number of packets in the OutgoingPacketQueue
90 ///
91 /// </summary>
92 internal int TextureOutgoingPacketQueueCount
93 {
94 get
95 {
96 if (TextureOutgoingPacketQueue == null)
97 return 0;
98 return TextureOutgoingPacketQueue.Count;
99 }
100 }
101
102 // private long LastThrottle; 90 // private long LastThrottle;
103 // private long ThrottleInterval; 91 // private long ThrottleInterval;
104 private Timer throttleTimer; 92 private Timer throttleTimer;
105 93
106 private UUID m_agentId; 94 private UUID m_agentId;
107 95
96 public event QueueEmpty OnQueueEmpty;
97
108 public LLPacketQueue(UUID agentId, ClientStackUserSettings userSettings) 98 public LLPacketQueue(UUID agentId, ClientStackUserSettings userSettings)
109 { 99 {
110 // While working on this, the BlockingQueue had me fooled for a bit. 100 // While working on this, the BlockingQueue had me fooled for a bit.
@@ -210,28 +200,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
210 switch (item.throttleType & ThrottleOutPacketType.TypeMask) 200 switch (item.throttleType & ThrottleOutPacketType.TypeMask)
211 { 201 {
212 case ThrottleOutPacketType.Resend: 202 case ThrottleOutPacketType.Resend:
213 ThrottleCheck(ref ResendThrottle, ref ResendOutgoingPacketQueue, item); 203 ThrottleCheck(ref ResendThrottle, ref ResendOutgoingPacketQueue, item, ThrottleOutPacketType.Resend);
214 break; 204 break;
215 case ThrottleOutPacketType.Texture: 205 case ThrottleOutPacketType.Texture:
216 ThrottleCheck(ref TextureThrottle, ref TextureOutgoingPacketQueue, item); 206 ThrottleCheck(ref TextureThrottle, ref TextureOutgoingPacketQueue, item, ThrottleOutPacketType.Texture);
217 break; 207 break;
218 case ThrottleOutPacketType.Task: 208 case ThrottleOutPacketType.Task:
219 if ((item.throttleType & ThrottleOutPacketType.LowPriority) != 0) 209 if ((item.throttleType & ThrottleOutPacketType.LowPriority) != 0)
220 ThrottleCheck(ref TaskThrottle, ref TaskLowpriorityPacketQueue, item); 210 ThrottleCheck(ref TaskThrottle, ref TaskLowpriorityPacketQueue, item, ThrottleOutPacketType.Task);
221 else 211 else
222 ThrottleCheck(ref TaskThrottle, ref TaskOutgoingPacketQueue, item); 212 ThrottleCheck(ref TaskThrottle, ref TaskOutgoingPacketQueue, item, ThrottleOutPacketType.Task);
223 break; 213 break;
224 case ThrottleOutPacketType.Land: 214 case ThrottleOutPacketType.Land:
225 ThrottleCheck(ref LandThrottle, ref LandOutgoingPacketQueue, item); 215 ThrottleCheck(ref LandThrottle, ref LandOutgoingPacketQueue, item, ThrottleOutPacketType.Land);
226 break; 216 break;
227 case ThrottleOutPacketType.Asset: 217 case ThrottleOutPacketType.Asset:
228 ThrottleCheck(ref AssetThrottle, ref AssetOutgoingPacketQueue, item); 218 ThrottleCheck(ref AssetThrottle, ref AssetOutgoingPacketQueue, item, ThrottleOutPacketType.Asset);
229 break; 219 break;
230 case ThrottleOutPacketType.Cloud: 220 case ThrottleOutPacketType.Cloud:
231 ThrottleCheck(ref CloudThrottle, ref CloudOutgoingPacketQueue, item); 221 ThrottleCheck(ref CloudThrottle, ref CloudOutgoingPacketQueue, item, ThrottleOutPacketType.Cloud);
232 break; 222 break;
233 case ThrottleOutPacketType.Wind: 223 case ThrottleOutPacketType.Wind:
234 ThrottleCheck(ref WindThrottle, ref WindOutgoingPacketQueue, item); 224 ThrottleCheck(ref WindThrottle, ref WindOutgoingPacketQueue, item, ThrottleOutPacketType.Wind);
235 break; 225 break;
236 226
237 default: 227 default:
@@ -283,43 +273,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP
283 { 273 {
284 lock (this) 274 lock (this)
285 { 275 {
286 while (PacketsWaiting()) 276 // These categories do not contain transactional packets so we can safely drop any pending data in them
277 LandOutgoingPacketQueue.Clear();
278 WindOutgoingPacketQueue.Clear();
279 CloudOutgoingPacketQueue.Clear();
280 TextureOutgoingPacketQueue.Clear();
281 AssetOutgoingPacketQueue.Clear();
282
283 // Now comes the fun part.. we dump all remaining resend and task packets into the send queue
284 while (ResendOutgoingPacketQueue.Count > 0 || TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0)
287 { 285 {
288 //Now comes the fun part.. we dump all our elements into m_packetQueue that we've saved up.
289 if (ResendOutgoingPacketQueue.Count > 0) 286 if (ResendOutgoingPacketQueue.Count > 0)
290 {
291 SendQueue.Enqueue(ResendOutgoingPacketQueue.Dequeue()); 287 SendQueue.Enqueue(ResendOutgoingPacketQueue.Dequeue());
292 } 288
293 if (LandOutgoingPacketQueue.Count > 0)
294 {
295 SendQueue.Enqueue(LandOutgoingPacketQueue.Dequeue());
296 }
297 if (WindOutgoingPacketQueue.Count > 0)
298 {
299 SendQueue.Enqueue(WindOutgoingPacketQueue.Dequeue());
300 }
301 if (CloudOutgoingPacketQueue.Count > 0)
302 {
303 SendQueue.Enqueue(CloudOutgoingPacketQueue.Dequeue());
304 }
305 if (TaskOutgoingPacketQueue.Count > 0) 289 if (TaskOutgoingPacketQueue.Count > 0)
306 {
307 SendQueue.PriorityEnqueue(TaskOutgoingPacketQueue.Dequeue()); 290 SendQueue.PriorityEnqueue(TaskOutgoingPacketQueue.Dequeue());
308 } 291
309 if (TaskLowpriorityPacketQueue.Count > 0) 292 if (TaskLowpriorityPacketQueue.Count > 0)
310 {
311 SendQueue.Enqueue(TaskLowpriorityPacketQueue.Dequeue()); 293 SendQueue.Enqueue(TaskLowpriorityPacketQueue.Dequeue());
312 }
313 if (TextureOutgoingPacketQueue.Count > 0)
314 {
315 SendQueue.Enqueue(TextureOutgoingPacketQueue.Dequeue());
316 }
317 if (AssetOutgoingPacketQueue.Count > 0)
318 {
319 SendQueue.Enqueue(AssetOutgoingPacketQueue.Dequeue());
320 }
321 } 294 }
322 // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets");
323 } 295 }
324 } 296 }
325 297
@@ -342,11 +314,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
342 314
343 public void Close() 315 public void Close()
344 { 316 {
317 Dispose();
318 }
319
320 public void Dispose()
321 {
345 Flush(); 322 Flush();
346 WipeClean(); // I'm sure there's a dirty joke in here somewhere. -AFrisby 323 WipeClean(); // I'm sure there's a dirty joke in here somewhere. -AFrisby
347 324
348 m_enabled = false; 325 m_enabled = false;
349 throttleTimer.Stop(); 326 throttleTimer.Stop();
327 throttleTimer.Close();
350 328
351 if (StatsManager.SimExtraStats != null) 329 if (StatsManager.SimExtraStats != null)
352 { 330 {
@@ -388,6 +366,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
388 366
389 int MaxThrottleLoops = 4550; // 50*7 packets can be dequeued at once. 367 int MaxThrottleLoops = 4550; // 50*7 packets can be dequeued at once.
390 int throttleLoops = 0; 368 int throttleLoops = 0;
369 List<ThrottleOutPacketType> e;
391 370
392 // We're going to dequeue all of the saved up packets until 371 // We're going to dequeue all of the saved up packets until
393 // we've hit the throttle limit or there's no more packets to send 372 // we've hit the throttle limit or there's no more packets to send
@@ -399,7 +378,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
399 bool qchanged = true; 378 bool qchanged = true;
400 379
401 ResetCounters(); 380 ResetCounters();
402 // m_log.Info("[THROTTLE]: Entering Throttle"); 381
403 while (TotalThrottle.UnderLimit() && qchanged && throttleLoops <= MaxThrottleLoops) 382 while (TotalThrottle.UnderLimit() && qchanged && throttleLoops <= MaxThrottleLoops)
404 { 383 {
405 qchanged = false; // We will break out of the loop if no work was accomplished 384 qchanged = false; // We will break out of the loop if no work was accomplished
@@ -425,6 +404,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
425 TotalThrottle.AddBytes(qpack.Length); 404 TotalThrottle.AddBytes(qpack.Length);
426 LandThrottle.AddBytes(qpack.Length); 405 LandThrottle.AddBytes(qpack.Length);
427 qchanged = true; 406 qchanged = true;
407
408 if (LandOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Land))
409 Empty.Add(ThrottleOutPacketType.Land);
428 } 410 }
429 411
430 if ((WindOutgoingPacketQueue.Count > 0) && WindThrottle.UnderLimit()) 412 if ((WindOutgoingPacketQueue.Count > 0) && WindThrottle.UnderLimit())
@@ -435,6 +417,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
435 TotalThrottle.AddBytes(qpack.Length); 417 TotalThrottle.AddBytes(qpack.Length);
436 WindThrottle.AddBytes(qpack.Length); 418 WindThrottle.AddBytes(qpack.Length);
437 qchanged = true; 419 qchanged = true;
420
421 if (WindOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Wind))
422 Empty.Add(ThrottleOutPacketType.Wind);
438 } 423 }
439 424
440 if ((CloudOutgoingPacketQueue.Count > 0) && CloudThrottle.UnderLimit()) 425 if ((CloudOutgoingPacketQueue.Count > 0) && CloudThrottle.UnderLimit())
@@ -445,6 +430,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
445 TotalThrottle.AddBytes(qpack.Length); 430 TotalThrottle.AddBytes(qpack.Length);
446 CloudThrottle.AddBytes(qpack.Length); 431 CloudThrottle.AddBytes(qpack.Length);
447 qchanged = true; 432 qchanged = true;
433
434 if (CloudOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Cloud))
435 Empty.Add(ThrottleOutPacketType.Cloud);
448 } 436 }
449 437
450 if ((TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0) && TaskThrottle.UnderLimit()) 438 if ((TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0) && TaskThrottle.UnderLimit())
@@ -464,6 +452,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
464 TotalThrottle.AddBytes(qpack.Length); 452 TotalThrottle.AddBytes(qpack.Length);
465 TaskThrottle.AddBytes(qpack.Length); 453 TaskThrottle.AddBytes(qpack.Length);
466 qchanged = true; 454 qchanged = true;
455
456 if (TaskOutgoingPacketQueue.Count == 0 && TaskLowpriorityPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Task))
457 Empty.Add(ThrottleOutPacketType.Task);
467 } 458 }
468 459
469 if ((TextureOutgoingPacketQueue.Count > 0) && TextureThrottle.UnderLimit()) 460 if ((TextureOutgoingPacketQueue.Count > 0) && TextureThrottle.UnderLimit())
@@ -474,6 +465,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
474 TotalThrottle.AddBytes(qpack.Length); 465 TotalThrottle.AddBytes(qpack.Length);
475 TextureThrottle.AddBytes(qpack.Length); 466 TextureThrottle.AddBytes(qpack.Length);
476 qchanged = true; 467 qchanged = true;
468
469 if (TextureOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Texture))
470 Empty.Add(ThrottleOutPacketType.Texture);
477 } 471 }
478 472
479 if ((AssetOutgoingPacketQueue.Count > 0) && AssetThrottle.UnderLimit()) 473 if ((AssetOutgoingPacketQueue.Count > 0) && AssetThrottle.UnderLimit())
@@ -484,10 +478,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP
484 TotalThrottle.AddBytes(qpack.Length); 478 TotalThrottle.AddBytes(qpack.Length);
485 AssetThrottle.AddBytes(qpack.Length); 479 AssetThrottle.AddBytes(qpack.Length);
486 qchanged = true; 480 qchanged = true;
481
482 if (AssetOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Asset))
483 Empty.Add(ThrottleOutPacketType.Asset);
487 } 484 }
488 } 485 }
489 // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets"); 486 // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets");
487
488 e = new List<ThrottleOutPacketType>(Empty);
489 Empty.Clear();
490 } 490 }
491
492 foreach (ThrottleOutPacketType t in e)
493 {
494 if (GetQueueCount(t) == 0)
495 TriggerOnQueueEmpty(t);
496 }
497 }
498
499 private void TriggerOnQueueEmpty(ThrottleOutPacketType queue)
500 {
501 QueueEmpty handlerQueueEmpty = OnQueueEmpty;
502
503 if (handlerQueueEmpty != null)
504 handlerQueueEmpty(queue);
491 } 505 }
492 506
493 private void ThrottleTimerElapsed(object sender, ElapsedEventArgs e) 507 private void ThrottleTimerElapsed(object sender, ElapsedEventArgs e)
@@ -497,7 +511,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
497 ProcessThrottle(); 511 ProcessThrottle();
498 } 512 }
499 513
500 private void ThrottleCheck(ref LLPacketThrottle throttle, ref Queue<LLQueItem> q, LLQueItem item) 514 private void ThrottleCheck(ref LLPacketThrottle throttle, ref Queue<LLQueItem> q, LLQueItem item, ThrottleOutPacketType itemType)
501 { 515 {
502 // The idea.. is if the packet throttle queues are empty 516 // The idea.. is if the packet throttle queues are empty
503 // and the client is under throttle for the type. Queue 517 // and the client is under throttle for the type. Queue
@@ -513,6 +527,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
513 throttle.AddBytes(item.Length); 527 throttle.AddBytes(item.Length);
514 TotalThrottle.AddBytes(item.Length); 528 TotalThrottle.AddBytes(item.Length);
515 SendQueue.Enqueue(item); 529 SendQueue.Enqueue(item);
530 lock (this)
531 {
532 if (!Empty.Contains(itemType))
533 Empty.Add(itemType);
534 }
516 } 535 }
517 catch (Exception e) 536 catch (Exception e)
518 { 537 {
@@ -698,5 +717,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
698 { 717 {
699 get { return throttleMultiplier; } 718 get { return throttleMultiplier; }
700 } 719 }
720
721 public int GetQueueCount(ThrottleOutPacketType queue)
722 {
723 switch (queue)
724 {
725 case ThrottleOutPacketType.Land:
726 return LandOutgoingPacketQueue.Count;
727 case ThrottleOutPacketType.Wind:
728 return WindOutgoingPacketQueue.Count;
729 case ThrottleOutPacketType.Cloud:
730 return CloudOutgoingPacketQueue.Count;
731 case ThrottleOutPacketType.Task:
732 return TaskOutgoingPacketQueue.Count;
733 case ThrottleOutPacketType.Texture:
734 return TextureOutgoingPacketQueue.Count;
735 case ThrottleOutPacketType.Asset:
736 return AssetOutgoingPacketQueue.Count;
737 }
738
739 return 0;
740 }
701 } 741 }
702} 742}