aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs742
1 files changed, 0 insertions, 742 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
deleted file mode 100644
index 3eed2e0..0000000
--- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
+++ /dev/null
@@ -1,742 +0,0 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Reflection;
31using System.Threading;
32using System.Timers;
33using log4net;
34using OpenMetaverse;
35using OpenSim.Framework;
36using OpenSim.Framework.Statistics;
37using OpenSim.Framework.Statistics.Interfaces;
38using Timer=System.Timers.Timer;
39
40namespace OpenSim.Region.ClientStack.LindenUDP
41{
42 public class LLPacketQueue : IPullStatsProvider, IDisposable
43 {
44 private static readonly ILog m_log
45 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46
47 /// <summary>
48 /// Is queueing enabled at all?
49 /// </summary>
50 private bool m_enabled = true;
51
52 private OpenSim.Framework.BlockingQueue<LLQueItem> SendQueue;
53
54 private Queue<LLQueItem> IncomingPacketQueue;
55 private Queue<LLQueItem> OutgoingPacketQueue;
56 private Queue<LLQueItem> ResendOutgoingPacketQueue;
57 private Queue<LLQueItem> LandOutgoingPacketQueue;
58 private Queue<LLQueItem> WindOutgoingPacketQueue;
59 private Queue<LLQueItem> CloudOutgoingPacketQueue;
60 private Queue<LLQueItem> TaskOutgoingPacketQueue;
61 private Queue<LLQueItem> TaskLowpriorityPacketQueue;
62 private Queue<LLQueItem> TextureOutgoingPacketQueue;
63 private Queue<LLQueItem> AssetOutgoingPacketQueue;
64
65 private List<ThrottleOutPacketType> Empty = new List<ThrottleOutPacketType>();
66 // m_log.Info("[THROTTLE]: Entering Throttle");
67 // private Dictionary<uint, uint> PendingAcks = new Dictionary<uint, uint>();
68 // private Dictionary<uint, Packet> NeedAck = new Dictionary<uint, Packet>();
69
70 // All throttle times and number of bytes are calculated by dividing by this value
71 // This value also determines how many times per throttletimems the timer will run
72 // If throttleimems is 1000 ms, then the timer will fire every 1000/7 milliseconds
73
74 private float throttleMultiplier = 2.0f; // Default value really doesn't matter.
75 private int throttleTimeDivisor = 7;
76
77 private int throttletimems = 1000;
78
79 internal LLPacketThrottle ResendThrottle;
80 internal LLPacketThrottle LandThrottle;
81 internal LLPacketThrottle WindThrottle;
82 internal LLPacketThrottle CloudThrottle;
83 internal LLPacketThrottle TaskThrottle;
84 internal LLPacketThrottle AssetThrottle;
85 internal LLPacketThrottle TextureThrottle;
86 internal LLPacketThrottle TotalThrottle;
87
88 private Dictionary<uint,int> contents = new Dictionary<uint, int>();
89
90 // private long LastThrottle;
91 // private long ThrottleInterval;
92 private Timer throttleTimer;
93
94 private UUID m_agentId;
95
96 public event QueueEmpty OnQueueEmpty;
97
98 public LLPacketQueue(UUID agentId, ClientStackUserSettings userSettings)
99 {
100 // While working on this, the BlockingQueue had me fooled for a bit.
101 // The Blocking queue causes the thread to stop until there's something
102 // in it to process. it's an on-purpose threadlock though because
103 // without it, the clientloop will suck up all sim resources.
104
105 SendQueue = new OpenSim.Framework.BlockingQueue<LLQueItem>();
106
107 IncomingPacketQueue = new Queue<LLQueItem>();
108 OutgoingPacketQueue = new Queue<LLQueItem>();
109 ResendOutgoingPacketQueue = new Queue<LLQueItem>();
110 LandOutgoingPacketQueue = new Queue<LLQueItem>();
111 WindOutgoingPacketQueue = new Queue<LLQueItem>();
112 CloudOutgoingPacketQueue = new Queue<LLQueItem>();
113 TaskOutgoingPacketQueue = new Queue<LLQueItem>();
114 TaskLowpriorityPacketQueue = new Queue<LLQueItem>();
115 TextureOutgoingPacketQueue = new Queue<LLQueItem>();
116 AssetOutgoingPacketQueue = new Queue<LLQueItem>();
117
118 // Store the throttle multiplier for posterity.
119 throttleMultiplier = userSettings.ClientThrottleMultipler;
120
121
122 int throttleMaxBPS = 1500000;
123 if (userSettings.TotalThrottleSettings != null)
124 throttleMaxBPS = userSettings.TotalThrottleSettings.Max;
125
126 // Set up the throttle classes (min, max, current) in bits per second
127 ResendThrottle = new LLPacketThrottle(5000, throttleMaxBPS / 15, 16000, userSettings.ClientThrottleMultipler);
128 LandThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 15, 2000, userSettings.ClientThrottleMultipler);
129 WindThrottle = new LLPacketThrottle(0, throttleMaxBPS / 15, 0, userSettings.ClientThrottleMultipler);
130 CloudThrottle = new LLPacketThrottle(0, throttleMaxBPS / 15, 0, userSettings.ClientThrottleMultipler);
131 TaskThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 3000, userSettings.ClientThrottleMultipler);
132 AssetThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 1000, userSettings.ClientThrottleMultipler);
133 TextureThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 4000, userSettings.ClientThrottleMultipler);
134
135
136 // Total Throttle trumps all - it is the number of bits in total that are allowed to go out per second.
137
138
139 ThrottleSettings totalThrottleSettings = userSettings.TotalThrottleSettings;
140 if (null == totalThrottleSettings)
141 {
142 totalThrottleSettings = new ThrottleSettings(0, throttleMaxBPS, 28000);
143 }
144
145 TotalThrottle
146 = new LLPacketThrottle(
147 totalThrottleSettings.Min, totalThrottleSettings.Max, totalThrottleSettings.Current,
148 userSettings.ClientThrottleMultipler);
149
150 throttleTimer = new Timer((int)(throttletimems / throttleTimeDivisor));
151 throttleTimer.Elapsed += ThrottleTimerElapsed;
152 throttleTimer.Start();
153
154 // TIMERS needed for this
155 // LastThrottle = DateTime.Now.Ticks;
156 // ThrottleInterval = (long)(throttletimems/throttleTimeDivisor);
157
158 m_agentId = agentId;
159
160 if (StatsManager.SimExtraStats != null)
161 {
162 StatsManager.SimExtraStats.RegisterPacketQueueStatsProvider(m_agentId, this);
163 }
164 }
165
166 /* STANDARD QUEUE MANIPULATION INTERFACES */
167
168 public void Enqueue(LLQueItem item)
169 {
170 if (!m_enabled)
171 {
172 return;
173 }
174 // We could micro lock, but that will tend to actually
175 // probably be worse than just synchronizing on SendQueue
176
177 if (item == null)
178 {
179 SendQueue.Enqueue(item);
180 return;
181 }
182
183 if (item.Incoming)
184 {
185 SendQueue.PriorityEnqueue(item);
186 return;
187 }
188
189 if (item.Sequence != 0)
190 lock (contents)
191 {
192 if (contents.ContainsKey(item.Sequence))
193 contents[item.Sequence] += 1;
194 else
195 contents.Add(item.Sequence, 1);
196 }
197
198 lock (this)
199 {
200 switch (item.throttleType & ThrottleOutPacketType.TypeMask)
201 {
202 case ThrottleOutPacketType.Resend:
203 ThrottleCheck(ref ResendThrottle, ref ResendOutgoingPacketQueue, item, ThrottleOutPacketType.Resend);
204 break;
205 case ThrottleOutPacketType.Texture:
206 ThrottleCheck(ref TextureThrottle, ref TextureOutgoingPacketQueue, item, ThrottleOutPacketType.Texture);
207 break;
208 case ThrottleOutPacketType.Task:
209 if ((item.throttleType & ThrottleOutPacketType.LowPriority) != 0)
210 ThrottleCheck(ref TaskThrottle, ref TaskLowpriorityPacketQueue, item, ThrottleOutPacketType.Task);
211 else
212 ThrottleCheck(ref TaskThrottle, ref TaskOutgoingPacketQueue, item, ThrottleOutPacketType.Task);
213 break;
214 case ThrottleOutPacketType.Land:
215 ThrottleCheck(ref LandThrottle, ref LandOutgoingPacketQueue, item, ThrottleOutPacketType.Land);
216 break;
217 case ThrottleOutPacketType.Asset:
218 ThrottleCheck(ref AssetThrottle, ref AssetOutgoingPacketQueue, item, ThrottleOutPacketType.Asset);
219 break;
220 case ThrottleOutPacketType.Cloud:
221 ThrottleCheck(ref CloudThrottle, ref CloudOutgoingPacketQueue, item, ThrottleOutPacketType.Cloud);
222 break;
223 case ThrottleOutPacketType.Wind:
224 ThrottleCheck(ref WindThrottle, ref WindOutgoingPacketQueue, item, ThrottleOutPacketType.Wind);
225 break;
226
227 default:
228 // Acknowledgements and other such stuff should go directly to the blocking Queue
229 // Throttling them may and likely 'will' be problematic
230 SendQueue.PriorityEnqueue(item);
231 break;
232 }
233 }
234 }
235
236 public LLQueItem Dequeue()
237 {
238 while (true)
239 {
240 LLQueItem item = SendQueue.Dequeue();
241 if (item == null)
242 return null;
243 if (item.Incoming)
244 return item;
245 item.TickCount = System.Environment.TickCount;
246 if (item.Sequence == 0)
247 return item;
248 lock (contents)
249 {
250 if (contents.ContainsKey(item.Sequence))
251 {
252 if (contents[item.Sequence] == 1)
253 contents.Remove(item.Sequence);
254 else
255 contents[item.Sequence] -= 1;
256 return item;
257 }
258 }
259 }
260 }
261
262 public void Cancel(uint sequence)
263 {
264 lock (contents) contents.Remove(sequence);
265 }
266
267 public bool Contains(uint sequence)
268 {
269 lock (contents) return contents.ContainsKey(sequence);
270 }
271
272 public void Flush()
273 {
274 lock (this)
275 {
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)
285 {
286 if (ResendOutgoingPacketQueue.Count > 0)
287 SendQueue.Enqueue(ResendOutgoingPacketQueue.Dequeue());
288
289 if (TaskOutgoingPacketQueue.Count > 0)
290 SendQueue.PriorityEnqueue(TaskOutgoingPacketQueue.Dequeue());
291
292 if (TaskLowpriorityPacketQueue.Count > 0)
293 SendQueue.Enqueue(TaskLowpriorityPacketQueue.Dequeue());
294 }
295 }
296 }
297
298 public void WipeClean()
299 {
300 lock (this)
301 {
302 ResendOutgoingPacketQueue.Clear();
303 LandOutgoingPacketQueue.Clear();
304 WindOutgoingPacketQueue.Clear();
305 CloudOutgoingPacketQueue.Clear();
306 TaskOutgoingPacketQueue.Clear();
307 TaskLowpriorityPacketQueue.Clear();
308 TextureOutgoingPacketQueue.Clear();
309 AssetOutgoingPacketQueue.Clear();
310 SendQueue.Clear();
311 lock (contents) contents.Clear();
312 }
313 }
314
315 public void Close()
316 {
317 Dispose();
318 }
319
320 public void Dispose()
321 {
322 Flush();
323 WipeClean(); // I'm sure there's a dirty joke in here somewhere. -AFrisby
324
325 m_enabled = false;
326 throttleTimer.Stop();
327 throttleTimer.Close();
328
329 if (StatsManager.SimExtraStats != null)
330 {
331 StatsManager.SimExtraStats.DeregisterPacketQueueStatsProvider(m_agentId);
332 }
333 }
334
335 private void ResetCounters()
336 {
337 ResendThrottle.Reset();
338 LandThrottle.Reset();
339 WindThrottle.Reset();
340 CloudThrottle.Reset();
341 TaskThrottle.Reset();
342 AssetThrottle.Reset();
343 TextureThrottle.Reset();
344 TotalThrottle.Reset();
345 }
346
347 private bool PacketsWaiting()
348 {
349 return (ResendOutgoingPacketQueue.Count > 0 ||
350 LandOutgoingPacketQueue.Count > 0 ||
351 WindOutgoingPacketQueue.Count > 0 ||
352 CloudOutgoingPacketQueue.Count > 0 ||
353 TaskOutgoingPacketQueue.Count > 0 ||
354 TaskLowpriorityPacketQueue.Count > 0 ||
355 AssetOutgoingPacketQueue.Count > 0 ||
356 TextureOutgoingPacketQueue.Count > 0);
357 }
358
359 public void ProcessThrottle()
360 {
361 // I was considering this.. Will an event fire if the thread it's on is blocked?
362
363 // Then I figured out.. it doesn't really matter.. because this thread won't be blocked for long
364 // The General overhead of the UDP protocol gets sent to the queue un-throttled by this
365 // so This'll pick up about around the right time.
366
367 int MaxThrottleLoops = 4550; // 50*7 packets can be dequeued at once.
368 int throttleLoops = 0;
369 List<ThrottleOutPacketType> e;
370
371 // We're going to dequeue all of the saved up packets until
372 // we've hit the throttle limit or there's no more packets to send
373 lock (this)
374 {
375 // this variable will be true if there was work done in the last execution of the
376 // loop, since each pass through the loop checks the queue length, we no longer
377 // need the check on entering the loop
378 bool qchanged = true;
379
380 ResetCounters();
381
382 while (TotalThrottle.UnderLimit() && qchanged && throttleLoops <= MaxThrottleLoops)
383 {
384 qchanged = false; // We will break out of the loop if no work was accomplished
385
386 throttleLoops++;
387 //Now comes the fun part.. we dump all our elements into m_packetQueue that we've saved up.
388 if ((ResendOutgoingPacketQueue.Count > 0) && ResendThrottle.UnderLimit())
389 {
390 LLQueItem qpack = ResendOutgoingPacketQueue.Dequeue();
391
392 SendQueue.Enqueue(qpack);
393 TotalThrottle.AddBytes(qpack.Length);
394 ResendThrottle.AddBytes(qpack.Length);
395
396 qchanged = true;
397 }
398
399 if ((LandOutgoingPacketQueue.Count > 0) && LandThrottle.UnderLimit())
400 {
401 LLQueItem qpack = LandOutgoingPacketQueue.Dequeue();
402
403 SendQueue.Enqueue(qpack);
404 TotalThrottle.AddBytes(qpack.Length);
405 LandThrottle.AddBytes(qpack.Length);
406 qchanged = true;
407
408 if (LandOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Land))
409 Empty.Add(ThrottleOutPacketType.Land);
410 }
411
412 if ((WindOutgoingPacketQueue.Count > 0) && WindThrottle.UnderLimit())
413 {
414 LLQueItem qpack = WindOutgoingPacketQueue.Dequeue();
415
416 SendQueue.Enqueue(qpack);
417 TotalThrottle.AddBytes(qpack.Length);
418 WindThrottle.AddBytes(qpack.Length);
419 qchanged = true;
420
421 if (WindOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Wind))
422 Empty.Add(ThrottleOutPacketType.Wind);
423 }
424
425 if ((CloudOutgoingPacketQueue.Count > 0) && CloudThrottle.UnderLimit())
426 {
427 LLQueItem qpack = CloudOutgoingPacketQueue.Dequeue();
428
429 SendQueue.Enqueue(qpack);
430 TotalThrottle.AddBytes(qpack.Length);
431 CloudThrottle.AddBytes(qpack.Length);
432 qchanged = true;
433
434 if (CloudOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Cloud))
435 Empty.Add(ThrottleOutPacketType.Cloud);
436 }
437
438 if ((TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0) && TaskThrottle.UnderLimit())
439 {
440 LLQueItem qpack;
441 if (TaskOutgoingPacketQueue.Count > 0)
442 {
443 qpack = TaskOutgoingPacketQueue.Dequeue();
444 SendQueue.PriorityEnqueue(qpack);
445 }
446 else
447 {
448 qpack = TaskLowpriorityPacketQueue.Dequeue();
449 SendQueue.Enqueue(qpack);
450 }
451
452 TotalThrottle.AddBytes(qpack.Length);
453 TaskThrottle.AddBytes(qpack.Length);
454 qchanged = true;
455
456 if (TaskOutgoingPacketQueue.Count == 0 && TaskLowpriorityPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Task))
457 Empty.Add(ThrottleOutPacketType.Task);
458 }
459
460 if ((TextureOutgoingPacketQueue.Count > 0) && TextureThrottle.UnderLimit())
461 {
462 LLQueItem qpack = TextureOutgoingPacketQueue.Dequeue();
463
464 SendQueue.Enqueue(qpack);
465 TotalThrottle.AddBytes(qpack.Length);
466 TextureThrottle.AddBytes(qpack.Length);
467 qchanged = true;
468
469 if (TextureOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Texture))
470 Empty.Add(ThrottleOutPacketType.Texture);
471 }
472
473 if ((AssetOutgoingPacketQueue.Count > 0) && AssetThrottle.UnderLimit())
474 {
475 LLQueItem qpack = AssetOutgoingPacketQueue.Dequeue();
476
477 SendQueue.Enqueue(qpack);
478 TotalThrottle.AddBytes(qpack.Length);
479 AssetThrottle.AddBytes(qpack.Length);
480 qchanged = true;
481
482 if (AssetOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Asset))
483 Empty.Add(ThrottleOutPacketType.Asset);
484 }
485 }
486 // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets");
487
488 e = new List<ThrottleOutPacketType>(Empty);
489 Empty.Clear();
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);
505 }
506
507 private void ThrottleTimerElapsed(object sender, ElapsedEventArgs e)
508 {
509 // just to change the signature, and that ProcessThrottle
510 // will be used elsewhere possibly
511 ProcessThrottle();
512 }
513
514 private void ThrottleCheck(ref LLPacketThrottle throttle, ref Queue<LLQueItem> q, LLQueItem item, ThrottleOutPacketType itemType)
515 {
516 // The idea.. is if the packet throttle queues are empty
517 // and the client is under throttle for the type. Queue
518 // it up directly. This basically short cuts having to
519 // wait for the timer to fire to put things into the
520 // output queue
521
522 if ((q.Count == 0) && (throttle.UnderLimit()))
523 {
524 try
525 {
526 Monitor.Enter(this);
527 throttle.AddBytes(item.Length);
528 TotalThrottle.AddBytes(item.Length);
529 SendQueue.Enqueue(item);
530 lock (this)
531 {
532 if (!Empty.Contains(itemType))
533 Empty.Add(itemType);
534 }
535 }
536 catch (Exception e)
537 {
538 // Probably a serialization exception
539 m_log.WarnFormat("ThrottleCheck: {0}", e.ToString());
540 }
541 finally
542 {
543 Monitor.Pulse(this);
544 Monitor.Exit(this);
545 }
546 }
547 else
548 {
549 q.Enqueue(item);
550 }
551 }
552
553 private static int ScaleThrottle(int value, int curmax, int newmax)
554 {
555 return (int)((value / (float)curmax) * newmax);
556 }
557
558 public byte[] GetThrottlesPacked(float multiplier)
559 {
560 int singlefloat = 4;
561 float tResend = ResendThrottle.Throttle*multiplier;
562 float tLand = LandThrottle.Throttle*multiplier;
563 float tWind = WindThrottle.Throttle*multiplier;
564 float tCloud = CloudThrottle.Throttle*multiplier;
565 float tTask = TaskThrottle.Throttle*multiplier;
566 float tTexture = TextureThrottle.Throttle*multiplier;
567 float tAsset = AssetThrottle.Throttle*multiplier;
568
569 byte[] throttles = new byte[singlefloat*7];
570 int i = 0;
571 Buffer.BlockCopy(BitConverter.GetBytes(tResend), 0, throttles, singlefloat*i, singlefloat);
572 i++;
573 Buffer.BlockCopy(BitConverter.GetBytes(tLand), 0, throttles, singlefloat*i, singlefloat);
574 i++;
575 Buffer.BlockCopy(BitConverter.GetBytes(tWind), 0, throttles, singlefloat*i, singlefloat);
576 i++;
577 Buffer.BlockCopy(BitConverter.GetBytes(tCloud), 0, throttles, singlefloat*i, singlefloat);
578 i++;
579 Buffer.BlockCopy(BitConverter.GetBytes(tTask), 0, throttles, singlefloat*i, singlefloat);
580 i++;
581 Buffer.BlockCopy(BitConverter.GetBytes(tTexture), 0, throttles, singlefloat*i, singlefloat);
582 i++;
583 Buffer.BlockCopy(BitConverter.GetBytes(tAsset), 0, throttles, singlefloat*i, singlefloat);
584
585 return throttles;
586 }
587
588 public void SetThrottleFromClient(byte[] throttle)
589 {
590 // From mantis http://opensimulator.org/mantis/view.php?id=1374
591 // it appears that sometimes we are receiving empty throttle byte arrays.
592 // TODO: Investigate this behaviour
593 if (throttle.Length == 0)
594 {
595 m_log.Warn("[PACKET QUEUE]: SetThrottleFromClient unexpectedly received a throttle byte array containing no elements!");
596 return;
597 }
598
599 int tResend = -1;
600 int tLand = -1;
601 int tWind = -1;
602 int tCloud = -1;
603 int tTask = -1;
604 int tTexture = -1;
605 int tAsset = -1;
606 int tall = -1;
607 int singlefloat = 4;
608
609 //Agent Throttle Block contains 7 single floatingpoint values.
610 int j = 0;
611
612 // Some Systems may be big endian...
613 // it might be smart to do this check more often...
614 if (!BitConverter.IsLittleEndian)
615 for (int i = 0; i < 7; i++)
616 Array.Reverse(throttle, j + i*singlefloat, singlefloat);
617
618 // values gotten from OpenMetaverse.org/wiki/Throttle. Thanks MW_
619 // bytes
620 // Convert to integer, since.. the full fp space isn't used.
621 tResend = (int) BitConverter.ToSingle(throttle, j);
622 j += singlefloat;
623 tLand = (int) BitConverter.ToSingle(throttle, j);
624 j += singlefloat;
625 tWind = (int) BitConverter.ToSingle(throttle, j);
626 j += singlefloat;
627 tCloud = (int) BitConverter.ToSingle(throttle, j);
628 j += singlefloat;
629 tTask = (int) BitConverter.ToSingle(throttle, j);
630 j += singlefloat;
631 tTexture = (int) BitConverter.ToSingle(throttle, j);
632 j += singlefloat;
633 tAsset = (int) BitConverter.ToSingle(throttle, j);
634
635 tall = tResend + tLand + tWind + tCloud + tTask + tTexture + tAsset;
636
637 /*
638 m_log.Info("[CLIENT]: Client AgentThrottle - Got throttle:resendbits=" + tResend +
639 " landbits=" + tLand +
640 " windbits=" + tWind +
641 " cloudbits=" + tCloud +
642 " taskbits=" + tTask +
643 " texturebits=" + tTexture +
644 " Assetbits=" + tAsset +
645 " Allbits=" + tall);
646 */
647
648
649 // Total Sanity
650 // Make sure that the client sent sane total values.
651
652 // If the client didn't send acceptable values....
653 // Scale the clients values down until they are acceptable.
654
655 if (tall <= TotalThrottle.Max)
656 {
657 ResendThrottle.Throttle = tResend;
658 LandThrottle.Throttle = tLand;
659 WindThrottle.Throttle = tWind;
660 CloudThrottle.Throttle = tCloud;
661 TaskThrottle.Throttle = tTask;
662 TextureThrottle.Throttle = tTexture;
663 AssetThrottle.Throttle = tAsset;
664 TotalThrottle.Throttle = tall;
665 }
666// else if (tall < 1)
667// {
668// // client is stupid, penalize him by minning everything
669// ResendThrottle.Throttle = ResendThrottle.Min;
670// LandThrottle.Throttle = LandThrottle.Min;
671// WindThrottle.Throttle = WindThrottle.Min;
672// CloudThrottle.Throttle = CloudThrottle.Min;
673// TaskThrottle.Throttle = TaskThrottle.Min;
674// TextureThrottle.Throttle = TextureThrottle.Min;
675// AssetThrottle.Throttle = AssetThrottle.Min;
676// TotalThrottle.Throttle = TotalThrottle.Min;
677// }
678 else
679 {
680 // we're over so figure out percentages and use those
681 ResendThrottle.Throttle = tResend;
682
683 LandThrottle.Throttle = ScaleThrottle(tLand, tall, TotalThrottle.Max);
684 WindThrottle.Throttle = ScaleThrottle(tWind, tall, TotalThrottle.Max);
685 CloudThrottle.Throttle = ScaleThrottle(tCloud, tall, TotalThrottle.Max);
686 TaskThrottle.Throttle = ScaleThrottle(tTask, tall, TotalThrottle.Max);
687 TextureThrottle.Throttle = ScaleThrottle(tTexture, tall, TotalThrottle.Max);
688 AssetThrottle.Throttle = ScaleThrottle(tAsset, tall, TotalThrottle.Max);
689 TotalThrottle.Throttle = TotalThrottle.Max;
690 }
691 // effectively wiggling the slider causes things reset
692// ResetCounters(); // DO NOT reset, better to send less for one period than more
693 }
694
695 // See IPullStatsProvider
696 public string GetStats()
697 {
698 return string.Format("{0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}",
699 SendQueue.Count(),
700 IncomingPacketQueue.Count,
701 OutgoingPacketQueue.Count,
702 ResendOutgoingPacketQueue.Count,
703 LandOutgoingPacketQueue.Count,
704 WindOutgoingPacketQueue.Count,
705 CloudOutgoingPacketQueue.Count,
706 TaskOutgoingPacketQueue.Count,
707 TextureOutgoingPacketQueue.Count,
708 AssetOutgoingPacketQueue.Count);
709 }
710
711 public LLQueItem[] GetQueueArray()
712 {
713 return SendQueue.GetQueueArray();
714 }
715
716 public float ThrottleMultiplier
717 {
718 get { return throttleMultiplier; }
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 }
741 }
742}