aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack
diff options
context:
space:
mode:
authorMelanie2011-04-18 20:17:29 +0100
committerMelanie2011-04-18 20:17:29 +0100
commitd1913f24295e0235ec31c1c546aece7de2209479 (patch)
tree548e2b8828d5292b05b40cc33036e77880099007 /OpenSim/Region/ClientStack
parentFix up client implementations (diff)
parentMerge branch 'master' into test-merge0418 (diff)
downloadopensim-SC-d1913f24295e0235ec31c1c546aece7de2209479.zip
opensim-SC-d1913f24295e0235ec31c1c546aece7de2209479.tar.gz
opensim-SC-d1913f24295e0235ec31c1c546aece7de2209479.tar.bz2
opensim-SC-d1913f24295e0235ec31c1c546aece7de2209479.tar.xz
Merge branch 'master' into careminster-presence-refactor
Diffstat (limited to 'OpenSim/Region/ClientStack')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs428
-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.cs288
5 files changed, 491 insertions, 347 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 1c3ecb2..803114f 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -301,77 +301,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
301 /// <summary>Used to adjust Sun Orbit values so Linden based viewers properly position sun</summary> 301 /// <summary>Used to adjust Sun Orbit values so Linden based viewers properly position sun</summary>
302 private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; 302 private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f;
303 303
304 // First log file or time has expired, start writing to a new log file
305//<MIC>
306// -----------------------------------------------------------------
307// -----------------------------------------------------------------
308// THIS IS DEBUGGING CODE & SHOULD BE REMOVED
309// -----------------------------------------------------------------
310// -----------------------------------------------------------------
311 public class QueueLogger
312 {
313 public Int32 start = 0;
314 public StreamWriter Log = null;
315 private Dictionary<UUID,int> m_idMap = new Dictionary<UUID,int>();
316
317 public QueueLogger()
318 {
319 DateTime now = DateTime.Now;
320 String fname = String.Format("queue-{0}.log", now.ToString("yyyyMMddHHmmss"));
321 Log = new StreamWriter(fname);
322
323 start = Util.EnvironmentTickCount();
324 }
325
326 public int LookupID(UUID uuid)
327 {
328 int localid;
329 if (! m_idMap.TryGetValue(uuid,out localid))
330 {
331 localid = m_idMap.Count + 1;
332 m_idMap[uuid] = localid;
333 }
334
335 return localid;
336 }
337 }
338
339 public static QueueLogger QueueLog = null;
340
341 // -----------------------------------------------------------------
342 public void LogAvatarUpdateEvent(UUID client, UUID avatar, Int32 timeinqueue)
343 {
344 if (QueueLog == null)
345 QueueLog = new QueueLogger();
346
347 Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start);
348 lock(QueueLog)
349 {
350 int cid = QueueLog.LookupID(client);
351 int aid = QueueLog.LookupID(avatar);
352 QueueLog.Log.WriteLine("{0},AU,AV{1:D4},AV{2:D4},{3}",ticks,cid,aid,timeinqueue);
353 }
354 }
355
356 // -----------------------------------------------------------------
357 public void LogQueueProcessEvent(UUID client, PriorityQueue queue, uint maxup)
358 {
359 if (QueueLog == null)
360 QueueLog = new QueueLogger();
361
362 Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start);
363 lock(QueueLog)
364 {
365 int cid = QueueLog.LookupID(client);
366 QueueLog.Log.WriteLine("{0},PQ,AV{1:D4},{2},{3}",ticks,cid,maxup,queue.ToString());
367 }
368 }
369// -----------------------------------------------------------------
370// -----------------------------------------------------------------
371// -----------------------------------------------------------------
372// -----------------------------------------------------------------
373//</MIC>
374
375 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 304 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
376 protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients 305 protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients
377 306
@@ -387,6 +316,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
387 316
388 private int m_cachedTextureSerial; 317 private int m_cachedTextureSerial;
389 private PriorityQueue m_entityUpdates; 318 private PriorityQueue m_entityUpdates;
319 private PriorityQueue m_entityProps;
390 private Prioritizer m_prioritizer; 320 private Prioritizer m_prioritizer;
391 private bool m_disableFacelights = false; 321 private bool m_disableFacelights = false;
392 322
@@ -435,9 +365,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
435 protected IAssetService m_assetService; 365 protected IAssetService m_assetService;
436 private const bool m_checkPackets = true; 366 private const bool m_checkPackets = true;
437 367
438 private Timer m_propertiesPacketTimer;
439 private List<ObjectPropertiesPacket.ObjectDataBlock> m_propertiesBlocks = new List<ObjectPropertiesPacket.ObjectDataBlock>();
440
441 #endregion Class Members 368 #endregion Class Members
442 369
443 #region Properties 370 #region Properties
@@ -521,6 +448,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
521 m_scene = scene; 448 m_scene = scene;
522 449
523 m_entityUpdates = new PriorityQueue(m_scene.Entities.Count); 450 m_entityUpdates = new PriorityQueue(m_scene.Entities.Count);
451 m_entityProps = new PriorityQueue(m_scene.Entities.Count);
524 m_fullUpdateDataBlocksBuilder = new List<ObjectUpdatePacket.ObjectDataBlock>(); 452 m_fullUpdateDataBlocksBuilder = new List<ObjectUpdatePacket.ObjectDataBlock>();
525 m_killRecord = new HashSet<uint>(); 453 m_killRecord = new HashSet<uint>();
526// m_attachmentsSent = new HashSet<uint>(); 454// m_attachmentsSent = new HashSet<uint>();
@@ -544,9 +472,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
544 m_udpClient.OnQueueEmpty += HandleQueueEmpty; 472 m_udpClient.OnQueueEmpty += HandleQueueEmpty;
545 m_udpClient.OnPacketStats += PopulateStats; 473 m_udpClient.OnPacketStats += PopulateStats;
546 474
547 m_propertiesPacketTimer = new Timer(100);
548 m_propertiesPacketTimer.Elapsed += ProcessObjectPropertiesPacket;
549
550 m_prioritizer = new Prioritizer(m_scene); 475 m_prioritizer = new Prioritizer(m_scene);
551 476
552 RegisterLocalPacketHandlers(); 477 RegisterLocalPacketHandlers();
@@ -2468,7 +2393,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2468 2393
2469 packet.Effect = effectBlocks; 2394 packet.Effect = effectBlocks;
2470 2395
2471 OutPacket(packet, ThrottleOutPacketType.State); 2396 // OutPacket(packet, ThrottleOutPacketType.State);
2397 OutPacket(packet, ThrottleOutPacketType.Task);
2472 } 2398 }
2473 2399
2474 public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, 2400 public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember,
@@ -3670,9 +3596,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3670 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); 3596 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation));
3671 } 3597 }
3672 3598
3673 private Int32 m_LastQueueFill = 0;
3674 private uint m_maxUpdates = 0;
3675
3676 private void ProcessEntityUpdates(int maxUpdates) 3599 private void ProcessEntityUpdates(int maxUpdates)
3677 { 3600 {
3678 OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); 3601 OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>();
@@ -3680,41 +3603,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3680 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); 3603 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
3681 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); 3604 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
3682 3605
3606 // Check to see if this is a flush
3683 if (maxUpdates <= 0) 3607 if (maxUpdates <= 0)
3684 { 3608 {
3685 m_maxUpdates = Int32.MaxValue; 3609 maxUpdates = Int32.MaxValue;
3686 }
3687 else
3688 {
3689 if (m_maxUpdates == 0 || m_LastQueueFill == 0)
3690 {
3691 m_maxUpdates = (uint)maxUpdates;
3692 }
3693 else
3694 {
3695 if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200)
3696 m_maxUpdates += 5;
3697 else
3698 m_maxUpdates = m_maxUpdates >> 1;
3699 }
3700 m_maxUpdates = Util.Clamp<uint>(m_maxUpdates,10,500);
3701 } 3610 }
3702 m_LastQueueFill = Util.EnvironmentTickCount(); 3611
3703
3704 int updatesThisCall = 0; 3612 int updatesThisCall = 0;
3705 3613
3706 // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race 3614 // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race
3707 // condition where a kill can be processed before an out-of-date update for the same object. 3615 // condition where a kill can be processed before an out-of-date update for the same object.
3708 float avgTimeDilation = 1.0f; 3616 float avgTimeDilation = 1.0f;
3709 3617 IEntityUpdate iupdate;
3710 EntityUpdate update;
3711 Int32 timeinqueue; // this is just debugging code & can be dropped later 3618 Int32 timeinqueue; // this is just debugging code & can be dropped later
3712 3619
3713 while (updatesThisCall < m_maxUpdates) 3620 while (updatesThisCall < maxUpdates)
3714 { 3621 {
3715 lock (m_entityUpdates.SyncRoot) 3622 lock (m_entityUpdates.SyncRoot)
3716 if (!m_entityUpdates.TryDequeue(out update, out timeinqueue)) 3623 if (!m_entityUpdates.TryDequeue(out iupdate, out timeinqueue))
3717 break; 3624 break;
3625
3626 EntityUpdate update = (EntityUpdate)iupdate;
3627
3718 avgTimeDilation += update.TimeDilation; 3628 avgTimeDilation += update.TimeDilation;
3719 avgTimeDilation *= 0.5f; 3629 avgTimeDilation *= 0.5f;
3720 3630
@@ -3770,7 +3680,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3770 if (!found) 3680 if (!found)
3771 continue; 3681 continue;
3772 } 3682 }
3773
3774 if (part.ParentGroup.IsAttachment && m_disableFacelights) 3683 if (part.ParentGroup.IsAttachment && m_disableFacelights)
3775 { 3684 {
3776 if (part.ParentGroup.RootPart.Shape.State != (byte)AttachmentPoint.LeftHand && 3685 if (part.ParentGroup.RootPart.Shape.State != (byte)AttachmentPoint.LeftHand &&
@@ -3785,7 +3694,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3785 3694
3786 #region UpdateFlags to packet type conversion 3695 #region UpdateFlags to packet type conversion
3787 3696
3788 PrimUpdateFlags updateFlags = update.Flags; 3697 PrimUpdateFlags updateFlags = (PrimUpdateFlags)update.Flags;
3789 3698
3790 bool canUseCompressed = true; 3699 bool canUseCompressed = true;
3791 bool canUseImproved = true; 3700 bool canUseImproved = true;
@@ -3960,12 +3869,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3960 3869
3961 #endregion Primitive Packet/Data Sending Methods 3870 #endregion Primitive Packet/Data Sending Methods
3962 3871
3872 // These are used to implement an adaptive backoff in the number
3873 // of updates converted to packets. Since we don't want packets
3874 // to sit in the queue with old data, only convert enough updates
3875 // to packets that can be sent in 200ms.
3876 private Int32 m_LastQueueFill = 0;
3877 private Int32 m_maxUpdates = 0;
3878
3963 void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) 3879 void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories)
3964 { 3880 {
3965 if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) 3881 if ((categories & ThrottleOutPacketTypeFlags.Task) != 0)
3966 { 3882 {
3883 if (m_maxUpdates == 0 || m_LastQueueFill == 0)
3884 {
3885 m_maxUpdates = m_udpServer.PrimUpdatesPerCallback;
3886 }
3887 else
3888 {
3889 if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200)
3890 m_maxUpdates += 5;
3891 else
3892 m_maxUpdates = m_maxUpdates >> 1;
3893 }
3894 m_maxUpdates = Util.Clamp<Int32>(m_maxUpdates,10,500);
3895 m_LastQueueFill = Util.EnvironmentTickCount();
3896
3967 if (m_entityUpdates.Count > 0) 3897 if (m_entityUpdates.Count > 0)
3968 ProcessEntityUpdates(m_udpServer.PrimUpdatesPerCallback); 3898 ProcessEntityUpdates(m_maxUpdates);
3899
3900 if (m_entityProps.Count > 0)
3901 ProcessEntityPropertyRequests(m_maxUpdates);
3969 } 3902 }
3970 3903
3971 if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0) 3904 if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0)
@@ -4079,135 +4012,206 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4079 OutPacket(pack, ThrottleOutPacketType.Task); 4012 OutPacket(pack, ThrottleOutPacketType.Task);
4080 } 4013 }
4081 4014
4082 public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, 4015 private class ObjectPropertyUpdate : IEntityUpdate
4083 uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask,
4084 uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category,
4085 UUID LastOwnerID, string ObjectName, string Description)
4086 { 4016 {
4087 ObjectPropertiesFamilyPacket objPropFamilyPack = (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); 4017 internal bool SendFamilyProps;
4088 // TODO: don't create new blocks if recycling an old packet 4018 internal bool SendObjectProps;
4089 4019
4090 ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); 4020 public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam, bool sendobj)
4091 objPropDB.RequestFlags = RequestFlags; 4021 : base(entity,flags)
4092 objPropDB.ObjectID = ObjectUUID; 4022 {
4093 if (OwnerID == GroupID) 4023 SendFamilyProps = sendfam;
4094 objPropDB.OwnerID = UUID.Zero; 4024 SendObjectProps = sendobj;
4095 else 4025 }
4096 objPropDB.OwnerID = OwnerID; 4026 public void Update(ObjectPropertyUpdate update)
4097 objPropDB.GroupID = GroupID; 4027 {
4098 objPropDB.BaseMask = BaseMask; 4028 SendFamilyProps = SendFamilyProps || update.SendFamilyProps;
4099 objPropDB.OwnerMask = OwnerMask; 4029 SendObjectProps = SendObjectProps || update.SendObjectProps;
4100 objPropDB.GroupMask = GroupMask; 4030 Flags |= update.Flags;
4101 objPropDB.EveryoneMask = EveryoneMask; 4031 }
4102 objPropDB.NextOwnerMask = NextOwnerMask; 4032 }
4103 4033
4104 // TODO: More properties are needed in SceneObjectPart! 4034 public void SendObjectPropertiesFamilyData(ISceneEntity entity, uint requestFlags)
4105 objPropDB.OwnershipCost = OwnershipCost;
4106 objPropDB.SaleType = SaleType;
4107 objPropDB.SalePrice = SalePrice;
4108 objPropDB.Category = Category;
4109 objPropDB.LastOwnerID = LastOwnerID;
4110 objPropDB.Name = Util.StringToBytes256(ObjectName);
4111 objPropDB.Description = Util.StringToBytes256(Description);
4112 objPropFamilyPack.ObjectData = objPropDB;
4113 objPropFamilyPack.Header.Zerocoded = true;
4114 OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task);
4115 }
4116
4117 public void SendObjectPropertiesReply(
4118 UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID,
4119 UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID,
4120 UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName,
4121 string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask,
4122 uint BaseMask, byte saleType, int salePrice)
4123 { 4035 {
4124 //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); 4036 uint priority = 0; // time based ordering only
4125 // TODO: don't create new blocks if recycling an old packet 4037 lock (m_entityProps.SyncRoot)
4038 m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false));
4039 }
4126 4040
4127 ObjectPropertiesPacket.ObjectDataBlock block = 4041 public void SendObjectPropertiesReply(ISceneEntity entity)
4128 new ObjectPropertiesPacket.ObjectDataBlock(); 4042 {
4043 uint priority = 0; // time based ordering only
4044 lock (m_entityProps.SyncRoot)
4045 m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false,true));
4046 }
4129 4047
4130 block.ItemID = ItemID; 4048 private void ProcessEntityPropertyRequests(int maxUpdates)
4131 block.CreationDate = CreationDate; 4049 {
4132 block.CreatorID = CreatorUUID; 4050 OpenSim.Framework.Lazy<List<ObjectPropertiesFamilyPacket.ObjectDataBlock>> objectFamilyBlocks =
4133 block.FolderID = FolderUUID; 4051 new OpenSim.Framework.Lazy<List<ObjectPropertiesFamilyPacket.ObjectDataBlock>>();
4134 block.FromTaskID = FromTaskUUID;
4135 block.GroupID = GroupUUID;
4136 block.InventorySerial = InventorySerial;
4137 4052
4138 block.LastOwnerID = LastOwnerUUID; 4053 OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>> objectPropertiesBlocks =
4139 // proper.ObjectData[0].LastOwnerID = UUID.Zero; 4054 new OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>>();
4140 4055
4141 block.ObjectID = ObjectUUID; 4056 IEntityUpdate iupdate;
4142 if (OwnerUUID == GroupUUID) 4057 Int32 timeinqueue; // this is just debugging code & can be dropped later
4143 block.OwnerID = UUID.Zero;
4144 else
4145 block.OwnerID = OwnerUUID;
4146 block.TouchName = Util.StringToBytes256(TouchTitle);
4147 block.TextureID = TextureID;
4148 block.SitName = Util.StringToBytes256(SitTitle);
4149 block.Name = Util.StringToBytes256(ItemName);
4150 block.Description = Util.StringToBytes256(ItemDescription);
4151 block.OwnerMask = OwnerMask;
4152 block.NextOwnerMask = NextOwnerMask;
4153 block.GroupMask = GroupMask;
4154 block.EveryoneMask = EveryoneMask;
4155 block.BaseMask = BaseMask;
4156 // proper.ObjectData[0].AggregatePerms = 53;
4157 // proper.ObjectData[0].AggregatePermTextures = 0;
4158 // proper.ObjectData[0].AggregatePermTexturesOwner = 0;
4159 block.SaleType = saleType;
4160 block.SalePrice = salePrice;
4161 4058
4162 lock (m_propertiesPacketTimer) 4059 int updatesThisCall = 0;
4060 while (updatesThisCall < m_maxUpdates)
4163 { 4061 {
4164 m_propertiesBlocks.Add(block); 4062 lock (m_entityProps.SyncRoot)
4063 if (!m_entityProps.TryDequeue(out iupdate, out timeinqueue))
4064 break;
4165 4065
4166 int length = 0; 4066 ObjectPropertyUpdate update = (ObjectPropertyUpdate)iupdate;
4167 foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) 4067 if (update.SendFamilyProps)
4168 { 4068 {
4169 length += b.Length; 4069 if (update.Entity is SceneObjectPart)
4070 {
4071 SceneObjectPart sop = (SceneObjectPart)update.Entity;
4072 ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags);
4073 objectFamilyBlocks.Value.Add(objPropDB);
4074 }
4170 } 4075 }
4171 if (length > 1100) // FIXME: use real MTU 4076
4077 if (update.SendObjectProps)
4172 { 4078 {
4173 ProcessObjectPropertiesPacket(null, null); 4079 if (update.Entity is SceneObjectPart)
4174 m_propertiesPacketTimer.Stop(); 4080 {
4175 return; 4081 SceneObjectPart sop = (SceneObjectPart)update.Entity;
4082 ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop);
4083 objectPropertiesBlocks.Value.Add(objPropDB);
4084 }
4176 } 4085 }
4177 4086
4178 m_propertiesPacketTimer.Stop(); 4087 updatesThisCall++;
4179 m_propertiesPacketTimer.Start();
4180 } 4088 }
4089
4181 4090
4182 //proper.Header.Zerocoded = true; 4091 Int32 ppcnt = 0;
4183 //OutPacket(proper, ThrottleOutPacketType.Task); 4092 Int32 pbcnt = 0;
4184 } 4093
4094 if (objectPropertiesBlocks.IsValueCreated)
4095 {
4096 List<ObjectPropertiesPacket.ObjectDataBlock> blocks = objectPropertiesBlocks.Value;
4185 4097
4186 private void ProcessObjectPropertiesPacket(Object sender, ElapsedEventArgs e) 4098 ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties);
4187 { 4099 packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count];
4188 ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); 4100 for (int i = 0; i < blocks.Count; i++)
4101 packet.ObjectData[i] = blocks[i];
4189 4102
4190 lock (m_propertiesPacketTimer) 4103 packet.Header.Zerocoded = true;
4104 OutPacket(packet, ThrottleOutPacketType.Task, true);
4105
4106 pbcnt += blocks.Count;
4107 ppcnt++;
4108 }
4109
4110 Int32 fpcnt = 0;
4111 Int32 fbcnt = 0;
4112
4113 if (objectFamilyBlocks.IsValueCreated)
4191 { 4114 {
4192 m_propertiesPacketTimer.Stop(); 4115 List<ObjectPropertiesFamilyPacket.ObjectDataBlock> blocks = objectFamilyBlocks.Value;
4193 4116
4194 if (m_propertiesBlocks.Count == 0) 4117 // ObjectPropertiesFamilyPacket objPropFamilyPack =
4195 return; 4118 // (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily);
4119 //
4120 // objPropFamilyPack.ObjectData = new ObjectPropertiesFamilyPacket.ObjectDataBlock[blocks.Count];
4121 // for (int i = 0; i < blocks.Count; i++)
4122 // objPropFamilyPack.ObjectData[i] = blocks[i];
4123 //
4124 // OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task, true);
4196 4125
4197 proper.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[m_propertiesBlocks.Count]; 4126 // one packet per object block... uggh...
4127 for (int i = 0; i < blocks.Count; i++)
4128 {
4129 ObjectPropertiesFamilyPacket packet =
4130 (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily);
4198 4131
4199 int index = 0; 4132 packet.ObjectData = blocks[i];
4133 packet.Header.Zerocoded = true;
4134 OutPacket(packet, ThrottleOutPacketType.Task);
4200 4135
4201 foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) 4136 fpcnt++;
4202 { 4137 fbcnt++;
4203 proper.ObjectData[index++] = b;
4204 } 4138 }
4205 4139
4206 m_propertiesBlocks.Clear();
4207 } 4140 }
4141
4142 // m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt);
4143 // m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt);
4144 }
4145
4146 private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags)
4147 {
4148 ObjectPropertiesFamilyPacket.ObjectDataBlock block = new ObjectPropertiesFamilyPacket.ObjectDataBlock();
4208 4149
4209 proper.Header.Zerocoded = true; 4150 block.RequestFlags = requestFlags;
4210 OutPacket(proper, ThrottleOutPacketType.Task); 4151 block.ObjectID = sop.UUID;
4152 if (sop.OwnerID == sop.GroupID)
4153 block.OwnerID = UUID.Zero;
4154 else
4155 block.OwnerID = sop.OwnerID;
4156 block.GroupID = sop.GroupID;
4157 block.BaseMask = sop.BaseMask;
4158 block.OwnerMask = sop.OwnerMask;
4159 block.GroupMask = sop.GroupMask;
4160 block.EveryoneMask = sop.EveryoneMask;
4161 block.NextOwnerMask = sop.NextOwnerMask;
4162
4163 // TODO: More properties are needed in SceneObjectPart!
4164 block.OwnershipCost = sop.OwnershipCost;
4165 block.SaleType = sop.ObjectSaleType;
4166 block.SalePrice = sop.SalePrice;
4167 block.Category = sop.Category;
4168 block.LastOwnerID = sop.CreatorID; // copied from old SOG call... is this right?
4169 block.Name = Util.StringToBytes256(sop.Name);
4170 block.Description = Util.StringToBytes256(sop.Description);
4171
4172 return block;
4173 }
4174
4175 private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop)
4176 {
4177 //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties);
4178 // TODO: don't create new blocks if recycling an old packet
4179
4180 ObjectPropertiesPacket.ObjectDataBlock block =
4181 new ObjectPropertiesPacket.ObjectDataBlock();
4182
4183 block.ObjectID = sop.UUID;
4184 block.Name = Util.StringToBytes256(sop.Name);
4185 block.Description = Util.StringToBytes256(sop.Description);
4186
4187 block.CreationDate = (ulong)sop.CreationDate * 1000000; // viewer wants date in microseconds
4188 block.CreatorID = sop.CreatorID;
4189 block.GroupID = sop.GroupID;
4190 block.LastOwnerID = sop.LastOwnerID;
4191 if (sop.OwnerID == sop.GroupID)
4192 block.OwnerID = UUID.Zero;
4193 else
4194 block.OwnerID = sop.OwnerID;
4195
4196 block.ItemID = sop.FromUserInventoryItemID;
4197 block.FolderID = UUID.Zero; // sop.FromFolderID ??
4198 block.FromTaskID = UUID.Zero; // ???
4199 block.InventorySerial = (short)sop.InventorySerial;
4200
4201 SceneObjectPart root = sop.ParentGroup.RootPart;
4202
4203 block.TouchName = Util.StringToBytes256(root.TouchName);
4204 block.TextureID = new byte[0]; // TextureID ???
4205 block.SitName = Util.StringToBytes256(root.SitName);
4206 block.OwnerMask = root.OwnerMask;
4207 block.NextOwnerMask = root.NextOwnerMask;
4208 block.GroupMask = root.GroupMask;
4209 block.EveryoneMask = root.EveryoneMask;
4210 block.BaseMask = root.BaseMask;
4211 block.SaleType = root.ObjectSaleType;
4212 block.SalePrice = root.SalePrice;
4213
4214 return block;
4211 } 4215 }
4212 4216
4213 #region Estate Data Sending Methods 4217 #region Estate Data Sending Methods
@@ -4548,6 +4552,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4548 4552
4549 public void SendForceClientSelectObjects(List<uint> ObjectIDs) 4553 public void SendForceClientSelectObjects(List<uint> ObjectIDs)
4550 { 4554 {
4555 m_log.WarnFormat("[LLCLIENTVIEW] sending select with {0} objects", ObjectIDs.Count);
4556
4551 bool firstCall = true; 4557 bool firstCall = true;
4552 const int MAX_OBJECTS_PER_PACKET = 251; 4558 const int MAX_OBJECTS_PER_PACKET = 251;
4553 ForceObjectSelectPacket pack = (ForceObjectSelectPacket)PacketPool.Instance.GetPacket(PacketType.ForceObjectSelect); 4559 ForceObjectSelectPacket pack = (ForceObjectSelectPacket)PacketPool.Instance.GetPacket(PacketType.ForceObjectSelect);
@@ -11491,7 +11497,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11491 if (logPacket) 11497 if (logPacket)
11492 m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); 11498 m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type);
11493 } 11499 }
11494 11500
11495 m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); 11501 m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting);
11496 } 11502 }
11497 11503
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
index 0fa074d..01d7122 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>
@@ -175,7 +177,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
175 m_maxRTO = maxRTO; 177 m_maxRTO = maxRTO;
176 178
177 // Create a token bucket throttle for this client that has the scene token bucket as a parent 179 // Create a token bucket throttle for this client that has the scene token bucket as a parent
178 m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); 180 m_throttleClient = new TokenBucket(parentThrottle, rates.TotalLimit);
181 // Create a token bucket throttle for the total categary with the client bucket as a throttle
182 m_throttleCategory = new TokenBucket(m_throttleClient, rates.TotalLimit);
179 // Create an array of token buckets for this clients different throttle categories 183 // Create an array of token buckets for this clients different throttle categories
180 m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; 184 m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
181 185
@@ -186,7 +190,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
186 // Initialize the packet outboxes, where packets sit while they are waiting for tokens 190 // Initialize the packet outboxes, where packets sit while they are waiting for tokens
187 m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>(); 191 m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
188 // Initialize the token buckets that control the throttling for each category 192 // Initialize the token buckets that control the throttling for each category
189 m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); 193 m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetLimit(type));
190 } 194 }
191 195
192 // Default the retransmission timeout to three seconds 196 // Default the retransmission timeout to three seconds
@@ -207,6 +211,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
207 m_packetOutboxes[i].Clear(); 211 m_packetOutboxes[i].Clear();
208 m_nextPackets[i] = null; 212 m_nextPackets[i] = null;
209 } 213 }
214
215 // pull the throttle out of the scene throttle
216 m_throttleClient.Parent.UnregisterRequest(m_throttleClient);
210 OnPacketStats = null; 217 OnPacketStats = null;
211 OnQueueEmpty = null; 218 OnQueueEmpty = null;
212 } 219 }
@@ -217,6 +224,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
217 /// <returns>Information about the client connection</returns> 224 /// <returns>Information about the client connection</returns>
218 public ClientInfo GetClientInfo() 225 public ClientInfo GetClientInfo()
219 { 226 {
227///<mic>
228 TokenBucket tb;
229
230 tb = m_throttleClient.Parent;
231 m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest,"ROOT");
232
233 tb = m_throttleClient;
234 m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CLIENT");
235
236 tb = m_throttleCategory;
237 m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CATEGORY");
238
239 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
240 {
241 tb = m_throttleCategories[i];
242 m_log.WarnFormat("[TOKENS] {4} <{0}:{1}>: Actual={2},Requested={3}",AgentID,i,tb.DripRate,tb.RequestedDripRate," BUCKET");
243 }
244
245///</mic>
246
220 // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists 247 // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists
221 // of pending and needed ACKs for every client every time some method wants information about 248 // of pending and needed ACKs for every client every time some method wants information about
222 // this connection is a recipe for poor performance 249 // this connection is a recipe for poor performance
@@ -224,13 +251,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
224 info.pendingAcks = new Dictionary<uint, uint>(); 251 info.pendingAcks = new Dictionary<uint, uint>();
225 info.needAck = new Dictionary<uint, byte[]>(); 252 info.needAck = new Dictionary<uint, byte[]>();
226 253
227 info.resendThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; 254 info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate;
228 info.landThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; 255 info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
229 info.windThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; 256 info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
230 info.cloudThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; 257 info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
231 info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; 258 // info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
232 info.assetThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; 259 info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
233 info.textureThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; 260 info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
261 info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
234 info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + 262 info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle +
235 info.taskThrottle + info.assetThrottle + info.textureThrottle; 263 info.taskThrottle + info.assetThrottle + info.textureThrottle;
236 264
@@ -318,8 +346,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
318 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; 346 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
319 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); 347 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
320 // State is a subcategory of task that we allocate a percentage to 348 // State is a subcategory of task that we allocate a percentage to
321 int state = (int)((float)task * STATE_TASK_PERCENTAGE); 349 int state = 0;
322 task -= state; 350 // int state = (int)((float)task * STATE_TASK_PERCENTAGE);
351 // task -= state;
323 352
324 // Make sure none of the throttles are set below our packet MTU, 353 // Make sure none of the throttles are set below our packet MTU,
325 // otherwise a throttle could become permanently clogged 354 // otherwise a throttle could become permanently clogged
@@ -340,40 +369,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP
340 // Update the token buckets with new throttle values 369 // Update the token buckets with new throttle values
341 TokenBucket bucket; 370 TokenBucket bucket;
342 371
343 bucket = m_throttle; 372 bucket = m_throttleCategory;
344 bucket.MaxBurst = total; 373 bucket.RequestedDripRate = total;
345 374
346 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; 375 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
347 bucket.DripRate = resend; 376 bucket.RequestedDripRate = resend;
348 bucket.MaxBurst = resend;
349 377
350 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; 378 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land];
351 bucket.DripRate = land; 379 bucket.RequestedDripRate = land;
352 bucket.MaxBurst = land;
353 380
354 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; 381 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind];
355 bucket.DripRate = wind; 382 bucket.RequestedDripRate = wind;
356 bucket.MaxBurst = wind;
357 383
358 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; 384 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud];
359 bucket.DripRate = cloud; 385 bucket.RequestedDripRate = cloud;
360 bucket.MaxBurst = cloud;
361 386
362 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; 387 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset];
363 bucket.DripRate = asset; 388 bucket.RequestedDripRate = asset;
364 bucket.MaxBurst = asset;
365 389
366 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; 390 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task];
367 bucket.DripRate = task + state; 391 bucket.RequestedDripRate = task;
368 bucket.MaxBurst = task + state;
369 392
370 bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; 393 bucket = m_throttleCategories[(int)ThrottleOutPacketType.State];
371 bucket.DripRate = state; 394 bucket.RequestedDripRate = state;
372 bucket.MaxBurst = state;
373 395
374 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; 396 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture];
375 bucket.DripRate = texture; 397 bucket.RequestedDripRate = texture;
376 bucket.MaxBurst = texture;
377 398
378 // Reset the packed throttles cached data 399 // Reset the packed throttles cached data
379 m_packedThrottles = null; 400 m_packedThrottles = null;
@@ -388,14 +409,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
388 data = new byte[7 * 4]; 409 data = new byte[7 * 4];
389 int i = 0; 410 int i = 0;
390 411
391 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4; 412 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate), 0, data, i, 4); i += 4;
392 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; 413 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate), 0, data, i, 4); i += 4;
393 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; 414 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate), 0, data, i, 4); i += 4;
394 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; 415 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate), 0, data, i, 4); i += 4;
395 Buffer.BlockCopy(Utils.FloatToBytes((float)(m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) + 416 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate), 0, data, i, 4); i += 4;
396 m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4; 417 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate), 0, data, i, 4); i += 4;
397 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; 418 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate), 0, data, i, 4); i += 4;
398 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4;
399 419
400 m_packedThrottles = data; 420 m_packedThrottles = data;
401 } 421 }
@@ -428,6 +448,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
428 448
429 TokenBucket bucket = m_throttleCategories[category]; 449 TokenBucket bucket = m_throttleCategories[category];
430 450
451 // Don't send this packet if there is already a packet waiting in the queue
452 // even if we have the tokens to send it, tokens should go to the already
453 // queued packets
454 if (queue.Count > 0)
455 {
456 queue.Enqueue(packet);
457 return true;
458 }
459
460
431 if (!forceQueue && bucket.RemoveTokens(packet.Buffer.DataLength)) 461 if (!forceQueue && bucket.RemoveTokens(packet.Buffer.DataLength))
432 { 462 {
433 // Enough tokens were removed from the bucket, the packet will not be queued 463 // 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 c865c0f..6decc7b 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 91e3d20..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;
65
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;
51 71
52 #region Properties 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,46 +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)
173 {
174 m_identifier = m_counter++;
175
176 Parent = parent;
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;
181 }
182
183#endregion Constructor
184
185 /// <summary>
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.
191 /// </summary>
192 private double DripRateModifier()
193 {
194 Int64 driprate = DripRate;
195 return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest;
196 }
197
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)
132 { 213 {
133 this.parent = parent; 214 m_children[child] = request;
134 MaxBurst = maxBurst; 215 // m_totalDripRequest = m_children.Values.Sum();
135 DripRate = dripRate; 216
136 lastDrip = Environment.TickCount; 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));
137 } 224 }
138 225
139 /// <summary> 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>
140 /// Remove a given number of tokens from the bucket 245 /// Remove a given number of tokens from the bucket
141 /// </summary> 246 /// </summary>
142 /// <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>
143 /// <returns>True if the requested number of tokens were removed from 248 /// <returns>True if the requested number of tokens were removed from
144 /// the bucket, otherwise false</returns> 249 /// the bucket, otherwise false</returns>
145 public bool RemoveTokens(int amount) 250 public bool RemoveTokens(Int64 amount)
146 { 251 {
147 if (maxBurst == 0) 252 // Deposit tokens for this interval
148 {
149 return true;
150 }
151
152 if (amount > maxBurst)
153 {
154 throw new Exception("amount " + amount + " exceeds maxBurst " + maxBurst);
155 }
156
157 Drip(); 253 Drip();
158 254
159 if (content < amount) 255 // If we have enough tokens then remove them and return
256 if (m_tokenCount - amount >= 0)
160 { 257 {
161 return false; 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;
261 return true;
162 } 262 }
163 263
164 if (parent != null && !parent.RemoveTokens(amount)) 264 return false;
165 { 265 }
166 return false;
167 }
168 266
169 content -= amount; 267 /// <summary>
170 return true; 268 /// Deposit tokens into the bucket from a child bucket that did
269 /// not use all of its available tokens
270 /// </summary>
271 private void Deposit(Int64 count)
272 {
273 m_tokenCount += count;
274
275 // Deposit the overflow in the parent bucket, this is how we share
276 // unused bandwidth
277 Int64 burstrate = BurstRate;
278 if (m_tokenCount > burstrate)
279 m_tokenCount = burstrate;
171 } 280 }
172 281
173 /// <summary> 282 /// <summary>
@@ -176,30 +285,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
176 /// call to Drip 285 /// call to Drip
177 /// </summary> 286 /// </summary>
178 /// <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>
179 public bool Drip() 288 private void Drip()
180 { 289 {
181 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)
182 { 293 {
183 content = maxBurst; 294 m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0");
184 return true; 295 return;
185 } 296 }
297
298 // Determine the interval over which we are adding tokens, never add
299 // more than a single quantum of tokens
300 Int32 now = Environment.TickCount & Int32.MaxValue;
301 Int32 deltaMS = Math.Min(now - m_lastDrip, m_ticksPerQuantum);
186 302
187 int now = Environment.TickCount; 303 m_lastDrip = now;
188 int deltaMS = now - lastDrip;
189 lastDrip = now;
190 304
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
191 if (deltaMS <= 0) 307 if (deltaMS <= 0)
192 { 308 return;
193 return false;
194 }
195 309
196 long dripAmount = (long)deltaMS * (long)tokensPerMS + (long)content; 310 Deposit(deltaMS * DripRate / m_ticksPerQuantum);
197 if (dripAmount > maxBurst)
198 {
199 dripAmount = maxBurst;
200 }
201 content = (int)dripAmount;
202 return true;
203 } 311 }
204 } 312 }
205} 313}