aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs431
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs106
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs2
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs14
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs297
5 files changed, 490 insertions, 360 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 76d7f79..1f7e66d 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -300,77 +300,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
300 /// <summary>Used to adjust Sun Orbit values so Linden based viewers properly position sun</summary> 300 /// <summary>Used to adjust Sun Orbit values so Linden based viewers properly position sun</summary>
301 private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; 301 private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f;
302 302
303 // First log file or time has expired, start writing to a new log file
304//<MIC>
305// -----------------------------------------------------------------
306// -----------------------------------------------------------------
307// THIS IS DEBUGGING CODE & SHOULD BE REMOVED
308// -----------------------------------------------------------------
309// -----------------------------------------------------------------
310 public class QueueLogger
311 {
312 public Int32 start = 0;
313 public StreamWriter Log = null;
314 private Dictionary<UUID,int> m_idMap = new Dictionary<UUID,int>();
315
316 public QueueLogger()
317 {
318 DateTime now = DateTime.Now;
319 String fname = String.Format("queue-{0}.log", now.ToString("yyyyMMddHHmmss"));
320 Log = new StreamWriter(fname);
321
322 start = Util.EnvironmentTickCount();
323 }
324
325 public int LookupID(UUID uuid)
326 {
327 int localid;
328 if (! m_idMap.TryGetValue(uuid,out localid))
329 {
330 localid = m_idMap.Count + 1;
331 m_idMap[uuid] = localid;
332 }
333
334 return localid;
335 }
336 }
337
338 public static QueueLogger QueueLog = null;
339
340 // -----------------------------------------------------------------
341 public void LogAvatarUpdateEvent(UUID client, UUID avatar, Int32 timeinqueue)
342 {
343 if (QueueLog == null)
344 QueueLog = new QueueLogger();
345
346 Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start);
347 lock(QueueLog)
348 {
349 int cid = QueueLog.LookupID(client);
350 int aid = QueueLog.LookupID(avatar);
351 QueueLog.Log.WriteLine("{0},AU,AV{1:D4},AV{2:D4},{3}",ticks,cid,aid,timeinqueue);
352 }
353 }
354
355 // -----------------------------------------------------------------
356 public void LogQueueProcessEvent(UUID client, PriorityQueue queue, uint maxup)
357 {
358 if (QueueLog == null)
359 QueueLog = new QueueLogger();
360
361 Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start);
362 lock(QueueLog)
363 {
364 int cid = QueueLog.LookupID(client);
365 QueueLog.Log.WriteLine("{0},PQ,AV{1:D4},{2},{3}",ticks,cid,maxup,queue.ToString());
366 }
367 }
368// -----------------------------------------------------------------
369// -----------------------------------------------------------------
370// -----------------------------------------------------------------
371// -----------------------------------------------------------------
372//</MIC>
373
374 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 303 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
375 protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients 304 protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients
376 305
@@ -386,6 +315,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
386 315
387 private int m_cachedTextureSerial; 316 private int m_cachedTextureSerial;
388 private PriorityQueue m_entityUpdates; 317 private PriorityQueue m_entityUpdates;
318 private PriorityQueue m_entityProps;
389 private Prioritizer m_prioritizer; 319 private Prioritizer m_prioritizer;
390 private bool m_disableFacelights = false; 320 private bool m_disableFacelights = false;
391 321
@@ -433,9 +363,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
433 protected IAssetService m_assetService; 363 protected IAssetService m_assetService;
434 private const bool m_checkPackets = true; 364 private const bool m_checkPackets = true;
435 365
436 private Timer m_propertiesPacketTimer;
437 private List<ObjectPropertiesPacket.ObjectDataBlock> m_propertiesBlocks = new List<ObjectPropertiesPacket.ObjectDataBlock>();
438
439 #endregion Class Members 366 #endregion Class Members
440 367
441 #region Properties 368 #region Properties
@@ -511,6 +438,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
511 m_scene = scene; 438 m_scene = scene;
512 439
513 m_entityUpdates = new PriorityQueue(m_scene.Entities.Count); 440 m_entityUpdates = new PriorityQueue(m_scene.Entities.Count);
441 m_entityProps = new PriorityQueue(m_scene.Entities.Count);
514 m_fullUpdateDataBlocksBuilder = new List<ObjectUpdatePacket.ObjectDataBlock>(); 442 m_fullUpdateDataBlocksBuilder = new List<ObjectUpdatePacket.ObjectDataBlock>();
515 m_killRecord = new HashSet<uint>(); 443 m_killRecord = new HashSet<uint>();
516// m_attachmentsSent = new HashSet<uint>(); 444// m_attachmentsSent = new HashSet<uint>();
@@ -534,9 +462,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
534 m_udpClient.OnQueueEmpty += HandleQueueEmpty; 462 m_udpClient.OnQueueEmpty += HandleQueueEmpty;
535 m_udpClient.OnPacketStats += PopulateStats; 463 m_udpClient.OnPacketStats += PopulateStats;
536 464
537 m_propertiesPacketTimer = new Timer(100);
538 m_propertiesPacketTimer.Elapsed += ProcessObjectPropertiesPacket;
539
540 m_prioritizer = new Prioritizer(m_scene); 465 m_prioritizer = new Prioritizer(m_scene);
541 466
542 RegisterLocalPacketHandlers(); 467 RegisterLocalPacketHandlers();
@@ -1610,7 +1535,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1610 } 1535 }
1611 else 1536 else
1612 { 1537 {
1613 OutPacket(kill, ThrottleOutPacketType.State); 1538 // OutPacket(kill, ThrottleOutPacketType.State);
1539 OutPacket(kill, ThrottleOutPacketType.Task);
1614 } 1540 }
1615 } 1541 }
1616 1542
@@ -2440,7 +2366,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2440 2366
2441 packet.Effect = effectBlocks; 2367 packet.Effect = effectBlocks;
2442 2368
2443 OutPacket(packet, ThrottleOutPacketType.State); 2369 // OutPacket(packet, ThrottleOutPacketType.State);
2370 OutPacket(packet, ThrottleOutPacketType.Task);
2444 } 2371 }
2445 2372
2446 public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, 2373 public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember,
@@ -3634,9 +3561,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3634 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); 3561 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation));
3635 } 3562 }
3636 3563
3637 private Int32 m_LastQueueFill = 0;
3638 private uint m_maxUpdates = 0;
3639
3640 private void ProcessEntityUpdates(int maxUpdates) 3564 private void ProcessEntityUpdates(int maxUpdates)
3641 { 3565 {
3642 OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); 3566 OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>();
@@ -3644,46 +3568,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3644 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); 3568 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
3645 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); 3569 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
3646 3570
3571 // Check to see if this is a flush
3647 if (maxUpdates <= 0) 3572 if (maxUpdates <= 0)
3648 { 3573 {
3649 m_maxUpdates = Int32.MaxValue; 3574 maxUpdates = Int32.MaxValue;
3650 }
3651 else
3652 {
3653 if (m_maxUpdates == 0 || m_LastQueueFill == 0)
3654 {
3655 m_maxUpdates = (uint)maxUpdates;
3656 }
3657 else
3658 {
3659 if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200)
3660 m_maxUpdates += 5;
3661 else
3662 m_maxUpdates = m_maxUpdates >> 1;
3663 }
3664 m_maxUpdates = Util.Clamp<uint>(m_maxUpdates,10,500);
3665 } 3575 }
3666 m_LastQueueFill = Util.EnvironmentTickCount(); 3576
3667
3668 int updatesThisCall = 0; 3577 int updatesThisCall = 0;
3669 3578
3670//<MIC>
3671// DEBUGGING CODE... REMOVE
3672// LogQueueProcessEvent(this.m_agentId,m_entityUpdates,m_maxUpdates);
3673//</MIC>
3674 // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race 3579 // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race
3675 // condition where a kill can be processed before an out-of-date update for the same object. 3580 // condition where a kill can be processed before an out-of-date update for the same object.
3676 lock (m_killRecord) 3581 lock (m_killRecord)
3677 { 3582 {
3678 float avgTimeDilation = 1.0f; 3583 float avgTimeDilation = 1.0f;
3679 EntityUpdate update; 3584 IEntityUpdate iupdate;
3680 Int32 timeinqueue; // this is just debugging code & can be dropped later 3585 Int32 timeinqueue; // this is just debugging code & can be dropped later
3681 3586
3682 while (updatesThisCall < m_maxUpdates) 3587 while (updatesThisCall < maxUpdates)
3683 { 3588 {
3684 lock (m_entityUpdates.SyncRoot) 3589 lock (m_entityUpdates.SyncRoot)
3685 if (!m_entityUpdates.TryDequeue(out update, out timeinqueue)) 3590 if (!m_entityUpdates.TryDequeue(out iupdate, out timeinqueue))
3686 break; 3591 break;
3592
3593 EntityUpdate update = (EntityUpdate)iupdate;
3594
3687 avgTimeDilation += update.TimeDilation; 3595 avgTimeDilation += update.TimeDilation;
3688 avgTimeDilation *= 0.5f; 3596 avgTimeDilation *= 0.5f;
3689 3597
@@ -3723,7 +3631,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3723 3631
3724 #region UpdateFlags to packet type conversion 3632 #region UpdateFlags to packet type conversion
3725 3633
3726 PrimUpdateFlags updateFlags = update.Flags; 3634 PrimUpdateFlags updateFlags = (PrimUpdateFlags)update.Flags;
3727 3635
3728 bool canUseCompressed = true; 3636 bool canUseCompressed = true;
3729 bool canUseImproved = true; 3637 bool canUseImproved = true;
@@ -3802,6 +3710,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3802 3710
3803 #endregion Block Construction 3711 #endregion Block Construction
3804 } 3712 }
3713
3805 3714
3806 #region Packet Sending 3715 #region Packet Sending
3807 3716
@@ -3902,12 +3811,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3902 3811
3903 #endregion Primitive Packet/Data Sending Methods 3812 #endregion Primitive Packet/Data Sending Methods
3904 3813
3814 // These are used to implement an adaptive backoff in the number
3815 // of updates converted to packets. Since we don't want packets
3816 // to sit in the queue with old data, only convert enough updates
3817 // to packets that can be sent in 200ms.
3818 private Int32 m_LastQueueFill = 0;
3819 private Int32 m_maxUpdates = 0;
3820
3905 void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) 3821 void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories)
3906 { 3822 {
3907 if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) 3823 if ((categories & ThrottleOutPacketTypeFlags.Task) != 0)
3908 { 3824 {
3825 if (m_maxUpdates == 0 || m_LastQueueFill == 0)
3826 {
3827 m_maxUpdates = m_udpServer.PrimUpdatesPerCallback;
3828 }
3829 else
3830 {
3831 if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200)
3832 m_maxUpdates += 5;
3833 else
3834 m_maxUpdates = m_maxUpdates >> 1;
3835 }
3836 m_maxUpdates = Util.Clamp<Int32>(m_maxUpdates,10,500);
3837 m_LastQueueFill = Util.EnvironmentTickCount();
3838
3909 if (m_entityUpdates.Count > 0) 3839 if (m_entityUpdates.Count > 0)
3910 ProcessEntityUpdates(m_udpServer.PrimUpdatesPerCallback); 3840 ProcessEntityUpdates(m_maxUpdates);
3841
3842 if (m_entityProps.Count > 0)
3843 ProcessEntityPropertyRequests(m_maxUpdates);
3911 } 3844 }
3912 3845
3913 if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0) 3846 if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0)
@@ -4021,132 +3954,206 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4021 OutPacket(pack, ThrottleOutPacketType.Task); 3954 OutPacket(pack, ThrottleOutPacketType.Task);
4022 } 3955 }
4023 3956
4024 public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, 3957 private class ObjectPropertyUpdate : IEntityUpdate
4025 uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask,
4026 uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category,
4027 UUID LastOwnerID, string ObjectName, string Description)
4028 { 3958 {
4029 ObjectPropertiesFamilyPacket objPropFamilyPack = (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); 3959 internal bool SendFamilyProps;
4030 // TODO: don't create new blocks if recycling an old packet 3960 internal bool SendObjectProps;
4031 3961
4032 ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); 3962 public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam, bool sendobj)
4033 objPropDB.RequestFlags = RequestFlags; 3963 : base(entity,flags)
4034 objPropDB.ObjectID = ObjectUUID; 3964 {
4035 if (OwnerID == GroupID) 3965 SendFamilyProps = sendfam;
4036 objPropDB.OwnerID = UUID.Zero; 3966 SendObjectProps = sendobj;
4037 else 3967 }
4038 objPropDB.OwnerID = OwnerID; 3968 public void Update(ObjectPropertyUpdate update)
4039 objPropDB.GroupID = GroupID; 3969 {
4040 objPropDB.BaseMask = BaseMask; 3970 SendFamilyProps = SendFamilyProps || update.SendFamilyProps;
4041 objPropDB.OwnerMask = OwnerMask; 3971 SendObjectProps = SendObjectProps || update.SendObjectProps;
4042 objPropDB.GroupMask = GroupMask; 3972 Flags |= update.Flags;
4043 objPropDB.EveryoneMask = EveryoneMask; 3973 }
4044 objPropDB.NextOwnerMask = NextOwnerMask; 3974 }
4045 3975
4046 // TODO: More properties are needed in SceneObjectPart! 3976 public void SendObjectPropertiesFamilyData(ISceneEntity entity, uint requestFlags)
4047 objPropDB.OwnershipCost = OwnershipCost;
4048 objPropDB.SaleType = SaleType;
4049 objPropDB.SalePrice = SalePrice;
4050 objPropDB.Category = Category;
4051 objPropDB.LastOwnerID = LastOwnerID;
4052 objPropDB.Name = Util.StringToBytes256(ObjectName);
4053 objPropDB.Description = Util.StringToBytes256(Description);
4054 objPropFamilyPack.ObjectData = objPropDB;
4055 objPropFamilyPack.Header.Zerocoded = true;
4056 OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task);
4057 }
4058
4059 public void SendObjectPropertiesReply(
4060 UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID,
4061 UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID,
4062 UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName,
4063 string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask,
4064 uint BaseMask, byte saleType, int salePrice)
4065 { 3977 {
4066 //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); 3978 uint priority = 0; // time based ordering only
4067 // TODO: don't create new blocks if recycling an old packet 3979 lock (m_entityProps.SyncRoot)
3980 m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false));
3981 }
4068 3982
4069 ObjectPropertiesPacket.ObjectDataBlock block = 3983 public void SendObjectPropertiesReply(ISceneEntity entity)
4070 new ObjectPropertiesPacket.ObjectDataBlock(); 3984 {
3985 uint priority = 0; // time based ordering only
3986 lock (m_entityProps.SyncRoot)
3987 m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false,true));
3988 }
4071 3989
4072 block.ItemID = ItemID; 3990 private void ProcessEntityPropertyRequests(int maxUpdates)
4073 block.CreationDate = CreationDate; 3991 {
4074 block.CreatorID = CreatorUUID; 3992 OpenSim.Framework.Lazy<List<ObjectPropertiesFamilyPacket.ObjectDataBlock>> objectFamilyBlocks =
4075 block.FolderID = FolderUUID; 3993 new OpenSim.Framework.Lazy<List<ObjectPropertiesFamilyPacket.ObjectDataBlock>>();
4076 block.FromTaskID = FromTaskUUID;
4077 block.GroupID = GroupUUID;
4078 block.InventorySerial = InventorySerial;
4079 3994
4080 block.LastOwnerID = LastOwnerUUID; 3995 OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>> objectPropertiesBlocks =
4081 // proper.ObjectData[0].LastOwnerID = UUID.Zero; 3996 new OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>>();
4082 3997
4083 block.ObjectID = ObjectUUID; 3998 IEntityUpdate iupdate;
4084 if (OwnerUUID == GroupUUID) 3999 Int32 timeinqueue; // this is just debugging code & can be dropped later
4085 block.OwnerID = UUID.Zero;
4086 else
4087 block.OwnerID = OwnerUUID;
4088 block.TouchName = Util.StringToBytes256(TouchTitle);
4089 block.TextureID = TextureID;
4090 block.SitName = Util.StringToBytes256(SitTitle);
4091 block.Name = Util.StringToBytes256(ItemName);
4092 block.Description = Util.StringToBytes256(ItemDescription);
4093 block.OwnerMask = OwnerMask;
4094 block.NextOwnerMask = NextOwnerMask;
4095 block.GroupMask = GroupMask;
4096 block.EveryoneMask = EveryoneMask;
4097 block.BaseMask = BaseMask;
4098 // proper.ObjectData[0].AggregatePerms = 53;
4099 // proper.ObjectData[0].AggregatePermTextures = 0;
4100 // proper.ObjectData[0].AggregatePermTexturesOwner = 0;
4101 block.SaleType = saleType;
4102 block.SalePrice = salePrice;
4103 4000
4104 lock (m_propertiesPacketTimer) 4001 int updatesThisCall = 0;
4002 while (updatesThisCall < m_maxUpdates)
4105 { 4003 {
4106 m_propertiesBlocks.Add(block); 4004 lock (m_entityProps.SyncRoot)
4005 if (!m_entityProps.TryDequeue(out iupdate, out timeinqueue))
4006 break;
4107 4007
4108 int length = 0; 4008 ObjectPropertyUpdate update = (ObjectPropertyUpdate)iupdate;
4109 foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) 4009 if (update.SendFamilyProps)
4110 { 4010 {
4111 length += b.Length; 4011 if (update.Entity is SceneObjectPart)
4012 {
4013 SceneObjectPart sop = (SceneObjectPart)update.Entity;
4014 ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags);
4015 objectFamilyBlocks.Value.Add(objPropDB);
4016 }
4112 } 4017 }
4113 if (length > 1100) // FIXME: use real MTU 4018
4019 if (update.SendObjectProps)
4114 { 4020 {
4115 ProcessObjectPropertiesPacket(null, null); 4021 if (update.Entity is SceneObjectPart)
4116 m_propertiesPacketTimer.Stop(); 4022 {
4117 return; 4023 SceneObjectPart sop = (SceneObjectPart)update.Entity;
4024 ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop);
4025 objectPropertiesBlocks.Value.Add(objPropDB);
4026 }
4118 } 4027 }
4119 4028
4120 m_propertiesPacketTimer.Stop(); 4029 updatesThisCall++;
4121 m_propertiesPacketTimer.Start();
4122 } 4030 }
4031
4123 4032
4124 //proper.Header.Zerocoded = true; 4033 Int32 ppcnt = 0;
4125 //OutPacket(proper, ThrottleOutPacketType.Task); 4034 Int32 pbcnt = 0;
4126 } 4035
4036 if (objectPropertiesBlocks.IsValueCreated)
4037 {
4038 List<ObjectPropertiesPacket.ObjectDataBlock> blocks = objectPropertiesBlocks.Value;
4127 4039
4128 private void ProcessObjectPropertiesPacket(Object sender, ElapsedEventArgs e) 4040 ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties);
4129 { 4041 packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count];
4130 ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); 4042 for (int i = 0; i < blocks.Count; i++)
4043 packet.ObjectData[i] = blocks[i];
4131 4044
4132 lock (m_propertiesPacketTimer) 4045 packet.Header.Zerocoded = true;
4133 { 4046 OutPacket(packet, ThrottleOutPacketType.Task, true);
4134 m_propertiesPacketTimer.Stop();
4135 4047
4136 proper.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[m_propertiesBlocks.Count]; 4048 pbcnt += blocks.Count;
4049 ppcnt++;
4050 }
4051
4052 Int32 fpcnt = 0;
4053 Int32 fbcnt = 0;
4054
4055 if (objectFamilyBlocks.IsValueCreated)
4056 {
4057 List<ObjectPropertiesFamilyPacket.ObjectDataBlock> blocks = objectFamilyBlocks.Value;
4137 4058
4138 int index = 0; 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);
4139 4067
4140 foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) 4068 // one packet per object block... uggh...
4069 for (int i = 0; i < blocks.Count; i++)
4141 { 4070 {
4142 proper.ObjectData[index++] = b; 4071 ObjectPropertiesFamilyPacket packet =
4143 } 4072 (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily);
4073
4074 packet.ObjectData = blocks[i];
4075 packet.Header.Zerocoded = true;
4076 OutPacket(packet, ThrottleOutPacketType.Task);
4144 4077
4145 m_propertiesBlocks.Clear(); 4078 fpcnt++;
4079 fbcnt++;
4080 }
4081
4146 } 4082 }
4083
4084 // m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt);
4085 // m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt);
4086 }
4087
4088 private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags)
4089 {
4090 ObjectPropertiesFamilyPacket.ObjectDataBlock block = new ObjectPropertiesFamilyPacket.ObjectDataBlock();
4091
4092 block.RequestFlags = requestFlags;
4093 block.ObjectID = sop.UUID;
4094 if (sop.OwnerID == sop.GroupID)
4095 block.OwnerID = UUID.Zero;
4096 else
4097 block.OwnerID = sop.OwnerID;
4098 block.GroupID = sop.GroupID;
4099 block.BaseMask = sop.BaseMask;
4100 block.OwnerMask = sop.OwnerMask;
4101 block.GroupMask = sop.GroupMask;
4102 block.EveryoneMask = sop.EveryoneMask;
4103 block.NextOwnerMask = sop.NextOwnerMask;
4104
4105 // TODO: More properties are needed in SceneObjectPart!
4106 block.OwnershipCost = sop.OwnershipCost;
4107 block.SaleType = sop.ObjectSaleType;
4108 block.SalePrice = sop.SalePrice;
4109 block.Category = sop.Category;
4110 block.LastOwnerID = sop.CreatorID; // copied from old SOG call... is this right?
4111 block.Name = Util.StringToBytes256(sop.Name);
4112 block.Description = Util.StringToBytes256(sop.Description);
4113
4114 return block;
4115 }
4147 4116
4148 proper.Header.Zerocoded = true; 4117 private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop)
4149 OutPacket(proper, ThrottleOutPacketType.Task); 4118 {
4119 //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties);
4120 // TODO: don't create new blocks if recycling an old packet
4121
4122 ObjectPropertiesPacket.ObjectDataBlock block =
4123 new ObjectPropertiesPacket.ObjectDataBlock();
4124
4125 block.ObjectID = sop.UUID;
4126 block.Name = Util.StringToBytes256(sop.Name);
4127 block.Description = Util.StringToBytes256(sop.Description);
4128
4129 block.CreationDate = (ulong)sop.CreationDate * 1000000; // viewer wants date in microseconds
4130 block.CreatorID = sop.CreatorID;
4131 block.GroupID = sop.GroupID;
4132 block.LastOwnerID = sop.LastOwnerID;
4133 if (sop.OwnerID == sop.GroupID)
4134 block.OwnerID = UUID.Zero;
4135 else
4136 block.OwnerID = sop.OwnerID;
4137
4138 block.ItemID = sop.FromUserInventoryItemID;
4139 block.FolderID = UUID.Zero; // sop.FromFolderID ??
4140 block.FromTaskID = UUID.Zero; // ???
4141 block.InventorySerial = (short)sop.InventorySerial;
4142
4143 SceneObjectPart root = sop.ParentGroup.RootPart;
4144
4145 block.TouchName = Util.StringToBytes256(root.TouchName);
4146 block.TextureID = new byte[0]; // TextureID ???
4147 block.SitName = Util.StringToBytes256(root.SitName);
4148 block.OwnerMask = root.OwnerMask;
4149 block.NextOwnerMask = root.NextOwnerMask;
4150 block.GroupMask = root.GroupMask;
4151 block.EveryoneMask = root.EveryoneMask;
4152 block.BaseMask = root.BaseMask;
4153 block.SaleType = root.ObjectSaleType;
4154 block.SalePrice = root.SalePrice;
4155
4156 return block;
4150 } 4157 }
4151 4158
4152 #region Estate Data Sending Methods 4159 #region Estate Data Sending Methods
@@ -4487,6 +4494,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4487 4494
4488 public void SendForceClientSelectObjects(List<uint> ObjectIDs) 4495 public void SendForceClientSelectObjects(List<uint> ObjectIDs)
4489 { 4496 {
4497 m_log.WarnFormat("[LLCLIENTVIEW] sending select with {0} objects", ObjectIDs.Count);
4498
4490 bool firstCall = true; 4499 bool firstCall = true;
4491 const int MAX_OBJECTS_PER_PACKET = 251; 4500 const int MAX_OBJECTS_PER_PACKET = 251;
4492 ForceObjectSelectPacket pack = (ForceObjectSelectPacket)PacketPool.Instance.GetPacket(PacketType.ForceObjectSelect); 4501 ForceObjectSelectPacket pack = (ForceObjectSelectPacket)PacketPool.Instance.GetPacket(PacketType.ForceObjectSelect);
@@ -11378,7 +11387,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11378 if (logPacket) 11387 if (logPacket)
11379 m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); 11388 m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type);
11380 } 11389 }
11381 11390
11382 m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); 11391 m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting);
11383 } 11392 }
11384 11393
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
index 9a8bfd3..7be8a0a 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
@@ -135,7 +135,9 @@ 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_throttle; 138 private readonly TokenBucket m_throttleClient;
139 /// <summary>Throttle bucket for this agent's connection</summary>
140 private readonly TokenBucket m_throttleCategory;
139 /// <summary>Throttle buckets for each packet category</summary> 141 /// <summary>Throttle buckets for each packet category</summary>
140 private readonly TokenBucket[] m_throttleCategories; 142 private readonly TokenBucket[] m_throttleCategories;
141 /// <summary>Outgoing queues for throttled packets</summary> 143 /// <summary>Outgoing queues for throttled packets</summary>
@@ -174,7 +176,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
174 m_maxRTO = maxRTO; 176 m_maxRTO = maxRTO;
175 177
176 // Create a token bucket throttle for this client that has the scene token bucket as a parent 178 // Create a token bucket throttle for this client that has the scene token bucket as a parent
177 m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); 179 m_throttleClient = new TokenBucket(parentThrottle, rates.TotalLimit);
180 // 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);
178 // Create an array of token buckets for this clients different throttle categories 182 // Create an array of token buckets for this clients different throttle categories
179 m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; 183 m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
180 184
@@ -185,7 +189,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
185 // Initialize the packet outboxes, where packets sit while they are waiting for tokens 189 // Initialize the packet outboxes, where packets sit while they are waiting for tokens
186 m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>(); 190 m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
187 // Initialize the token buckets that control the throttling for each category 191 // Initialize the token buckets that control the throttling for each category
188 m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); 192 m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetLimit(type));
189 } 193 }
190 194
191 // Default the retransmission timeout to three seconds 195 // Default the retransmission timeout to three seconds
@@ -206,6 +210,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
206 m_packetOutboxes[i].Clear(); 210 m_packetOutboxes[i].Clear();
207 m_nextPackets[i] = null; 211 m_nextPackets[i] = null;
208 } 212 }
213
214 // pull the throttle out of the scene throttle
215 m_throttleClient.Parent.UnregisterRequest(m_throttleClient);
209 OnPacketStats = null; 216 OnPacketStats = null;
210 OnQueueEmpty = null; 217 OnQueueEmpty = null;
211 } 218 }
@@ -216,6 +223,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
216 /// <returns>Information about the client connection</returns> 223 /// <returns>Information about the client connection</returns>
217 public ClientInfo GetClientInfo() 224 public ClientInfo GetClientInfo()
218 { 225 {
226///<mic>
227 TokenBucket tb;
228
229 tb = m_throttleClient.Parent;
230 m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest,"ROOT");
231
232 tb = m_throttleClient;
233 m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CLIENT");
234
235 tb = m_throttleCategory;
236 m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CATEGORY");
237
238 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
239 {
240 tb = m_throttleCategories[i];
241 m_log.WarnFormat("[TOKENS] {4} <{0}:{1}>: Actual={2},Requested={3}",AgentID,i,tb.DripRate,tb.RequestedDripRate," BUCKET");
242 }
243
244///</mic>
245
219 // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists 246 // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists
220 // of pending and needed ACKs for every client every time some method wants information about 247 // of pending and needed ACKs for every client every time some method wants information about
221 // this connection is a recipe for poor performance 248 // this connection is a recipe for poor performance
@@ -223,13 +250,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
223 info.pendingAcks = new Dictionary<uint, uint>(); 250 info.pendingAcks = new Dictionary<uint, uint>();
224 info.needAck = new Dictionary<uint, byte[]>(); 251 info.needAck = new Dictionary<uint, byte[]>();
225 252
226 info.resendThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; 253 info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate;
227 info.landThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; 254 info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
228 info.windThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; 255 info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
229 info.cloudThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; 256 info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
230 info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; 257 // info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
231 info.assetThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; 258 info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
232 info.textureThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; 259 info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
260 info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
233 info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + 261 info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle +
234 info.taskThrottle + info.assetThrottle + info.textureThrottle; 262 info.taskThrottle + info.assetThrottle + info.textureThrottle;
235 263
@@ -317,8 +345,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
317 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; 345 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
318 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); 346 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
319 // State is a subcategory of task that we allocate a percentage to 347 // State is a subcategory of task that we allocate a percentage to
320 int state = (int)((float)task * STATE_TASK_PERCENTAGE); 348 int state = 0;
321 task -= state; 349 // int state = (int)((float)task * STATE_TASK_PERCENTAGE);
350 // task -= state;
322 351
323 // Make sure none of the throttles are set below our packet MTU, 352 // Make sure none of the throttles are set below our packet MTU,
324 // otherwise a throttle could become permanently clogged 353 // otherwise a throttle could become permanently clogged
@@ -339,40 +368,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP
339 // Update the token buckets with new throttle values 368 // Update the token buckets with new throttle values
340 TokenBucket bucket; 369 TokenBucket bucket;
341 370
342 bucket = m_throttle; 371 bucket = m_throttleCategory;
343 bucket.MaxBurst = total; 372 bucket.RequestedDripRate = total;
344 373
345 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; 374 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
346 bucket.DripRate = resend; 375 bucket.RequestedDripRate = resend;
347 bucket.MaxBurst = resend;
348 376
349 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; 377 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land];
350 bucket.DripRate = land; 378 bucket.RequestedDripRate = land;
351 bucket.MaxBurst = land;
352 379
353 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; 380 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind];
354 bucket.DripRate = wind; 381 bucket.RequestedDripRate = wind;
355 bucket.MaxBurst = wind;
356 382
357 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; 383 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud];
358 bucket.DripRate = cloud; 384 bucket.RequestedDripRate = cloud;
359 bucket.MaxBurst = cloud;
360 385
361 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; 386 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset];
362 bucket.DripRate = asset; 387 bucket.RequestedDripRate = asset;
363 bucket.MaxBurst = asset;
364 388
365 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; 389 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task];
366 bucket.DripRate = task + state; 390 bucket.RequestedDripRate = task;
367 bucket.MaxBurst = task + state;
368 391
369 bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; 392 bucket = m_throttleCategories[(int)ThrottleOutPacketType.State];
370 bucket.DripRate = state; 393 bucket.RequestedDripRate = state;
371 bucket.MaxBurst = state;
372 394
373 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; 395 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture];
374 bucket.DripRate = texture; 396 bucket.RequestedDripRate = texture;
375 bucket.MaxBurst = texture;
376 397
377 // Reset the packed throttles cached data 398 // Reset the packed throttles cached data
378 m_packedThrottles = null; 399 m_packedThrottles = null;
@@ -387,14 +408,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
387 data = new byte[7 * 4]; 408 data = new byte[7 * 4];
388 int i = 0; 409 int i = 0;
389 410
390 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4; 411 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate), 0, data, i, 4); i += 4;
391 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; 412 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate), 0, data, i, 4); i += 4;
392 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; 413 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate), 0, data, i, 4); i += 4;
393 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; 414 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate), 0, data, i, 4); i += 4;
394 Buffer.BlockCopy(Utils.FloatToBytes((float)(m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) + 415 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate), 0, data, i, 4); i += 4;
395 m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4; 416 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate), 0, data, i, 4); i += 4;
396 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; 417 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate), 0, data, i, 4); i += 4;
397 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4;
398 418
399 m_packedThrottles = data; 419 m_packedThrottles = data;
400 } 420 }
@@ -420,6 +440,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
420 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category]; 440 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category];
421 TokenBucket bucket = m_throttleCategories[category]; 441 TokenBucket bucket = m_throttleCategories[category];
422 442
443 // Don't send this packet if there is already a packet waiting in the queue
444 // even if we have the tokens to send it, tokens should go to the already
445 // queued packets
446 if (queue.Count > 0)
447 {
448 queue.Enqueue(packet);
449 return true;
450 }
451
452
423 if (!forceQueue && bucket.RemoveTokens(packet.Buffer.DataLength)) 453 if (!forceQueue && bucket.RemoveTokens(packet.Buffer.DataLength))
424 { 454 {
425 // Enough tokens were removed from the bucket, the packet will not be queued 455 // Enough tokens were removed from the bucket, the packet will not be queued
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
index 583214c..d08b25f 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
@@ -228,7 +228,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
228 } 228 }
229 #endregion BinaryStats 229 #endregion BinaryStats
230 230
231 m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps); 231 m_throttle = new TokenBucket(null, sceneThrottleBps);
232 ThrottleRates = new ThrottleRates(configSource); 232 ThrottleRates = new ThrottleRates(configSource);
233 } 233 }
234 234
diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs
index 364ce4b..b62ec07 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs
@@ -78,7 +78,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
78 } 78 }
79 } 79 }
80 80
81 public bool Enqueue(uint pqueue, EntityUpdate value) 81 public bool Enqueue(uint pqueue, IEntityUpdate value)
82 { 82 {
83 LookupItem lookup; 83 LookupItem lookup;
84 84
@@ -87,7 +87,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
87 if (m_lookupTable.TryGetValue(localid, out lookup)) 87 if (m_lookupTable.TryGetValue(localid, out lookup))
88 { 88 {
89 entry = lookup.Heap[lookup.Handle].EntryOrder; 89 entry = lookup.Heap[lookup.Handle].EntryOrder;
90 value.Flags |= lookup.Heap[lookup.Handle].Value.Flags; 90 value.Update(lookup.Heap[lookup.Handle].Value);
91 lookup.Heap.Remove(lookup.Handle); 91 lookup.Heap.Remove(lookup.Handle);
92 } 92 }
93 93
@@ -99,7 +99,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
99 return true; 99 return true;
100 } 100 }
101 101
102 internal bool TryDequeue(out EntityUpdate value, out Int32 timeinqueue) 102 internal bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue)
103 { 103 {
104 for (int i = 0; i < m_numberOfQueues; ++i) 104 for (int i = 0; i < m_numberOfQueues; ++i)
105 { 105 {
@@ -122,7 +122,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
122 } 122 }
123 123
124 timeinqueue = 0; 124 timeinqueue = 0;
125 value = default(EntityUpdate); 125 value = default(IEntityUpdate);
126 return false; 126 return false;
127 } 127 }
128 128
@@ -175,8 +175,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
175#region MinHeapItem 175#region MinHeapItem
176 private struct MinHeapItem : IComparable<MinHeapItem> 176 private struct MinHeapItem : IComparable<MinHeapItem>
177 { 177 {
178 private EntityUpdate value; 178 private IEntityUpdate value;
179 internal EntityUpdate Value { 179 internal IEntityUpdate Value {
180 get { 180 get {
181 return this.value; 181 return this.value;
182 } 182 }
@@ -212,7 +212,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
212 this.pqueue = pqueue; 212 this.pqueue = pqueue;
213 } 213 }
214 214
215 internal MinHeapItem(uint pqueue, UInt64 entryorder, EntityUpdate value) 215 internal MinHeapItem(uint pqueue, UInt64 entryorder, IEntityUpdate value)
216 { 216 {
217 this.entrytime = Util.EnvironmentTickCount(); 217 this.entrytime = Util.EnvironmentTickCount();
218 this.entryorder = entryorder; 218 this.entryorder = entryorder;
diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
index 0a8331f..07b0a1d 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
@@ -26,6 +26,10 @@
26 */ 26 */
27 27
28using System; 28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Reflection;
32using log4net;
29 33
30namespace OpenSim.Region.ClientStack.LindenUDP 34namespace OpenSim.Region.ClientStack.LindenUDP
31{ 35{
@@ -35,89 +39,126 @@ namespace OpenSim.Region.ClientStack.LindenUDP
35 /// </summary> 39 /// </summary>
36 public class TokenBucket 40 public class TokenBucket
37 { 41 {
38 /// <summary>Parent bucket to this bucket, or null if this is a root 42 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
39 /// bucket</summary> 43 private static Int32 m_counter = 0;
40 TokenBucket parent; 44
41 /// <summary>Size of the bucket in bytes. If zero, the bucket has 45 private Int32 m_identifier;
42 /// infinite capacity</summary> 46
43 int maxBurst; 47 /// <summary>
44 /// <summary>Rate that the bucket fills, in bytes per millisecond. If 48 /// Number of ticks (ms) per quantum, drip rate and max burst
45 /// zero, the bucket always remains full</summary> 49 /// are defined over this interval.
46 int tokensPerMS; 50 /// </summary>
47 /// <summary>Number of tokens currently in the bucket</summary> 51 private const Int32 m_ticksPerQuantum = 1000;
48 int content; 52
53 /// <summary>
54 /// This is the number of quantums worth of packets that can
55 /// be accommodated during a burst
56 /// </summary>
57 private const Double m_quantumsPerBurst = 1.5;
58
59 /// <summary>
60 /// </summary>
61 private const Int32 m_minimumDripRate = 1400;
62
49 /// <summary>Time of the last drip, in system ticks</summary> 63 /// <summary>Time of the last drip, in system ticks</summary>
50 int lastDrip; 64 private Int32 m_lastDrip;
51 65
52 #region Properties 66 /// <summary>
67 /// The number of bytes that can be sent at this moment. This is the
68 /// current number of tokens in the bucket
69 /// </summary>
70 private Int64 m_tokenCount;
71
72 /// <summary>
73 /// Map of children buckets and their requested maximum burst rate
74 /// </summary>
75 private Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>();
76
77#region Properties
53 78
54 /// <summary> 79 /// <summary>
55 /// The parent bucket of this bucket, or null if this bucket has no 80 /// The parent bucket of this bucket, or null if this bucket has no
56 /// parent. The parent bucket will limit the aggregate bandwidth of all 81 /// parent. The parent bucket will limit the aggregate bandwidth of all
57 /// of its children buckets 82 /// of its children buckets
58 /// </summary> 83 /// </summary>
84 private TokenBucket m_parent;
59 public TokenBucket Parent 85 public TokenBucket Parent
60 { 86 {
61 get { return parent; } 87 get { return m_parent; }
88 set { m_parent = value; }
62 } 89 }
63 90
64 /// <summary> 91 /// <summary>
65 /// Maximum burst rate in bytes per second. This is the maximum number 92 /// Maximum burst rate in bytes per second. This is the maximum number
66 /// of tokens that can accumulate in the bucket at any one time 93 /// of tokens that can accumulate in the bucket at any one time. This
94 /// also sets the total request for leaf nodes
67 /// </summary> 95 /// </summary>
68 public int MaxBurst 96 private Int64 m_burstRate;
97 public Int64 RequestedBurstRate
69 { 98 {
70 get { return maxBurst; } 99 get { return m_burstRate; }
71 set { maxBurst = (value >= 0 ? value : 0); } 100 set { m_burstRate = (value < 0 ? 0 : value); }
72 } 101 }
73 102
103 public Int64 BurstRate
104 {
105 get {
106 double rate = RequestedBurstRate * BurstRateModifier();
107 if (rate < m_minimumDripRate * m_quantumsPerBurst)
108 rate = m_minimumDripRate * m_quantumsPerBurst;
109
110 return (Int64) rate;
111 }
112 }
113
74 /// <summary> 114 /// <summary>
75 /// The speed limit of this bucket in bytes per second. This is the 115 /// The speed limit of this bucket in bytes per second. This is the
76 /// number of tokens that are added to the bucket per second 116 /// number of tokens that are added to the bucket per quantum
77 /// </summary> 117 /// </summary>
78 /// <remarks>Tokens are added to the bucket any time 118 /// <remarks>Tokens are added to the bucket any time
79 /// <seealso cref="RemoveTokens"/> is called, at the granularity of 119 /// <seealso cref="RemoveTokens"/> is called, at the granularity of
80 /// the system tick interval (typically around 15-22ms)</remarks> 120 /// the system tick interval (typically around 15-22ms)</remarks>
81 public int DripRate 121 private Int64 m_dripRate;
122 public Int64 RequestedDripRate
82 { 123 {
83 get { return tokensPerMS * 1000; } 124 get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); }
84 set 125 set {
85 { 126 m_dripRate = (value < 0 ? 0 : value);
86 if (value == 0) 127 m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
87 tokensPerMS = 0; 128 m_totalDripRequest = m_dripRate;
88 else 129 if (m_parent != null)
89 { 130 m_parent.RegisterRequest(this,m_dripRate);
90 int bpms = (int)((float)value / 1000.0f);
91
92 if (bpms <= 0)
93 tokensPerMS = 1; // 1 byte/ms is the minimum granularity
94 else
95 tokensPerMS = bpms;
96 }
97 } 131 }
98 } 132 }
99 133
100 /// <summary> 134 public Int64 DripRate
101 /// The speed limit of this bucket in bytes per millisecond
102 /// </summary>
103 public int DripPerMS
104 { 135 {
105 get { return tokensPerMS; } 136 get {
137 if (m_parent == null)
138 return Math.Min(RequestedDripRate,TotalDripRequest);
139
140 double rate = (double)RequestedDripRate * m_parent.DripRateModifier();
141 if (rate < m_minimumDripRate)
142 rate = m_minimumDripRate;
143
144 return (Int64)rate;
145 }
106 } 146 }
107 147
108 /// <summary> 148 /// <summary>
109 /// The number of bytes that can be sent at this moment. This is the 149 /// The current total of the requested maximum burst rates of
110 /// current number of tokens in the bucket 150 /// this bucket's children buckets.
111 /// <remarks>If this bucket has a parent bucket that does not have
112 /// enough tokens for a request, <seealso cref="RemoveTokens"/> will
113 /// return false regardless of the content of this bucket</remarks>
114 /// </summary> 151 /// </summary>
115 public int Content 152 private Int64 m_totalDripRequest;
116 { 153 public Int64 TotalDripRequest
117 get { return content; } 154 {
118 } 155 get { return m_totalDripRequest; }
156 set { m_totalDripRequest = value; }
157 }
158
159#endregion Properties
119 160
120 #endregion Properties 161#region Constructor
121 162
122 /// <summary> 163 /// <summary>
123 /// Default constructor 164 /// Default constructor
@@ -128,56 +169,114 @@ namespace OpenSim.Region.ClientStack.LindenUDP
128 /// zero if this bucket has no maximum capacity</param> 169 /// zero if this bucket has no maximum capacity</param>
129 /// <param name="dripRate">Rate that the bucket fills, in bytes per 170 /// <param name="dripRate">Rate that the bucket fills, in bytes per
130 /// second. If zero, the bucket always remains full</param> 171 /// second. If zero, the bucket always remains full</param>
131 public TokenBucket(TokenBucket parent, int maxBurst, int dripRate) 172 public TokenBucket(TokenBucket parent, Int64 dripRate)
132 { 173 {
133 this.parent = parent; 174 m_identifier = m_counter++;
134 MaxBurst = maxBurst; 175
135 DripRate = dripRate; 176 Parent = parent;
136 lastDrip = Environment.TickCount & Int32.MaxValue; 177 RequestedDripRate = dripRate;
178 // TotalDripRequest = dripRate; // this will be overwritten when a child node registers
179 // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst);
180 m_lastDrip = Environment.TickCount & Int32.MaxValue;
137 } 181 }
138 182
183#endregion Constructor
184
139 /// <summary> 185 /// <summary>
140 /// Remove a given number of tokens from the bucket 186 /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning
187 /// no modification if the requested bandwidth is less than the
188 /// max burst bandwidth all the way to the root of the throttle
189 /// hierarchy. However, if any of the parents is over-booked, then
190 /// the modifier will be less than 1.
141 /// </summary> 191 /// </summary>
142 /// <param name="amount">Number of tokens to remove from the bucket</param> 192 private double DripRateModifier()
143 /// <returns>True if the requested number of tokens were removed from
144 /// the bucket, otherwise false</returns>
145 public bool RemoveTokens(int amount)
146 { 193 {
147 bool dummy; 194 Int64 driprate = DripRate;
148 return RemoveTokens(amount, out dummy); 195 return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest;
149 } 196 }
150 197
151 /// <summary> 198 /// <summary>
199 /// </summary>
200 private double BurstRateModifier()
201 {
202 // for now... burst rate is always m_quantumsPerBurst (constant)
203 // larger than drip rate so the ratio of burst requests is the
204 // same as the drip ratio
205 return DripRateModifier();
206 }
207
208 /// <summary>
209 /// Register drip rate requested by a child of this throttle. Pass the
210 /// changes up the hierarchy.
211 /// </summary>
212 public void RegisterRequest(TokenBucket child, Int64 request)
213 {
214 m_children[child] = request;
215 // m_totalDripRequest = m_children.Values.Sum();
216
217 m_totalDripRequest = 0;
218 foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
219 m_totalDripRequest += cref.Value;
220
221 // Pass the new values up to the parent
222 if (m_parent != null)
223 m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
224 }
225
226 /// <summary>
227 /// Remove the rate requested by a child of this throttle. Pass the
228 /// changes up the hierarchy.
229 /// </summary>
230 public void UnregisterRequest(TokenBucket child)
231 {
232 m_children.Remove(child);
233 // m_totalDripRequest = m_children.Values.Sum();
234
235 m_totalDripRequest = 0;
236 foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
237 m_totalDripRequest += cref.Value;
238
239 // Pass the new values up to the parent
240 if (m_parent != null)
241 m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
242 }
243
244 /// <summary>
152 /// Remove a given number of tokens from the bucket 245 /// Remove a given number of tokens from the bucket
153 /// </summary> 246 /// </summary>
154 /// <param name="amount">Number of tokens to remove from the bucket</param> 247 /// <param name="amount">Number of tokens to remove from the bucket</param>
155 /// <param name="dripSucceeded">True if tokens were added to the bucket
156 /// during this call, otherwise false</param>
157 /// <returns>True if the requested number of tokens were removed from 248 /// <returns>True if the requested number of tokens were removed from
158 /// the bucket, otherwise false</returns> 249 /// the bucket, otherwise false</returns>
159 public bool RemoveTokens(int amount, out bool dripSucceeded) 250 public bool RemoveTokens(Int64 amount)
160 { 251 {
161 if (maxBurst == 0) 252 // Deposit tokens for this interval
253 Drip();
254
255 // If we have enough tokens then remove them and return
256 if (m_tokenCount - amount >= 0)
162 { 257 {
163 dripSucceeded = true; 258 // we don't have to remove from the parent, the drip rate is already
259 // reflective of the drip rate limits in the parent
260 m_tokenCount -= amount;
164 return true; 261 return true;
165 } 262 }
166 263
167 dripSucceeded = Drip(); 264 return false;
265 }
168 266
169 if (content - amount >= 0) 267 /// <summary>
170 { 268 /// Deposit tokens into the bucket from a child bucket that did
171 if (parent != null && !parent.RemoveTokens(amount)) 269 /// not use all of its available tokens
172 return false; 270 /// </summary>
271 private void Deposit(Int64 count)
272 {
273 m_tokenCount += count;
173 274
174 content -= amount; 275 // Deposit the overflow in the parent bucket, this is how we share
175 return true; 276 // unused bandwidth
176 } 277 Int64 burstrate = BurstRate;
177 else 278 if (m_tokenCount > burstrate)
178 { 279 m_tokenCount = burstrate;
179 return false;
180 }
181 } 280 }
182 281
183 /// <summary> 282 /// <summary>
@@ -186,37 +285,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
186 /// call to Drip 285 /// call to Drip
187 /// </summary> 286 /// </summary>
188 /// <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>
189 public bool Drip() 288 private void Drip()
190 { 289 {
191 if (tokensPerMS == 0) 290 // This should never happen... means we are a leaf node and were created
291 // with no drip rate...
292 if (DripRate == 0)
192 { 293 {
193 content = maxBurst; 294 m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0");
194 return true; 295 return;
195 } 296 }
196 else 297
197 { 298 // Determine the interval over which we are adding tokens, never add
198 int now = Environment.TickCount & Int32.MaxValue; 299 // more than a single quantum of tokens
199 int deltaMS = now - lastDrip; 300 Int32 now = Environment.TickCount & Int32.MaxValue;
301 Int32 deltaMS = Math.Min(now - m_lastDrip, m_ticksPerQuantum);
200 302
201 if (deltaMS <= 0) 303 m_lastDrip = now;
202 {
203 if (deltaMS < 0)
204 lastDrip = now;
205 return false;
206 }
207 304
208 int dripAmount = deltaMS * tokensPerMS; 305 // This can be 0 in the very unusual case that the timer wrapped
306 // It can be 0 if we try add tokens at a sub-tick rate
307 if (deltaMS <= 0)
308 return;
209 309
210 content = Math.Min(content + dripAmount, maxBurst); 310 Deposit(deltaMS * DripRate / m_ticksPerQuantum);
211 lastDrip = now;
212
213 if (dripAmount < 0 || content < 0)
214 // sim has been idle for too long, integer has overflown
215 // previous calculation is meaningless, let's put it at correct max
216 content = maxBurst;
217
218 return true;
219 }
220 } 311 }
221 } 312 }
222} 313}