aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs393
1 files changed, 200 insertions, 193 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 2a59a0c..3b7328d 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -327,7 +327,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
327 /// thread servicing the m_primFullUpdates queue after a kill. If this happens the object persists as an 327 /// thread servicing the m_primFullUpdates queue after a kill. If this happens the object persists as an
328 /// ownerless phantom. 328 /// ownerless phantom.
329 /// 329 ///
330 /// All manipulation of this set has to occur under an m_entityUpdates.SyncRoot lock 330 /// All manipulation of this set has to occur under a lock
331 /// 331 ///
332 /// </value> 332 /// </value>
333 protected HashSet<uint> m_killRecord; 333 protected HashSet<uint> m_killRecord;
@@ -1521,7 +1521,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1521 1521
1522 if (m_scene.GetScenePresence(localID) == null) 1522 if (m_scene.GetScenePresence(localID) == null)
1523 { 1523 {
1524 lock (m_entityUpdates.SyncRoot) 1524 // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race
1525 // condition where a kill can be processed before an out-of-date update for the same object.
1526 lock (m_killRecord)
1525 { 1527 {
1526 m_killRecord.Add(localID); 1528 m_killRecord.Add(localID);
1527 1529
@@ -3558,221 +3560,226 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3558 if (maxUpdates <= 0) maxUpdates = Int32.MaxValue; 3560 if (maxUpdates <= 0) maxUpdates = Int32.MaxValue;
3559 int updatesThisCall = 0; 3561 int updatesThisCall = 0;
3560 3562
3561 EntityUpdate update; 3563 // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race
3562 while (updatesThisCall < maxUpdates) 3564 // condition where a kill can be processed before an out-of-date update for the same object.
3563 { 3565 lock (m_killRecord)
3564 lock (m_entityUpdates.SyncRoot) 3566 {
3565 if (!m_entityUpdates.TryDequeue(out update)) 3567 EntityUpdate update;
3566 break; 3568 while (updatesThisCall < maxUpdates)
3567 3569 {
3568 if (update.Entity is SceneObjectPart) 3570 lock (m_entityUpdates.SyncRoot)
3569 { 3571 if (!m_entityUpdates.TryDequeue(out update))
3570 SceneObjectPart part = (SceneObjectPart)update.Entity; 3572 break;
3571 3573
3572 // Please do not remove this unless you can demonstrate on the OpenSim mailing list that a client 3574 if (update.Entity is SceneObjectPart)
3573 // will never receive an update after a prim kill. Even then, keeping the kill record may be a good
3574 // safety measure.
3575 //
3576 // If a Linden Lab 1.23.5 client (and possibly later and earlier) receives an object update
3577 // after a kill, it will keep displaying the deleted object until relog. OpenSim currently performs
3578 // updates and kills on different threads with different scheduling strategies, hence this protection.
3579 //
3580 // This doesn't appear to apply to child prims - a client will happily ignore these updates
3581 // after the root prim has been deleted.
3582 if (m_killRecord.Contains(part.LocalId))
3583 {
3584 // m_log.WarnFormat(
3585 // "[CLIENT]: Preventing update for prim with local id {0} after client for user {1} told it was deleted",
3586 // part.LocalId, Name);
3587 continue;
3588 }
3589
3590 if (part.ParentGroup.IsAttachment && m_disableFacelights)
3591 { 3575 {
3592 if (part.ParentGroup.RootPart.Shape.State != (byte)AttachmentPoint.LeftHand && 3576 SceneObjectPart part = (SceneObjectPart)update.Entity;
3593 part.ParentGroup.RootPart.Shape.State != (byte)AttachmentPoint.RightHand) 3577
3578 // Please do not remove this unless you can demonstrate on the OpenSim mailing list that a client
3579 // will never receive an update after a prim kill. Even then, keeping the kill record may be a good
3580 // safety measure.
3581 //
3582 // If a Linden Lab 1.23.5 client (and possibly later and earlier) receives an object update
3583 // after a kill, it will keep displaying the deleted object until relog. OpenSim currently performs
3584 // updates and kills on different threads with different scheduling strategies, hence this protection.
3585 //
3586 // This doesn't appear to apply to child prims - a client will happily ignore these updates
3587 // after the root prim has been deleted.
3588 if (m_killRecord.Contains(part.LocalId))
3594 { 3589 {
3595 part.Shape.LightEntry = false; 3590 // m_log.WarnFormat(
3591 // "[CLIENT]: Preventing update for prim with local id {0} after client for user {1} told it was deleted",
3592 // part.LocalId, Name);
3593 continue;
3594 }
3595
3596 if (part.ParentGroup.IsAttachment && m_disableFacelights)
3597 {
3598 if (part.ParentGroup.RootPart.Shape.State != (byte)AttachmentPoint.LeftHand &&
3599 part.ParentGroup.RootPart.Shape.State != (byte)AttachmentPoint.RightHand)
3600 {
3601 part.Shape.LightEntry = false;
3602 }
3596 } 3603 }
3597 } 3604 }
3598 } 3605
3599 3606 ++updatesThisCall;
3600 ++updatesThisCall; 3607
3601 3608 #region UpdateFlags to packet type conversion
3602 #region UpdateFlags to packet type conversion 3609
3603 3610 PrimUpdateFlags updateFlags = update.Flags;
3604 PrimUpdateFlags updateFlags = update.Flags; 3611
3605 3612 bool canUseCompressed = true;
3606 bool canUseCompressed = true; 3613 bool canUseImproved = true;
3607 bool canUseImproved = true; 3614
3608 3615 // Compressed object updates only make sense for LL primitives
3609 // Compressed object updates only make sense for LL primitives 3616 if (!(update.Entity is SceneObjectPart))
3610 if (!(update.Entity is SceneObjectPart))
3611 {
3612 canUseCompressed = false;
3613 }
3614
3615 if (updateFlags.HasFlag(PrimUpdateFlags.FullUpdate))
3616 {
3617 canUseCompressed = false;
3618 canUseImproved = false;
3619 }
3620 else
3621 {
3622 if (updateFlags.HasFlag(PrimUpdateFlags.Velocity) ||
3623 updateFlags.HasFlag(PrimUpdateFlags.Acceleration) ||
3624 updateFlags.HasFlag(PrimUpdateFlags.CollisionPlane) ||
3625 updateFlags.HasFlag(PrimUpdateFlags.Joint))
3626 { 3617 {
3627 canUseCompressed = false; 3618 canUseCompressed = false;
3628 } 3619 }
3629 3620
3630 if (updateFlags.HasFlag(PrimUpdateFlags.PrimFlags) || 3621 if (updateFlags.HasFlag(PrimUpdateFlags.FullUpdate))
3631 updateFlags.HasFlag(PrimUpdateFlags.ParentID) ||
3632 updateFlags.HasFlag(PrimUpdateFlags.Scale) ||
3633 updateFlags.HasFlag(PrimUpdateFlags.PrimData) ||
3634 updateFlags.HasFlag(PrimUpdateFlags.Text) ||
3635 updateFlags.HasFlag(PrimUpdateFlags.NameValue) ||
3636 updateFlags.HasFlag(PrimUpdateFlags.ExtraData) ||
3637 updateFlags.HasFlag(PrimUpdateFlags.TextureAnim) ||
3638 updateFlags.HasFlag(PrimUpdateFlags.Sound) ||
3639 updateFlags.HasFlag(PrimUpdateFlags.Particles) ||
3640 updateFlags.HasFlag(PrimUpdateFlags.Material) ||
3641 updateFlags.HasFlag(PrimUpdateFlags.ClickAction) ||
3642 updateFlags.HasFlag(PrimUpdateFlags.MediaURL) ||
3643 updateFlags.HasFlag(PrimUpdateFlags.Joint))
3644 { 3622 {
3623 canUseCompressed = false;
3645 canUseImproved = false; 3624 canUseImproved = false;
3646 } 3625 }
3647 }
3648
3649 #endregion UpdateFlags to packet type conversion
3650
3651 #region Block Construction
3652
3653 // TODO: Remove this once we can build compressed updates
3654 canUseCompressed = false;
3655
3656 if (!canUseImproved && !canUseCompressed)
3657 {
3658 if (update.Entity is ScenePresence)
3659 {
3660 objectUpdateBlocks.Value.Add(CreateAvatarUpdateBlock((ScenePresence)update.Entity));
3661 }
3662 else 3626 else
3663 { 3627 {
3664// if (update.Entity is SceneObjectPart && ((SceneObjectPart)update.Entity).IsAttachment) 3628 if (updateFlags.HasFlag(PrimUpdateFlags.Velocity) ||
3665// { 3629 updateFlags.HasFlag(PrimUpdateFlags.Acceleration) ||
3666// SceneObjectPart sop = (SceneObjectPart)update.Entity; 3630 updateFlags.HasFlag(PrimUpdateFlags.CollisionPlane) ||
3667// string text = sop.Text; 3631 updateFlags.HasFlag(PrimUpdateFlags.Joint))
3668// if (text.IndexOf("\n") >= 0) 3632 {
3669// text = text.Remove(text.IndexOf("\n")); 3633 canUseCompressed = false;
3670// 3634 }
3671// if (m_attachmentsSent.Contains(sop.ParentID))
3672// {
3673//// m_log.DebugFormat(
3674//// "[CLIENT]: Sending full info about attached prim {0} text {1}",
3675//// sop.LocalId, text);
3676//
3677// objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock(sop, this.m_agentId));
3678//
3679// m_attachmentsSent.Add(sop.LocalId);
3680// }
3681// else
3682// {
3683// m_log.DebugFormat(
3684// "[CLIENT]: Requeueing full update of prim {0} text {1} since we haven't sent its parent {2} yet",
3685// sop.LocalId, text, sop.ParentID);
3686//
3687// m_entityUpdates.Enqueue(double.MaxValue, update, sop.LocalId);
3688// }
3689// }
3690// else
3691// {
3692 objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId));
3693// }
3694 }
3695 }
3696 else if (!canUseImproved)
3697 {
3698 compressedUpdateBlocks.Value.Add(CreateCompressedUpdateBlock((SceneObjectPart)update.Entity, updateFlags));
3699 }
3700 else
3701 {
3702 if (update.Entity is ScenePresence && ((ScenePresence)update.Entity).UUID == AgentId)
3703 // Self updates go into a special list
3704 terseAgentUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)));
3705 else
3706 // Everything else goes here
3707 terseUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)));
3708 }
3709
3710 #endregion Block Construction
3711 }
3712
3713 #region Packet Sending
3714 3635
3715 const float TIME_DILATION = 1.0f; 3636 if (updateFlags.HasFlag(PrimUpdateFlags.PrimFlags) ||
3716 ushort timeDilation = Utils.FloatToUInt16(TIME_DILATION, 0.0f, 1.0f); 3637 updateFlags.HasFlag(PrimUpdateFlags.ParentID) ||
3717 3638 updateFlags.HasFlag(PrimUpdateFlags.Scale) ||
3718 if (terseAgentUpdateBlocks.IsValueCreated) 3639 updateFlags.HasFlag(PrimUpdateFlags.PrimData) ||
3719 { 3640 updateFlags.HasFlag(PrimUpdateFlags.Text) ||
3720 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseAgentUpdateBlocks.Value; 3641 updateFlags.HasFlag(PrimUpdateFlags.NameValue) ||
3721 3642 updateFlags.HasFlag(PrimUpdateFlags.ExtraData) ||
3722 ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); 3643 updateFlags.HasFlag(PrimUpdateFlags.TextureAnim) ||
3723 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; 3644 updateFlags.HasFlag(PrimUpdateFlags.Sound) ||
3724 packet.RegionData.TimeDilation = timeDilation; 3645 updateFlags.HasFlag(PrimUpdateFlags.Particles) ||
3725 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; 3646 updateFlags.HasFlag(PrimUpdateFlags.Material) ||
3726 3647 updateFlags.HasFlag(PrimUpdateFlags.ClickAction) ||
3727 for (int i = 0; i < blocks.Count; i++) 3648 updateFlags.HasFlag(PrimUpdateFlags.MediaURL) ||
3728 packet.ObjectData[i] = blocks[i]; 3649 updateFlags.HasFlag(PrimUpdateFlags.Joint))
3729 3650 {
3730 OutPacket(packet, ThrottleOutPacketType.Unknown, true); 3651 canUseImproved = false;
3731 } 3652 }
3732 3653 }
3733 if (objectUpdateBlocks.IsValueCreated)
3734 {
3735 List<ObjectUpdatePacket.ObjectDataBlock> blocks = objectUpdateBlocks.Value;
3736 3654
3737 ObjectUpdatePacket packet = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); 3655 #endregion UpdateFlags to packet type conversion
3738 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
3739 packet.RegionData.TimeDilation = timeDilation;
3740 packet.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[blocks.Count];
3741 3656
3742 for (int i = 0; i < blocks.Count; i++) 3657 #region Block Construction
3743 packet.ObjectData[i] = blocks[i];
3744 3658
3745 OutPacket(packet, ThrottleOutPacketType.Task, true); 3659 // TODO: Remove this once we can build compressed updates
3746 } 3660 canUseCompressed = false;
3747 3661
3748 if (compressedUpdateBlocks.IsValueCreated) 3662 if (!canUseImproved && !canUseCompressed)
3749 { 3663 {
3750 List<ObjectUpdateCompressedPacket.ObjectDataBlock> blocks = compressedUpdateBlocks.Value; 3664 if (update.Entity is ScenePresence)
3665 {
3666 objectUpdateBlocks.Value.Add(CreateAvatarUpdateBlock((ScenePresence)update.Entity));
3667 }
3668 else
3669 {
3670 // if (update.Entity is SceneObjectPart && ((SceneObjectPart)update.Entity).IsAttachment)
3671 // {
3672 // SceneObjectPart sop = (SceneObjectPart)update.Entity;
3673 // string text = sop.Text;
3674 // if (text.IndexOf("\n") >= 0)
3675 // text = text.Remove(text.IndexOf("\n"));
3676 //
3677 // if (m_attachmentsSent.Contains(sop.ParentID))
3678 // {
3679 //// m_log.DebugFormat(
3680 //// "[CLIENT]: Sending full info about attached prim {0} text {1}",
3681 //// sop.LocalId, text);
3682 //
3683 // objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock(sop, this.m_agentId));
3684 //
3685 // m_attachmentsSent.Add(sop.LocalId);
3686 // }
3687 // else
3688 // {
3689 // m_log.DebugFormat(
3690 // "[CLIENT]: Requeueing full update of prim {0} text {1} since we haven't sent its parent {2} yet",
3691 // sop.LocalId, text, sop.ParentID);
3692 //
3693 // m_entityUpdates.Enqueue(double.MaxValue, update, sop.LocalId);
3694 // }
3695 // }
3696 // else
3697 // {
3698 objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId));
3699 // }
3700 }
3701 }
3702 else if (!canUseImproved)
3703 {
3704 compressedUpdateBlocks.Value.Add(CreateCompressedUpdateBlock((SceneObjectPart)update.Entity, updateFlags));
3705 }
3706 else
3707 {
3708 if (update.Entity is ScenePresence && ((ScenePresence)update.Entity).UUID == AgentId)
3709 // Self updates go into a special list
3710 terseAgentUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)));
3711 else
3712 // Everything else goes here
3713 terseUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)));
3714 }
3751 3715
3752 ObjectUpdateCompressedPacket packet = (ObjectUpdateCompressedPacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdateCompressed); 3716 #endregion Block Construction
3753 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; 3717 }
3754 packet.RegionData.TimeDilation = timeDilation;
3755 packet.ObjectData = new ObjectUpdateCompressedPacket.ObjectDataBlock[blocks.Count];
3756 3718
3757 for (int i = 0; i < blocks.Count; i++) 3719 #region Packet Sending
3758 packet.ObjectData[i] = blocks[i]; 3720
3721 const float TIME_DILATION = 1.0f;
3722 ushort timeDilation = Utils.FloatToUInt16(TIME_DILATION, 0.0f, 1.0f);
3759 3723
3760 OutPacket(packet, ThrottleOutPacketType.Task, true); 3724 if (terseAgentUpdateBlocks.IsValueCreated)
3761 } 3725 {
3726 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseAgentUpdateBlocks.Value;
3762 3727
3763 if (terseUpdateBlocks.IsValueCreated) 3728 ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket();
3764 { 3729 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
3765 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value; 3730 packet.RegionData.TimeDilation = timeDilation;
3731 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];
3766 3732
3767 ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); 3733 for (int i = 0; i < blocks.Count; i++)
3768 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; 3734 packet.ObjectData[i] = blocks[i];
3769 packet.RegionData.TimeDilation = timeDilation;
3770 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];
3771 3735
3772 for (int i = 0; i < blocks.Count; i++) 3736 OutPacket(packet, ThrottleOutPacketType.Unknown, true);
3773 packet.ObjectData[i] = blocks[i]; 3737 }
3774 3738
3775 OutPacket(packet, ThrottleOutPacketType.Task, true); 3739 if (objectUpdateBlocks.IsValueCreated)
3740 {
3741 List<ObjectUpdatePacket.ObjectDataBlock> blocks = objectUpdateBlocks.Value;
3742
3743 ObjectUpdatePacket packet = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate);
3744 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
3745 packet.RegionData.TimeDilation = timeDilation;
3746 packet.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[blocks.Count];
3747
3748 for (int i = 0; i < blocks.Count; i++)
3749 packet.ObjectData[i] = blocks[i];
3750
3751 OutPacket(packet, ThrottleOutPacketType.Task, true);
3752 }
3753
3754 if (compressedUpdateBlocks.IsValueCreated)
3755 {
3756 List<ObjectUpdateCompressedPacket.ObjectDataBlock> blocks = compressedUpdateBlocks.Value;
3757
3758 ObjectUpdateCompressedPacket packet = (ObjectUpdateCompressedPacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdateCompressed);
3759 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
3760 packet.RegionData.TimeDilation = timeDilation;
3761 packet.ObjectData = new ObjectUpdateCompressedPacket.ObjectDataBlock[blocks.Count];
3762
3763 for (int i = 0; i < blocks.Count; i++)
3764 packet.ObjectData[i] = blocks[i];
3765
3766 OutPacket(packet, ThrottleOutPacketType.Task, true);
3767 }
3768
3769 if (terseUpdateBlocks.IsValueCreated)
3770 {
3771 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value;
3772
3773 ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket();
3774 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
3775 packet.RegionData.TimeDilation = timeDilation;
3776 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];
3777
3778 for (int i = 0; i < blocks.Count; i++)
3779 packet.ObjectData[i] = blocks[i];
3780
3781 OutPacket(packet, ThrottleOutPacketType.Task, true);
3782 }
3776 } 3783 }
3777 3784
3778 #endregion Packet Sending 3785 #endregion Packet Sending