diff --git a/OpenSim/Region/Framework/Interfaces/ISceneViewer.cs b/OpenSim/Region/Framework/Interfaces/ISceneViewer.cs new file mode 100644 index 0000000000..1b2cdee6e5 --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/ISceneViewer.cs @@ -0,0 +1,14 @@ +using System; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface ISceneViewer + { + void Reset(); + void Close(); + int MaxPrimsPerFrame { get; set; } + void QueuePartForUpdate(SceneObjectPart part); + void SendPrimUpdates(); + } +} diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index a9a150af2d..dd00ff40a8 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -1145,13 +1145,13 @@ if (m_shape != null) { List avatars = m_parentGroup.Scene.GetScenePresences(); for (int i = 0; i < avatars.Count; i++) { - avatars[i].QueuePartForUpdate(this); + avatars[i].SceneViewer.QueuePartForUpdate(this); } } public void AddFullUpdateToAvatar(ScenePresence presence) { - presence.QueuePartForUpdate(this); + presence.SceneViewer.QueuePartForUpdate(this); } public void AddNewParticleSystem(Primitive.ParticleSystem pSystem) @@ -1170,13 +1170,13 @@ if (m_shape != null) { List avatars = m_parentGroup.Scene.GetScenePresences(); for (int i = 0; i < avatars.Count; i++) { - avatars[i].QueuePartForUpdate(this); + avatars[i].SceneViewer.QueuePartForUpdate(this); } } public void AddTerseUpdateToAvatar(ScenePresence presence) { - presence.QueuePartForUpdate(this); + presence.SceneViewer.QueuePartForUpdate(this); } public void AddTextureAnimation(Primitive.TextureAnimation pTexAnim) diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index cdd1a85b76..1bee3c0a7d 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -74,6 +74,9 @@ namespace OpenSim.Region.Framework.Scenes public static byte[] DefaultTexture; public UUID currentParcelUUID = UUID.Zero; + + private ISceneViewer m_sceneViewer; + private AnimationSet m_animations = new AnimationSet(); private Dictionary scriptedcontrols = new Dictionary(); private ScriptControlled IgnoredControls = ScriptControlled.CONTROL_ZERO; @@ -222,11 +225,6 @@ namespace OpenSim.Region.Framework.Scenes /// private Vector3 posLastSignificantMove; - private UpdateQueue m_partsUpdateQueue = new UpdateQueue(); - private Queue m_pendingObjects; - - private Dictionary m_updateTimes = new Dictionary(); - // For teleports and crossings callbacks string m_callbackURI; ulong m_rootRegionHandle; @@ -396,8 +394,8 @@ namespace OpenSim.Region.Framework.Scenes public int MaxPrimsPerFrame { - get { return m_maxPrimsPerFrame; } - set { m_maxPrimsPerFrame = value; } + get { return m_sceneViewer.MaxPrimsPerFrame; } + set { m_sceneViewer.MaxPrimsPerFrame = value; } } /// @@ -527,6 +525,11 @@ namespace OpenSim.Region.Framework.Scenes } } + public ISceneViewer SceneViewer + { + get { return m_sceneViewer; } + } + public void AdjustKnownSeeds() { Dictionary seeds = Scene.CapsModule.GetChildrenSeeds(UUID); @@ -601,7 +604,7 @@ namespace OpenSim.Region.Framework.Scenes private ScenePresence(IClientAPI client, Scene world, RegionInfo reginfo) { - + CreateSceneViewer(); m_regionHandle = reginfo.RegionHandle; m_controllingClient = client; m_firstname = m_controllingClient.FirstName; @@ -643,15 +646,22 @@ namespace OpenSim.Region.Framework.Scenes AvatarWearable[] wearables) : this(client, world, reginfo) { + CreateSceneViewer(); m_appearance = new AvatarAppearance(m_uuid, wearables, visualParams); } public ScenePresence(IClientAPI client, Scene world, RegionInfo reginfo, AvatarAppearance appearance) : this(client, world, reginfo) { + CreateSceneViewer(); m_appearance = appearance; } + private void CreateSceneViewer() + { + m_sceneViewer = new SceneViewer(this); + } + public void RegisterToEvents() { m_controllingClient.OnRequestWearables += SendWearables; @@ -704,13 +714,7 @@ namespace OpenSim.Region.Framework.Scenes /// public void QueuePartForUpdate(SceneObjectPart part) { - //if (InterestList.Contains(part.ParentGroup)) - //{ - lock (m_partsUpdateQueue) - { - m_partsUpdateQueue.Enqueue(part); - } - // } + m_sceneViewer.QueuePartForUpdate(part); } public uint GenerateClientFlags(UUID ObjectID) @@ -725,122 +729,9 @@ namespace OpenSim.Region.Framework.Scenes /// public void SendPrimUpdates() { - // if (m_scene.QuadTree.GetNodeID(this.AbsolutePosition.X, this.AbsolutePosition.Y) != m_currentQuadNode) - //{ - // this.UpdateQuadTreeNode(); - //this.RefreshQuadObject(); - //} m_perfMonMS = Environment.TickCount; - if (m_pendingObjects == null) - { - if (!m_isChildAgent || m_scene.m_seeIntoRegionFromNeighbor) - { - m_pendingObjects = new Queue(); - - List ents = new List(m_scene.Entities); - if (!m_isChildAgent) // Proximity sort makes no sense for - { // Child agents - ents.Sort(delegate(EntityBase a, EntityBase b) - { - return Vector3.Distance(AbsolutePosition, a.AbsolutePosition).CompareTo(Vector3.Distance(AbsolutePosition, b.AbsolutePosition)); - }); - } - - foreach (EntityBase e in ents) - { - if (e is SceneObjectGroup) - m_pendingObjects.Enqueue((SceneObjectGroup)e); - } - } - } - - while (m_pendingObjects != null && m_pendingObjects.Count > 0 && m_partsUpdateQueue.Count < m_maxPrimsPerFrame) - { - SceneObjectGroup g = m_pendingObjects.Dequeue(); - - // This is where we should check for draw distance - // do culling and stuff. Problem with that is that until - // we recheck in movement, that won't work right. - // So it's not implemented now. - // - - // Don't even queue if we have sent this one - // - if (!m_updateTimes.ContainsKey(g.UUID)) - g.ScheduleFullUpdateToAvatar(this); - } - - while (m_partsUpdateQueue.Count > 0) - { - SceneObjectPart part = m_partsUpdateQueue.Dequeue(); - - if (part.ParentGroup == null || part.ParentGroup.IsDeleted) - continue; - - if (m_updateTimes.ContainsKey(part.UUID)) - { - ScenePartUpdate update = m_updateTimes[part.UUID]; - - // We deal with the possibility that two updates occur at - // the same unix time at the update point itself. - - if ((update.LastFullUpdateTime < part.TimeStampFull) || - part.IsAttachment) - { -// m_log.DebugFormat( -// "[SCENE PRESENCE]: Fully updating prim {0}, {1} - part timestamp {2}", -// part.Name, part.UUID, part.TimeStampFull); - - part.SendFullUpdate(ControllingClient, - GenerateClientFlags(part.UUID)); - - // We'll update to the part's timestamp rather than - // the current time to avoid the race condition - // whereby the next tick occurs while we are doing - // this update. If this happened, then subsequent - // updates which occurred on the same tick or the - // next tick of the last update would be ignored. - - update.LastFullUpdateTime = part.TimeStampFull; - - } - else if (update.LastTerseUpdateTime <= part.TimeStampTerse) - { -// m_log.DebugFormat( -// "[SCENE PRESENCE]: Tersely updating prim {0}, {1} - part timestamp {2}", -// part.Name, part.UUID, part.TimeStampTerse); - - part.SendTerseUpdateToClient(ControllingClient); - - update.LastTerseUpdateTime = part.TimeStampTerse; - } - } - else - { - //never been sent to client before so do full update - ScenePartUpdate update = new ScenePartUpdate(); - update.FullID = part.UUID; - update.LastFullUpdateTime = part.TimeStampFull; - m_updateTimes.Add(part.UUID, update); - - // Attachment handling - // - if (part.ParentGroup.RootPart.Shape.PCode == 9 && part.ParentGroup.RootPart.Shape.State != 0) - { - if (part != part.ParentGroup.RootPart) - continue; - - part.ParentGroup.SendFullUpdateToClient(ControllingClient); - continue; - } - - part.SendFullUpdate(ControllingClient, - GenerateClientFlags(part.UUID)); - } - } - - ControllingClient.FlushPrimUpdates(); + m_sceneViewer.SendPrimUpdates(); m_scene.StatsReporter.AddAgentTime(Environment.TickCount - m_perfMonMS); } @@ -935,7 +826,7 @@ namespace OpenSim.Region.Framework.Scenes // On the next prim update, all objects will be sent // - m_pendingObjects = null; + m_sceneViewer.Reset(); m_isChildAgent = false; @@ -3062,7 +2953,7 @@ namespace OpenSim.Region.Framework.Scenes // Sends out the objects in the user's draw distance if m_sendTasksToChild is true. if (m_scene.m_seeIntoRegionFromNeighbor) - m_pendingObjects = null; + m_sceneViewer.Reset(); //cAgentData.AVHeight; //cAgentData.regionHandle; @@ -3269,7 +3160,6 @@ namespace OpenSim.Region.Framework.Scenes { Primitive.TextureEntry textu = AvatarAppearance.GetDefaultTexture(); DefaultTexture = textu.GetBytes(); - } public class NewForce @@ -3279,20 +3169,6 @@ namespace OpenSim.Region.Framework.Scenes public float Z; } - public class ScenePartUpdate - { - public UUID FullID; - public uint LastFullUpdateTime; - public uint LastTerseUpdateTime; - - public ScenePartUpdate() - { - FullID = UUID.Zero; - LastFullUpdateTime = 0; - LastTerseUpdateTime = 0; - } - } - public override void SetText(string text, Vector3 color, double alpha) { throw new Exception("Can't set Text on avatar."); @@ -3394,14 +3270,7 @@ namespace OpenSim.Region.Framework.Scenes { m_knownChildRegions.Clear(); } - lock (m_updateTimes) - { - m_updateTimes.Clear(); - } - lock (m_partsUpdateQueue) - { - m_partsUpdateQueue.Clear(); - } + m_sceneViewer.Close(); RemoveFromPhysicalScene(); GC.Collect(); @@ -3413,7 +3282,8 @@ namespace OpenSim.Region.Framework.Scenes { Primitive.TextureEntry textu = AvatarAppearance.GetDefaultTexture(); DefaultTexture = textu.GetBytes(); - } + } + CreateSceneViewer(); } public void AddAttachment(SceneObjectGroup gobj) diff --git a/OpenSim/Region/Framework/Scenes/SceneViewer.cs b/OpenSim/Region/Framework/Scenes/SceneViewer.cs new file mode 100644 index 0000000000..4e542b89ac --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/SceneViewer.cs @@ -0,0 +1,226 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using OpenMetaverse; +using log4net; +using OpenSim.Framework; +using OpenSim.Framework.Client; +using OpenSim.Framework.Communications.Cache; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes.Types; + +namespace OpenSim.Region.Framework.Scenes +{ + public class SceneViewer : ISceneViewer + { + protected ScenePresence m_presence; + protected UpdateQueue m_partsUpdateQueue = new UpdateQueue(); + protected Queue m_pendingObjects; + + protected Dictionary m_updateTimes = new Dictionary(); + + protected int m_maxPrimsPerFrame = 200; + + public int MaxPrimsPerFrame + { + get { return m_maxPrimsPerFrame; } + set { m_maxPrimsPerFrame = value; } + } + + public SceneViewer() + { + } + + public SceneViewer(ScenePresence presence) + { + m_presence = presence; + } + + /// + /// Add the part to the queue of parts for which we need to send an update to the client + /// + /// + public void QueuePartForUpdate(SceneObjectPart part) + { + lock (m_partsUpdateQueue) + { + m_partsUpdateQueue.Enqueue(part); + } + } + + public void SendPrimUpdates() + { + if (m_pendingObjects == null) + { + if (!m_presence.IsChildAgent || (m_presence.Scene.m_seeIntoRegionFromNeighbor)) + { + m_pendingObjects = new Queue(); + + List ents = new List(m_presence.Scene.Entities); + if (!m_presence.IsChildAgent) // Proximity sort makes no sense for + { // Child agents + ents.Sort(delegate(EntityBase a, EntityBase b) + { + return Vector3.Distance(m_presence.AbsolutePosition, a.AbsolutePosition).CompareTo(Vector3.Distance(m_presence.AbsolutePosition, b.AbsolutePosition)); + }); + } + + foreach (EntityBase e in ents) + { + if (e is SceneObjectGroup) + m_pendingObjects.Enqueue((SceneObjectGroup)e); + } + } + } + + while (m_pendingObjects != null && m_pendingObjects.Count > 0 && m_partsUpdateQueue.Count < m_maxPrimsPerFrame) + { + SceneObjectGroup g = m_pendingObjects.Dequeue(); + + // This is where we should check for draw distance + // do culling and stuff. Problem with that is that until + // we recheck in movement, that won't work right. + // So it's not implemented now. + // + + // Don't even queue if we have sent this one + // + if (!m_updateTimes.ContainsKey(g.UUID)) + g.ScheduleFullUpdateToAvatar(m_presence); + } + + while (m_partsUpdateQueue.Count > 0) + { + SceneObjectPart part = m_partsUpdateQueue.Dequeue(); + + if (part.ParentGroup == null || part.ParentGroup.IsDeleted) + continue; + + if (m_updateTimes.ContainsKey(part.UUID)) + { + ScenePartUpdate update = m_updateTimes[part.UUID]; + + // We deal with the possibility that two updates occur at + // the same unix time at the update point itself. + + if ((update.LastFullUpdateTime < part.TimeStampFull) || + part.IsAttachment) + { +// m_log.DebugFormat( +// "[SCENE PRESENCE]: Fully updating prim {0}, {1} - part timestamp {2}", +// part.Name, part.UUID, part.TimeStampFull); + + part.SendFullUpdate(m_presence.ControllingClient, + m_presence.GenerateClientFlags(part.UUID)); + + // We'll update to the part's timestamp rather than + // the current time to avoid the race condition + // whereby the next tick occurs while we are doing + // this update. If this happened, then subsequent + // updates which occurred on the same tick or the + // next tick of the last update would be ignored. + + update.LastFullUpdateTime = part.TimeStampFull; + + } + else if (update.LastTerseUpdateTime <= part.TimeStampTerse) + { +// m_log.DebugFormat( +// "[SCENE PRESENCE]: Tersely updating prim {0}, {1} - part timestamp {2}", +// part.Name, part.UUID, part.TimeStampTerse); + + part.SendTerseUpdateToClient(m_presence.ControllingClient); + + update.LastTerseUpdateTime = part.TimeStampTerse; + } + } + else + { + //never been sent to client before so do full update + ScenePartUpdate update = new ScenePartUpdate(); + update.FullID = part.UUID; + update.LastFullUpdateTime = part.TimeStampFull; + m_updateTimes.Add(part.UUID, update); + + // Attachment handling + // + if (part.ParentGroup.RootPart.Shape.PCode == 9 && part.ParentGroup.RootPart.Shape.State != 0) + { + if (part != part.ParentGroup.RootPart) + continue; + + part.ParentGroup.SendFullUpdateToClient(m_presence.ControllingClient); + continue; + } + + part.SendFullUpdate(m_presence.ControllingClient, + m_presence.GenerateClientFlags(part.UUID)); + } + } + + m_presence.ControllingClient.FlushPrimUpdates(); + } + + public void Reset() + { + lock (m_pendingObjects) + { + m_pendingObjects.Clear(); + m_pendingObjects = null; + } + } + + public void Close() + { + lock (m_updateTimes) + { + m_updateTimes.Clear(); + } + lock (m_partsUpdateQueue) + { + m_partsUpdateQueue.Clear(); + } + Reset(); + } + + public class ScenePartUpdate + { + public UUID FullID; + public uint LastFullUpdateTime; + public uint LastTerseUpdateTime; + + public ScenePartUpdate() + { + FullID = UUID.Zero; + LastFullUpdateTime = 0; + LastTerseUpdateTime = 0; + } + } + } +}