aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorDan Lake2011-04-18 16:48:49 -0700
committerMic Bowman2011-04-19 08:10:01 -0700
commit08d8a3e5808b790fbbd7ba3f460603db66aeaff2 (patch)
tree251da046af3aaec1c1594731d7bbf41d87c4d5cd
parentMove mesh on/off swtich from [Startup] to [Mesh] in anticipation of future co... (diff)
downloadopensim-SC_OLD-08d8a3e5808b790fbbd7ba3f460603db66aeaff2.zip
opensim-SC_OLD-08d8a3e5808b790fbbd7ba3f460603db66aeaff2.tar.gz
opensim-SC_OLD-08d8a3e5808b790fbbd7ba3f460603db66aeaff2.tar.bz2
opensim-SC_OLD-08d8a3e5808b790fbbd7ba3f460603db66aeaff2.tar.xz
Requeue unacknowledged entity updates rather than resend then "as is".
Often, by the time the UDPServer realizes that an entity update packet has not been acknowledged, there is a newer update for the same entity already queued up or there is a higher priority update that should be sent first. This patch eliminates 1:1 packet resends for unacked entity update packets. Insteawd, unacked update packets are decomposed into the original entity updates and those updates are placed back into the priority queues based on their new priority but the original update timestamp. This will generally place them at the head of the line to be put back on the wire as a new outgoing packet but prevents the resend queue from filling up with multiple stale updates for the same entity. This new approach takes advantage of the UDP nature of the Linden protocol in that the intent of a reliable update packet is that if it goes unacknowledge, SOMETHING has to happen to get the update to the client. We are simply making sure that we are resending current object state rather than stale object state. Additionally, this patch includes a generalized callback mechanism so that any caller can specify their own method to call when a packet expires without being acknowledged. We use this mechanism to requeue update packets and otherwise use the UDPServer default method of just putting expired packets in the resend queue.
-rw-r--r--OpenSim/Framework/IClientAPI.cs59
-rw-r--r--OpenSim/Framework/Util.cs17
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs96
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs64
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs6
5 files changed, 176 insertions, 66 deletions
diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs
index f573c32..069987b 100644
--- a/OpenSim/Framework/IClientAPI.cs
+++ b/OpenSim/Framework/IClientAPI.cs
@@ -572,34 +572,69 @@ namespace OpenSim.Framework
572 572
573 public class IEntityUpdate 573 public class IEntityUpdate
574 { 574 {
575 public ISceneEntity Entity; 575 private ISceneEntity m_entity;
576 public uint Flags; 576 private uint m_flags;
577 private int m_updateTime;
578
579 public ISceneEntity Entity
580 {
581 get { return m_entity; }
582 }
583
584 public uint Flags
585 {
586 get { return m_flags; }
587 }
588
589 public int UpdateTime
590 {
591 get { return m_updateTime; }
592 }
577 593
578 public virtual void Update(IEntityUpdate update) 594 public virtual void Update(IEntityUpdate update)
579 { 595 {
580 this.Flags |= update.Flags; 596 m_flags |= update.Flags;
597
598 // Use the older of the updates as the updateTime
599 if (Util.EnvironmentTickCountCompare(UpdateTime, update.UpdateTime) > 0)
600 m_updateTime = update.UpdateTime;
581 } 601 }
582 602
583 public IEntityUpdate(ISceneEntity entity, uint flags) 603 public IEntityUpdate(ISceneEntity entity, uint flags)
584 { 604 {
585 Entity = entity; 605 m_entity = entity;
586 Flags = flags; 606 m_flags = flags;
607 m_updateTime = Util.EnvironmentTickCount();
608 }
609
610 public IEntityUpdate(ISceneEntity entity, uint flags, Int32 updateTime)
611 {
612 m_entity = entity;
613 m_flags = flags;
614 m_updateTime = updateTime;
587 } 615 }
588 } 616 }
589
590 617
591 public class EntityUpdate : IEntityUpdate 618 public class EntityUpdate : IEntityUpdate
592 { 619 {
593 // public ISceneEntity Entity; 620 private float m_timeDilation;
594 // public PrimUpdateFlags Flags; 621
595 public float TimeDilation; 622 public float TimeDilation
623 {
624 get { return m_timeDilation; }
625 }
596 626
597 public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation) 627 public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation)
598 : base(entity,(uint)flags) 628 : base(entity, (uint)flags)
599 { 629 {
600 //Entity = entity;
601 // Flags = flags; 630 // Flags = flags;
602 TimeDilation = timedilation; 631 m_timeDilation = timedilation;
632 }
633
634 public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation, Int32 updateTime)
635 : base(entity,(uint)flags,updateTime)
636 {
637 m_timeDilation = timedilation;
603 } 638 }
604 } 639 }
605 640
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs
index 5a5046e..aaa2724 100644
--- a/OpenSim/Framework/Util.cs
+++ b/OpenSim/Framework/Util.cs
@@ -1537,6 +1537,23 @@ namespace OpenSim.Framework
1537 return (diff >= 0) ? diff : (diff + EnvironmentTickCountMask + 1); 1537 return (diff >= 0) ? diff : (diff + EnvironmentTickCountMask + 1);
1538 } 1538 }
1539 1539
1540 // Returns value of Tick Count A - TickCount B accounting for wrapping of TickCount
1541 // Assumes both tcA and tcB came from previous calls to Util.EnvironmentTickCount().
1542 // A positive return value indicates A occured later than B
1543 public static Int32 EnvironmentTickCountCompare(Int32 tcA, Int32 tcB)
1544 {
1545 // A, B and TC are all between 0 and 0x3fffffff
1546 int tc = EnvironmentTickCount();
1547
1548 if (tc - tcA >= 0)
1549 tcA += EnvironmentTickCountMask + 1;
1550
1551 if (tc - tcB >= 0)
1552 tcB += EnvironmentTickCountMask + 1;
1553
1554 return tcA - tcB;
1555 }
1556
1540 /// <summary> 1557 /// <summary>
1541 /// Prints the call stack at any given point. Useful for debugging. 1558 /// Prints the call stack at any given point. Useful for debugging.
1542 /// </summary> 1559 /// </summary>
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 1f7e66d..14c5d6c 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -3561,6 +3561,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3561 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); 3561 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation));
3562 } 3562 }
3563 3563
3564 /// <summary>
3565 /// Requeue an EntityUpdate when it was not acknowledged by the client.
3566 /// We will update the priority and put it in the correct queue, merging update flags
3567 /// with any other updates that may be queued for the same entity.
3568 /// The original update time is used for the merged update.
3569 /// </summary>
3570 public void ResendPrimUpdate(EntityUpdate update)
3571 {
3572 // If the update exists in priority queue, it will be updated.
3573 // If it does not exist then it will be added with the current (rather than its original) priority
3574 uint priority = m_prioritizer.GetUpdatePriority(this, update.Entity);
3575
3576 lock (m_entityUpdates.SyncRoot)
3577 m_entityUpdates.Enqueue(priority, update);
3578 }
3579
3580 /// <summary>
3581 /// Requeue a list of EntityUpdates when they were not acknowledged by the client.
3582 /// We will update the priority and put it in the correct queue, merging update flags
3583 /// with any other updates that may be queued for the same entity.
3584 /// The original update time is used for the merged update.
3585 /// </summary>
3586 void ResendPrimUpdates(List<EntityUpdate> updates)
3587 {
3588 foreach (EntityUpdate update in updates)
3589 ResendPrimUpdate(update);
3590 }
3591
3564 private void ProcessEntityUpdates(int maxUpdates) 3592 private void ProcessEntityUpdates(int maxUpdates)
3565 { 3593 {
3566 OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); 3594 OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>();
@@ -3568,6 +3596,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3568 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); 3596 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
3569 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); 3597 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
3570 3598
3599 OpenSim.Framework.Lazy<List<EntityUpdate>> objectUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
3600 OpenSim.Framework.Lazy<List<EntityUpdate>> compressedUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
3601 OpenSim.Framework.Lazy<List<EntityUpdate>> terseUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
3602 OpenSim.Framework.Lazy<List<EntityUpdate>> terseAgentUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
3603
3571 // Check to see if this is a flush 3604 // Check to see if this is a flush
3572 if (maxUpdates <= 0) 3605 if (maxUpdates <= 0)
3573 { 3606 {
@@ -3583,7 +3616,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3583 float avgTimeDilation = 1.0f; 3616 float avgTimeDilation = 1.0f;
3584 IEntityUpdate iupdate; 3617 IEntityUpdate iupdate;
3585 Int32 timeinqueue; // this is just debugging code & can be dropped later 3618 Int32 timeinqueue; // this is just debugging code & can be dropped later
3586 3619
3587 while (updatesThisCall < maxUpdates) 3620 while (updatesThisCall < maxUpdates)
3588 { 3621 {
3589 lock (m_entityUpdates.SyncRoot) 3622 lock (m_entityUpdates.SyncRoot)
@@ -3688,24 +3721,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3688 if (update.Entity is ScenePresence) 3721 if (update.Entity is ScenePresence)
3689 { 3722 {
3690 objectUpdateBlocks.Value.Add(CreateAvatarUpdateBlock((ScenePresence)update.Entity)); 3723 objectUpdateBlocks.Value.Add(CreateAvatarUpdateBlock((ScenePresence)update.Entity));
3724 objectUpdates.Value.Add(update);
3691 } 3725 }
3692 else 3726 else
3693 { 3727 {
3694 objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId)); 3728 objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId));
3729 objectUpdates.Value.Add(update);
3695 } 3730 }
3696 } 3731 }
3697 else if (!canUseImproved) 3732 else if (!canUseImproved)
3698 { 3733 {
3699 compressedUpdateBlocks.Value.Add(CreateCompressedUpdateBlock((SceneObjectPart)update.Entity, updateFlags)); 3734 compressedUpdateBlocks.Value.Add(CreateCompressedUpdateBlock((SceneObjectPart)update.Entity, updateFlags));
3735 compressedUpdates.Value.Add(update);
3700 } 3736 }
3701 else 3737 else
3702 { 3738 {
3703 if (update.Entity is ScenePresence && ((ScenePresence)update.Entity).UUID == AgentId) 3739 if (update.Entity is ScenePresence && ((ScenePresence)update.Entity).UUID == AgentId)
3740 {
3704 // Self updates go into a special list 3741 // Self updates go into a special list
3705 terseAgentUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures))); 3742 terseAgentUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)));
3743 terseAgentUpdates.Value.Add(update);
3744 }
3706 else 3745 else
3746 {
3707 // Everything else goes here 3747 // Everything else goes here
3708 terseUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures))); 3748 terseUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)));
3749 terseUpdates.Value.Add(update);
3750 }
3709 } 3751 }
3710 3752
3711 #endregion Block Construction 3753 #endregion Block Construction
@@ -3713,28 +3755,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3713 3755
3714 3756
3715 #region Packet Sending 3757 #region Packet Sending
3716
3717 //const float TIME_DILATION = 1.0f;
3718
3719
3720 ushort timeDilation = Utils.FloatToUInt16(avgTimeDilation, 0.0f, 1.0f); 3758 ushort timeDilation = Utils.FloatToUInt16(avgTimeDilation, 0.0f, 1.0f);
3721 3759
3722 if (terseAgentUpdateBlocks.IsValueCreated) 3760 if (terseAgentUpdateBlocks.IsValueCreated)
3723 { 3761 {
3724 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseAgentUpdateBlocks.Value; 3762 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseAgentUpdateBlocks.Value;
3725 3763
3726 ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); 3764 ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket();
3727 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; 3765 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
3728 packet.RegionData.TimeDilation = timeDilation; 3766 packet.RegionData.TimeDilation = timeDilation;
3729 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; 3767 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];
3730 3768
3731 for (int i = 0; i < blocks.Count; i++) 3769 for (int i = 0; i < blocks.Count; i++)
3732 packet.ObjectData[i] = blocks[i]; 3770 packet.ObjectData[i] = blocks[i];
3733 3771 // If any of the packets created from this call go unacknowledged, all of the updates will be resent
3734 3772 OutPacket(packet, ThrottleOutPacketType.Unknown, true, delegate() { ResendPrimUpdates(terseAgentUpdates.Value); });
3735 OutPacket(packet, ThrottleOutPacketType.Unknown, true);
3736 } 3773 }
3737 3774
3738 if (objectUpdateBlocks.IsValueCreated) 3775 if (objectUpdateBlocks.IsValueCreated)
3739 { 3776 {
3740 List<ObjectUpdatePacket.ObjectDataBlock> blocks = objectUpdateBlocks.Value; 3777 List<ObjectUpdatePacket.ObjectDataBlock> blocks = objectUpdateBlocks.Value;
@@ -3746,8 +3783,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3746 3783
3747 for (int i = 0; i < blocks.Count; i++) 3784 for (int i = 0; i < blocks.Count; i++)
3748 packet.ObjectData[i] = blocks[i]; 3785 packet.ObjectData[i] = blocks[i];
3749 3786 // If any of the packets created from this call go unacknowledged, all of the updates will be resent
3750 OutPacket(packet, ThrottleOutPacketType.Task, true); 3787 OutPacket(packet, ThrottleOutPacketType.Task, true, delegate() { ResendPrimUpdates(objectUpdates.Value); });
3751 } 3788 }
3752 3789
3753 if (compressedUpdateBlocks.IsValueCreated) 3790 if (compressedUpdateBlocks.IsValueCreated)
@@ -3761,10 +3798,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3761 3798
3762 for (int i = 0; i < blocks.Count; i++) 3799 for (int i = 0; i < blocks.Count; i++)
3763 packet.ObjectData[i] = blocks[i]; 3800 packet.ObjectData[i] = blocks[i];
3764 3801 // If any of the packets created from this call go unacknowledged, all of the updates will be resent
3765 OutPacket(packet, ThrottleOutPacketType.Task, true); 3802 OutPacket(packet, ThrottleOutPacketType.Task, true, delegate() { ResendPrimUpdates(compressedUpdates.Value); });
3766 } 3803 }
3767 3804
3768 if (terseUpdateBlocks.IsValueCreated) 3805 if (terseUpdateBlocks.IsValueCreated)
3769 { 3806 {
3770 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value; 3807 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value;
@@ -3776,8 +3813,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3776 3813
3777 for (int i = 0; i < blocks.Count; i++) 3814 for (int i = 0; i < blocks.Count; i++)
3778 packet.ObjectData[i] = blocks[i]; 3815 packet.ObjectData[i] = blocks[i];
3779 3816 // If any of the packets created from this call go unacknowledged, all of the updates will be resent
3780 OutPacket(packet, ThrottleOutPacketType.Task, true); 3817 OutPacket(packet, ThrottleOutPacketType.Task, true, delegate() { ResendPrimUpdates(terseUpdates.Value); });
3781 } 3818 }
3782 } 3819 }
3783 3820
@@ -3969,7 +4006,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3969 { 4006 {
3970 SendFamilyProps = SendFamilyProps || update.SendFamilyProps; 4007 SendFamilyProps = SendFamilyProps || update.SendFamilyProps;
3971 SendObjectProps = SendObjectProps || update.SendObjectProps; 4008 SendObjectProps = SendObjectProps || update.SendObjectProps;
3972 Flags |= update.Flags; 4009 // other properties may need to be updated by base class
4010 base.Update(update);
3973 } 4011 }
3974 } 4012 }
3975 4013
@@ -11363,6 +11401,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11363 /// handles splitting manually</param> 11401 /// handles splitting manually</param>
11364 protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting) 11402 protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting)
11365 { 11403 {
11404 OutPacket(packet, throttlePacketType, doAutomaticSplitting, null);
11405 }
11406
11407 /// <summary>
11408 /// This is the starting point for sending a simulator packet out to the client
11409 /// </summary>
11410 /// <param name="packet">Packet to send</param>
11411 /// <param name="throttlePacketType">Throttling category for the packet</param>
11412 /// <param name="doAutomaticSplitting">True to automatically split oversized
11413 /// packets (the default), or false to disable splitting if the calling code
11414 /// handles splitting manually</param>
11415 /// <param name="method">The method to be called in the event this packet is reliable
11416 /// and unacknowledged. The server will provide normal resend capability if you do not
11417 /// provide your own method.</param>
11418 protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting, UnackedPacketMethod method)
11419 {
11366 if (m_debugPacketLevel > 0) 11420 if (m_debugPacketLevel > 0)
11367 { 11421 {
11368 bool logPacket = true; 11422 bool logPacket = true;
@@ -11388,7 +11442,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11388 m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); 11442 m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type);
11389 } 11443 }
11390 11444
11391 m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); 11445 m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting, method);
11392 } 11446 }
11393 11447
11394 public bool AddMoney(int debit) 11448 public bool AddMoney(int debit)
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
index d08b25f..0848979 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
@@ -297,7 +297,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
297 delegate(IClientAPI client) 297 delegate(IClientAPI client)
298 { 298 {
299 if (client is LLClientView) 299 if (client is LLClientView)
300 SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category); 300 SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null);
301 } 301 }
302 ); 302 );
303 } 303 }
@@ -309,7 +309,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
309 delegate(IClientAPI client) 309 delegate(IClientAPI client)
310 { 310 {
311 if (client is LLClientView) 311 if (client is LLClientView)
312 SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category); 312 SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null);
313 } 313 }
314 ); 314 );
315 } 315 }
@@ -322,7 +322,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
322 /// <param name="packet"></param> 322 /// <param name="packet"></param>
323 /// <param name="category"></param> 323 /// <param name="category"></param>
324 /// <param name="allowSplitting"></param> 324 /// <param name="allowSplitting"></param>
325 public void SendPacket(LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting) 325 public void SendPacket(LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting, UnackedPacketMethod method)
326 { 326 {
327 // CoarseLocationUpdate packets cannot be split in an automated way 327 // CoarseLocationUpdate packets cannot be split in an automated way
328 if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting) 328 if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting)
@@ -339,13 +339,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
339 for (int i = 0; i < packetCount; i++) 339 for (int i = 0; i < packetCount; i++)
340 { 340 {
341 byte[] data = datas[i]; 341 byte[] data = datas[i];
342 SendPacketData(udpClient, data, packet.Type, category); 342 SendPacketData(udpClient, data, packet.Type, category, method);
343 } 343 }
344 } 344 }
345 else 345 else
346 { 346 {
347 byte[] data = packet.ToBytes(); 347 byte[] data = packet.ToBytes();
348 SendPacketData(udpClient, data, packet.Type, category); 348 SendPacketData(udpClient, data, packet.Type, category, method);
349 } 349 }
350 } 350 }
351 351
@@ -356,7 +356,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
356 /// <param name="data"></param> 356 /// <param name="data"></param>
357 /// <param name="type"></param> 357 /// <param name="type"></param>
358 /// <param name="category"></param> 358 /// <param name="category"></param>
359 public void SendPacketData(LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category) 359 public void SendPacketData(LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category, UnackedPacketMethod method)
360 { 360 {
361 int dataLength = data.Length; 361 int dataLength = data.Length;
362 bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; 362 bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0;
@@ -411,7 +411,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
411 411
412 #region Queue or Send 412 #region Queue or Send
413 413
414 OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category); 414 OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category, null);
415 // If we were not provided a method for handling unacked, use the UDPServer default method
416 outgoingPacket.UnackedMethod = ((method == null) ? delegate() { ResendUnacked(outgoingPacket); } : method);
415 417
416 // If a Linden Lab 1.23.5 client receives an update packet after a kill packet for an object, it will 418 // If a Linden Lab 1.23.5 client receives an update packet after a kill packet for an object, it will
417 // continue to display the deleted object until relog. Therefore, we need to always queue a kill object 419 // continue to display the deleted object until relog. Therefore, we need to always queue a kill object
@@ -445,7 +447,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
445 packet.Header.Reliable = false; 447 packet.Header.Reliable = false;
446 packet.Packets = blocks.ToArray(); 448 packet.Packets = blocks.ToArray();
447 449
448 SendPacket(udpClient, packet, ThrottleOutPacketType.Unknown, true); 450 SendPacket(udpClient, packet, ThrottleOutPacketType.Unknown, true, null);
449 } 451 }
450 } 452 }
451 453
@@ -458,17 +460,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
458 // We *could* get OldestUnacked, but it would hurt performance and not provide any benefit 460 // We *could* get OldestUnacked, but it would hurt performance and not provide any benefit
459 pc.PingID.OldestUnacked = 0; 461 pc.PingID.OldestUnacked = 0;
460 462
461 SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false); 463 SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false, null);
462 } 464 }
463 465
464 public void CompletePing(LLUDPClient udpClient, byte pingID) 466 public void CompletePing(LLUDPClient udpClient, byte pingID)
465 { 467 {
466 CompletePingCheckPacket completePing = new CompletePingCheckPacket(); 468 CompletePingCheckPacket completePing = new CompletePingCheckPacket();
467 completePing.PingID.PingID = pingID; 469 completePing.PingID.PingID = pingID;
468 SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false); 470 SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false, null);
469 } 471 }
470 472
471 public void ResendUnacked(LLUDPClient udpClient) 473 public void HandleUnacked(LLUDPClient udpClient)
472 { 474 {
473 if (!udpClient.IsConnected) 475 if (!udpClient.IsConnected)
474 return; 476 return;
@@ -488,31 +490,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
488 490
489 if (expiredPackets != null) 491 if (expiredPackets != null)
490 { 492 {
491 //m_log.Debug("[LLUDPSERVER]: Resending " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO); 493 //m_log.Debug("[LLUDPSERVER]: Handling " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO);
492
493 // Exponential backoff of the retransmission timeout 494 // Exponential backoff of the retransmission timeout
494 udpClient.BackoffRTO(); 495 udpClient.BackoffRTO();
496 for (int i = 0; i < expiredPackets.Count; ++i)
497 expiredPackets[i].UnackedMethod();
498 }
499 }
495 500
496 // Resend packets 501 public void ResendUnacked(OutgoingPacket outgoingPacket)
497 for (int i = 0; i < expiredPackets.Count; i++) 502 {
498 { 503 //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed",
499 OutgoingPacket outgoingPacket = expiredPackets[i]; 504 // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount);
500
501 //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed",
502 // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount);
503 505
504 // Set the resent flag 506 // Set the resent flag
505 outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); 507 outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
506 outgoingPacket.Category = ThrottleOutPacketType.Resend; 508 outgoingPacket.Category = ThrottleOutPacketType.Resend;
507 509
508 // Bump up the resend count on this packet 510 // Bump up the resend count on this packet
509 Interlocked.Increment(ref outgoingPacket.ResendCount); 511 Interlocked.Increment(ref outgoingPacket.ResendCount);
510 512
511 // Requeue or resend the packet 513 // Requeue or resend the packet
512 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, false)) 514 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, false))
513 SendPacketFinal(outgoingPacket); 515 SendPacketFinal(outgoingPacket);
514 }
515 }
516 } 516 }
517 517
518 public void Flush(LLUDPClient udpClient) 518 public void Flush(LLUDPClient udpClient)
@@ -1096,7 +1096,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1096 if (udpClient.IsConnected) 1096 if (udpClient.IsConnected)
1097 { 1097 {
1098 if (m_resendUnacked) 1098 if (m_resendUnacked)
1099 ResendUnacked(udpClient); 1099 HandleUnacked(udpClient);
1100 1100
1101 if (m_sendAcks) 1101 if (m_sendAcks)
1102 SendAcks(udpClient); 1102 SendAcks(udpClient);
@@ -1152,7 +1152,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1152 nticksUnack++; 1152 nticksUnack++;
1153 watch2.Start(); 1153 watch2.Start();
1154 1154
1155 ResendUnacked(udpClient); 1155 HandleUnacked(udpClient);
1156 1156
1157 watch2.Stop(); 1157 watch2.Stop();
1158 avgResendUnackedTicks = (nticksUnack - 1)/(float)nticksUnack * avgResendUnackedTicks + (watch2.ElapsedTicks / (float)nticksUnack); 1158 avgResendUnackedTicks = (nticksUnack - 1)/(float)nticksUnack * avgResendUnackedTicks + (watch2.ElapsedTicks / (float)nticksUnack);
diff --git a/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs b/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs
index 1a1a1cb..f4f024b 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs
@@ -31,6 +31,7 @@ using OpenMetaverse;
31 31
32namespace OpenSim.Region.ClientStack.LindenUDP 32namespace OpenSim.Region.ClientStack.LindenUDP
33{ 33{
34 public delegate void UnackedPacketMethod();
34 /// <summary> 35 /// <summary>
35 /// Holds a reference to the <seealso cref="LLUDPClient"/> this packet is 36 /// Holds a reference to the <seealso cref="LLUDPClient"/> this packet is
36 /// destined for, along with the serialized packet data, sequence number 37 /// destined for, along with the serialized packet data, sequence number
@@ -52,6 +53,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
52 public int TickCount; 53 public int TickCount;
53 /// <summary>Category this packet belongs to</summary> 54 /// <summary>Category this packet belongs to</summary>
54 public ThrottleOutPacketType Category; 55 public ThrottleOutPacketType Category;
56 /// <summary>The delegate to be called if this packet is determined to be unacknowledged</summary>
57 public UnackedPacketMethod UnackedMethod;
55 58
56 /// <summary> 59 /// <summary>
57 /// Default constructor 60 /// Default constructor
@@ -60,11 +63,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
60 /// <param name="buffer">Serialized packet data. If the flags or sequence number 63 /// <param name="buffer">Serialized packet data. If the flags or sequence number
61 /// need to be updated, they will be injected directly into this binary buffer</param> 64 /// need to be updated, they will be injected directly into this binary buffer</param>
62 /// <param name="category">Throttling category for this packet</param> 65 /// <param name="category">Throttling category for this packet</param>
63 public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category) 66 public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category, UnackedPacketMethod method)
64 { 67 {
65 Client = client; 68 Client = client;
66 Buffer = buffer; 69 Buffer = buffer;
67 Category = category; 70 Category = category;
71 UnackedMethod = method;
68 } 72 }
69 } 73 }
70} 74}