diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs | 6 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs | 455 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs | 115 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs | 365 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs | 21 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs | 122 |
6 files changed, 571 insertions, 513 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; | |||
38 | namespace OpenSim.Region.ClientStack.LindenUDP | 38 | namespace 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..d8bd36d 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 |
@@ -3164,16 +3137,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3164 | // Unlike the other timers, this one is only started after | 3137 | // Unlike the other timers, this one is only started after |
3165 | // the first request is seen. | 3138 | // the first request is seen. |
3166 | 3139 | ||
3167 | void ProcessTextureRequests(object sender, ElapsedEventArgs e) | 3140 | void HandleQueueEmpty(ThrottleOutPacketType queue) |
3141 | { | ||
3142 | switch (queue) | ||
3143 | { | ||
3144 | case ThrottleOutPacketType.Texture: | ||
3145 | ProcessTextureRequests(); | ||
3146 | break; | ||
3147 | } | ||
3148 | } | ||
3149 | |||
3150 | void ProcessTextureRequests() | ||
3168 | { | 3151 | { |
3169 | if (m_imageManager != null) | 3152 | if (m_imageManager != null) |
3170 | { | 3153 | { |
3171 | if (m_imageManager.ProcessImageQueue(m_textureSendLimit, | 3154 | m_imageManager.ProcessImageQueue(m_textureSendLimit, |
3172 | m_textureDataLimit)) | 3155 | m_textureDataLimit); |
3173 | { | ||
3174 | lock (m_textureRequestTimer) | ||
3175 | m_textureRequestTimer.Start(); | ||
3176 | } | ||
3177 | } | 3156 | } |
3178 | } | 3157 | } |
3179 | 3158 | ||
@@ -5275,13 +5254,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
5275 | // for the client session anyway, in order to protect ourselves against bad code in plugins | 5254 | // for the client session anyway, in order to protect ourselves against bad code in plugins |
5276 | try | 5255 | try |
5277 | { | 5256 | { |
5278 | List<byte> visualparams = new List<byte>(); | 5257 | byte[] visualparams = new byte[appear.VisualParam.Length]; |
5279 | foreach (AgentSetAppearancePacket.VisualParamBlock x in appear.VisualParam) | 5258 | for (int i = 0; i < appear.VisualParam.Length; i++) |
5280 | { | 5259 | visualparams[i] = appear.VisualParam[i].ParamValue; |
5281 | visualparams.Add(x.ParamValue); | ||
5282 | } | ||
5283 | 5260 | ||
5284 | handlerSetAppearance(appear.ObjectData.TextureEntry, visualparams); | 5261 | Primitive.TextureEntry te = null; |
5262 | if (appear.ObjectData.TextureEntry.Length > 1) | ||
5263 | te = new Primitive.TextureEntry(appear.ObjectData.TextureEntry, 0, appear.ObjectData.TextureEntry.Length); | ||
5264 | |||
5265 | handlerSetAppearance(te, visualparams); | ||
5285 | } | 5266 | } |
5286 | catch (Exception e) | 5267 | catch (Exception e) |
5287 | { | 5268 | { |
@@ -6624,8 +6605,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
6624 | if (m_imageManager != null) | 6605 | if (m_imageManager != null) |
6625 | { | 6606 | { |
6626 | m_imageManager.EnqueueReq(args); | 6607 | m_imageManager.EnqueueReq(args); |
6627 | lock (m_textureRequestTimer) | ||
6628 | m_textureRequestTimer.Start(); | ||
6629 | } | 6608 | } |
6630 | } | 6609 | } |
6631 | } | 6610 | } |
@@ -11026,5 +11005,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11026 | } | 11005 | } |
11027 | 11006 | ||
11028 | #endregion | 11007 | #endregion |
11008 | |||
11009 | public void SendRebakeAvatarTextures(UUID textureID) | ||
11010 | { | ||
11011 | RebakeAvatarTexturesPacket pack = | ||
11012 | (RebakeAvatarTexturesPacket)PacketPool.Instance.GetPacket(PacketType.RebakeAvatarTextures); | ||
11013 | |||
11014 | pack.TextureData = new RebakeAvatarTexturesPacket.TextureDataBlock(); | ||
11015 | pack.TextureData.TextureID = textureID; | ||
11016 | OutPacket(pack, ThrottleOutPacketType.Task); | ||
11017 | } | ||
11029 | } | 11018 | } |
11030 | } | 11019 | } |
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 | ||
28 | using System; | 28 | using System; |
29 | using System.Threading; | 29 | using System.Threading; |
30 | using System.Collections; | ||
30 | using System.Collections.Generic; | 31 | using System.Collections.Generic; |
32 | using System.Reflection; | ||
31 | using OpenMetaverse; | 33 | using OpenMetaverse; |
32 | using OpenMetaverse.Imaging; | 34 | using OpenMetaverse.Imaging; |
33 | using OpenSim.Framework; | 35 | using OpenSim.Framework; |
34 | using OpenSim.Region.Framework.Interfaces; | 36 | using OpenSim.Region.Framework.Interfaces; |
35 | using OpenSim.Services.Interfaces; | 37 | using OpenSim.Services.Interfaces; |
36 | using log4net; | 38 | using log4net; |
37 | using System.Reflection; | ||
38 | 39 | ||
39 | namespace OpenSim.Region.ClientStack.LindenUDP | 40 | namespace 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..37f6ca7 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,16 @@ 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 | return; | ||
781 | |||
782 | handlerQueueEmpty(queue); | ||
783 | } | ||
784 | |||
771 | // Convert the packet to bytes and stuff it onto the send queue | 785 | // Convert the packet to bytes and stuff it onto the send queue |
772 | // | 786 | // |
773 | public void ProcessOutPacket(LLQueItem item) | 787 | public void ProcessOutPacket(LLQueItem item) |
@@ -849,5 +863,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
849 | m_PacketQueue.Close(); | 863 | m_PacketQueue.Close(); |
850 | Thread.CurrentThread.Abort(); | 864 | Thread.CurrentThread.Abort(); |
851 | } | 865 | } |
866 | |||
867 | public int GetQueueCount(ThrottleOutPacketType queue) | ||
868 | { | ||
869 | return m_PacketQueue.GetQueueCount(queue); | ||
870 | } | ||
852 | } | 871 | } |
853 | } | 872 | } |
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs index c427870..ff739c5 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 | ||
40 | namespace OpenSim.Region.ClientStack.LindenUDP | 40 | namespace 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: |
@@ -293,30 +283,42 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
293 | if (LandOutgoingPacketQueue.Count > 0) | 283 | if (LandOutgoingPacketQueue.Count > 0) |
294 | { | 284 | { |
295 | SendQueue.Enqueue(LandOutgoingPacketQueue.Dequeue()); | 285 | SendQueue.Enqueue(LandOutgoingPacketQueue.Dequeue()); |
286 | TriggerOnQueueEmpty(ThrottleOutPacketType.Land); | ||
296 | } | 287 | } |
297 | if (WindOutgoingPacketQueue.Count > 0) | 288 | if (WindOutgoingPacketQueue.Count > 0) |
298 | { | 289 | { |
299 | SendQueue.Enqueue(WindOutgoingPacketQueue.Dequeue()); | 290 | SendQueue.Enqueue(WindOutgoingPacketQueue.Dequeue()); |
291 | TriggerOnQueueEmpty(ThrottleOutPacketType.Wind); | ||
300 | } | 292 | } |
301 | if (CloudOutgoingPacketQueue.Count > 0) | 293 | if (CloudOutgoingPacketQueue.Count > 0) |
302 | { | 294 | { |
303 | SendQueue.Enqueue(CloudOutgoingPacketQueue.Dequeue()); | 295 | SendQueue.Enqueue(CloudOutgoingPacketQueue.Dequeue()); |
296 | TriggerOnQueueEmpty(ThrottleOutPacketType.Cloud); | ||
304 | } | 297 | } |
298 | bool tasksSent = false; | ||
305 | if (TaskOutgoingPacketQueue.Count > 0) | 299 | if (TaskOutgoingPacketQueue.Count > 0) |
306 | { | 300 | { |
301 | tasksSent = true; | ||
307 | SendQueue.PriorityEnqueue(TaskOutgoingPacketQueue.Dequeue()); | 302 | SendQueue.PriorityEnqueue(TaskOutgoingPacketQueue.Dequeue()); |
308 | } | 303 | } |
309 | if (TaskLowpriorityPacketQueue.Count > 0) | 304 | if (TaskLowpriorityPacketQueue.Count > 0) |
310 | { | 305 | { |
306 | tasksSent = true; | ||
311 | SendQueue.Enqueue(TaskLowpriorityPacketQueue.Dequeue()); | 307 | SendQueue.Enqueue(TaskLowpriorityPacketQueue.Dequeue()); |
312 | } | 308 | } |
309 | if (tasksSent) | ||
310 | { | ||
311 | TriggerOnQueueEmpty(ThrottleOutPacketType.Task); | ||
312 | } | ||
313 | if (TextureOutgoingPacketQueue.Count > 0) | 313 | if (TextureOutgoingPacketQueue.Count > 0) |
314 | { | 314 | { |
315 | SendQueue.Enqueue(TextureOutgoingPacketQueue.Dequeue()); | 315 | SendQueue.Enqueue(TextureOutgoingPacketQueue.Dequeue()); |
316 | TriggerOnQueueEmpty(ThrottleOutPacketType.Texture); | ||
316 | } | 317 | } |
317 | if (AssetOutgoingPacketQueue.Count > 0) | 318 | if (AssetOutgoingPacketQueue.Count > 0) |
318 | { | 319 | { |
319 | SendQueue.Enqueue(AssetOutgoingPacketQueue.Dequeue()); | 320 | SendQueue.Enqueue(AssetOutgoingPacketQueue.Dequeue()); |
321 | TriggerOnQueueEmpty(ThrottleOutPacketType.Asset); | ||
320 | } | 322 | } |
321 | } | 323 | } |
322 | // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets"); | 324 | // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets"); |
@@ -342,11 +344,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
342 | 344 | ||
343 | public void Close() | 345 | public void Close() |
344 | { | 346 | { |
347 | Dispose(); | ||
348 | } | ||
349 | |||
350 | public void Dispose() | ||
351 | { | ||
345 | Flush(); | 352 | Flush(); |
346 | WipeClean(); // I'm sure there's a dirty joke in here somewhere. -AFrisby | 353 | WipeClean(); // I'm sure there's a dirty joke in here somewhere. -AFrisby |
347 | 354 | ||
348 | m_enabled = false; | 355 | m_enabled = false; |
349 | throttleTimer.Stop(); | 356 | throttleTimer.Stop(); |
357 | throttleTimer.Close(); | ||
350 | 358 | ||
351 | if (StatsManager.SimExtraStats != null) | 359 | if (StatsManager.SimExtraStats != null) |
352 | { | 360 | { |
@@ -388,6 +396,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
388 | 396 | ||
389 | int MaxThrottleLoops = 4550; // 50*7 packets can be dequeued at once. | 397 | int MaxThrottleLoops = 4550; // 50*7 packets can be dequeued at once. |
390 | int throttleLoops = 0; | 398 | int throttleLoops = 0; |
399 | List<ThrottleOutPacketType> e; | ||
391 | 400 | ||
392 | // We're going to dequeue all of the saved up packets until | 401 | // 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 | 402 | // we've hit the throttle limit or there's no more packets to send |
@@ -399,7 +408,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
399 | bool qchanged = true; | 408 | bool qchanged = true; |
400 | 409 | ||
401 | ResetCounters(); | 410 | ResetCounters(); |
402 | // m_log.Info("[THROTTLE]: Entering Throttle"); | 411 | |
403 | while (TotalThrottle.UnderLimit() && qchanged && throttleLoops <= MaxThrottleLoops) | 412 | while (TotalThrottle.UnderLimit() && qchanged && throttleLoops <= MaxThrottleLoops) |
404 | { | 413 | { |
405 | qchanged = false; // We will break out of the loop if no work was accomplished | 414 | qchanged = false; // We will break out of the loop if no work was accomplished |
@@ -425,6 +434,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
425 | TotalThrottle.AddBytes(qpack.Length); | 434 | TotalThrottle.AddBytes(qpack.Length); |
426 | LandThrottle.AddBytes(qpack.Length); | 435 | LandThrottle.AddBytes(qpack.Length); |
427 | qchanged = true; | 436 | qchanged = true; |
437 | |||
438 | if (LandOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Land)) | ||
439 | Empty.Add(ThrottleOutPacketType.Land); | ||
428 | } | 440 | } |
429 | 441 | ||
430 | if ((WindOutgoingPacketQueue.Count > 0) && WindThrottle.UnderLimit()) | 442 | if ((WindOutgoingPacketQueue.Count > 0) && WindThrottle.UnderLimit()) |
@@ -435,6 +447,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
435 | TotalThrottle.AddBytes(qpack.Length); | 447 | TotalThrottle.AddBytes(qpack.Length); |
436 | WindThrottle.AddBytes(qpack.Length); | 448 | WindThrottle.AddBytes(qpack.Length); |
437 | qchanged = true; | 449 | qchanged = true; |
450 | |||
451 | if (WindOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Wind)) | ||
452 | Empty.Add(ThrottleOutPacketType.Wind); | ||
438 | } | 453 | } |
439 | 454 | ||
440 | if ((CloudOutgoingPacketQueue.Count > 0) && CloudThrottle.UnderLimit()) | 455 | if ((CloudOutgoingPacketQueue.Count > 0) && CloudThrottle.UnderLimit()) |
@@ -445,6 +460,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
445 | TotalThrottle.AddBytes(qpack.Length); | 460 | TotalThrottle.AddBytes(qpack.Length); |
446 | CloudThrottle.AddBytes(qpack.Length); | 461 | CloudThrottle.AddBytes(qpack.Length); |
447 | qchanged = true; | 462 | qchanged = true; |
463 | |||
464 | if (CloudOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Cloud)) | ||
465 | Empty.Add(ThrottleOutPacketType.Cloud); | ||
448 | } | 466 | } |
449 | 467 | ||
450 | if ((TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0) && TaskThrottle.UnderLimit()) | 468 | if ((TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0) && TaskThrottle.UnderLimit()) |
@@ -464,6 +482,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
464 | TotalThrottle.AddBytes(qpack.Length); | 482 | TotalThrottle.AddBytes(qpack.Length); |
465 | TaskThrottle.AddBytes(qpack.Length); | 483 | TaskThrottle.AddBytes(qpack.Length); |
466 | qchanged = true; | 484 | qchanged = true; |
485 | |||
486 | if (TaskOutgoingPacketQueue.Count == 0 && TaskLowpriorityPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Task)) | ||
487 | Empty.Add(ThrottleOutPacketType.Task); | ||
467 | } | 488 | } |
468 | 489 | ||
469 | if ((TextureOutgoingPacketQueue.Count > 0) && TextureThrottle.UnderLimit()) | 490 | if ((TextureOutgoingPacketQueue.Count > 0) && TextureThrottle.UnderLimit()) |
@@ -474,6 +495,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
474 | TotalThrottle.AddBytes(qpack.Length); | 495 | TotalThrottle.AddBytes(qpack.Length); |
475 | TextureThrottle.AddBytes(qpack.Length); | 496 | TextureThrottle.AddBytes(qpack.Length); |
476 | qchanged = true; | 497 | qchanged = true; |
498 | |||
499 | if (TextureOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Texture)) | ||
500 | Empty.Add(ThrottleOutPacketType.Texture); | ||
477 | } | 501 | } |
478 | 502 | ||
479 | if ((AssetOutgoingPacketQueue.Count > 0) && AssetThrottle.UnderLimit()) | 503 | if ((AssetOutgoingPacketQueue.Count > 0) && AssetThrottle.UnderLimit()) |
@@ -484,12 +508,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
484 | TotalThrottle.AddBytes(qpack.Length); | 508 | TotalThrottle.AddBytes(qpack.Length); |
485 | AssetThrottle.AddBytes(qpack.Length); | 509 | AssetThrottle.AddBytes(qpack.Length); |
486 | qchanged = true; | 510 | qchanged = true; |
511 | |||
512 | if (AssetOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Asset)) | ||
513 | Empty.Add(ThrottleOutPacketType.Asset); | ||
487 | } | 514 | } |
488 | } | 515 | } |
489 | // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets"); | 516 | // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets"); |
517 | |||
518 | e = new List<ThrottleOutPacketType>(Empty); | ||
519 | Empty.Clear(); | ||
520 | } | ||
521 | |||
522 | foreach (ThrottleOutPacketType t in e) | ||
523 | { | ||
524 | if (GetQueueCount(t) == 0) | ||
525 | TriggerOnQueueEmpty(t); | ||
490 | } | 526 | } |
491 | } | 527 | } |
492 | 528 | ||
529 | private void TriggerOnQueueEmpty(ThrottleOutPacketType queue) | ||
530 | { | ||
531 | QueueEmpty handlerQueueEmpty = OnQueueEmpty; | ||
532 | |||
533 | if (handlerQueueEmpty == null) | ||
534 | return; | ||
535 | |||
536 | handlerQueueEmpty(queue); | ||
537 | } | ||
538 | |||
493 | private void ThrottleTimerElapsed(object sender, ElapsedEventArgs e) | 539 | private void ThrottleTimerElapsed(object sender, ElapsedEventArgs e) |
494 | { | 540 | { |
495 | // just to change the signature, and that ProcessThrottle | 541 | // just to change the signature, and that ProcessThrottle |
@@ -497,7 +543,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
497 | ProcessThrottle(); | 543 | ProcessThrottle(); |
498 | } | 544 | } |
499 | 545 | ||
500 | private void ThrottleCheck(ref LLPacketThrottle throttle, ref Queue<LLQueItem> q, LLQueItem item) | 546 | private void ThrottleCheck(ref LLPacketThrottle throttle, ref Queue<LLQueItem> q, LLQueItem item, ThrottleOutPacketType itemType) |
501 | { | 547 | { |
502 | // The idea.. is if the packet throttle queues are empty | 548 | // The idea.. is if the packet throttle queues are empty |
503 | // and the client is under throttle for the type. Queue | 549 | // and the client is under throttle for the type. Queue |
@@ -513,6 +559,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
513 | throttle.AddBytes(item.Length); | 559 | throttle.AddBytes(item.Length); |
514 | TotalThrottle.AddBytes(item.Length); | 560 | TotalThrottle.AddBytes(item.Length); |
515 | SendQueue.Enqueue(item); | 561 | SendQueue.Enqueue(item); |
562 | lock (this) | ||
563 | { | ||
564 | if (!Empty.Contains(itemType)) | ||
565 | Empty.Add(itemType); | ||
566 | } | ||
516 | } | 567 | } |
517 | catch (Exception e) | 568 | catch (Exception e) |
518 | { | 569 | { |
@@ -698,5 +749,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
698 | { | 749 | { |
699 | get { return throttleMultiplier; } | 750 | get { return throttleMultiplier; } |
700 | } | 751 | } |
752 | |||
753 | public int GetQueueCount(ThrottleOutPacketType queue) | ||
754 | { | ||
755 | switch (queue) | ||
756 | { | ||
757 | case ThrottleOutPacketType.Land: | ||
758 | return LandOutgoingPacketQueue.Count; | ||
759 | case ThrottleOutPacketType.Wind: | ||
760 | return WindOutgoingPacketQueue.Count; | ||
761 | case ThrottleOutPacketType.Cloud: | ||
762 | return CloudOutgoingPacketQueue.Count; | ||
763 | case ThrottleOutPacketType.Task: | ||
764 | return TaskOutgoingPacketQueue.Count; | ||
765 | case ThrottleOutPacketType.Texture: | ||
766 | return TextureOutgoingPacketQueue.Count; | ||
767 | case ThrottleOutPacketType.Asset: | ||
768 | return AssetOutgoingPacketQueue.Count; | ||
769 | } | ||
770 | |||
771 | return 0; | ||
772 | } | ||
701 | } | 773 | } |
702 | } | 774 | } |