diff options
Diffstat (limited to 'OpenSim/Region/ClientStack')
7 files changed, 302 insertions, 338 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 1f7e66d..6129e10 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs | |||
@@ -3561,6 +3561,44 @@ 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 | private 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 | private void ResendPrimUpdates(List<EntityUpdate> updates, OutgoingPacket oPacket) | ||
3587 | { | ||
3588 | // m_log.WarnFormat("[CLIENT] resending prim update {0}",updates[0].UpdateTime); | ||
3589 | |||
3590 | // Remove the update packet from the list of packets waiting for acknowledgement | ||
3591 | // because we are requeuing the list of updates. They will be resent in new packets | ||
3592 | // with the most recent state and priority. | ||
3593 | m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber); | ||
3594 | |||
3595 | // Count this as a resent packet since we are going to requeue all of the updates contained in it | ||
3596 | Interlocked.Increment(ref m_udpClient.PacketsResent); | ||
3597 | |||
3598 | foreach (EntityUpdate update in updates) | ||
3599 | ResendPrimUpdate(update); | ||
3600 | } | ||
3601 | |||
3564 | private void ProcessEntityUpdates(int maxUpdates) | 3602 | private void ProcessEntityUpdates(int maxUpdates) |
3565 | { | 3603 | { |
3566 | OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); | 3604 | OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); |
@@ -3568,6 +3606,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3568 | OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); | 3606 | 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>>(); | 3607 | OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); |
3570 | 3608 | ||
3609 | OpenSim.Framework.Lazy<List<EntityUpdate>> objectUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>(); | ||
3610 | OpenSim.Framework.Lazy<List<EntityUpdate>> compressedUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>(); | ||
3611 | OpenSim.Framework.Lazy<List<EntityUpdate>> terseUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>(); | ||
3612 | OpenSim.Framework.Lazy<List<EntityUpdate>> terseAgentUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>(); | ||
3613 | |||
3571 | // Check to see if this is a flush | 3614 | // Check to see if this is a flush |
3572 | if (maxUpdates <= 0) | 3615 | if (maxUpdates <= 0) |
3573 | { | 3616 | { |
@@ -3583,7 +3626,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3583 | float avgTimeDilation = 1.0f; | 3626 | float avgTimeDilation = 1.0f; |
3584 | IEntityUpdate iupdate; | 3627 | IEntityUpdate iupdate; |
3585 | Int32 timeinqueue; // this is just debugging code & can be dropped later | 3628 | Int32 timeinqueue; // this is just debugging code & can be dropped later |
3586 | 3629 | ||
3587 | while (updatesThisCall < maxUpdates) | 3630 | while (updatesThisCall < maxUpdates) |
3588 | { | 3631 | { |
3589 | lock (m_entityUpdates.SyncRoot) | 3632 | lock (m_entityUpdates.SyncRoot) |
@@ -3688,24 +3731,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3688 | if (update.Entity is ScenePresence) | 3731 | if (update.Entity is ScenePresence) |
3689 | { | 3732 | { |
3690 | objectUpdateBlocks.Value.Add(CreateAvatarUpdateBlock((ScenePresence)update.Entity)); | 3733 | objectUpdateBlocks.Value.Add(CreateAvatarUpdateBlock((ScenePresence)update.Entity)); |
3734 | objectUpdates.Value.Add(update); | ||
3691 | } | 3735 | } |
3692 | else | 3736 | else |
3693 | { | 3737 | { |
3694 | objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId)); | 3738 | objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId)); |
3739 | objectUpdates.Value.Add(update); | ||
3695 | } | 3740 | } |
3696 | } | 3741 | } |
3697 | else if (!canUseImproved) | 3742 | else if (!canUseImproved) |
3698 | { | 3743 | { |
3699 | compressedUpdateBlocks.Value.Add(CreateCompressedUpdateBlock((SceneObjectPart)update.Entity, updateFlags)); | 3744 | compressedUpdateBlocks.Value.Add(CreateCompressedUpdateBlock((SceneObjectPart)update.Entity, updateFlags)); |
3745 | compressedUpdates.Value.Add(update); | ||
3700 | } | 3746 | } |
3701 | else | 3747 | else |
3702 | { | 3748 | { |
3703 | if (update.Entity is ScenePresence && ((ScenePresence)update.Entity).UUID == AgentId) | 3749 | if (update.Entity is ScenePresence && ((ScenePresence)update.Entity).UUID == AgentId) |
3750 | { | ||
3704 | // Self updates go into a special list | 3751 | // Self updates go into a special list |
3705 | terseAgentUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures))); | 3752 | terseAgentUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures))); |
3753 | terseAgentUpdates.Value.Add(update); | ||
3754 | } | ||
3706 | else | 3755 | else |
3756 | { | ||
3707 | // Everything else goes here | 3757 | // Everything else goes here |
3708 | terseUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures))); | 3758 | terseUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures))); |
3759 | terseUpdates.Value.Add(update); | ||
3760 | } | ||
3709 | } | 3761 | } |
3710 | 3762 | ||
3711 | #endregion Block Construction | 3763 | #endregion Block Construction |
@@ -3713,28 +3765,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3713 | 3765 | ||
3714 | 3766 | ||
3715 | #region Packet Sending | 3767 | #region Packet Sending |
3716 | |||
3717 | //const float TIME_DILATION = 1.0f; | ||
3718 | |||
3719 | |||
3720 | ushort timeDilation = Utils.FloatToUInt16(avgTimeDilation, 0.0f, 1.0f); | 3768 | ushort timeDilation = Utils.FloatToUInt16(avgTimeDilation, 0.0f, 1.0f); |
3721 | 3769 | ||
3722 | if (terseAgentUpdateBlocks.IsValueCreated) | 3770 | if (terseAgentUpdateBlocks.IsValueCreated) |
3723 | { | 3771 | { |
3724 | List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseAgentUpdateBlocks.Value; | 3772 | List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseAgentUpdateBlocks.Value; |
3725 | 3773 | ||
3726 | ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); | 3774 | ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); |
3727 | packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; | 3775 | packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; |
3728 | packet.RegionData.TimeDilation = timeDilation; | 3776 | packet.RegionData.TimeDilation = timeDilation; |
3729 | packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; | 3777 | packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; |
3730 | 3778 | ||
3731 | for (int i = 0; i < blocks.Count; i++) | 3779 | for (int i = 0; i < blocks.Count; i++) |
3732 | packet.ObjectData[i] = blocks[i]; | 3780 | packet.ObjectData[i] = blocks[i]; |
3733 | 3781 | // If any of the packets created from this call go unacknowledged, all of the updates will be resent | |
3734 | 3782 | OutPacket(packet, ThrottleOutPacketType.Unknown, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseAgentUpdates.Value, oPacket); }); | |
3735 | OutPacket(packet, ThrottleOutPacketType.Unknown, true); | ||
3736 | } | 3783 | } |
3737 | 3784 | ||
3738 | if (objectUpdateBlocks.IsValueCreated) | 3785 | if (objectUpdateBlocks.IsValueCreated) |
3739 | { | 3786 | { |
3740 | List<ObjectUpdatePacket.ObjectDataBlock> blocks = objectUpdateBlocks.Value; | 3787 | List<ObjectUpdatePacket.ObjectDataBlock> blocks = objectUpdateBlocks.Value; |
@@ -3746,8 +3793,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3746 | 3793 | ||
3747 | for (int i = 0; i < blocks.Count; i++) | 3794 | for (int i = 0; i < blocks.Count; i++) |
3748 | packet.ObjectData[i] = blocks[i]; | 3795 | packet.ObjectData[i] = blocks[i]; |
3749 | 3796 | // If any of the packets created from this call go unacknowledged, all of the updates will be resent | |
3750 | OutPacket(packet, ThrottleOutPacketType.Task, true); | 3797 | OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(objectUpdates.Value, oPacket); }); |
3751 | } | 3798 | } |
3752 | 3799 | ||
3753 | if (compressedUpdateBlocks.IsValueCreated) | 3800 | if (compressedUpdateBlocks.IsValueCreated) |
@@ -3761,10 +3808,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3761 | 3808 | ||
3762 | for (int i = 0; i < blocks.Count; i++) | 3809 | for (int i = 0; i < blocks.Count; i++) |
3763 | packet.ObjectData[i] = blocks[i]; | 3810 | packet.ObjectData[i] = blocks[i]; |
3764 | 3811 | // If any of the packets created from this call go unacknowledged, all of the updates will be resent | |
3765 | OutPacket(packet, ThrottleOutPacketType.Task, true); | 3812 | OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(compressedUpdates.Value, oPacket); }); |
3766 | } | 3813 | } |
3767 | 3814 | ||
3768 | if (terseUpdateBlocks.IsValueCreated) | 3815 | if (terseUpdateBlocks.IsValueCreated) |
3769 | { | 3816 | { |
3770 | List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value; | 3817 | List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value; |
@@ -3776,8 +3823,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3776 | 3823 | ||
3777 | for (int i = 0; i < blocks.Count; i++) | 3824 | for (int i = 0; i < blocks.Count; i++) |
3778 | packet.ObjectData[i] = blocks[i]; | 3825 | packet.ObjectData[i] = blocks[i]; |
3779 | 3826 | // If any of the packets created from this call go unacknowledged, all of the updates will be resent | |
3780 | OutPacket(packet, ThrottleOutPacketType.Task, true); | 3827 | OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseUpdates.Value, oPacket); }); |
3781 | } | 3828 | } |
3782 | } | 3829 | } |
3783 | 3830 | ||
@@ -3969,7 +4016,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3969 | { | 4016 | { |
3970 | SendFamilyProps = SendFamilyProps || update.SendFamilyProps; | 4017 | SendFamilyProps = SendFamilyProps || update.SendFamilyProps; |
3971 | SendObjectProps = SendObjectProps || update.SendObjectProps; | 4018 | SendObjectProps = SendObjectProps || update.SendObjectProps; |
3972 | Flags |= update.Flags; | 4019 | // other properties may need to be updated by base class |
4020 | base.Update(update); | ||
3973 | } | 4021 | } |
3974 | } | 4022 | } |
3975 | 4023 | ||
@@ -3980,6 +4028,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3980 | m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false)); | 4028 | m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false)); |
3981 | } | 4029 | } |
3982 | 4030 | ||
4031 | private void ResendPropertyUpdate(ObjectPropertyUpdate update) | ||
4032 | { | ||
4033 | uint priority = 0; | ||
4034 | lock (m_entityProps.SyncRoot) | ||
4035 | m_entityProps.Enqueue(priority, update); | ||
4036 | } | ||
4037 | |||
4038 | private void ResendPropertyUpdates(List<ObjectPropertyUpdate> updates, OutgoingPacket oPacket) | ||
4039 | { | ||
4040 | // m_log.WarnFormat("[CLIENT] resending object property {0}",updates[0].UpdateTime); | ||
4041 | |||
4042 | // Remove the update packet from the list of packets waiting for acknowledgement | ||
4043 | // because we are requeuing the list of updates. They will be resent in new packets | ||
4044 | // with the most recent state. | ||
4045 | m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber); | ||
4046 | |||
4047 | // Count this as a resent packet since we are going to requeue all of the updates contained in it | ||
4048 | Interlocked.Increment(ref m_udpClient.PacketsResent); | ||
4049 | |||
4050 | foreach (ObjectPropertyUpdate update in updates) | ||
4051 | ResendPropertyUpdate(update); | ||
4052 | } | ||
4053 | |||
3983 | public void SendObjectPropertiesReply(ISceneEntity entity) | 4054 | public void SendObjectPropertiesReply(ISceneEntity entity) |
3984 | { | 4055 | { |
3985 | uint priority = 0; // time based ordering only | 4056 | uint priority = 0; // time based ordering only |
@@ -3995,6 +4066,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3995 | OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>> objectPropertiesBlocks = | 4066 | OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>> objectPropertiesBlocks = |
3996 | new OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>>(); | 4067 | new OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>>(); |
3997 | 4068 | ||
4069 | OpenSim.Framework.Lazy<List<ObjectPropertyUpdate>> familyUpdates = | ||
4070 | new OpenSim.Framework.Lazy<List<ObjectPropertyUpdate>>(); | ||
4071 | |||
4072 | OpenSim.Framework.Lazy<List<ObjectPropertyUpdate>> propertyUpdates = | ||
4073 | new OpenSim.Framework.Lazy<List<ObjectPropertyUpdate>>(); | ||
4074 | |||
3998 | IEntityUpdate iupdate; | 4075 | IEntityUpdate iupdate; |
3999 | Int32 timeinqueue; // this is just debugging code & can be dropped later | 4076 | Int32 timeinqueue; // this is just debugging code & can be dropped later |
4000 | 4077 | ||
@@ -4013,6 +4090,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4013 | SceneObjectPart sop = (SceneObjectPart)update.Entity; | 4090 | SceneObjectPart sop = (SceneObjectPart)update.Entity; |
4014 | ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags); | 4091 | ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags); |
4015 | objectFamilyBlocks.Value.Add(objPropDB); | 4092 | objectFamilyBlocks.Value.Add(objPropDB); |
4093 | familyUpdates.Value.Add(update); | ||
4016 | } | 4094 | } |
4017 | } | 4095 | } |
4018 | 4096 | ||
@@ -4023,6 +4101,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4023 | SceneObjectPart sop = (SceneObjectPart)update.Entity; | 4101 | SceneObjectPart sop = (SceneObjectPart)update.Entity; |
4024 | ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop); | 4102 | ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop); |
4025 | objectPropertiesBlocks.Value.Add(objPropDB); | 4103 | objectPropertiesBlocks.Value.Add(objPropDB); |
4104 | propertyUpdates.Value.Add(update); | ||
4026 | } | 4105 | } |
4027 | } | 4106 | } |
4028 | 4107 | ||
@@ -4030,12 +4109,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4030 | } | 4109 | } |
4031 | 4110 | ||
4032 | 4111 | ||
4033 | Int32 ppcnt = 0; | 4112 | // Int32 ppcnt = 0; |
4034 | Int32 pbcnt = 0; | 4113 | // Int32 pbcnt = 0; |
4035 | 4114 | ||
4036 | if (objectPropertiesBlocks.IsValueCreated) | 4115 | if (objectPropertiesBlocks.IsValueCreated) |
4037 | { | 4116 | { |
4038 | List<ObjectPropertiesPacket.ObjectDataBlock> blocks = objectPropertiesBlocks.Value; | 4117 | List<ObjectPropertiesPacket.ObjectDataBlock> blocks = objectPropertiesBlocks.Value; |
4118 | List<ObjectPropertyUpdate> updates = propertyUpdates.Value; | ||
4039 | 4119 | ||
4040 | ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); | 4120 | ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); |
4041 | packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count]; | 4121 | packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count]; |
@@ -4043,28 +4123,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4043 | packet.ObjectData[i] = blocks[i]; | 4123 | packet.ObjectData[i] = blocks[i]; |
4044 | 4124 | ||
4045 | packet.Header.Zerocoded = true; | 4125 | packet.Header.Zerocoded = true; |
4046 | OutPacket(packet, ThrottleOutPacketType.Task, true); | ||
4047 | 4126 | ||
4048 | pbcnt += blocks.Count; | 4127 | // Pass in the delegate so that if this packet needs to be resent, we send the current properties |
4049 | ppcnt++; | 4128 | // of the object rather than the properties when the packet was created |
4129 | OutPacket(packet, ThrottleOutPacketType.Task, true, | ||
4130 | delegate(OutgoingPacket oPacket) | ||
4131 | { | ||
4132 | ResendPropertyUpdates(updates, oPacket); | ||
4133 | }); | ||
4134 | |||
4135 | // pbcnt += blocks.Count; | ||
4136 | // ppcnt++; | ||
4050 | } | 4137 | } |
4051 | 4138 | ||
4052 | Int32 fpcnt = 0; | 4139 | // Int32 fpcnt = 0; |
4053 | Int32 fbcnt = 0; | 4140 | // Int32 fbcnt = 0; |
4054 | 4141 | ||
4055 | if (objectFamilyBlocks.IsValueCreated) | 4142 | if (objectFamilyBlocks.IsValueCreated) |
4056 | { | 4143 | { |
4057 | List<ObjectPropertiesFamilyPacket.ObjectDataBlock> blocks = objectFamilyBlocks.Value; | 4144 | List<ObjectPropertiesFamilyPacket.ObjectDataBlock> blocks = objectFamilyBlocks.Value; |
4058 | 4145 | ||
4059 | // ObjectPropertiesFamilyPacket objPropFamilyPack = | ||
4060 | // (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); | ||
4061 | // | ||
4062 | // objPropFamilyPack.ObjectData = new ObjectPropertiesFamilyPacket.ObjectDataBlock[blocks.Count]; | ||
4063 | // for (int i = 0; i < blocks.Count; i++) | ||
4064 | // objPropFamilyPack.ObjectData[i] = blocks[i]; | ||
4065 | // | ||
4066 | // OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task, true); | ||
4067 | |||
4068 | // one packet per object block... uggh... | 4146 | // one packet per object block... uggh... |
4069 | for (int i = 0; i < blocks.Count; i++) | 4147 | for (int i = 0; i < blocks.Count; i++) |
4070 | { | 4148 | { |
@@ -4073,10 +4151,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4073 | 4151 | ||
4074 | packet.ObjectData = blocks[i]; | 4152 | packet.ObjectData = blocks[i]; |
4075 | packet.Header.Zerocoded = true; | 4153 | packet.Header.Zerocoded = true; |
4076 | OutPacket(packet, ThrottleOutPacketType.Task); | ||
4077 | 4154 | ||
4078 | fpcnt++; | 4155 | // Pass in the delegate so that if this packet needs to be resent, we send the current properties |
4079 | fbcnt++; | 4156 | // of the object rather than the properties when the packet was created |
4157 | List<ObjectPropertyUpdate> updates = new List<ObjectPropertyUpdate>(); | ||
4158 | updates.Add(familyUpdates.Value[i]); | ||
4159 | OutPacket(packet, ThrottleOutPacketType.Task, true, | ||
4160 | delegate(OutgoingPacket oPacket) | ||
4161 | { | ||
4162 | ResendPropertyUpdates(updates, oPacket); | ||
4163 | }); | ||
4164 | |||
4165 | // fpcnt++; | ||
4166 | // fbcnt++; | ||
4080 | } | 4167 | } |
4081 | 4168 | ||
4082 | } | 4169 | } |
@@ -4113,7 +4200,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
4113 | 4200 | ||
4114 | return block; | 4201 | return block; |
4115 | } | 4202 | } |
4116 | 4203 | ||
4117 | private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop) | 4204 | private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop) |
4118 | { | 4205 | { |
4119 | //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); | 4206 | //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); |
@@ -11363,6 +11450,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11363 | /// handles splitting manually</param> | 11450 | /// handles splitting manually</param> |
11364 | protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting) | 11451 | protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting) |
11365 | { | 11452 | { |
11453 | OutPacket(packet, throttlePacketType, doAutomaticSplitting, null); | ||
11454 | } | ||
11455 | |||
11456 | /// <summary> | ||
11457 | /// This is the starting point for sending a simulator packet out to the client | ||
11458 | /// </summary> | ||
11459 | /// <param name="packet">Packet to send</param> | ||
11460 | /// <param name="throttlePacketType">Throttling category for the packet</param> | ||
11461 | /// <param name="doAutomaticSplitting">True to automatically split oversized | ||
11462 | /// packets (the default), or false to disable splitting if the calling code | ||
11463 | /// handles splitting manually</param> | ||
11464 | /// <param name="method">The method to be called in the event this packet is reliable | ||
11465 | /// and unacknowledged. The server will provide normal resend capability if you do not | ||
11466 | /// provide your own method.</param> | ||
11467 | protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting, UnackedPacketMethod method) | ||
11468 | { | ||
11366 | if (m_debugPacketLevel > 0) | 11469 | if (m_debugPacketLevel > 0) |
11367 | { | 11470 | { |
11368 | bool logPacket = true; | 11471 | bool logPacket = true; |
@@ -11388,7 +11491,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
11388 | m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); | 11491 | m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); |
11389 | } | 11492 | } |
11390 | 11493 | ||
11391 | m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); | 11494 | m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting, method); |
11392 | } | 11495 | } |
11393 | 11496 | ||
11394 | public bool AddMoney(int debit) | 11497 | public bool AddMoney(int debit) |
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 7be8a0a..20bfec8 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | |||
@@ -135,7 +135,12 @@ 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_throttleClient; | 138 | private readonly AdaptiveTokenBucket m_throttleClient; |
139 | public AdaptiveTokenBucket FlowThrottle | ||
140 | { | ||
141 | get { return m_throttleClient; } | ||
142 | } | ||
143 | |||
139 | /// <summary>Throttle bucket for this agent's connection</summary> | 144 | /// <summary>Throttle bucket for this agent's connection</summary> |
140 | private readonly TokenBucket m_throttleCategory; | 145 | private readonly TokenBucket m_throttleCategory; |
141 | /// <summary>Throttle buckets for each packet category</summary> | 146 | /// <summary>Throttle buckets for each packet category</summary> |
@@ -176,7 +181,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
176 | m_maxRTO = maxRTO; | 181 | m_maxRTO = maxRTO; |
177 | 182 | ||
178 | // Create a token bucket throttle for this client that has the scene token bucket as a parent | 183 | // Create a token bucket throttle for this client that has the scene token bucket as a parent |
179 | m_throttleClient = new TokenBucket(parentThrottle, rates.TotalLimit); | 184 | m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.TotalLimit); |
180 | // Create a token bucket throttle for the total categary with the client bucket as a throttle | 185 | // Create a token bucket throttle for the total categary with the client bucket as a throttle |
181 | m_throttleCategory = new TokenBucket(m_throttleClient, rates.TotalLimit); | 186 | m_throttleCategory = new TokenBucket(m_throttleClient, rates.TotalLimit); |
182 | // Create an array of token buckets for this clients different throttle categories | 187 | // Create an array of token buckets for this clients different throttle categories |
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index d08b25f..bd58ddc 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(OutgoingPacket oPacket) { ResendUnacked(oPacket); } : 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(expiredPackets[i]); | ||
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..76c6c14 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs | |||
@@ -31,6 +31,8 @@ using OpenMetaverse; | |||
31 | 31 | ||
32 | namespace OpenSim.Region.ClientStack.LindenUDP | 32 | namespace OpenSim.Region.ClientStack.LindenUDP |
33 | { | 33 | { |
34 | |||
35 | public delegate void UnackedPacketMethod(OutgoingPacket oPacket); | ||
34 | /// <summary> | 36 | /// <summary> |
35 | /// Holds a reference to the <seealso cref="LLUDPClient"/> this packet is | 37 | /// Holds a reference to the <seealso cref="LLUDPClient"/> this packet is |
36 | /// destined for, along with the serialized packet data, sequence number | 38 | /// destined for, along with the serialized packet data, sequence number |
@@ -52,6 +54,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
52 | public int TickCount; | 54 | public int TickCount; |
53 | /// <summary>Category this packet belongs to</summary> | 55 | /// <summary>Category this packet belongs to</summary> |
54 | public ThrottleOutPacketType Category; | 56 | public ThrottleOutPacketType Category; |
57 | /// <summary>The delegate to be called if this packet is determined to be unacknowledged</summary> | ||
58 | public UnackedPacketMethod UnackedMethod; | ||
55 | 59 | ||
56 | /// <summary> | 60 | /// <summary> |
57 | /// Default constructor | 61 | /// Default constructor |
@@ -60,11 +64,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
60 | /// <param name="buffer">Serialized packet data. If the flags or sequence number | 64 | /// <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> | 65 | /// need to be updated, they will be injected directly into this binary buffer</param> |
62 | /// <param name="category">Throttling category for this packet</param> | 66 | /// <param name="category">Throttling category for this packet</param> |
63 | public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category) | 67 | public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category, UnackedPacketMethod method) |
64 | { | 68 | { |
65 | Client = client; | 69 | Client = client; |
66 | Buffer = buffer; | 70 | Buffer = buffer; |
67 | Category = category; | 71 | Category = category; |
72 | UnackedMethod = method; | ||
68 | } | 73 | } |
69 | } | 74 | } |
70 | } | 75 | } |
diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs deleted file mode 100644 index b62ec07..0000000 --- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs +++ /dev/null | |||
@@ -1,245 +0,0 @@ | |||
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 | |||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Reflection; | ||
32 | |||
33 | using OpenSim.Framework; | ||
34 | using OpenSim.Framework.Client; | ||
35 | using log4net; | ||
36 | |||
37 | namespace 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, IEntityUpdate 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.Update(lookup.Heap[lookup.Handle].Value); | ||
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 IEntityUpdate 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(IEntityUpdate); | ||
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 IEntityUpdate value; | ||
179 | internal IEntityUpdate 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, IEntityUpdate 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 | } | ||
diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index 07b0a1d..4ee6d3a 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs | |||
@@ -48,31 +48,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
48 | /// Number of ticks (ms) per quantum, drip rate and max burst | 48 | /// Number of ticks (ms) per quantum, drip rate and max burst |
49 | /// are defined over this interval. | 49 | /// are defined over this interval. |
50 | /// </summary> | 50 | /// </summary> |
51 | private const Int32 m_ticksPerQuantum = 1000; | 51 | protected const Int32 m_ticksPerQuantum = 1000; |
52 | 52 | ||
53 | /// <summary> | 53 | /// <summary> |
54 | /// This is the number of quantums worth of packets that can | 54 | /// This is the number of quantums worth of packets that can |
55 | /// be accommodated during a burst | 55 | /// be accommodated during a burst |
56 | /// </summary> | 56 | /// </summary> |
57 | private const Double m_quantumsPerBurst = 1.5; | 57 | protected const Double m_quantumsPerBurst = 1.5; |
58 | 58 | ||
59 | /// <summary> | 59 | /// <summary> |
60 | /// </summary> | 60 | /// </summary> |
61 | private const Int32 m_minimumDripRate = 1400; | 61 | protected const Int32 m_minimumDripRate = 1400; |
62 | 62 | ||
63 | /// <summary>Time of the last drip, in system ticks</summary> | 63 | /// <summary>Time of the last drip, in system ticks</summary> |
64 | private Int32 m_lastDrip; | 64 | protected Int32 m_lastDrip; |
65 | 65 | ||
66 | /// <summary> | 66 | /// <summary> |
67 | /// The number of bytes that can be sent at this moment. This is the | 67 | /// The number of bytes that can be sent at this moment. This is the |
68 | /// current number of tokens in the bucket | 68 | /// current number of tokens in the bucket |
69 | /// </summary> | 69 | /// </summary> |
70 | private Int64 m_tokenCount; | 70 | protected Int64 m_tokenCount; |
71 | 71 | ||
72 | /// <summary> | 72 | /// <summary> |
73 | /// Map of children buckets and their requested maximum burst rate | 73 | /// Map of children buckets and their requested maximum burst rate |
74 | /// </summary> | 74 | /// </summary> |
75 | private Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>(); | 75 | protected Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>(); |
76 | 76 | ||
77 | #region Properties | 77 | #region Properties |
78 | 78 | ||
@@ -81,7 +81,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
81 | /// parent. The parent bucket will limit the aggregate bandwidth of all | 81 | /// parent. The parent bucket will limit the aggregate bandwidth of all |
82 | /// of its children buckets | 82 | /// of its children buckets |
83 | /// </summary> | 83 | /// </summary> |
84 | private TokenBucket m_parent; | 84 | protected TokenBucket m_parent; |
85 | public TokenBucket Parent | 85 | public TokenBucket Parent |
86 | { | 86 | { |
87 | get { return m_parent; } | 87 | get { return m_parent; } |
@@ -93,7 +93,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
93 | /// of tokens that can accumulate in the bucket at any one time. This | 93 | /// of tokens that can accumulate in the bucket at any one time. This |
94 | /// also sets the total request for leaf nodes | 94 | /// also sets the total request for leaf nodes |
95 | /// </summary> | 95 | /// </summary> |
96 | private Int64 m_burstRate; | 96 | protected Int64 m_burstRate; |
97 | public Int64 RequestedBurstRate | 97 | public Int64 RequestedBurstRate |
98 | { | 98 | { |
99 | get { return m_burstRate; } | 99 | get { return m_burstRate; } |
@@ -118,8 +118,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
118 | /// <remarks>Tokens are added to the bucket any time | 118 | /// <remarks>Tokens are added to the bucket any time |
119 | /// <seealso cref="RemoveTokens"/> is called, at the granularity of | 119 | /// <seealso cref="RemoveTokens"/> is called, at the granularity of |
120 | /// the system tick interval (typically around 15-22ms)</remarks> | 120 | /// the system tick interval (typically around 15-22ms)</remarks> |
121 | private Int64 m_dripRate; | 121 | protected Int64 m_dripRate; |
122 | public Int64 RequestedDripRate | 122 | public virtual Int64 RequestedDripRate |
123 | { | 123 | { |
124 | get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } | 124 | get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } |
125 | set { | 125 | set { |
@@ -131,7 +131,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
131 | } | 131 | } |
132 | } | 132 | } |
133 | 133 | ||
134 | public Int64 DripRate | 134 | public virtual Int64 DripRate |
135 | { | 135 | { |
136 | get { | 136 | get { |
137 | if (m_parent == null) | 137 | if (m_parent == null) |
@@ -149,7 +149,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
149 | /// The current total of the requested maximum burst rates of | 149 | /// The current total of the requested maximum burst rates of |
150 | /// this bucket's children buckets. | 150 | /// this bucket's children buckets. |
151 | /// </summary> | 151 | /// </summary> |
152 | private Int64 m_totalDripRequest; | 152 | protected Int64 m_totalDripRequest; |
153 | public Int64 TotalDripRequest | 153 | public Int64 TotalDripRequest |
154 | { | 154 | { |
155 | get { return m_totalDripRequest; } | 155 | get { return m_totalDripRequest; } |
@@ -189,7 +189,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
189 | /// hierarchy. However, if any of the parents is over-booked, then | 189 | /// hierarchy. However, if any of the parents is over-booked, then |
190 | /// the modifier will be less than 1. | 190 | /// the modifier will be less than 1. |
191 | /// </summary> | 191 | /// </summary> |
192 | private double DripRateModifier() | 192 | protected double DripRateModifier() |
193 | { | 193 | { |
194 | Int64 driprate = DripRate; | 194 | Int64 driprate = DripRate; |
195 | return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; | 195 | return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; |
@@ -197,7 +197,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
197 | 197 | ||
198 | /// <summary> | 198 | /// <summary> |
199 | /// </summary> | 199 | /// </summary> |
200 | private double BurstRateModifier() | 200 | protected double BurstRateModifier() |
201 | { | 201 | { |
202 | // for now... burst rate is always m_quantumsPerBurst (constant) | 202 | // for now... burst rate is always m_quantumsPerBurst (constant) |
203 | // larger than drip rate so the ratio of burst requests is the | 203 | // larger than drip rate so the ratio of burst requests is the |
@@ -268,7 +268,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
268 | /// Deposit tokens into the bucket from a child bucket that did | 268 | /// Deposit tokens into the bucket from a child bucket that did |
269 | /// not use all of its available tokens | 269 | /// not use all of its available tokens |
270 | /// </summary> | 270 | /// </summary> |
271 | private void Deposit(Int64 count) | 271 | protected void Deposit(Int64 count) |
272 | { | 272 | { |
273 | m_tokenCount += count; | 273 | m_tokenCount += count; |
274 | 274 | ||
@@ -285,7 +285,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
285 | /// call to Drip | 285 | /// call to Drip |
286 | /// </summary> | 286 | /// </summary> |
287 | /// <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> |
288 | private void Drip() | 288 | protected void Drip() |
289 | { | 289 | { |
290 | // This should never happen... means we are a leaf node and were created | 290 | // This should never happen... means we are a leaf node and were created |
291 | // with no drip rate... | 291 | // with no drip rate... |
@@ -310,4 +310,64 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
310 | Deposit(deltaMS * DripRate / m_ticksPerQuantum); | 310 | Deposit(deltaMS * DripRate / m_ticksPerQuantum); |
311 | } | 311 | } |
312 | } | 312 | } |
313 | |||
314 | public class AdaptiveTokenBucket : TokenBucket | ||
315 | { | ||
316 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
317 | |||
318 | // <summary> | ||
319 | // The minimum rate for flow control. | ||
320 | // </summary> | ||
321 | protected const Int64 m_minimumFlow = m_minimumDripRate * 10; | ||
322 | |||
323 | // <summary> | ||
324 | // The maximum rate for flow control. Drip rate can never be | ||
325 | // greater than this. | ||
326 | // </summary> | ||
327 | protected Int64 m_maxDripRate = 0; | ||
328 | protected Int64 MaxDripRate | ||
329 | { | ||
330 | get { return (m_maxDripRate == 0 ? m_totalDripRequest : m_maxDripRate); } | ||
331 | set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); } | ||
332 | } | ||
333 | |||
334 | // <summary> | ||
335 | // | ||
336 | // </summary> | ||
337 | public virtual Int64 AdjustedDripRate | ||
338 | { | ||
339 | get { return m_dripRate; } | ||
340 | set { | ||
341 | m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value,m_minimumFlow,MaxDripRate); | ||
342 | m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); | ||
343 | if (m_parent != null) | ||
344 | m_parent.RegisterRequest(this,m_dripRate); | ||
345 | } | ||
346 | } | ||
347 | |||
348 | // <summary> | ||
349 | // | ||
350 | // </summary> | ||
351 | public AdaptiveTokenBucket(TokenBucket parent, Int64 maxDripRate) : base(parent,m_minimumFlow) | ||
352 | { | ||
353 | MaxDripRate = maxDripRate; | ||
354 | } | ||
355 | |||
356 | // <summary> | ||
357 | // | ||
358 | // </summary> | ||
359 | public void ExpirePackets(Int32 count) | ||
360 | { | ||
361 | // m_log.WarnFormat("[ADAPTIVEBUCKET] drop {0} by {1} expired packets",AdjustedDripRate,count); | ||
362 | AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,count)); | ||
363 | } | ||
364 | |||
365 | // <summary> | ||
366 | // | ||
367 | // </summary> | ||
368 | public void AcknowledgePackets(Int32 count) | ||
369 | { | ||
370 | AdjustedDripRate = AdjustedDripRate + count; | ||
371 | } | ||
372 | } | ||
313 | } | 373 | } |
diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index d195110..90a87fa 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs | |||
@@ -83,6 +83,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
83 | 83 | ||
84 | /// <summary> | 84 | /// <summary> |
85 | /// Marks a packet as acknowledged | 85 | /// Marks a packet as acknowledged |
86 | /// This method is used when an acknowledgement is received from the network for a previously | ||
87 | /// sent packet. Effects of removal this way are to update unacked byte count, adjust RTT | ||
88 | /// and increase throttle to the coresponding client. | ||
86 | /// </summary> | 89 | /// </summary> |
87 | /// <param name="sequenceNumber">Sequence number of the packet to | 90 | /// <param name="sequenceNumber">Sequence number of the packet to |
88 | /// acknowledge</param> | 91 | /// acknowledge</param> |
@@ -95,6 +98,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
95 | } | 98 | } |
96 | 99 | ||
97 | /// <summary> | 100 | /// <summary> |
101 | /// Marks a packet as no longer needing acknowledgement without a received acknowledgement. | ||
102 | /// This method is called when a packet expires and we no longer need an acknowledgement. | ||
103 | /// When some reliable packet types expire, they are handled in a way other than simply | ||
104 | /// resending them. The only effect of removal this way is to update unacked byte count. | ||
105 | /// </summary> | ||
106 | /// <param name="sequenceNumber">Sequence number of the packet to | ||
107 | /// acknowledge</param> | ||
108 | /// <remarks>The packet is removed from the collection immediately. | ||
109 | /// This function is not threadsafe. It must be called by the thread calling GetExpiredPackets.</remarks> | ||
110 | public void Remove(uint sequenceNumber) | ||
111 | { | ||
112 | OutgoingPacket removedPacket; | ||
113 | if (m_packets.TryGetValue(sequenceNumber, out removedPacket)) | ||
114 | { | ||
115 | if (removedPacket != null) | ||
116 | { | ||
117 | m_packets.Remove(sequenceNumber); | ||
118 | |||
119 | // Update stats | ||
120 | Interlocked.Add(ref removedPacket.Client.UnackedBytes, -removedPacket.Buffer.DataLength); | ||
121 | } | ||
122 | } | ||
123 | } | ||
124 | |||
125 | /// <summary> | ||
98 | /// Returns a list of all of the packets with a TickCount older than | 126 | /// Returns a list of all of the packets with a TickCount older than |
99 | /// the specified timeout | 127 | /// the specified timeout |
100 | /// </summary> | 128 | /// </summary> |
@@ -130,6 +158,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
130 | // is actually sent out again | 158 | // is actually sent out again |
131 | packet.TickCount = 0; | 159 | packet.TickCount = 0; |
132 | 160 | ||
161 | // As with other network applications, assume that an expired packet is | ||
162 | // an indication of some network problem, slow transmission | ||
163 | packet.Client.FlowThrottle.ExpirePackets(1); | ||
164 | |||
133 | expiredPackets.Add(packet); | 165 | expiredPackets.Add(packet); |
134 | } | 166 | } |
135 | } | 167 | } |
@@ -157,6 +189,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
157 | { | 189 | { |
158 | m_packets.Remove(pendingRemove.SequenceNumber); | 190 | m_packets.Remove(pendingRemove.SequenceNumber); |
159 | 191 | ||
192 | // As with other network applications, assume that an acknowledged packet is an | ||
193 | // indication that the network can handle a little more load, speed up the transmission | ||
194 | ackedPacket.Client.FlowThrottle.AcknowledgePackets(ackedPacket.Buffer.DataLength); | ||
195 | |||
160 | // Update stats | 196 | // Update stats |
161 | Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength); | 197 | Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength); |
162 | 198 | ||