aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--OpenSim/Framework/IClientAPI.cs59
-rw-r--r--OpenSim/Framework/PriorityQueue.cs (renamed from OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs)43
-rw-r--r--OpenSim/Framework/Util.cs17
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs187
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs9
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs64
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs7
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs92
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs36
-rw-r--r--OpenSim/Region/Framework/Scenes/Prioritizer.cs4
10 files changed, 396 insertions, 122 deletions
diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs
index f573c32..069987b 100644
--- a/OpenSim/Framework/IClientAPI.cs
+++ b/OpenSim/Framework/IClientAPI.cs
@@ -572,34 +572,69 @@ namespace OpenSim.Framework
572 572
573 public class IEntityUpdate 573 public class IEntityUpdate
574 { 574 {
575 public ISceneEntity Entity; 575 private ISceneEntity m_entity;
576 public uint Flags; 576 private uint m_flags;
577 private int m_updateTime;
578
579 public ISceneEntity Entity
580 {
581 get { return m_entity; }
582 }
583
584 public uint Flags
585 {
586 get { return m_flags; }
587 }
588
589 public int UpdateTime
590 {
591 get { return m_updateTime; }
592 }
577 593
578 public virtual void Update(IEntityUpdate update) 594 public virtual void Update(IEntityUpdate update)
579 { 595 {
580 this.Flags |= update.Flags; 596 m_flags |= update.Flags;
597
598 // Use the older of the updates as the updateTime
599 if (Util.EnvironmentTickCountCompare(UpdateTime, update.UpdateTime) > 0)
600 m_updateTime = update.UpdateTime;
581 } 601 }
582 602
583 public IEntityUpdate(ISceneEntity entity, uint flags) 603 public IEntityUpdate(ISceneEntity entity, uint flags)
584 { 604 {
585 Entity = entity; 605 m_entity = entity;
586 Flags = flags; 606 m_flags = flags;
607 m_updateTime = Util.EnvironmentTickCount();
608 }
609
610 public IEntityUpdate(ISceneEntity entity, uint flags, Int32 updateTime)
611 {
612 m_entity = entity;
613 m_flags = flags;
614 m_updateTime = updateTime;
587 } 615 }
588 } 616 }
589
590 617
591 public class EntityUpdate : IEntityUpdate 618 public class EntityUpdate : IEntityUpdate
592 { 619 {
593 // public ISceneEntity Entity; 620 private float m_timeDilation;
594 // public PrimUpdateFlags Flags; 621
595 public float TimeDilation; 622 public float TimeDilation
623 {
624 get { return m_timeDilation; }
625 }
596 626
597 public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation) 627 public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation)
598 : base(entity,(uint)flags) 628 : base(entity, (uint)flags)
599 { 629 {
600 //Entity = entity;
601 // Flags = flags; 630 // Flags = flags;
602 TimeDilation = timedilation; 631 m_timeDilation = timedilation;
632 }
633
634 public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation, Int32 updateTime)
635 : base(entity,(uint)flags,updateTime)
636 {
637 m_timeDilation = timedilation;
603 } 638 }
604 } 639 }
605 640
diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Framework/PriorityQueue.cs
index b62ec07..eec2a92 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs
+++ b/OpenSim/Framework/PriorityQueue.cs
@@ -34,20 +34,21 @@ using OpenSim.Framework;
34using OpenSim.Framework.Client; 34using OpenSim.Framework.Client;
35using log4net; 35using log4net;
36 36
37namespace OpenSim.Region.ClientStack.LindenUDP 37namespace OpenSim.Framework
38{ 38{
39 public class PriorityQueue 39 public class PriorityQueue
40 { 40 {
41 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 41 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
42 42
43 internal delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity); 43 public delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity);
44 44
45 // Heap[0] for self updates 45 // Heap[0] for self updates
46 // Heap[1..12] for entity updates 46 // Heap[1..12] for entity updates
47 47
48 internal const uint m_numberOfQueues = 12; 48 public const uint NumberOfQueues = 12;
49 public const uint ImmediateQueue = 0;
49 50
50 private MinHeap<MinHeapItem>[] m_heaps = new MinHeap<MinHeapItem>[m_numberOfQueues]; 51 private MinHeap<MinHeapItem>[] m_heaps = new MinHeap<MinHeapItem>[NumberOfQueues];
51 private Dictionary<uint, LookupItem> m_lookupTable; 52 private Dictionary<uint, LookupItem> m_lookupTable;
52 private uint m_nextQueue = 0; 53 private uint m_nextQueue = 0;
53 private UInt64 m_nextRequest = 0; 54 private UInt64 m_nextRequest = 0;
@@ -57,9 +58,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
57 get { return this.m_syncRoot; } 58 get { return this.m_syncRoot; }
58 } 59 }
59 60
60 internal PriorityQueue() : this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY) { } 61 public PriorityQueue() : this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY) { }
61 62
62 internal PriorityQueue(int capacity) 63 public PriorityQueue(int capacity)
63 { 64 {
64 m_lookupTable = new Dictionary<uint, LookupItem>(capacity); 65 m_lookupTable = new Dictionary<uint, LookupItem>(capacity);
65 66
@@ -67,7 +68,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
67 m_heaps[i] = new MinHeap<MinHeapItem>(capacity); 68 m_heaps[i] = new MinHeap<MinHeapItem>(capacity);
68 } 69 }
69 70
70 internal int Count 71 public int Count
71 { 72 {
72 get 73 get
73 { 74 {
@@ -91,7 +92,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
91 lookup.Heap.Remove(lookup.Handle); 92 lookup.Heap.Remove(lookup.Handle);
92 } 93 }
93 94
94 pqueue = Util.Clamp<uint>(pqueue, 0, m_numberOfQueues - 1); 95 pqueue = Util.Clamp<uint>(pqueue, 0, NumberOfQueues - 1);
95 lookup.Heap = m_heaps[pqueue]; 96 lookup.Heap = m_heaps[pqueue];
96 lookup.Heap.Add(new MinHeapItem(pqueue, entry, value), ref lookup.Handle); 97 lookup.Heap.Add(new MinHeapItem(pqueue, entry, value), ref lookup.Handle);
97 m_lookupTable[localid] = lookup; 98 m_lookupTable[localid] = lookup;
@@ -99,18 +100,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP
99 return true; 100 return true;
100 } 101 }
101 102
102 internal bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue) 103 public bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue)
103 { 104 {
104 for (int i = 0; i < m_numberOfQueues; ++i) 105 // If there is anything in priority queue 0, return it first no
106 // matter what else. Breaks fairness. But very useful.
107 if (m_heaps[ImmediateQueue].Count > 0)
108 {
109 MinHeapItem item = m_heaps[ImmediateQueue].RemoveMin();
110 m_lookupTable.Remove(item.Value.Entity.LocalId);
111 timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime);
112 value = item.Value;
113
114 return true;
115 }
116
117 for (int i = 0; i < NumberOfQueues; ++i)
105 { 118 {
106 // To get the fair queing, we cycle through each of the 119 // To get the fair queing, we cycle through each of the
107 // queues when finding an element to dequeue, this code 120 // queues when finding an element to dequeue, this code
108 // assumes that the distribution of updates in the queues 121 // assumes that the distribution of updates in the queues
109 // is polynomial, probably quadractic (eg distance of PI * R^2) 122 // is polynomial, probably quadractic (eg distance of PI * R^2)
110 uint h = (uint)((m_nextQueue + i) % m_numberOfQueues); 123 uint h = (uint)((m_nextQueue + i) % NumberOfQueues);
111 if (m_heaps[h].Count > 0) 124 if (m_heaps[h].Count > 0)
112 { 125 {
113 m_nextQueue = (uint)((h + 1) % m_numberOfQueues); 126 m_nextQueue = (uint)((h + 1) % NumberOfQueues);
114 127
115 MinHeapItem item = m_heaps[h].RemoveMin(); 128 MinHeapItem item = m_heaps[h].RemoveMin();
116 m_lookupTable.Remove(item.Value.Entity.LocalId); 129 m_lookupTable.Remove(item.Value.Entity.LocalId);
@@ -126,7 +139,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
126 return false; 139 return false;
127 } 140 }
128 141
129 internal void Reprioritize(UpdatePriorityHandler handler) 142 public void Reprioritize(UpdatePriorityHandler handler)
130 { 143 {
131 MinHeapItem item; 144 MinHeapItem item;
132 foreach (LookupItem lookup in new List<LookupItem>(this.m_lookupTable.Values)) 145 foreach (LookupItem lookup in new List<LookupItem>(this.m_lookupTable.Values))
@@ -140,7 +153,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
140 { 153 {
141 // unless the priority queue has changed, there is no need to modify 154 // unless the priority queue has changed, there is no need to modify
142 // the entry 155 // the entry
143 pqueue = Util.Clamp<uint>(pqueue, 0, m_numberOfQueues - 1); 156 pqueue = Util.Clamp<uint>(pqueue, 0, NumberOfQueues - 1);
144 if (pqueue != item.PriorityQueue) 157 if (pqueue != item.PriorityQueue)
145 { 158 {
146 lookup.Heap.Remove(lookup.Handle); 159 lookup.Heap.Remove(lookup.Handle);
@@ -164,7 +177,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
164 public override string ToString() 177 public override string ToString()
165 { 178 {
166 string s = ""; 179 string s = "";
167 for (int i = 0; i < m_numberOfQueues; i++) 180 for (int i = 0; i < NumberOfQueues; i++)
168 { 181 {
169 if (s != "") s += ","; 182 if (s != "") s += ",";
170 s += m_heaps[i].Count.ToString(); 183 s += m_heaps[i].Count.ToString();
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs
index 5a5046e..aaa2724 100644
--- a/OpenSim/Framework/Util.cs
+++ b/OpenSim/Framework/Util.cs
@@ -1537,6 +1537,23 @@ namespace OpenSim.Framework
1537 return (diff >= 0) ? diff : (diff + EnvironmentTickCountMask + 1); 1537 return (diff >= 0) ? diff : (diff + EnvironmentTickCountMask + 1);
1538 } 1538 }
1539 1539
1540 // Returns value of Tick Count A - TickCount B accounting for wrapping of TickCount
1541 // Assumes both tcA and tcB came from previous calls to Util.EnvironmentTickCount().
1542 // A positive return value indicates A occured later than B
1543 public static Int32 EnvironmentTickCountCompare(Int32 tcA, Int32 tcB)
1544 {
1545 // A, B and TC are all between 0 and 0x3fffffff
1546 int tc = EnvironmentTickCount();
1547
1548 if (tc - tcA >= 0)
1549 tcA += EnvironmentTickCountMask + 1;
1550
1551 if (tc - tcB >= 0)
1552 tcB += EnvironmentTickCountMask + 1;
1553
1554 return tcA - tcB;
1555 }
1556
1540 /// <summary> 1557 /// <summary>
1541 /// Prints the call stack at any given point. Useful for debugging. 1558 /// Prints the call stack at any given point. Useful for debugging.
1542 /// </summary> 1559 /// </summary>
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 1f7e66d..6129e10 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -3561,6 +3561,44 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3561 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); 3561 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation));
3562 } 3562 }
3563 3563
3564 /// <summary>
3565 /// Requeue an EntityUpdate when it was not acknowledged by the client.
3566 /// We will update the priority and put it in the correct queue, merging update flags
3567 /// with any other updates that may be queued for the same entity.
3568 /// The original update time is used for the merged update.
3569 /// </summary>
3570 private void ResendPrimUpdate(EntityUpdate update)
3571 {
3572 // If the update exists in priority queue, it will be updated.
3573 // If it does not exist then it will be added with the current (rather than its original) priority
3574 uint priority = m_prioritizer.GetUpdatePriority(this, update.Entity);
3575
3576 lock (m_entityUpdates.SyncRoot)
3577 m_entityUpdates.Enqueue(priority, update);
3578 }
3579
3580 /// <summary>
3581 /// Requeue a list of EntityUpdates when they were not acknowledged by the client.
3582 /// We will update the priority and put it in the correct queue, merging update flags
3583 /// with any other updates that may be queued for the same entity.
3584 /// The original update time is used for the merged update.
3585 /// </summary>
3586 private void ResendPrimUpdates(List<EntityUpdate> updates, OutgoingPacket oPacket)
3587 {
3588 // m_log.WarnFormat("[CLIENT] resending prim update {0}",updates[0].UpdateTime);
3589
3590 // Remove the update packet from the list of packets waiting for acknowledgement
3591 // because we are requeuing the list of updates. They will be resent in new packets
3592 // with the most recent state and priority.
3593 m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber);
3594
3595 // Count this as a resent packet since we are going to requeue all of the updates contained in it
3596 Interlocked.Increment(ref m_udpClient.PacketsResent);
3597
3598 foreach (EntityUpdate update in updates)
3599 ResendPrimUpdate(update);
3600 }
3601
3564 private void ProcessEntityUpdates(int maxUpdates) 3602 private void ProcessEntityUpdates(int maxUpdates)
3565 { 3603 {
3566 OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); 3604 OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>();
@@ -3568,6 +3606,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3568 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); 3606 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
3569 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); 3607 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
3570 3608
3609 OpenSim.Framework.Lazy<List<EntityUpdate>> objectUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
3610 OpenSim.Framework.Lazy<List<EntityUpdate>> compressedUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
3611 OpenSim.Framework.Lazy<List<EntityUpdate>> terseUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
3612 OpenSim.Framework.Lazy<List<EntityUpdate>> terseAgentUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
3613
3571 // Check to see if this is a flush 3614 // Check to see if this is a flush
3572 if (maxUpdates <= 0) 3615 if (maxUpdates <= 0)
3573 { 3616 {
@@ -3583,7 +3626,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3583 float avgTimeDilation = 1.0f; 3626 float avgTimeDilation = 1.0f;
3584 IEntityUpdate iupdate; 3627 IEntityUpdate iupdate;
3585 Int32 timeinqueue; // this is just debugging code & can be dropped later 3628 Int32 timeinqueue; // this is just debugging code & can be dropped later
3586 3629
3587 while (updatesThisCall < maxUpdates) 3630 while (updatesThisCall < maxUpdates)
3588 { 3631 {
3589 lock (m_entityUpdates.SyncRoot) 3632 lock (m_entityUpdates.SyncRoot)
@@ -3688,24 +3731,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3688 if (update.Entity is ScenePresence) 3731 if (update.Entity is ScenePresence)
3689 { 3732 {
3690 objectUpdateBlocks.Value.Add(CreateAvatarUpdateBlock((ScenePresence)update.Entity)); 3733 objectUpdateBlocks.Value.Add(CreateAvatarUpdateBlock((ScenePresence)update.Entity));
3734 objectUpdates.Value.Add(update);
3691 } 3735 }
3692 else 3736 else
3693 { 3737 {
3694 objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId)); 3738 objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId));
3739 objectUpdates.Value.Add(update);
3695 } 3740 }
3696 } 3741 }
3697 else if (!canUseImproved) 3742 else if (!canUseImproved)
3698 { 3743 {
3699 compressedUpdateBlocks.Value.Add(CreateCompressedUpdateBlock((SceneObjectPart)update.Entity, updateFlags)); 3744 compressedUpdateBlocks.Value.Add(CreateCompressedUpdateBlock((SceneObjectPart)update.Entity, updateFlags));
3745 compressedUpdates.Value.Add(update);
3700 } 3746 }
3701 else 3747 else
3702 { 3748 {
3703 if (update.Entity is ScenePresence && ((ScenePresence)update.Entity).UUID == AgentId) 3749 if (update.Entity is ScenePresence && ((ScenePresence)update.Entity).UUID == AgentId)
3750 {
3704 // Self updates go into a special list 3751 // Self updates go into a special list
3705 terseAgentUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures))); 3752 terseAgentUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)));
3753 terseAgentUpdates.Value.Add(update);
3754 }
3706 else 3755 else
3756 {
3707 // Everything else goes here 3757 // Everything else goes here
3708 terseUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures))); 3758 terseUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)));
3759 terseUpdates.Value.Add(update);
3760 }
3709 } 3761 }
3710 3762
3711 #endregion Block Construction 3763 #endregion Block Construction
@@ -3713,28 +3765,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3713 3765
3714 3766
3715 #region Packet Sending 3767 #region Packet Sending
3716
3717 //const float TIME_DILATION = 1.0f;
3718
3719
3720 ushort timeDilation = Utils.FloatToUInt16(avgTimeDilation, 0.0f, 1.0f); 3768 ushort timeDilation = Utils.FloatToUInt16(avgTimeDilation, 0.0f, 1.0f);
3721 3769
3722 if (terseAgentUpdateBlocks.IsValueCreated) 3770 if (terseAgentUpdateBlocks.IsValueCreated)
3723 { 3771 {
3724 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseAgentUpdateBlocks.Value; 3772 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseAgentUpdateBlocks.Value;
3725 3773
3726 ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); 3774 ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket();
3727 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; 3775 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
3728 packet.RegionData.TimeDilation = timeDilation; 3776 packet.RegionData.TimeDilation = timeDilation;
3729 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; 3777 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];
3730 3778
3731 for (int i = 0; i < blocks.Count; i++) 3779 for (int i = 0; i < blocks.Count; i++)
3732 packet.ObjectData[i] = blocks[i]; 3780 packet.ObjectData[i] = blocks[i];
3733 3781 // If any of the packets created from this call go unacknowledged, all of the updates will be resent
3734 3782 OutPacket(packet, ThrottleOutPacketType.Unknown, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseAgentUpdates.Value, oPacket); });
3735 OutPacket(packet, ThrottleOutPacketType.Unknown, true);
3736 } 3783 }
3737 3784
3738 if (objectUpdateBlocks.IsValueCreated) 3785 if (objectUpdateBlocks.IsValueCreated)
3739 { 3786 {
3740 List<ObjectUpdatePacket.ObjectDataBlock> blocks = objectUpdateBlocks.Value; 3787 List<ObjectUpdatePacket.ObjectDataBlock> blocks = objectUpdateBlocks.Value;
@@ -3746,8 +3793,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3746 3793
3747 for (int i = 0; i < blocks.Count; i++) 3794 for (int i = 0; i < blocks.Count; i++)
3748 packet.ObjectData[i] = blocks[i]; 3795 packet.ObjectData[i] = blocks[i];
3749 3796 // If any of the packets created from this call go unacknowledged, all of the updates will be resent
3750 OutPacket(packet, ThrottleOutPacketType.Task, true); 3797 OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(objectUpdates.Value, oPacket); });
3751 } 3798 }
3752 3799
3753 if (compressedUpdateBlocks.IsValueCreated) 3800 if (compressedUpdateBlocks.IsValueCreated)
@@ -3761,10 +3808,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3761 3808
3762 for (int i = 0; i < blocks.Count; i++) 3809 for (int i = 0; i < blocks.Count; i++)
3763 packet.ObjectData[i] = blocks[i]; 3810 packet.ObjectData[i] = blocks[i];
3764 3811 // If any of the packets created from this call go unacknowledged, all of the updates will be resent
3765 OutPacket(packet, ThrottleOutPacketType.Task, true); 3812 OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(compressedUpdates.Value, oPacket); });
3766 } 3813 }
3767 3814
3768 if (terseUpdateBlocks.IsValueCreated) 3815 if (terseUpdateBlocks.IsValueCreated)
3769 { 3816 {
3770 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value; 3817 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value;
@@ -3776,8 +3823,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3776 3823
3777 for (int i = 0; i < blocks.Count; i++) 3824 for (int i = 0; i < blocks.Count; i++)
3778 packet.ObjectData[i] = blocks[i]; 3825 packet.ObjectData[i] = blocks[i];
3779 3826 // If any of the packets created from this call go unacknowledged, all of the updates will be resent
3780 OutPacket(packet, ThrottleOutPacketType.Task, true); 3827 OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseUpdates.Value, oPacket); });
3781 } 3828 }
3782 } 3829 }
3783 3830
@@ -3969,7 +4016,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3969 { 4016 {
3970 SendFamilyProps = SendFamilyProps || update.SendFamilyProps; 4017 SendFamilyProps = SendFamilyProps || update.SendFamilyProps;
3971 SendObjectProps = SendObjectProps || update.SendObjectProps; 4018 SendObjectProps = SendObjectProps || update.SendObjectProps;
3972 Flags |= update.Flags; 4019 // other properties may need to be updated by base class
4020 base.Update(update);
3973 } 4021 }
3974 } 4022 }
3975 4023
@@ -3980,6 +4028,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3980 m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false)); 4028 m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false));
3981 } 4029 }
3982 4030
4031 private void ResendPropertyUpdate(ObjectPropertyUpdate update)
4032 {
4033 uint priority = 0;
4034 lock (m_entityProps.SyncRoot)
4035 m_entityProps.Enqueue(priority, update);
4036 }
4037
4038 private void ResendPropertyUpdates(List<ObjectPropertyUpdate> updates, OutgoingPacket oPacket)
4039 {
4040 // m_log.WarnFormat("[CLIENT] resending object property {0}",updates[0].UpdateTime);
4041
4042 // Remove the update packet from the list of packets waiting for acknowledgement
4043 // because we are requeuing the list of updates. They will be resent in new packets
4044 // with the most recent state.
4045 m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber);
4046
4047 // Count this as a resent packet since we are going to requeue all of the updates contained in it
4048 Interlocked.Increment(ref m_udpClient.PacketsResent);
4049
4050 foreach (ObjectPropertyUpdate update in updates)
4051 ResendPropertyUpdate(update);
4052 }
4053
3983 public void SendObjectPropertiesReply(ISceneEntity entity) 4054 public void SendObjectPropertiesReply(ISceneEntity entity)
3984 { 4055 {
3985 uint priority = 0; // time based ordering only 4056 uint priority = 0; // time based ordering only
@@ -3995,6 +4066,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3995 OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>> objectPropertiesBlocks = 4066 OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>> objectPropertiesBlocks =
3996 new OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>>(); 4067 new OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>>();
3997 4068
4069 OpenSim.Framework.Lazy<List<ObjectPropertyUpdate>> familyUpdates =
4070 new OpenSim.Framework.Lazy<List<ObjectPropertyUpdate>>();
4071
4072 OpenSim.Framework.Lazy<List<ObjectPropertyUpdate>> propertyUpdates =
4073 new OpenSim.Framework.Lazy<List<ObjectPropertyUpdate>>();
4074
3998 IEntityUpdate iupdate; 4075 IEntityUpdate iupdate;
3999 Int32 timeinqueue; // this is just debugging code & can be dropped later 4076 Int32 timeinqueue; // this is just debugging code & can be dropped later
4000 4077
@@ -4013,6 +4090,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4013 SceneObjectPart sop = (SceneObjectPart)update.Entity; 4090 SceneObjectPart sop = (SceneObjectPart)update.Entity;
4014 ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags); 4091 ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags);
4015 objectFamilyBlocks.Value.Add(objPropDB); 4092 objectFamilyBlocks.Value.Add(objPropDB);
4093 familyUpdates.Value.Add(update);
4016 } 4094 }
4017 } 4095 }
4018 4096
@@ -4023,6 +4101,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4023 SceneObjectPart sop = (SceneObjectPart)update.Entity; 4101 SceneObjectPart sop = (SceneObjectPart)update.Entity;
4024 ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop); 4102 ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop);
4025 objectPropertiesBlocks.Value.Add(objPropDB); 4103 objectPropertiesBlocks.Value.Add(objPropDB);
4104 propertyUpdates.Value.Add(update);
4026 } 4105 }
4027 } 4106 }
4028 4107
@@ -4030,12 +4109,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4030 } 4109 }
4031 4110
4032 4111
4033 Int32 ppcnt = 0; 4112 // Int32 ppcnt = 0;
4034 Int32 pbcnt = 0; 4113 // Int32 pbcnt = 0;
4035 4114
4036 if (objectPropertiesBlocks.IsValueCreated) 4115 if (objectPropertiesBlocks.IsValueCreated)
4037 { 4116 {
4038 List<ObjectPropertiesPacket.ObjectDataBlock> blocks = objectPropertiesBlocks.Value; 4117 List<ObjectPropertiesPacket.ObjectDataBlock> blocks = objectPropertiesBlocks.Value;
4118 List<ObjectPropertyUpdate> updates = propertyUpdates.Value;
4039 4119
4040 ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); 4120 ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties);
4041 packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count]; 4121 packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count];
@@ -4043,28 +4123,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4043 packet.ObjectData[i] = blocks[i]; 4123 packet.ObjectData[i] = blocks[i];
4044 4124
4045 packet.Header.Zerocoded = true; 4125 packet.Header.Zerocoded = true;
4046 OutPacket(packet, ThrottleOutPacketType.Task, true);
4047 4126
4048 pbcnt += blocks.Count; 4127 // Pass in the delegate so that if this packet needs to be resent, we send the current properties
4049 ppcnt++; 4128 // of the object rather than the properties when the packet was created
4129 OutPacket(packet, ThrottleOutPacketType.Task, true,
4130 delegate(OutgoingPacket oPacket)
4131 {
4132 ResendPropertyUpdates(updates, oPacket);
4133 });
4134
4135 // pbcnt += blocks.Count;
4136 // ppcnt++;
4050 } 4137 }
4051 4138
4052 Int32 fpcnt = 0; 4139 // Int32 fpcnt = 0;
4053 Int32 fbcnt = 0; 4140 // Int32 fbcnt = 0;
4054 4141
4055 if (objectFamilyBlocks.IsValueCreated) 4142 if (objectFamilyBlocks.IsValueCreated)
4056 { 4143 {
4057 List<ObjectPropertiesFamilyPacket.ObjectDataBlock> blocks = objectFamilyBlocks.Value; 4144 List<ObjectPropertiesFamilyPacket.ObjectDataBlock> blocks = objectFamilyBlocks.Value;
4058 4145
4059 // ObjectPropertiesFamilyPacket objPropFamilyPack =
4060 // (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily);
4061 //
4062 // objPropFamilyPack.ObjectData = new ObjectPropertiesFamilyPacket.ObjectDataBlock[blocks.Count];
4063 // for (int i = 0; i < blocks.Count; i++)
4064 // objPropFamilyPack.ObjectData[i] = blocks[i];
4065 //
4066 // OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task, true);
4067
4068 // one packet per object block... uggh... 4146 // one packet per object block... uggh...
4069 for (int i = 0; i < blocks.Count; i++) 4147 for (int i = 0; i < blocks.Count; i++)
4070 { 4148 {
@@ -4073,10 +4151,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4073 4151
4074 packet.ObjectData = blocks[i]; 4152 packet.ObjectData = blocks[i];
4075 packet.Header.Zerocoded = true; 4153 packet.Header.Zerocoded = true;
4076 OutPacket(packet, ThrottleOutPacketType.Task);
4077 4154
4078 fpcnt++; 4155 // Pass in the delegate so that if this packet needs to be resent, we send the current properties
4079 fbcnt++; 4156 // of the object rather than the properties when the packet was created
4157 List<ObjectPropertyUpdate> updates = new List<ObjectPropertyUpdate>();
4158 updates.Add(familyUpdates.Value[i]);
4159 OutPacket(packet, ThrottleOutPacketType.Task, true,
4160 delegate(OutgoingPacket oPacket)
4161 {
4162 ResendPropertyUpdates(updates, oPacket);
4163 });
4164
4165 // fpcnt++;
4166 // fbcnt++;
4080 } 4167 }
4081 4168
4082 } 4169 }
@@ -4113,7 +4200,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4113 4200
4114 return block; 4201 return block;
4115 } 4202 }
4116 4203
4117 private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop) 4204 private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop)
4118 { 4205 {
4119 //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); 4206 //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties);
@@ -11363,6 +11450,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11363 /// handles splitting manually</param> 11450 /// handles splitting manually</param>
11364 protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting) 11451 protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting)
11365 { 11452 {
11453 OutPacket(packet, throttlePacketType, doAutomaticSplitting, null);
11454 }
11455
11456 /// <summary>
11457 /// This is the starting point for sending a simulator packet out to the client
11458 /// </summary>
11459 /// <param name="packet">Packet to send</param>
11460 /// <param name="throttlePacketType">Throttling category for the packet</param>
11461 /// <param name="doAutomaticSplitting">True to automatically split oversized
11462 /// packets (the default), or false to disable splitting if the calling code
11463 /// handles splitting manually</param>
11464 /// <param name="method">The method to be called in the event this packet is reliable
11465 /// and unacknowledged. The server will provide normal resend capability if you do not
11466 /// provide your own method.</param>
11467 protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting, UnackedPacketMethod method)
11468 {
11366 if (m_debugPacketLevel > 0) 11469 if (m_debugPacketLevel > 0)
11367 { 11470 {
11368 bool logPacket = true; 11471 bool logPacket = true;
@@ -11388,7 +11491,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11388 m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); 11491 m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type);
11389 } 11492 }
11390 11493
11391 m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); 11494 m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting, method);
11392 } 11495 }
11393 11496
11394 public bool AddMoney(int debit) 11497 public bool AddMoney(int debit)
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
index 7be8a0a..20bfec8 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
@@ -135,7 +135,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
135 private int m_nextOnQueueEmpty = 1; 135 private int m_nextOnQueueEmpty = 1;
136 136
137 /// <summary>Throttle bucket for this agent's connection</summary> 137 /// <summary>Throttle bucket for this agent's connection</summary>
138 private readonly TokenBucket m_throttleClient; 138 private readonly AdaptiveTokenBucket m_throttleClient;
139 public AdaptiveTokenBucket FlowThrottle
140 {
141 get { return m_throttleClient; }
142 }
143
139 /// <summary>Throttle bucket for this agent's connection</summary> 144 /// <summary>Throttle bucket for this agent's connection</summary>
140 private readonly TokenBucket m_throttleCategory; 145 private readonly TokenBucket m_throttleCategory;
141 /// <summary>Throttle buckets for each packet category</summary> 146 /// <summary>Throttle buckets for each packet category</summary>
@@ -176,7 +181,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
176 m_maxRTO = maxRTO; 181 m_maxRTO = maxRTO;
177 182
178 // Create a token bucket throttle for this client that has the scene token bucket as a parent 183 // Create a token bucket throttle for this client that has the scene token bucket as a parent
179 m_throttleClient = new TokenBucket(parentThrottle, rates.TotalLimit); 184 m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.TotalLimit);
180 // Create a token bucket throttle for the total categary with the client bucket as a throttle 185 // Create a token bucket throttle for the total categary with the client bucket as a throttle
181 m_throttleCategory = new TokenBucket(m_throttleClient, rates.TotalLimit); 186 m_throttleCategory = new TokenBucket(m_throttleClient, rates.TotalLimit);
182 // Create an array of token buckets for this clients different throttle categories 187 // Create an array of token buckets for this clients different throttle categories
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
index d08b25f..bd58ddc 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
@@ -297,7 +297,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
297 delegate(IClientAPI client) 297 delegate(IClientAPI client)
298 { 298 {
299 if (client is LLClientView) 299 if (client is LLClientView)
300 SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category); 300 SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null);
301 } 301 }
302 ); 302 );
303 } 303 }
@@ -309,7 +309,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
309 delegate(IClientAPI client) 309 delegate(IClientAPI client)
310 { 310 {
311 if (client is LLClientView) 311 if (client is LLClientView)
312 SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category); 312 SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null);
313 } 313 }
314 ); 314 );
315 } 315 }
@@ -322,7 +322,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
322 /// <param name="packet"></param> 322 /// <param name="packet"></param>
323 /// <param name="category"></param> 323 /// <param name="category"></param>
324 /// <param name="allowSplitting"></param> 324 /// <param name="allowSplitting"></param>
325 public void SendPacket(LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting) 325 public void SendPacket(LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting, UnackedPacketMethod method)
326 { 326 {
327 // CoarseLocationUpdate packets cannot be split in an automated way 327 // CoarseLocationUpdate packets cannot be split in an automated way
328 if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting) 328 if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting)
@@ -339,13 +339,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
339 for (int i = 0; i < packetCount; i++) 339 for (int i = 0; i < packetCount; i++)
340 { 340 {
341 byte[] data = datas[i]; 341 byte[] data = datas[i];
342 SendPacketData(udpClient, data, packet.Type, category); 342 SendPacketData(udpClient, data, packet.Type, category, method);
343 } 343 }
344 } 344 }
345 else 345 else
346 { 346 {
347 byte[] data = packet.ToBytes(); 347 byte[] data = packet.ToBytes();
348 SendPacketData(udpClient, data, packet.Type, category); 348 SendPacketData(udpClient, data, packet.Type, category, method);
349 } 349 }
350 } 350 }
351 351
@@ -356,7 +356,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
356 /// <param name="data"></param> 356 /// <param name="data"></param>
357 /// <param name="type"></param> 357 /// <param name="type"></param>
358 /// <param name="category"></param> 358 /// <param name="category"></param>
359 public void SendPacketData(LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category) 359 public void SendPacketData(LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category, UnackedPacketMethod method)
360 { 360 {
361 int dataLength = data.Length; 361 int dataLength = data.Length;
362 bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; 362 bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0;
@@ -411,7 +411,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
411 411
412 #region Queue or Send 412 #region Queue or Send
413 413
414 OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category); 414 OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category, null);
415 // If we were not provided a method for handling unacked, use the UDPServer default method
416 outgoingPacket.UnackedMethod = ((method == null) ? delegate(OutgoingPacket oPacket) { ResendUnacked(oPacket); } : method);
415 417
416 // If a Linden Lab 1.23.5 client receives an update packet after a kill packet for an object, it will 418 // If a Linden Lab 1.23.5 client receives an update packet after a kill packet for an object, it will
417 // continue to display the deleted object until relog. Therefore, we need to always queue a kill object 419 // continue to display the deleted object until relog. Therefore, we need to always queue a kill object
@@ -445,7 +447,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
445 packet.Header.Reliable = false; 447 packet.Header.Reliable = false;
446 packet.Packets = blocks.ToArray(); 448 packet.Packets = blocks.ToArray();
447 449
448 SendPacket(udpClient, packet, ThrottleOutPacketType.Unknown, true); 450 SendPacket(udpClient, packet, ThrottleOutPacketType.Unknown, true, null);
449 } 451 }
450 } 452 }
451 453
@@ -458,17 +460,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
458 // We *could* get OldestUnacked, but it would hurt performance and not provide any benefit 460 // We *could* get OldestUnacked, but it would hurt performance and not provide any benefit
459 pc.PingID.OldestUnacked = 0; 461 pc.PingID.OldestUnacked = 0;
460 462
461 SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false); 463 SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false, null);
462 } 464 }
463 465
464 public void CompletePing(LLUDPClient udpClient, byte pingID) 466 public void CompletePing(LLUDPClient udpClient, byte pingID)
465 { 467 {
466 CompletePingCheckPacket completePing = new CompletePingCheckPacket(); 468 CompletePingCheckPacket completePing = new CompletePingCheckPacket();
467 completePing.PingID.PingID = pingID; 469 completePing.PingID.PingID = pingID;
468 SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false); 470 SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false, null);
469 } 471 }
470 472
471 public void ResendUnacked(LLUDPClient udpClient) 473 public void HandleUnacked(LLUDPClient udpClient)
472 { 474 {
473 if (!udpClient.IsConnected) 475 if (!udpClient.IsConnected)
474 return; 476 return;
@@ -488,31 +490,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
488 490
489 if (expiredPackets != null) 491 if (expiredPackets != null)
490 { 492 {
491 //m_log.Debug("[LLUDPSERVER]: Resending " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO); 493 //m_log.Debug("[LLUDPSERVER]: Handling " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO);
492
493 // Exponential backoff of the retransmission timeout 494 // Exponential backoff of the retransmission timeout
494 udpClient.BackoffRTO(); 495 udpClient.BackoffRTO();
496 for (int i = 0; i < expiredPackets.Count; ++i)
497 expiredPackets[i].UnackedMethod(expiredPackets[i]);
498 }
499 }
495 500
496 // Resend packets 501 public void ResendUnacked(OutgoingPacket outgoingPacket)
497 for (int i = 0; i < expiredPackets.Count; i++) 502 {
498 { 503 //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed",
499 OutgoingPacket outgoingPacket = expiredPackets[i]; 504 // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount);
500
501 //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed",
502 // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount);
503 505
504 // Set the resent flag 506 // Set the resent flag
505 outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); 507 outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
506 outgoingPacket.Category = ThrottleOutPacketType.Resend; 508 outgoingPacket.Category = ThrottleOutPacketType.Resend;
507 509
508 // Bump up the resend count on this packet 510 // Bump up the resend count on this packet
509 Interlocked.Increment(ref outgoingPacket.ResendCount); 511 Interlocked.Increment(ref outgoingPacket.ResendCount);
510 512
511 // Requeue or resend the packet 513 // Requeue or resend the packet
512 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, false)) 514 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, false))
513 SendPacketFinal(outgoingPacket); 515 SendPacketFinal(outgoingPacket);
514 }
515 }
516 } 516 }
517 517
518 public void Flush(LLUDPClient udpClient) 518 public void Flush(LLUDPClient udpClient)
@@ -1096,7 +1096,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1096 if (udpClient.IsConnected) 1096 if (udpClient.IsConnected)
1097 { 1097 {
1098 if (m_resendUnacked) 1098 if (m_resendUnacked)
1099 ResendUnacked(udpClient); 1099 HandleUnacked(udpClient);
1100 1100
1101 if (m_sendAcks) 1101 if (m_sendAcks)
1102 SendAcks(udpClient); 1102 SendAcks(udpClient);
@@ -1152,7 +1152,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1152 nticksUnack++; 1152 nticksUnack++;
1153 watch2.Start(); 1153 watch2.Start();
1154 1154
1155 ResendUnacked(udpClient); 1155 HandleUnacked(udpClient);
1156 1156
1157 watch2.Stop(); 1157 watch2.Stop();
1158 avgResendUnackedTicks = (nticksUnack - 1)/(float)nticksUnack * avgResendUnackedTicks + (watch2.ElapsedTicks / (float)nticksUnack); 1158 avgResendUnackedTicks = (nticksUnack - 1)/(float)nticksUnack * avgResendUnackedTicks + (watch2.ElapsedTicks / (float)nticksUnack);
diff --git a/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs b/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs
index 1a1a1cb..76c6c14 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs
@@ -31,6 +31,8 @@ using OpenMetaverse;
31 31
32namespace OpenSim.Region.ClientStack.LindenUDP 32namespace OpenSim.Region.ClientStack.LindenUDP
33{ 33{
34
35 public delegate void UnackedPacketMethod(OutgoingPacket oPacket);
34 /// <summary> 36 /// <summary>
35 /// Holds a reference to the <seealso cref="LLUDPClient"/> this packet is 37 /// Holds a reference to the <seealso cref="LLUDPClient"/> this packet is
36 /// destined for, along with the serialized packet data, sequence number 38 /// destined for, along with the serialized packet data, sequence number
@@ -52,6 +54,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
52 public int TickCount; 54 public int TickCount;
53 /// <summary>Category this packet belongs to</summary> 55 /// <summary>Category this packet belongs to</summary>
54 public ThrottleOutPacketType Category; 56 public ThrottleOutPacketType Category;
57 /// <summary>The delegate to be called if this packet is determined to be unacknowledged</summary>
58 public UnackedPacketMethod UnackedMethod;
55 59
56 /// <summary> 60 /// <summary>
57 /// Default constructor 61 /// Default constructor
@@ -60,11 +64,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
60 /// <param name="buffer">Serialized packet data. If the flags or sequence number 64 /// <param name="buffer">Serialized packet data. If the flags or sequence number
61 /// need to be updated, they will be injected directly into this binary buffer</param> 65 /// need to be updated, they will be injected directly into this binary buffer</param>
62 /// <param name="category">Throttling category for this packet</param> 66 /// <param name="category">Throttling category for this packet</param>
63 public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category) 67 public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category, UnackedPacketMethod method)
64 { 68 {
65 Client = client; 69 Client = client;
66 Buffer = buffer; 70 Buffer = buffer;
67 Category = category; 71 Category = category;
72 UnackedMethod = method;
68 } 73 }
69 } 74 }
70} 75}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
index 07b0a1d..4ee6d3a 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
@@ -48,31 +48,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP
48 /// Number of ticks (ms) per quantum, drip rate and max burst 48 /// Number of ticks (ms) per quantum, drip rate and max burst
49 /// are defined over this interval. 49 /// are defined over this interval.
50 /// </summary> 50 /// </summary>
51 private const Int32 m_ticksPerQuantum = 1000; 51 protected const Int32 m_ticksPerQuantum = 1000;
52 52
53 /// <summary> 53 /// <summary>
54 /// This is the number of quantums worth of packets that can 54 /// This is the number of quantums worth of packets that can
55 /// be accommodated during a burst 55 /// be accommodated during a burst
56 /// </summary> 56 /// </summary>
57 private const Double m_quantumsPerBurst = 1.5; 57 protected const Double m_quantumsPerBurst = 1.5;
58 58
59 /// <summary> 59 /// <summary>
60 /// </summary> 60 /// </summary>
61 private const Int32 m_minimumDripRate = 1400; 61 protected const Int32 m_minimumDripRate = 1400;
62 62
63 /// <summary>Time of the last drip, in system ticks</summary> 63 /// <summary>Time of the last drip, in system ticks</summary>
64 private Int32 m_lastDrip; 64 protected Int32 m_lastDrip;
65 65
66 /// <summary> 66 /// <summary>
67 /// The number of bytes that can be sent at this moment. This is the 67 /// The number of bytes that can be sent at this moment. This is the
68 /// current number of tokens in the bucket 68 /// current number of tokens in the bucket
69 /// </summary> 69 /// </summary>
70 private Int64 m_tokenCount; 70 protected Int64 m_tokenCount;
71 71
72 /// <summary> 72 /// <summary>
73 /// Map of children buckets and their requested maximum burst rate 73 /// Map of children buckets and their requested maximum burst rate
74 /// </summary> 74 /// </summary>
75 private Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>(); 75 protected Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>();
76 76
77#region Properties 77#region Properties
78 78
@@ -81,7 +81,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
81 /// parent. The parent bucket will limit the aggregate bandwidth of all 81 /// parent. The parent bucket will limit the aggregate bandwidth of all
82 /// of its children buckets 82 /// of its children buckets
83 /// </summary> 83 /// </summary>
84 private TokenBucket m_parent; 84 protected TokenBucket m_parent;
85 public TokenBucket Parent 85 public TokenBucket Parent
86 { 86 {
87 get { return m_parent; } 87 get { return m_parent; }
@@ -93,7 +93,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
93 /// of tokens that can accumulate in the bucket at any one time. This 93 /// of tokens that can accumulate in the bucket at any one time. This
94 /// also sets the total request for leaf nodes 94 /// also sets the total request for leaf nodes
95 /// </summary> 95 /// </summary>
96 private Int64 m_burstRate; 96 protected Int64 m_burstRate;
97 public Int64 RequestedBurstRate 97 public Int64 RequestedBurstRate
98 { 98 {
99 get { return m_burstRate; } 99 get { return m_burstRate; }
@@ -118,8 +118,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
118 /// <remarks>Tokens are added to the bucket any time 118 /// <remarks>Tokens are added to the bucket any time
119 /// <seealso cref="RemoveTokens"/> is called, at the granularity of 119 /// <seealso cref="RemoveTokens"/> is called, at the granularity of
120 /// the system tick interval (typically around 15-22ms)</remarks> 120 /// the system tick interval (typically around 15-22ms)</remarks>
121 private Int64 m_dripRate; 121 protected Int64 m_dripRate;
122 public Int64 RequestedDripRate 122 public virtual Int64 RequestedDripRate
123 { 123 {
124 get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } 124 get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); }
125 set { 125 set {
@@ -131,7 +131,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
131 } 131 }
132 } 132 }
133 133
134 public Int64 DripRate 134 public virtual Int64 DripRate
135 { 135 {
136 get { 136 get {
137 if (m_parent == null) 137 if (m_parent == null)
@@ -149,7 +149,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
149 /// The current total of the requested maximum burst rates of 149 /// The current total of the requested maximum burst rates of
150 /// this bucket's children buckets. 150 /// this bucket's children buckets.
151 /// </summary> 151 /// </summary>
152 private Int64 m_totalDripRequest; 152 protected Int64 m_totalDripRequest;
153 public Int64 TotalDripRequest 153 public Int64 TotalDripRequest
154 { 154 {
155 get { return m_totalDripRequest; } 155 get { return m_totalDripRequest; }
@@ -189,7 +189,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
189 /// hierarchy. However, if any of the parents is over-booked, then 189 /// hierarchy. However, if any of the parents is over-booked, then
190 /// the modifier will be less than 1. 190 /// the modifier will be less than 1.
191 /// </summary> 191 /// </summary>
192 private double DripRateModifier() 192 protected double DripRateModifier()
193 { 193 {
194 Int64 driprate = DripRate; 194 Int64 driprate = DripRate;
195 return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; 195 return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest;
@@ -197,7 +197,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
197 197
198 /// <summary> 198 /// <summary>
199 /// </summary> 199 /// </summary>
200 private double BurstRateModifier() 200 protected double BurstRateModifier()
201 { 201 {
202 // for now... burst rate is always m_quantumsPerBurst (constant) 202 // for now... burst rate is always m_quantumsPerBurst (constant)
203 // larger than drip rate so the ratio of burst requests is the 203 // larger than drip rate so the ratio of burst requests is the
@@ -268,7 +268,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
268 /// Deposit tokens into the bucket from a child bucket that did 268 /// Deposit tokens into the bucket from a child bucket that did
269 /// not use all of its available tokens 269 /// not use all of its available tokens
270 /// </summary> 270 /// </summary>
271 private void Deposit(Int64 count) 271 protected void Deposit(Int64 count)
272 { 272 {
273 m_tokenCount += count; 273 m_tokenCount += count;
274 274
@@ -285,7 +285,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
285 /// call to Drip 285 /// call to Drip
286 /// </summary> 286 /// </summary>
287 /// <returns>True if tokens were added to the bucket, otherwise false</returns> 287 /// <returns>True if tokens were added to the bucket, otherwise false</returns>
288 private void Drip() 288 protected void Drip()
289 { 289 {
290 // This should never happen... means we are a leaf node and were created 290 // This should never happen... means we are a leaf node and were created
291 // with no drip rate... 291 // with no drip rate...
@@ -310,4 +310,64 @@ namespace OpenSim.Region.ClientStack.LindenUDP
310 Deposit(deltaMS * DripRate / m_ticksPerQuantum); 310 Deposit(deltaMS * DripRate / m_ticksPerQuantum);
311 } 311 }
312 } 312 }
313
314 public class AdaptiveTokenBucket : TokenBucket
315 {
316 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
317
318 // <summary>
319 // The minimum rate for flow control.
320 // </summary>
321 protected const Int64 m_minimumFlow = m_minimumDripRate * 10;
322
323 // <summary>
324 // The maximum rate for flow control. Drip rate can never be
325 // greater than this.
326 // </summary>
327 protected Int64 m_maxDripRate = 0;
328 protected Int64 MaxDripRate
329 {
330 get { return (m_maxDripRate == 0 ? m_totalDripRequest : m_maxDripRate); }
331 set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); }
332 }
333
334 // <summary>
335 //
336 // </summary>
337 public virtual Int64 AdjustedDripRate
338 {
339 get { return m_dripRate; }
340 set {
341 m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value,m_minimumFlow,MaxDripRate);
342 m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
343 if (m_parent != null)
344 m_parent.RegisterRequest(this,m_dripRate);
345 }
346 }
347
348 // <summary>
349 //
350 // </summary>
351 public AdaptiveTokenBucket(TokenBucket parent, Int64 maxDripRate) : base(parent,m_minimumFlow)
352 {
353 MaxDripRate = maxDripRate;
354 }
355
356 // <summary>
357 //
358 // </summary>
359 public void ExpirePackets(Int32 count)
360 {
361 // m_log.WarnFormat("[ADAPTIVEBUCKET] drop {0} by {1} expired packets",AdjustedDripRate,count);
362 AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,count));
363 }
364
365 // <summary>
366 //
367 // </summary>
368 public void AcknowledgePackets(Int32 count)
369 {
370 AdjustedDripRate = AdjustedDripRate + count;
371 }
372 }
313} 373}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
index d195110..90a87fa 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
@@ -83,6 +83,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
83 83
84 /// <summary> 84 /// <summary>
85 /// Marks a packet as acknowledged 85 /// Marks a packet as acknowledged
86 /// This method is used when an acknowledgement is received from the network for a previously
87 /// sent packet. Effects of removal this way are to update unacked byte count, adjust RTT
88 /// and increase throttle to the coresponding client.
86 /// </summary> 89 /// </summary>
87 /// <param name="sequenceNumber">Sequence number of the packet to 90 /// <param name="sequenceNumber">Sequence number of the packet to
88 /// acknowledge</param> 91 /// acknowledge</param>
@@ -95,6 +98,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP
95 } 98 }
96 99
97 /// <summary> 100 /// <summary>
101 /// Marks a packet as no longer needing acknowledgement without a received acknowledgement.
102 /// This method is called when a packet expires and we no longer need an acknowledgement.
103 /// When some reliable packet types expire, they are handled in a way other than simply
104 /// resending them. The only effect of removal this way is to update unacked byte count.
105 /// </summary>
106 /// <param name="sequenceNumber">Sequence number of the packet to
107 /// acknowledge</param>
108 /// <remarks>The packet is removed from the collection immediately.
109 /// This function is not threadsafe. It must be called by the thread calling GetExpiredPackets.</remarks>
110 public void Remove(uint sequenceNumber)
111 {
112 OutgoingPacket removedPacket;
113 if (m_packets.TryGetValue(sequenceNumber, out removedPacket))
114 {
115 if (removedPacket != null)
116 {
117 m_packets.Remove(sequenceNumber);
118
119 // Update stats
120 Interlocked.Add(ref removedPacket.Client.UnackedBytes, -removedPacket.Buffer.DataLength);
121 }
122 }
123 }
124
125 /// <summary>
98 /// Returns a list of all of the packets with a TickCount older than 126 /// Returns a list of all of the packets with a TickCount older than
99 /// the specified timeout 127 /// the specified timeout
100 /// </summary> 128 /// </summary>
@@ -130,6 +158,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
130 // is actually sent out again 158 // is actually sent out again
131 packet.TickCount = 0; 159 packet.TickCount = 0;
132 160
161 // As with other network applications, assume that an expired packet is
162 // an indication of some network problem, slow transmission
163 packet.Client.FlowThrottle.ExpirePackets(1);
164
133 expiredPackets.Add(packet); 165 expiredPackets.Add(packet);
134 } 166 }
135 } 167 }
@@ -157,6 +189,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
157 { 189 {
158 m_packets.Remove(pendingRemove.SequenceNumber); 190 m_packets.Remove(pendingRemove.SequenceNumber);
159 191
192 // As with other network applications, assume that an acknowledged packet is an
193 // indication that the network can handle a little more load, speed up the transmission
194 ackedPacket.Client.FlowThrottle.AcknowledgePackets(ackedPacket.Buffer.DataLength);
195
160 // Update stats 196 // Update stats
161 Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength); 197 Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength);
162 198
diff --git a/OpenSim/Region/Framework/Scenes/Prioritizer.cs b/OpenSim/Region/Framework/Scenes/Prioritizer.cs
index e3ed905..2e80156 100644
--- a/OpenSim/Region/Framework/Scenes/Prioritizer.cs
+++ b/OpenSim/Region/Framework/Scenes/Prioritizer.cs
@@ -88,7 +88,7 @@ namespace OpenSim.Region.Framework.Scenes
88 88
89 // If this is an update for our own avatar give it the highest priority 89 // If this is an update for our own avatar give it the highest priority
90 if (client.AgentId == entity.UUID) 90 if (client.AgentId == entity.UUID)
91 return 0; 91 return PriorityQueue.ImmediateQueue;
92 92
93 uint priority; 93 uint priority;
94 94
@@ -172,7 +172,7 @@ namespace OpenSim.Region.Framework.Scenes
172 172
173 // m_log.WarnFormat("[PRIORITIZER] attempt to use agent {0} not in the scene",client.AgentId); 173 // m_log.WarnFormat("[PRIORITIZER] attempt to use agent {0} not in the scene",client.AgentId);
174 // throw new InvalidOperationException("Prioritization agent not defined"); 174 // throw new InvalidOperationException("Prioritization agent not defined");
175 return Int32.MaxValue; 175 return PriorityQueue.NumberOfQueues - 1;
176 } 176 }
177 177
178 // Use group position for child prims, since we are putting child prims in 178 // Use group position for child prims, since we are putting child prims in