From 55a69f3f2d43c2475ace7761177751886b910be8 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Fri, 1 May 2009 16:29:15 +0000 Subject: [PATCH] Improve prim sending by combining multiple prim updates into a single packet --- OpenSim/Framework/IScene.cs | 2 + .../ClientStack/LindenUDP/LLClientView.cs | 273 +++++++++++++----- OpenSim/Region/Framework/Scenes/SceneBase.cs | 5 + .../Region/Framework/Scenes/ScenePresence.cs | 4 +- 4 files changed, 209 insertions(+), 75 deletions(-) diff --git a/OpenSim/Framework/IScene.cs b/OpenSim/Framework/IScene.cs index c9cdadbd5c..6aebf038cb 100644 --- a/OpenSim/Framework/IScene.cs +++ b/OpenSim/Framework/IScene.cs @@ -62,6 +62,8 @@ namespace OpenSim.Framework RegionStatus RegionStatus { get; set; } ClientManager ClientManager { get; } + float TimeDilation { get; } + event restart OnRestart; void AddNewClient(IClientAPI client); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 29ff86bc76..05ebcacc41 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -69,10 +69,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_cachedTextureSerial; private Timer m_clientPingTimer; - private Timer m_terseUpdateTimer; - private Dictionary m_terseUpdates = new Dictionary(); + private Timer m_avatarTerseUpdateTimer; + private Dictionary m_avatarTerseUpdates = new Dictionary(); private ushort m_terseTimeDilationLast = 0; + private Timer m_primTerseUpdateTimer; + private List m_primTerseUpdates = new List(); + private Timer m_primFullUpdateTimer; + private List m_primFullUpdates = + new List(); + private bool m_clientBlocked; private int m_probesWithNoIngressPackets; @@ -121,8 +127,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected string m_activeGroupName = String.Empty; protected ulong m_activeGroupPowers; protected Dictionary m_groupPowers = new Dictionary(); - protected int m_terseUpdateRate = 50; - protected int m_terseUpdatesPerPacket = 5; + protected int m_avatarTerseUpdateRate = 50; + protected int m_avatarTerseUpdatesPerPacket = 5; + + // LL uses these limits, apparently. Compressed terse would be + // 23, but we don't have that yet + // + protected int m_primTerseUpdatesPerPacket = 10; + protected int m_primFullUpdatesPerPacket = 14; + + protected int m_primTerseUpdateRate = 10; + protected int m_primFullUpdateRate = 14; // LLClientView Only public delegate void BinaryGenericMessage(Object sender, string method, byte[][] args); @@ -528,7 +543,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Shut down timers m_clientPingTimer.Stop(); - m_terseUpdateTimer.Stop(); + m_avatarTerseUpdateTimer.Stop(); + m_primTerseUpdateTimer.Stop(); + m_primFullUpdateTimer.Stop(); // This is just to give the client a reasonable chance of // flushing out all it's packets. There should probably @@ -609,7 +626,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP { // Shut down timers m_clientPingTimer.Stop(); - m_terseUpdateTimer.Stop(); + m_avatarTerseUpdateTimer.Stop(); + m_primTerseUpdateTimer.Stop(); + m_primFullUpdateTimer.Stop(); } public void Restart() @@ -621,9 +640,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_clientPingTimer.Elapsed += CheckClientConnectivity; m_clientPingTimer.Enabled = true; - m_terseUpdateTimer = new Timer(m_terseUpdateRate); - m_terseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates); - m_terseUpdateTimer.AutoReset = false; + m_avatarTerseUpdateTimer = new Timer(m_avatarTerseUpdateRate); + m_avatarTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates); + m_avatarTerseUpdateTimer.AutoReset = false; + + m_primTerseUpdateTimer = new Timer(m_primTerseUpdateRate); + m_primTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimTerseUpdates); + m_primTerseUpdateTimer.AutoReset = false; + + m_primFullUpdateTimer = new Timer(m_primFullUpdateRate); + m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates); + m_primFullUpdateTimer.AutoReset = false; } public void Terminate() @@ -845,10 +872,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_clientPingTimer.Elapsed += CheckClientConnectivity; m_clientPingTimer.Enabled = true; - m_terseUpdateTimer = new Timer(m_terseUpdateRate); - m_terseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates); - m_terseUpdateTimer.AutoReset = false; + m_avatarTerseUpdateTimer = new Timer(m_avatarTerseUpdateRate); + m_avatarTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates); + m_avatarTerseUpdateTimer.AutoReset = false; + m_primTerseUpdateTimer = new Timer(m_primTerseUpdateRate); + m_primTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimTerseUpdates); + m_primTerseUpdateTimer.AutoReset = false; + + m_primFullUpdateTimer = new Timer(m_primFullUpdateRate); + m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates); + m_primFullUpdateTimer.AutoReset = false; m_scene.AddNewClient(this); RefreshGroupMembership(); @@ -2724,20 +2758,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP CreateAvatarImprovedBlock(localID, position, velocity, rotation); bool sendpacketnow = false; - lock (m_terseUpdates) + lock (m_avatarTerseUpdates) { // Only one update per avatar per packet. No need to send old ones so just overwrite them. - m_terseUpdates[localID] = terseBlock; + m_avatarTerseUpdates[localID] = terseBlock; m_terseTimeDilationLast = timeDilation; // If packet is full or own movement packet, send it. - if (agentid == m_agentId || m_terseUpdates.Count >= m_terseUpdatesPerPacket) + if (agentid == m_agentId || m_avatarTerseUpdates.Count >= m_avatarTerseUpdatesPerPacket) { - m_terseUpdateTimer.Stop(); + m_avatarTerseUpdateTimer.Stop(); sendpacketnow = true; } - else if (m_terseUpdates.Count == 1) - m_terseUpdateTimer.Start(); + else if (m_avatarTerseUpdates.Count == 1) + m_avatarTerseUpdateTimer.Start(); } // Call ProcessAvatarTerseUpdates outside the lock if (sendpacketnow) @@ -2746,14 +2780,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void ProcessAvatarTerseUpdates(object sender, ElapsedEventArgs e) { - lock (m_terseUpdates) + lock (m_avatarTerseUpdates) { ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); terse.RegionData.RegionHandle = Scene.RegionInfo.RegionHandle; - terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[m_terseUpdates.Count]; + terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[m_avatarTerseUpdates.Count]; int i = 0; - foreach (KeyValuePair dbe in m_terseUpdates) + foreach (KeyValuePair dbe in m_avatarTerseUpdates) { terse.ObjectData[i] = dbe.Value; i++; @@ -2764,7 +2798,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP terse.Header.Zerocoded = true; OutPacket(terse, ThrottleOutPacketType.Task); - m_terseUpdates.Clear(); + m_avatarTerseUpdates.Clear(); } } @@ -2859,81 +2893,124 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) rotation = Quaternion.Identity; - ObjectUpdatePacket outPacket = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); + ObjectUpdatePacket.ObjectDataBlock objectData = + new ObjectUpdatePacket.ObjectDataBlock(); + objectData = CreatePrimUpdateBlock(primShape, flags); + objectData.ID = localID; + objectData.FullID = objectID; + objectData.OwnerID = ownerID; - // TODO: don't create new blocks if recycling an old packet - outPacket.RegionData.RegionHandle = regionHandle; - outPacket.RegionData.TimeDilation = timeDilation; - outPacket.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; - - outPacket.ObjectData[0] = CreatePrimUpdateBlock(primShape, flags); - - outPacket.ObjectData[0].ID = localID; - outPacket.ObjectData[0].FullID = objectID; - outPacket.ObjectData[0].OwnerID = ownerID; - - outPacket.ObjectData[0].Text = LLUtil.StringToPacketBytes(text); - outPacket.ObjectData[0].TextColor[0] = color[0]; - outPacket.ObjectData[0].TextColor[1] = color[1]; - outPacket.ObjectData[0].TextColor[2] = color[2]; - outPacket.ObjectData[0].TextColor[3] = color[3]; - outPacket.ObjectData[0].ParentID = parentID; - outPacket.ObjectData[0].PSBlock = particleSystem; - outPacket.ObjectData[0].ClickAction = clickAction; - outPacket.ObjectData[0].Material = material; - outPacket.ObjectData[0].Flags = 0; + objectData.Text = LLUtil.StringToPacketBytes(text); + objectData.TextColor[0] = color[0]; + objectData.TextColor[1] = color[1]; + objectData.TextColor[2] = color[2]; + objectData.TextColor[3] = color[3]; + objectData.ParentID = parentID; + objectData.PSBlock = particleSystem; + objectData.ClickAction = clickAction; + objectData.Material = material; + objectData.Flags = 0; if (attachment) { // Necessary??? - outPacket.ObjectData[0].JointAxisOrAnchor = new Vector3(0, 0, 2); - outPacket.ObjectData[0].JointPivot = new Vector3(0, 0, 0); + objectData.JointAxisOrAnchor = new Vector3(0, 0, 2); + objectData.JointPivot = new Vector3(0, 0, 0); // Item from inventory??? - outPacket.ObjectData[0].NameValue = + objectData.NameValue = Utils.StringToBytes("AttachItemID STRING RW SV " + AssetId.Guid); - outPacket.ObjectData[0].State = (byte)((AttachPoint % 16) * 16 + (AttachPoint / 16)); + objectData.State = (byte)((AttachPoint % 16) * 16 + (AttachPoint / 16)); } // Xantor 20080528: Send sound info as well // Xantor 20080530: Zero out everything if there's no SoundId, so zerocompression will work again - outPacket.ObjectData[0].Sound = SoundId; + objectData.Sound = SoundId; if (SoundId == UUID.Zero) { - outPacket.ObjectData[0].OwnerID = UUID.Zero; - outPacket.ObjectData[0].Gain = 0.0f; - outPacket.ObjectData[0].Radius = 0.0f; - outPacket.ObjectData[0].Flags = 0; + objectData.OwnerID = UUID.Zero; + objectData.Gain = 0.0f; + objectData.Radius = 0.0f; + objectData.Flags = 0; } else { - outPacket.ObjectData[0].OwnerID = ownerID; - outPacket.ObjectData[0].Gain = (float)SoundGain; - outPacket.ObjectData[0].Radius = (float)SoundRadius; - outPacket.ObjectData[0].Flags = SoundFlags; + objectData.OwnerID = ownerID; + objectData.Gain = (float)SoundGain; + objectData.Radius = (float)SoundRadius; + objectData.Flags = SoundFlags; } byte[] pb = pos.GetBytes(); - Array.Copy(pb, 0, outPacket.ObjectData[0].ObjectData, 0, pb.Length); + Array.Copy(pb, 0, objectData.ObjectData, 0, pb.Length); byte[] vel = velocity.GetBytes(); - Array.Copy(vel, 0, outPacket.ObjectData[0].ObjectData, pb.Length, vel.Length); + Array.Copy(vel, 0, objectData.ObjectData, pb.Length, vel.Length); byte[] rot = rotation.GetBytes(); - Array.Copy(rot, 0, outPacket.ObjectData[0].ObjectData, 36, rot.Length); + Array.Copy(rot, 0, objectData.ObjectData, 36, rot.Length); byte[] rvel = rotational_velocity.GetBytes(); - Array.Copy(rvel, 0, outPacket.ObjectData[0].ObjectData, 36 + rot.Length, rvel.Length); + Array.Copy(rvel, 0, objectData.ObjectData, 36 + rot.Length, rvel.Length); if (textureanim.Length > 0) { - outPacket.ObjectData[0].TextureAnim = textureanim; + objectData.TextureAnim = textureanim; } - outPacket.Header.Zerocoded = true; - OutPacket(outPacket, ThrottleOutPacketType.Task | ThrottleOutPacketType.LowPriority); + lock (m_primFullUpdates) + { + if (m_primFullUpdates.Count == 0) + m_primFullUpdateTimer.Start(); + + m_primFullUpdates.Add(objectData); + + if (m_primFullUpdates.Count >= m_primFullUpdatesPerPacket) + ProcessPrimFullUpdates(this, null); + } + } + + void ProcessPrimFullUpdates(object sender, ElapsedEventArgs e) + { + lock (m_primFullUpdates) + { + if (m_primFullUpdates.Count == 0) + { + m_primFullUpdateTimer.Stop(); + + return; + } + + ObjectUpdatePacket outPacket = + (ObjectUpdatePacket)PacketPool.Instance.GetPacket( + PacketType.ObjectUpdate); + + outPacket.RegionData.RegionHandle = + Scene.RegionInfo.RegionHandle; + outPacket.RegionData.TimeDilation = + (ushort)(Scene.TimeDilation * ushort.MaxValue); + + int count = m_primFullUpdates.Count; + if (count > m_primFullUpdatesPerPacket) + count = m_primFullUpdatesPerPacket; + + outPacket.ObjectData = + new ObjectUpdatePacket.ObjectDataBlock[count]; + + for (int index = 0 ; index < count ; index++) + { + outPacket.ObjectData[index] = m_primFullUpdates[0]; + m_primFullUpdates.RemoveAt(0); + } + + outPacket.Header.Zerocoded = true; + OutPacket(outPacket, ThrottleOutPacketType.Task | ThrottleOutPacketType.LowPriority); + + if (m_primFullUpdates.Count == 0) + m_primFullUpdateTimer.Stop(); + } } /// @@ -2947,15 +3024,65 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) rotation = Quaternion.Identity; - ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); - // TODO: don't create new blocks if recycling an old packet - terse.RegionData.RegionHandle = regionHandle; - terse.RegionData.TimeDilation = timeDilation; - terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1]; - terse.ObjectData[0] = CreatePrimImprovedBlock(localID, position, rotation, velocity, rotationalvelocity, state); // AssetID should fall into here probably somehow... - terse.Header.Reliable = false; - terse.Header.Zerocoded = true; - OutPacket(terse, ThrottleOutPacketType.Task | ThrottleOutPacketType.LowPriority); + + ImprovedTerseObjectUpdatePacket.ObjectDataBlock objectData = + CreatePrimImprovedBlock(localID, position, rotation, + velocity, rotationalvelocity, state); + + lock (m_primTerseUpdates) + { + if (m_primTerseUpdates.Count == 0) + m_primTerseUpdateTimer.Start(); + + m_primTerseUpdates.Add(objectData); + + if (m_primTerseUpdates.Count >= m_primTerseUpdatesPerPacket) + ProcessPrimTerseUpdates(this, null); + } + } + + void ProcessPrimTerseUpdates(object sender, ElapsedEventArgs e) + { + lock (m_primTerseUpdates) + { + if (m_primTerseUpdates.Count == 0) + { + m_primTerseUpdateTimer.Stop(); + + return; + } + + ImprovedTerseObjectUpdatePacket outPacket = + (ImprovedTerseObjectUpdatePacket) + PacketPool.Instance.GetPacket( + PacketType.ImprovedTerseObjectUpdate); + + outPacket.RegionData.RegionHandle = + Scene.RegionInfo.RegionHandle; + outPacket.RegionData.TimeDilation = + (ushort)(Scene.TimeDilation * ushort.MaxValue); + + int count = m_primTerseUpdates.Count; + if (count > m_primTerseUpdatesPerPacket) + count = m_primTerseUpdatesPerPacket; + + outPacket.ObjectData = + new ImprovedTerseObjectUpdatePacket. + ObjectDataBlock[count]; + + for (int index = 0 ; index < count ; index++) + { + outPacket.ObjectData[index] = m_primTerseUpdates[0]; + m_primTerseUpdates.RemoveAt(0); + } + + outPacket.Header.Reliable = false; + outPacket.Header.Zerocoded = true; + OutPacket(outPacket, ThrottleOutPacketType.Task | ThrottleOutPacketType.LowPriority); + + if (m_primTerseUpdates.Count == 0) + m_primTerseUpdateTimer.Stop(); + } } public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID) diff --git a/OpenSim/Region/Framework/Scenes/SceneBase.cs b/OpenSim/Region/Framework/Scenes/SceneBase.cs index 4f5f523965..6ab7e7a085 100644 --- a/OpenSim/Region/Framework/Scenes/SceneBase.cs +++ b/OpenSim/Region/Framework/Scenes/SceneBase.cs @@ -97,6 +97,11 @@ namespace OpenSim.Region.Framework.Scenes get { return m_clientManager; } } + public float TimeDilation + { + get { return 1.0f; } + } + protected ulong m_regionHandle; protected string m_regionName; protected RegionInfo m_regInfo; diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 5bfd9472bb..a352eac194 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -745,7 +745,7 @@ namespace OpenSim.Region.Framework.Scenes } } - while (m_pendingObjects != null && m_pendingObjects.Count > 0 && m_partsUpdateQueue.Count < 60) + while (m_pendingObjects != null && m_pendingObjects.Count > 0 && m_partsUpdateQueue.Count < 120) { SceneObjectGroup g = m_pendingObjects.Dequeue(); @@ -834,7 +834,7 @@ namespace OpenSim.Region.Framework.Scenes updateCount++; } - if (updateCount > 60) + if (updateCount > 300) break; }