aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs597
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs4
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs245
3 files changed, 518 insertions, 328 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 789e86c..5b2484d 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -49,6 +49,8 @@ using Timer = System.Timers.Timer;
49using AssetLandmark = OpenSim.Framework.AssetLandmark; 49using AssetLandmark = OpenSim.Framework.AssetLandmark;
50using Nini.Config; 50using Nini.Config;
51 51
52using System.IO;
53
52namespace OpenSim.Region.ClientStack.LindenUDP 54namespace OpenSim.Region.ClientStack.LindenUDP
53{ 55{
54 public delegate bool PacketMethod(IClientAPI simClient, Packet packet); 56 public delegate bool PacketMethod(IClientAPI simClient, Packet packet);
@@ -299,6 +301,77 @@ namespace OpenSim.Region.ClientStack.LindenUDP
299 /// <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>
300 private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; 302 private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f;
301 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
302 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 375 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
303 protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients 376 protected static Dictionary<PacketType, PacketMethod> PacketHandlers = new Dictionary<PacketType, PacketMethod>(); //Global/static handlers for all clients
304 377
@@ -3575,6 +3648,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3575 3648
3576 #region Primitive Packet/Data Sending Methods 3649 #region Primitive Packet/Data Sending Methods
3577 3650
3651
3578 /// <summary> 3652 /// <summary>
3579 /// Generate one of the object update packets based on PrimUpdateFlags 3653 /// Generate one of the object update packets based on PrimUpdateFlags
3580 /// and broadcast the packet to clients 3654 /// and broadcast the packet to clients
@@ -3590,12 +3664,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3590 return; // Don't send updates for other people's HUDs 3664 return; // Don't send updates for other people's HUDs
3591 } 3665 }
3592 3666
3593 double priority = m_prioritizer.GetUpdatePriority(this, entity); 3667 uint priority = m_prioritizer.GetUpdatePriority(this, entity);
3594 3668
3595 lock (m_entityUpdates.SyncRoot) 3669 lock (m_entityUpdates.SyncRoot)
3596 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation), entity.LocalId); 3670 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation));
3597 } 3671 }
3598 3672
3673 private Int32 m_LastQueueFill = 0;
3674 private uint m_maxUpdates = 0;
3675
3599 private void ProcessEntityUpdates(int maxUpdates) 3676 private void ProcessEntityUpdates(int maxUpdates)
3600 { 3677 {
3601 OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); 3678 OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>();
@@ -3603,195 +3680,232 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3603 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); 3680 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
3604 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); 3681 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
3605 3682
3606 if (maxUpdates <= 0) maxUpdates = Int32.MaxValue; 3683 if (maxUpdates <= 0)
3684 {
3685 m_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 }
3702 m_LastQueueFill = Util.EnvironmentTickCount();
3703
3607 int updatesThisCall = 0; 3704 int updatesThisCall = 0;
3608 3705
3609 float avgTimeDilation = 0; 3706//<MIC>
3707// DEBUGGING CODE... REMOVE
3708// LogQueueProcessEvent(this.m_agentId,m_entityUpdates,m_maxUpdates);
3709//</MIC>
3710 // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race
3711 // condition where a kill can be processed before an out-of-date update for the same object.
3712 float avgTimeDilation = 1.0f;
3610 3713
3611 EntityUpdate update; 3714 lock (m_killRecord)
3612 while (updatesThisCall < maxUpdates)
3613 { 3715 {
3614 lock (m_entityUpdates.SyncRoot) 3716 EntityUpdate update;
3615 if (!m_entityUpdates.TryDequeue(out update)) 3717 Int32 timeinqueue; // this is just debugging code & can be dropped later
3616 break; 3718
3617 3719 while (updatesThisCall < m_maxUpdates)
3618 avgTimeDilation += update.TimeDilation;
3619 avgTimeDilation *= 0.5f;
3620
3621 if (update.Entity is SceneObjectPart)
3622 { 3720 {
3623 SceneObjectPart part = (SceneObjectPart)update.Entity; 3721 lock (m_entityUpdates.SyncRoot)
3722 if (!m_entityUpdates.TryDequeue(out update, out timeinqueue))
3723 break;
3724 avgTimeDilation += update.TimeDilation;
3725 avgTimeDilation *= 0.5f;
3624 3726
3625 // Please do not remove this unless you can demonstrate on the OpenSim mailing list that a client 3727 if (update.Entity is SceneObjectPart)
3626 // will never receive an update after a prim kill. Even then, keeping the kill record may be a good
3627 // safety measure.
3628 //
3629 // If a Linden Lab 1.23.5 client (and possibly later and earlier) receives an object update
3630 // after a kill, it will keep displaying the deleted object until relog. OpenSim currently performs
3631 // updates and kills on different threads with different scheduling strategies, hence this protection.
3632 //
3633 // This doesn't appear to apply to child prims - a client will happily ignore these updates
3634 // after the root prim has been deleted.
3635 lock (m_killRecord)
3636 { 3728 {
3637 if (m_killRecord.Contains(part.LocalId)) 3729 SceneObjectPart part = (SceneObjectPart)update.Entity;
3638 continue; 3730
3639 if (m_killRecord.Contains(part.ParentGroup.RootPart.LocalId)) 3731 // Please do not remove this unless you can demonstrate on the OpenSim mailing list that a client
3640 continue; 3732 // will never receive an update after a prim kill. Even then, keeping the kill record may be a good
3641 } 3733 // safety measure.
3642 3734 //
3643 if (part.ParentGroup.IsDeleted) 3735 // If a Linden Lab 1.23.5 client (and possibly later and earlier) receives an object update
3644 continue; 3736 // after a kill, it will keep displaying the deleted object until relog. OpenSim currently performs
3737 // updates and kills on different threads with different scheduling strategies, hence this protection.
3738 //
3739 // This doesn't appear to apply to child prims - a client will happily ignore these updates
3740 // after the root prim has been deleted.
3741 lock (m_killRecord)
3742 {
3743 if (m_killRecord.Contains(part.LocalId))
3744 continue;
3745 if (m_killRecord.Contains(part.ParentGroup.RootPart.LocalId))
3746 continue;
3747 }
3645 3748
3646 if (part.ParentGroup.IsAttachment) 3749 if (part.ParentGroup.IsDeleted)
3647 { // Someone else's HUD, why are we getting these?
3648 if (part.ParentGroup.OwnerID != AgentId &&
3649 part.ParentGroup.RootPart.Shape.State >= 30)
3650 continue;
3651 ScenePresence sp;
3652 // Owner is not in the sim, don't update it to
3653 // anyone
3654 if (!m_scene.TryGetScenePresence(part.OwnerID, out sp))
3655 continue; 3750 continue;
3656 3751
3657 List<SceneObjectGroup> atts = sp.Attachments; 3752 if (part.ParentGroup.IsAttachment)
3658 bool found = false; 3753 { // Someone else's HUD, why are we getting these?
3659 foreach (SceneObjectGroup att in atts) 3754 if (part.ParentGroup.OwnerID != AgentId &&
3660 { 3755 part.ParentGroup.RootPart.Shape.State >= 30)
3661 if (att == part.ParentGroup) 3756 continue;
3757 ScenePresence sp;
3758 // Owner is not in the sim, don't update it to
3759 // anyone
3760 if (!m_scene.TryGetScenePresence(part.OwnerID, out sp))
3761 continue;
3762
3763 List<SceneObjectGroup> atts = sp.Attachments;
3764 bool found = false;
3765 foreach (SceneObjectGroup att in atts)
3662 { 3766 {
3663 found = true; 3767 if (att == part.ParentGroup)
3664 break; 3768 {
3769 found = true;
3770 break;
3771 }
3665 } 3772 }
3666 }
3667 3773
3668 // It's an attachment of a valid avatar, but 3774 // It's an attachment of a valid avatar, but
3669 // doesn't seem to be attached, skip 3775 // doesn't seem to be attached, skip
3670 if (!found) 3776 if (!found)
3671 continue; 3777 continue;
3672 } 3778 }
3673 3779
3674 if (part.ParentGroup.IsAttachment && m_disableFacelights) 3780 if (part.ParentGroup.IsAttachment && m_disableFacelights)
3675 {
3676 if (part.ParentGroup.RootPart.Shape.State != (byte)AttachmentPoint.LeftHand &&
3677 part.ParentGroup.RootPart.Shape.State != (byte)AttachmentPoint.RightHand)
3678 { 3781 {
3679 part.Shape.LightEntry = false; 3782 if (part.ParentGroup.RootPart.Shape.State != (byte)AttachmentPoint.LeftHand &&
3783 part.ParentGroup.RootPart.Shape.State != (byte)AttachmentPoint.RightHand)
3784 {
3785 part.Shape.LightEntry = false;
3786 }
3680 } 3787 }
3681 } 3788 }
3682 }
3683
3684 ++updatesThisCall;
3685 3789
3686 #region UpdateFlags to packet type conversion 3790 ++updatesThisCall;
3687 3791
3688 PrimUpdateFlags updateFlags = update.Flags; 3792 #region UpdateFlags to packet type conversion
3689 3793
3690 bool canUseCompressed = true; 3794 PrimUpdateFlags updateFlags = update.Flags;
3691 bool canUseImproved = true;
3692 3795
3693 // Compressed object updates only make sense for LL primitives 3796 bool canUseCompressed = true;
3694 if (!(update.Entity is SceneObjectPart)) 3797 bool canUseImproved = true;
3695 {
3696 canUseCompressed = false;
3697 }
3698 3798
3699 if (updateFlags.HasFlag(PrimUpdateFlags.FullUpdate)) 3799 // Compressed object updates only make sense for LL primitives
3700 { 3800 if (!(update.Entity is SceneObjectPart))
3701 canUseCompressed = false;
3702 canUseImproved = false;
3703 }
3704 else
3705 {
3706 if (updateFlags.HasFlag(PrimUpdateFlags.Velocity) ||
3707 updateFlags.HasFlag(PrimUpdateFlags.Acceleration) ||
3708 updateFlags.HasFlag(PrimUpdateFlags.CollisionPlane) ||
3709 updateFlags.HasFlag(PrimUpdateFlags.Joint))
3710 { 3801 {
3711 canUseCompressed = false; 3802 canUseCompressed = false;
3712 } 3803 }
3713 3804
3714 if (updateFlags.HasFlag(PrimUpdateFlags.PrimFlags) || 3805 if (updateFlags.HasFlag(PrimUpdateFlags.FullUpdate))
3715 updateFlags.HasFlag(PrimUpdateFlags.ParentID) ||
3716 updateFlags.HasFlag(PrimUpdateFlags.Scale) ||
3717 updateFlags.HasFlag(PrimUpdateFlags.PrimData) ||
3718 updateFlags.HasFlag(PrimUpdateFlags.Text) ||
3719 updateFlags.HasFlag(PrimUpdateFlags.NameValue) ||
3720 updateFlags.HasFlag(PrimUpdateFlags.ExtraData) ||
3721 updateFlags.HasFlag(PrimUpdateFlags.TextureAnim) ||
3722 updateFlags.HasFlag(PrimUpdateFlags.Sound) ||
3723 updateFlags.HasFlag(PrimUpdateFlags.Particles) ||
3724 updateFlags.HasFlag(PrimUpdateFlags.Material) ||
3725 updateFlags.HasFlag(PrimUpdateFlags.ClickAction) ||
3726 updateFlags.HasFlag(PrimUpdateFlags.MediaURL) ||
3727 updateFlags.HasFlag(PrimUpdateFlags.Joint))
3728 { 3806 {
3807 canUseCompressed = false;
3729 canUseImproved = false; 3808 canUseImproved = false;
3730 } 3809 }
3731 } 3810 else
3811 {
3812 if (updateFlags.HasFlag(PrimUpdateFlags.Velocity) ||
3813 updateFlags.HasFlag(PrimUpdateFlags.Acceleration) ||
3814 updateFlags.HasFlag(PrimUpdateFlags.CollisionPlane) ||
3815 updateFlags.HasFlag(PrimUpdateFlags.Joint))
3816 {
3817 if (update.Entity is ScenePresence)
3818 {
3819 objectUpdateBlocks.Value.Add(CreateAvatarUpdateBlock((ScenePresence)update.Entity));
3820 }
3821 else
3822 {
3823 objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId));
3824 }
3825 }
3732 3826
3733 #endregion UpdateFlags to packet type conversion 3827 if (updateFlags.HasFlag(PrimUpdateFlags.PrimFlags) ||
3828 updateFlags.HasFlag(PrimUpdateFlags.ParentID) ||
3829 updateFlags.HasFlag(PrimUpdateFlags.Scale) ||
3830 updateFlags.HasFlag(PrimUpdateFlags.PrimData) ||
3831 updateFlags.HasFlag(PrimUpdateFlags.Text) ||
3832 updateFlags.HasFlag(PrimUpdateFlags.NameValue) ||
3833 updateFlags.HasFlag(PrimUpdateFlags.ExtraData) ||
3834 updateFlags.HasFlag(PrimUpdateFlags.TextureAnim) ||
3835 updateFlags.HasFlag(PrimUpdateFlags.Sound) ||
3836 updateFlags.HasFlag(PrimUpdateFlags.Particles) ||
3837 updateFlags.HasFlag(PrimUpdateFlags.Material) ||
3838 updateFlags.HasFlag(PrimUpdateFlags.ClickAction) ||
3839 updateFlags.HasFlag(PrimUpdateFlags.MediaURL) ||
3840 updateFlags.HasFlag(PrimUpdateFlags.Joint))
3841 {
3842 canUseImproved = false;
3843 }
3844 }
3734 3845
3735 #region Block Construction 3846 #endregion UpdateFlags to packet type conversion
3736 3847
3737 // TODO: Remove this once we can build compressed updates 3848 #region Block Construction
3738 canUseCompressed = false;
3739 3849
3740 if (!canUseImproved && !canUseCompressed) 3850 // TODO: Remove this once we can build compressed updates
3741 { 3851 canUseCompressed = false;
3742 if (update.Entity is ScenePresence) 3852
3853 if (!canUseImproved && !canUseCompressed)
3854 {
3855 if (update.Entity is ScenePresence)
3856 {
3857 objectUpdateBlocks.Value.Add(CreateAvatarUpdateBlock((ScenePresence)update.Entity));
3858 }
3859 else
3860 {
3861 // if (update.Entity is SceneObjectPart && ((SceneObjectPart)update.Entity).IsAttachment)
3862 // {
3863 // SceneObjectPart sop = (SceneObjectPart)update.Entity;
3864 // string text = sop.Text;
3865 // if (text.IndexOf("\n") >= 0)
3866 // text = text.Remove(text.IndexOf("\n"));
3867 //
3868 // if (m_attachmentsSent.Contains(sop.ParentID))
3869 // {
3870 //// m_log.DebugFormat(
3871 //// "[CLIENT]: Sending full info about attached prim {0} text {1}",
3872 //// sop.LocalId, text);
3873 //
3874 // objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock(sop, this.m_agentId));
3875 //
3876 // m_attachmentsSent.Add(sop.LocalId);
3877 // }
3878 // else
3879 // {
3880 // m_log.DebugFormat(
3881 // "[CLIENT]: Requeueing full update of prim {0} text {1} since we haven't sent its parent {2} yet",
3882 // sop.LocalId, text, sop.ParentID);
3883 //
3884 // m_entityUpdates.Enqueue(double.MaxValue, update, sop.LocalId);
3885 // }
3886 // }
3887 // else
3888 // {
3889 objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId));
3890 // }
3891 }
3892 }
3893 else if (!canUseImproved)
3743 { 3894 {
3744 objectUpdateBlocks.Value.Add(CreateAvatarUpdateBlock((ScenePresence)update.Entity)); 3895 compressedUpdateBlocks.Value.Add(CreateCompressedUpdateBlock((SceneObjectPart)update.Entity, updateFlags));
3745 } 3896 }
3746 else 3897 else
3747 { 3898 {
3748// if (update.Entity is SceneObjectPart && ((SceneObjectPart)update.Entity).IsAttachment) 3899 if (update.Entity is ScenePresence && ((ScenePresence)update.Entity).UUID == AgentId)
3749// { 3900 // Self updates go into a special list
3750// SceneObjectPart sop = (SceneObjectPart)update.Entity; 3901 terseAgentUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)));
3751// string text = sop.Text; 3902 else
3752// if (text.IndexOf("\n") >= 0) 3903 // Everything else goes here
3753// text = text.Remove(text.IndexOf("\n")); 3904 terseUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)));
3754//
3755// if (m_attachmentsSent.Contains(sop.ParentID))
3756// {
3757//// m_log.DebugFormat(
3758//// "[CLIENT]: Sending full info about attached prim {0} text {1}",
3759//// sop.LocalId, text);
3760//
3761// objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock(sop, this.m_agentId));
3762//
3763// m_attachmentsSent.Add(sop.LocalId);
3764// }
3765// else
3766// {
3767// m_log.DebugFormat(
3768// "[CLIENT]: Requeueing full update of prim {0} text {1} since we haven't sent its parent {2} yet",
3769// sop.LocalId, text, sop.ParentID);
3770//
3771// m_entityUpdates.Enqueue(double.MaxValue, update, sop.LocalId);
3772// }
3773// }
3774// else
3775// {
3776 objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId));
3777// }
3778 } 3905 }
3779 }
3780 else if (!canUseImproved)
3781 {
3782 compressedUpdateBlocks.Value.Add(CreateCompressedUpdateBlock((SceneObjectPart)update.Entity, updateFlags));
3783 }
3784 else
3785 {
3786 if (update.Entity is ScenePresence && ((ScenePresence)update.Entity).UUID == AgentId)
3787 // Self updates go into a special list
3788 terseAgentUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)));
3789 else
3790 // Everything else goes here
3791 terseUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)));
3792 }
3793 3906
3794 #endregion Block Construction 3907 #endregion Block Construction
3908 }
3795 } 3909 }
3796 3910
3797 #region Packet Sending 3911 #region Packet Sending
@@ -3864,26 +3978,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3864 3978
3865 public void ReprioritizeUpdates() 3979 public void ReprioritizeUpdates()
3866 { 3980 {
3867 //m_log.Debug("[CLIENT]: Reprioritizing prim updates for " + m_firstName + " " + m_lastName);
3868
3869 lock (m_entityUpdates.SyncRoot) 3981 lock (m_entityUpdates.SyncRoot)
3870 m_entityUpdates.Reprioritize(UpdatePriorityHandler); 3982 m_entityUpdates.Reprioritize(UpdatePriorityHandler);
3871 } 3983 }
3872 3984
3873 private bool UpdatePriorityHandler(ref double priority, uint localID) 3985 private bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity)
3874 { 3986 {
3875 EntityBase entity; 3987 if (entity != null)
3876 if (m_scene.Entities.TryGetValue(localID, out entity))
3877 { 3988 {
3878 priority = m_prioritizer.GetUpdatePriority(this, entity); 3989 priority = m_prioritizer.GetUpdatePriority(this, entity);
3990 return true;
3879 } 3991 }
3880 3992
3881 return priority != double.NaN; 3993 return false;
3882 } 3994 }
3883 3995
3884 public void FlushPrimUpdates() 3996 public void FlushPrimUpdates()
3885 { 3997 {
3886 m_log.Debug("[CLIENT]: Flushing prim updates to " + m_firstName + " " + m_lastName); 3998 m_log.WarnFormat("[CLIENT]: Flushing prim updates to " + m_firstName + " " + m_lastName);
3887 3999
3888 while (m_entityUpdates.Count > 0) 4000 while (m_entityUpdates.Count > 0)
3889 ProcessEntityUpdates(-1); 4001 ProcessEntityUpdates(-1);
@@ -11831,171 +11943,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11831 OutPacket(pack, ThrottleOutPacketType.Task); 11943 OutPacket(pack, ThrottleOutPacketType.Task);
11832 } 11944 }
11833 11945
11834 #region PriorityQueue
11835 public class PriorityQueue
11836 {
11837 internal delegate bool UpdatePriorityHandler(ref double priority, uint local_id);
11838
11839 private MinHeap<MinHeapItem>[] m_heaps = new MinHeap<MinHeapItem>[1];
11840 private Dictionary<uint, LookupItem> m_lookupTable;
11841 private Comparison<double> m_comparison;
11842 private object m_syncRoot = new object();
11843
11844 internal PriorityQueue() :
11845 this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY, Comparer<double>.Default) { }
11846 internal PriorityQueue(int capacity) :
11847 this(capacity, Comparer<double>.Default) { }
11848 internal PriorityQueue(IComparer<double> comparer) :
11849 this(new Comparison<double>(comparer.Compare)) { }
11850 internal PriorityQueue(Comparison<double> comparison) :
11851 this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY, comparison) { }
11852 internal PriorityQueue(int capacity, IComparer<double> comparer) :
11853 this(capacity, new Comparison<double>(comparer.Compare)) { }
11854 internal PriorityQueue(int capacity, Comparison<double> comparison)
11855 {
11856 m_lookupTable = new Dictionary<uint, LookupItem>(capacity);
11857
11858 for (int i = 0; i < m_heaps.Length; ++i)
11859 m_heaps[i] = new MinHeap<MinHeapItem>(capacity);
11860 this.m_comparison = comparison;
11861 }
11862
11863 public object SyncRoot { get { return this.m_syncRoot; } }
11864 internal int Count
11865 {
11866 get
11867 {
11868 int count = 0;
11869 for (int i = 0; i < m_heaps.Length; ++i)
11870 count = m_heaps[i].Count;
11871 return count;
11872 }
11873 }
11874
11875 public bool Enqueue(double priority, EntityUpdate value, uint local_id)
11876 {
11877 LookupItem item;
11878
11879 if (m_lookupTable.TryGetValue(local_id, out item))
11880 {
11881 // Combine flags
11882 value.Flags |= item.Heap[item.Handle].Value.Flags;
11883
11884 item.Heap[item.Handle] = new MinHeapItem(priority, value, local_id, this.m_comparison);
11885 return false;
11886 }
11887 else
11888 {
11889 item.Heap = m_heaps[0];
11890 item.Heap.Add(new MinHeapItem(priority, value, local_id, this.m_comparison), ref item.Handle);
11891 m_lookupTable.Add(local_id, item);
11892 return true;
11893 }
11894 }
11895
11896 internal EntityUpdate Peek()
11897 {
11898 for (int i = 0; i < m_heaps.Length; ++i)
11899 if (m_heaps[i].Count > 0)
11900 return m_heaps[i].Min().Value;
11901 throw new InvalidOperationException(string.Format("The {0} is empty", this.GetType().ToString()));
11902 }
11903
11904 internal bool TryDequeue(out EntityUpdate value)
11905 {
11906 for (int i = 0; i < m_heaps.Length; ++i)
11907 {
11908 if (m_heaps[i].Count > 0)
11909 {
11910 MinHeapItem item = m_heaps[i].RemoveMin();
11911 m_lookupTable.Remove(item.LocalID);
11912 value = item.Value;
11913 return true;
11914 }
11915 }
11916
11917 value = default(EntityUpdate);
11918 return false;
11919 }
11920
11921 internal void Reprioritize(UpdatePriorityHandler handler)
11922 {
11923 MinHeapItem item;
11924 double priority;
11925
11926 foreach (LookupItem lookup in new List<LookupItem>(this.m_lookupTable.Values))
11927 {
11928 if (lookup.Heap.TryGetValue(lookup.Handle, out item))
11929 {
11930 priority = item.Priority;
11931 if (handler(ref priority, item.LocalID))
11932 {
11933 if (lookup.Heap.ContainsHandle(lookup.Handle))
11934 lookup.Heap[lookup.Handle] =
11935 new MinHeapItem(priority, item.Value, item.LocalID, this.m_comparison);
11936 }
11937 else
11938 {
11939 m_log.Warn("[LLCLIENTVIEW]: UpdatePriorityHandler returned false, dropping update");
11940 lookup.Heap.Remove(lookup.Handle);
11941 this.m_lookupTable.Remove(item.LocalID);
11942 }
11943 }
11944 }
11945 }
11946
11947 #region MinHeapItem
11948 private struct MinHeapItem : IComparable<MinHeapItem>
11949 {
11950 private double priority;
11951 private EntityUpdate value;
11952 private uint local_id;
11953 private Comparison<double> comparison;
11954
11955 internal MinHeapItem(double priority, EntityUpdate value, uint local_id) :
11956 this(priority, value, local_id, Comparer<double>.Default) { }
11957 internal MinHeapItem(double priority, EntityUpdate value, uint local_id, IComparer<double> comparer) :
11958 this(priority, value, local_id, new Comparison<double>(comparer.Compare)) { }
11959 internal MinHeapItem(double priority, EntityUpdate value, uint local_id, Comparison<double> comparison)
11960 {
11961 this.priority = priority;
11962 this.value = value;
11963 this.local_id = local_id;
11964 this.comparison = comparison;
11965 }
11966
11967 internal double Priority { get { return this.priority; } }
11968 internal EntityUpdate Value { get { return this.value; } }
11969 internal uint LocalID { get { return this.local_id; } }
11970
11971 public override string ToString()
11972 {
11973 StringBuilder sb = new StringBuilder();
11974 sb.Append("[");
11975 sb.Append(this.priority.ToString());
11976 sb.Append(",");
11977 if (this.value != null)
11978 sb.Append(this.value.ToString());
11979 sb.Append("]");
11980 return sb.ToString();
11981 }
11982
11983 public int CompareTo(MinHeapItem other)
11984 {
11985 return this.comparison(this.priority, other.priority);
11986 }
11987 }
11988 #endregion
11989
11990 #region LookupItem
11991 private struct LookupItem
11992 {
11993 internal MinHeap<MinHeapItem> Heap;
11994 internal IHandle Handle;
11995 }
11996 #endregion
11997 }
11998
11999 public struct PacketProcessor 11946 public struct PacketProcessor
12000 { 11947 {
12001 public PacketMethod method; 11948 public PacketMethod method;
@@ -12016,8 +11963,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
12016 } 11963 }
12017 } 11964 }
12018 11965
12019 #endregion
12020
12021 public static OSD BuildEvent(string eventName, OSD eventBody) 11966 public static OSD BuildEvent(string eventName, OSD eventBody)
12022 { 11967 {
12023 OSDMap osdEvent = new OSDMap(2); 11968 OSDMap osdEvent = new OSDMap(2);
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
index d6159cd..0fa074d 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
@@ -149,7 +149,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
149 /// <summary>Caches packed throttle information</summary> 149 /// <summary>Caches packed throttle information</summary>
150 private byte[] m_packedThrottles; 150 private byte[] m_packedThrottles;
151 151
152 private int m_defaultRTO = 3000; 152 private int m_defaultRTO = 1000; // 1sec is the recommendation in the RFC
153 private int m_maxRTO = 60000; 153 private int m_maxRTO = 60000;
154 public bool m_deliverPackets = true; 154 public bool m_deliverPackets = true;
155 155
@@ -567,7 +567,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
567 int rto = (int)(SRTT + Math.Max(m_udpServer.TickCountResolution, K * RTTVAR)); 567 int rto = (int)(SRTT + Math.Max(m_udpServer.TickCountResolution, K * RTTVAR));
568 568
569 // Clamp the retransmission timeout to manageable values 569 // Clamp the retransmission timeout to manageable values
570 rto = Utils.Clamp(RTO, m_defaultRTO, m_maxRTO); 570 rto = Utils.Clamp(rto, m_defaultRTO, m_maxRTO);
571 571
572 RTO = rto; 572 RTO = rto;
573 573
diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs
new file mode 100644
index 0000000..364ce4b
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs
@@ -0,0 +1,245 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Reflection;
32
33using OpenSim.Framework;
34using OpenSim.Framework.Client;
35using log4net;
36
37namespace OpenSim.Region.ClientStack.LindenUDP
38{
39 public class PriorityQueue
40 {
41 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
42
43 internal delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity);
44
45 // Heap[0] for self updates
46 // Heap[1..12] for entity updates
47
48 internal const uint m_numberOfQueues = 12;
49
50 private MinHeap<MinHeapItem>[] m_heaps = new MinHeap<MinHeapItem>[m_numberOfQueues];
51 private Dictionary<uint, LookupItem> m_lookupTable;
52 private uint m_nextQueue = 0;
53 private UInt64 m_nextRequest = 0;
54
55 private object m_syncRoot = new object();
56 public object SyncRoot {
57 get { return this.m_syncRoot; }
58 }
59
60 internal PriorityQueue() : this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY) { }
61
62 internal PriorityQueue(int capacity)
63 {
64 m_lookupTable = new Dictionary<uint, LookupItem>(capacity);
65
66 for (int i = 0; i < m_heaps.Length; ++i)
67 m_heaps[i] = new MinHeap<MinHeapItem>(capacity);
68 }
69
70 internal int Count
71 {
72 get
73 {
74 int count = 0;
75 for (int i = 0; i < m_heaps.Length; ++i)
76 count += m_heaps[i].Count;
77 return count;
78 }
79 }
80
81 public bool Enqueue(uint pqueue, EntityUpdate value)
82 {
83 LookupItem lookup;
84
85 uint localid = value.Entity.LocalId;
86 UInt64 entry = m_nextRequest++;
87 if (m_lookupTable.TryGetValue(localid, out lookup))
88 {
89 entry = lookup.Heap[lookup.Handle].EntryOrder;
90 value.Flags |= lookup.Heap[lookup.Handle].Value.Flags;
91 lookup.Heap.Remove(lookup.Handle);
92 }
93
94 pqueue = Util.Clamp<uint>(pqueue, 0, m_numberOfQueues - 1);
95 lookup.Heap = m_heaps[pqueue];
96 lookup.Heap.Add(new MinHeapItem(pqueue, entry, value), ref lookup.Handle);
97 m_lookupTable[localid] = lookup;
98
99 return true;
100 }
101
102 internal bool TryDequeue(out EntityUpdate value, out Int32 timeinqueue)
103 {
104 for (int i = 0; i < m_numberOfQueues; ++i)
105 {
106 // To get the fair queing, we cycle through each of the
107 // queues when finding an element to dequeue, this code
108 // assumes that the distribution of updates in the queues
109 // is polynomial, probably quadractic (eg distance of PI * R^2)
110 uint h = (uint)((m_nextQueue + i) % m_numberOfQueues);
111 if (m_heaps[h].Count > 0)
112 {
113 m_nextQueue = (uint)((h + 1) % m_numberOfQueues);
114
115 MinHeapItem item = m_heaps[h].RemoveMin();
116 m_lookupTable.Remove(item.Value.Entity.LocalId);
117 timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime);
118 value = item.Value;
119
120 return true;
121 }
122 }
123
124 timeinqueue = 0;
125 value = default(EntityUpdate);
126 return false;
127 }
128
129 internal void Reprioritize(UpdatePriorityHandler handler)
130 {
131 MinHeapItem item;
132 foreach (LookupItem lookup in new List<LookupItem>(this.m_lookupTable.Values))
133 {
134 if (lookup.Heap.TryGetValue(lookup.Handle, out item))
135 {
136 uint pqueue = item.PriorityQueue;
137 uint localid = item.Value.Entity.LocalId;
138
139 if (handler(ref pqueue, item.Value.Entity))
140 {
141 // unless the priority queue has changed, there is no need to modify
142 // the entry
143 pqueue = Util.Clamp<uint>(pqueue, 0, m_numberOfQueues - 1);
144 if (pqueue != item.PriorityQueue)
145 {
146 lookup.Heap.Remove(lookup.Handle);
147
148 LookupItem litem = lookup;
149 litem.Heap = m_heaps[pqueue];
150 litem.Heap.Add(new MinHeapItem(pqueue, item), ref litem.Handle);
151 m_lookupTable[localid] = litem;
152 }
153 }
154 else
155 {
156 // m_log.WarnFormat("[PQUEUE]: UpdatePriorityHandler returned false for {0}",item.Value.Entity.UUID);
157 lookup.Heap.Remove(lookup.Handle);
158 this.m_lookupTable.Remove(localid);
159 }
160 }
161 }
162 }
163
164 public override string ToString()
165 {
166 string s = "";
167 for (int i = 0; i < m_numberOfQueues; i++)
168 {
169 if (s != "") s += ",";
170 s += m_heaps[i].Count.ToString();
171 }
172 return s;
173 }
174
175#region MinHeapItem
176 private struct MinHeapItem : IComparable<MinHeapItem>
177 {
178 private EntityUpdate value;
179 internal EntityUpdate Value {
180 get {
181 return this.value;
182 }
183 }
184
185 private uint pqueue;
186 internal uint PriorityQueue {
187 get {
188 return this.pqueue;
189 }
190 }
191
192 private Int32 entrytime;
193 internal Int32 EntryTime {
194 get {
195 return this.entrytime;
196 }
197 }
198
199 private UInt64 entryorder;
200 internal UInt64 EntryOrder
201 {
202 get {
203 return this.entryorder;
204 }
205 }
206
207 internal MinHeapItem(uint pqueue, MinHeapItem other)
208 {
209 this.entrytime = other.entrytime;
210 this.entryorder = other.entryorder;
211 this.value = other.value;
212 this.pqueue = pqueue;
213 }
214
215 internal MinHeapItem(uint pqueue, UInt64 entryorder, EntityUpdate value)
216 {
217 this.entrytime = Util.EnvironmentTickCount();
218 this.entryorder = entryorder;
219 this.value = value;
220 this.pqueue = pqueue;
221 }
222
223 public override string ToString()
224 {
225 return String.Format("[{0},{1},{2}]",pqueue,entryorder,value.Entity.LocalId);
226 }
227
228 public int CompareTo(MinHeapItem other)
229 {
230 // I'm assuming that the root part of an SOG is added to the update queue
231 // before the component parts
232 return Comparer<UInt64>.Default.Compare(this.EntryOrder, other.EntryOrder);
233 }
234 }
235#endregion
236
237#region LookupItem
238 private struct LookupItem
239 {
240 internal MinHeap<MinHeapItem> Heap;
241 internal IHandle Handle;
242 }
243#endregion
244 }
245}