diff --git a/OpenSim/Addons/Groups/GroupsModule.cs b/OpenSim/Addons/Groups/GroupsModule.cs index 82e2d6fe14..69d03a934f 100644 --- a/OpenSim/Addons/Groups/GroupsModule.cs +++ b/OpenSim/Addons/Groups/GroupsModule.cs @@ -1402,19 +1402,18 @@ namespace OpenSim.Groups if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff - UserAccount account = m_sceneList[0].UserAccountService.GetUserAccount(remoteClient.Scene.RegionInfo.ScopeID, dataForAgentID); - string firstname, lastname; - if (account != null) + string firstname = "Unknown", lastname = "Unknown"; + string name = m_UserManagement.GetUserName(dataForAgentID); + if (!string.IsNullOrEmpty(name)) { - firstname = account.FirstName; - lastname = account.LastName; + string[] parts = name.Split(new char[] { ' ' }); + if (parts.Length >= 2) + { + firstname = parts[0]; + lastname = parts[1]; + } } - else - { - firstname = "Unknown"; - lastname = "Unknown"; - } - + remoteClient.SendAgentDataUpdate(dataForAgentID, activeGroupID, firstname, lastname, activeGroupPowers, activeGroupName, activeGroupTitle); diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 6c9e7c9f01..22cc79da33 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -834,6 +834,8 @@ namespace OpenSim.Framework /// event UpdateAgent OnAgentUpdate; + event UpdateAgent OnAgentCameraUpdate; + event AgentRequestSit OnAgentRequestSit; event AgentSit OnAgentSit; event AvatarPickerRequest OnAvatarPickerRequest; @@ -1489,7 +1491,7 @@ namespace OpenSim.Framework void SendChangeUserRights(UUID agentID, UUID friendID, int rights); void SendTextBoxRequest(string message, int chatChannel, string objectname, UUID ownerID, string ownerFirstName, string ownerLastName, UUID objectId); - void StopFlying(ISceneEntity presence); + void SendAgentTerseUpdate(ISceneEntity presence); void SendPlacesReply(UUID queryID, UUID transactionID, PlacesReplyData[] data); } diff --git a/OpenSim/Framework/Monitoring/Stats/Stat.cs b/OpenSim/Framework/Monitoring/Stats/Stat.cs index 9629b6e654..ffd5132822 100644 --- a/OpenSim/Framework/Monitoring/Stats/Stat.cs +++ b/OpenSim/Framework/Monitoring/Stats/Stat.cs @@ -225,7 +225,13 @@ namespace OpenSim.Framework.Monitoring public virtual string ToConsoleString() { StringBuilder sb = new StringBuilder(); - sb.AppendFormat("{0}.{1}.{2} : {3} {4}", Category, Container, ShortName, Value, UnitName); + sb.AppendFormat( + "{0}.{1}.{2} : {3}{4}", + Category, + Container, + ShortName, + Value, + UnitName == null || UnitName == "" ? "" : string.Format(" {0}", UnitName)); AppendMeasuresOfInterest(sb); @@ -253,6 +259,8 @@ namespace OpenSim.Framework.Monitoring == MeasuresOfInterest.AverageChangeOverTime) { double totalChange = 0; + double lastChangeOverTime = 0; + double? penultimateSample = null; double? lastSample = null; lock (m_samples) @@ -266,13 +274,25 @@ namespace OpenSim.Framework.Monitoring if (lastSample != null) totalChange += s - (double)lastSample; + penultimateSample = lastSample; lastSample = s; } } + if (lastSample != null && penultimateSample != null) + lastChangeOverTime + = ((double)lastSample - (double)penultimateSample) / (Watchdog.WATCHDOG_INTERVAL_MS / 1000); + int divisor = m_samples.Count <= 1 ? 1 : m_samples.Count - 1; - sb.AppendFormat(", {0:0.##} {1}/s", totalChange / divisor / (Watchdog.WATCHDOG_INTERVAL_MS / 1000), UnitName); + double averageChangeOverTime = totalChange / divisor / (Watchdog.WATCHDOG_INTERVAL_MS / 1000); + + sb.AppendFormat( + ", {0:0.##}{1}/s, {2:0.##}{3}/s", + lastChangeOverTime, + UnitName == null || UnitName == "" ? "" : string.Format(" {0}", UnitName), + averageChangeOverTime, + UnitName == null || UnitName == "" ? "" : string.Format(" {0}", UnitName)); } } } diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index ac5e77e4b0..0e20e38691 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -96,6 +96,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public event Action OnCompleteMovementToRegion; public event UpdateAgent OnPreAgentUpdate; public event UpdateAgent OnAgentUpdate; + public event UpdateAgent OnAgentCameraUpdate; public event AgentRequestSit OnAgentRequestSit; public event AgentSit OnAgentSit; public event AvatarPickerRequest OnAvatarPickerRequest; @@ -368,7 +369,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// This does mean that agent updates must be processed synchronously, at least for each client, and called methods /// cannot retain a reference to it outside of that method. /// - private AgentUpdateArgs m_lastAgentUpdateArgs; + private AgentUpdateArgs m_thisAgentUpdateArgs = new AgentUpdateArgs(); protected Dictionary m_packetHandlers = new Dictionary(); protected Dictionary m_genericPacketHandlers = new Dictionary(); //PauPaw:Local Generic Message handlers @@ -505,6 +506,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_udpServer = udpServer; m_udpClient = udpClient; m_udpClient.OnQueueEmpty += HandleQueueEmpty; + m_udpClient.HasUpdates += HandleHasUpdates; m_udpClient.OnPacketStats += PopulateStats; m_prioritizer = new Prioritizer(m_scene); @@ -4164,8 +4166,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) { +// if (!m_udpServer.IsRunningOutbound) +// return; + if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) { +// if (!m_udpServer.IsRunningOutbound) +// return; + if (m_maxUpdates == 0 || m_LastQueueFill == 0) { m_maxUpdates = m_udpServer.PrimUpdatesPerCallback; @@ -4191,6 +4199,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP ImageManager.ProcessImageQueue(m_udpServer.TextureSendLimit); } + internal bool HandleHasUpdates(ThrottleOutPacketTypeFlags categories) + { + bool hasUpdates = false; + + if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) + { + if (m_entityUpdates.Count > 0) + hasUpdates = true; + else if (m_entityProps.Count > 0) + hasUpdates = true; + } + + if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0) + { + if (ImageManager.HasUpdates()) + hasUpdates = true; + } + + return hasUpdates; + } + public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID) { AssetUploadCompletePacket newPack = new AssetUploadCompletePacket(); @@ -5058,7 +5087,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP SceneObjectPart part = (SceneObjectPart)entity; attachPoint = part.ParentGroup.AttachmentPoint; - + attachPoint = ((attachPoint % 16) * 16 + (attachPoint / 16)); // m_log.DebugFormat( // "[LLCLIENTVIEW]: Sending attachPoint {0} for {1} {2} to {3}", // attachPoint, part.Name, part.LocalId, Name); @@ -5086,7 +5115,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP pos += 4; // Avatar/CollisionPlane - data[pos++] = (byte)((attachPoint % 16) * 16 + (attachPoint / 16)); ; + data[pos++] = (byte) attachPoint; if (avatar) { data[pos++] = 1; @@ -5618,83 +5647,137 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Packet Handlers + public int TotalAgentUpdates { get; set; } + #region Scene/Avatar - private bool HandleAgentUpdate(IClientAPI sener, Packet packet) + // Threshold for body rotation to be a significant agent update + private const float QDELTA = 0.000001f; + // Threshold for camera rotation to be a significant agent update + private const float VDELTA = 0.01f; + + /// + /// This checks the update significance against the last update made. + /// + /// Can only be called by one thread at a time + /// + /// + public bool CheckAgentUpdateSignificance(AgentUpdatePacket.AgentDataBlock x) { - if (OnAgentUpdate != null) + return CheckAgentMovementUpdateSignificance(x) || CheckAgentCameraUpdateSignificance(x); + } + + /// + /// This checks the movement/state update significance against the last update made. + /// + /// Can only be called by one thread at a time + /// + /// + private bool CheckAgentMovementUpdateSignificance(AgentUpdatePacket.AgentDataBlock x) + { + float qdelta1 = 1 - (float)Math.Pow(Quaternion.Dot(x.BodyRotation, m_thisAgentUpdateArgs.BodyRotation), 2); + //qdelta2 = 1 - (float)Math.Pow(Quaternion.Dot(x.HeadRotation, m_thisAgentUpdateArgs.HeadRotation), 2); + + bool movementSignificant = + (qdelta1 > QDELTA) // significant if body rotation above threshold + // Ignoring head rotation altogether, because it's not being used for anything interesting up the stack + // || (qdelta2 > QDELTA * 10) // significant if head rotation above threshold + || (x.ControlFlags != m_thisAgentUpdateArgs.ControlFlags) // significant if control flags changed + || (x.ControlFlags != (byte)AgentManager.ControlFlags.NONE) // significant if user supplying any movement update commands + || (x.Far != m_thisAgentUpdateArgs.Far) // significant if far distance changed + || (x.Flags != m_thisAgentUpdateArgs.Flags) // significant if Flags changed + || (x.State != m_thisAgentUpdateArgs.State) // significant if Stats changed + ; + //if (movementSignificant) + //{ + //m_log.DebugFormat("[LLCLIENTVIEW]: Bod {0} {1}", + // qdelta1, qdelta2); + //m_log.DebugFormat("[LLCLIENTVIEW]: St {0} {1} {2} {3}", + // x.ControlFlags, x.Flags, x.Far, x.State); + //} + return movementSignificant; + } + + /// + /// This checks the camera update significance against the last update made. + /// + /// Can only be called by one thread at a time + /// + /// + private bool CheckAgentCameraUpdateSignificance(AgentUpdatePacket.AgentDataBlock x) + { + float vdelta1 = Vector3.Distance(x.CameraAtAxis, m_thisAgentUpdateArgs.CameraAtAxis); + float vdelta2 = Vector3.Distance(x.CameraCenter, m_thisAgentUpdateArgs.CameraCenter); + float vdelta3 = Vector3.Distance(x.CameraLeftAxis, m_thisAgentUpdateArgs.CameraLeftAxis); + float vdelta4 = Vector3.Distance(x.CameraUpAxis, m_thisAgentUpdateArgs.CameraUpAxis); + + bool cameraSignificant = + (vdelta1 > VDELTA) || + (vdelta2 > VDELTA) || + (vdelta3 > VDELTA) || + (vdelta4 > VDELTA) + ; + + //if (cameraSignificant) + //{ + //m_log.DebugFormat("[LLCLIENTVIEW]: Cam1 {0} {1}", + // x.CameraAtAxis, x.CameraCenter); + //m_log.DebugFormat("[LLCLIENTVIEW]: Cam2 {0} {1}", + // x.CameraLeftAxis, x.CameraUpAxis); + //} + + return cameraSignificant; + } + + private bool HandleAgentUpdate(IClientAPI sener, Packet packet) + { + // We got here, which means that something in agent update was significant + + AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet; + AgentUpdatePacket.AgentDataBlock x = agentUpdate.AgentData; + + if (x.AgentID != AgentId || x.SessionID != SessionId) + return false; + + // Before we update the current m_thisAgentUpdateArgs, let's check this again + // to see what exactly changed + bool movement = CheckAgentMovementUpdateSignificance(x); + bool camera = CheckAgentCameraUpdateSignificance(x); + + m_thisAgentUpdateArgs.AgentID = x.AgentID; + m_thisAgentUpdateArgs.BodyRotation = x.BodyRotation; + m_thisAgentUpdateArgs.CameraAtAxis = x.CameraAtAxis; + m_thisAgentUpdateArgs.CameraCenter = x.CameraCenter; + m_thisAgentUpdateArgs.CameraLeftAxis = x.CameraLeftAxis; + m_thisAgentUpdateArgs.CameraUpAxis = x.CameraUpAxis; + m_thisAgentUpdateArgs.ControlFlags = x.ControlFlags; + m_thisAgentUpdateArgs.Far = x.Far; + m_thisAgentUpdateArgs.Flags = x.Flags; + m_thisAgentUpdateArgs.HeadRotation = x.HeadRotation; + m_thisAgentUpdateArgs.SessionID = x.SessionID; + m_thisAgentUpdateArgs.State = x.State; + + UpdateAgent handlerAgentUpdate = OnAgentUpdate; + UpdateAgent handlerPreAgentUpdate = OnPreAgentUpdate; + UpdateAgent handlerAgentCameraUpdate = OnAgentCameraUpdate; + + // Was there a significant movement/state change? + if (movement) { - AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet; + if (handlerPreAgentUpdate != null) + OnPreAgentUpdate(this, m_thisAgentUpdateArgs); - #region Packet Session and User Check - if (agentUpdate.AgentData.SessionID != SessionId || agentUpdate.AgentData.AgentID != AgentId) - { - PacketPool.Instance.ReturnPacket(packet); - return false; - } - #endregion - - bool update = false; - AgentUpdatePacket.AgentDataBlock x = agentUpdate.AgentData; - - if (m_lastAgentUpdateArgs != null) - { - // These should be ordered from most-likely to - // least likely to change. I've made an initial - // guess at that. - update = - ( - (x.BodyRotation != m_lastAgentUpdateArgs.BodyRotation) || - (x.CameraAtAxis != m_lastAgentUpdateArgs.CameraAtAxis) || - (x.CameraCenter != m_lastAgentUpdateArgs.CameraCenter) || - (x.CameraLeftAxis != m_lastAgentUpdateArgs.CameraLeftAxis) || - (x.CameraUpAxis != m_lastAgentUpdateArgs.CameraUpAxis) || - (x.ControlFlags != m_lastAgentUpdateArgs.ControlFlags) || - (x.ControlFlags != 0) || - (x.Far != m_lastAgentUpdateArgs.Far) || - (x.Flags != m_lastAgentUpdateArgs.Flags) || - (x.State != m_lastAgentUpdateArgs.State) || - (x.HeadRotation != m_lastAgentUpdateArgs.HeadRotation) || - (x.SessionID != m_lastAgentUpdateArgs.SessionID) || - (x.AgentID != m_lastAgentUpdateArgs.AgentID) - ); - } - else - { - m_lastAgentUpdateArgs = new AgentUpdateArgs(); - update = true; - } - - if (update) - { -// m_log.DebugFormat("[LLCLIENTVIEW]: Triggered AgentUpdate for {0}", sener.Name); - - m_lastAgentUpdateArgs.AgentID = x.AgentID; - m_lastAgentUpdateArgs.BodyRotation = x.BodyRotation; - m_lastAgentUpdateArgs.CameraAtAxis = x.CameraAtAxis; - m_lastAgentUpdateArgs.CameraCenter = x.CameraCenter; - m_lastAgentUpdateArgs.CameraLeftAxis = x.CameraLeftAxis; - m_lastAgentUpdateArgs.CameraUpAxis = x.CameraUpAxis; - m_lastAgentUpdateArgs.ControlFlags = x.ControlFlags; - m_lastAgentUpdateArgs.Far = x.Far; - m_lastAgentUpdateArgs.Flags = x.Flags; - m_lastAgentUpdateArgs.HeadRotation = x.HeadRotation; - m_lastAgentUpdateArgs.SessionID = x.SessionID; - m_lastAgentUpdateArgs.State = x.State; - - UpdateAgent handlerAgentUpdate = OnAgentUpdate; - UpdateAgent handlerPreAgentUpdate = OnPreAgentUpdate; - - if (handlerPreAgentUpdate != null) - OnPreAgentUpdate(this, m_lastAgentUpdateArgs); - - if (handlerAgentUpdate != null) - OnAgentUpdate(this, m_lastAgentUpdateArgs); - - handlerAgentUpdate = null; - handlerPreAgentUpdate = null; - } + if (handlerAgentUpdate != null) + OnAgentUpdate(this, m_thisAgentUpdateArgs); } + // Was there a significant camera(s) change? + if (camera) + if (handlerAgentCameraUpdate != null) + handlerAgentCameraUpdate(this, m_thisAgentUpdateArgs); + + handlerAgentUpdate = null; + handlerPreAgentUpdate = null; + handlerAgentCameraUpdate = null; PacketPool.Instance.ReturnPacket(packet); @@ -12813,7 +12896,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(dialog, ThrottleOutPacketType.Task); } - public void StopFlying(ISceneEntity p) + public void SendAgentTerseUpdate(ISceneEntity p) { if (p is ScenePresence) { @@ -12827,25 +12910,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP Vector3 pos = presence.AbsolutePosition; - if (presence.Appearance.AvatarHeight != 127.0f) - pos += new Vector3(0f, 0f, (presence.Appearance.AvatarHeight/6f)); - else - pos += new Vector3(0f, 0f, (1.56f/6f)); - - presence.AbsolutePosition = pos; - - // attach a suitable collision plane regardless of the actual situation to force the LLClient to land. - // Collision plane below the avatar's position a 6th of the avatar's height is suitable. - // Mind you, that this method doesn't get called if the avatar's velocity magnitude is greater then a - // certain amount.. because the LLClient wouldn't land in that situation anyway. - - // why are we still testing for this really old height value default??? - if (presence.Appearance.AvatarHeight != 127.0f) - presence.CollisionPlane = new Vector4(0, 0, 0, pos.Z - presence.Appearance.AvatarHeight/6f); - else - presence.CollisionPlane = new Vector4(0, 0, 0, pos.Z - (1.56f/6f)); - - ImprovedTerseObjectUpdatePacket.ObjectDataBlock block = CreateImprovedTerseBlock(p, false); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLImageManager.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLImageManager.cs index 073c357d1a..41dd4d115a 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLImageManager.cs @@ -206,6 +206,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } + public bool HasUpdates() + { + J2KImage image = GetHighestPriorityImage(); + + return image != null && image.IsDecoded; + } + public bool ProcessImageQueue(int packetsToSend) { int packetsSent = 0; diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs index f7ed14dd9d..d52ad7e668 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs @@ -31,6 +31,7 @@ using System.Net; using System.Threading; using log4net; using OpenSim.Framework; +using OpenSim.Framework.Monitoring; using OpenMetaverse; using OpenMetaverse.Packets; @@ -81,6 +82,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// hooked to put more data on the empty queue public event QueueEmpty OnQueueEmpty; + public event Func HasUpdates; + /// AgentID for this client public readonly UUID AgentID; /// The remote address of the connected client @@ -645,15 +648,38 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Throttle categories to fire the callback for private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories) { - if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty) +// if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty) + if (!m_isQueueEmptyRunning && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty) { + m_isQueueEmptyRunning = true; + + int start = Environment.TickCount & Int32.MaxValue; + const int MIN_CALLBACK_MS = 30; + + m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; + if (m_nextOnQueueEmpty == 0) + m_nextOnQueueEmpty = 1; + // Use a value of 0 to signal that FireQueueEmpty is running - m_nextOnQueueEmpty = 0; - // Asynchronously run the callback - Util.FireAndForget(FireQueueEmpty, categories); +// m_nextOnQueueEmpty = 0; + + m_categories = categories; + + if (HasUpdates(m_categories)) + { + // Asynchronously run the callback + Util.FireAndForget(FireQueueEmpty, categories); + } + else + { + m_isQueueEmptyRunning = false; + } } } + private bool m_isQueueEmptyRunning; + private ThrottleOutPacketTypeFlags m_categories = 0; + /// /// Fires the OnQueueEmpty callback and sets the minimum time that it /// can be called again @@ -663,22 +689,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// signature private void FireQueueEmpty(object o) { - const int MIN_CALLBACK_MS = 30; +// int start = Environment.TickCount & Int32.MaxValue; +// const int MIN_CALLBACK_MS = 30; - ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o; - QueueEmpty callback = OnQueueEmpty; - - int start = Environment.TickCount & Int32.MaxValue; +// if (m_udpServer.IsRunningOutbound) +// { + ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o; + QueueEmpty callback = OnQueueEmpty; - if (callback != null) - { - try { callback(categories); } - catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); } - } + if (callback != null) + { +// if (m_udpServer.IsRunningOutbound) +// { + try { callback(categories); } + catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); } +// } + } +// } - m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; - if (m_nextOnQueueEmpty == 0) - m_nextOnQueueEmpty = 1; +// m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; +// if (m_nextOnQueueEmpty == 0) +// m_nextOnQueueEmpty = 1; + +// } + + m_isQueueEmptyRunning = false; } internal void ForceThrottleSetting(int throttle, int setting) { diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index b4ac021e85..7a4c6f4eb6 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -67,11 +67,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP { m_udpServer.AddScene(scene); + StatsManager.RegisterStat( + new Stat( + "IncomingUDPReceivesCount", + "Number of UDP receives performed", + "Number of UDP receives performed", + "", + "clientstack", + scene.Name, + StatType.Pull, + MeasuresOfInterest.AverageChangeOverTime, + stat => stat.Value = m_udpServer.UdpReceives, + StatVerbosity.Debug)); + StatsManager.RegisterStat( new Stat( "IncomingPacketsProcessedCount", - "Number of inbound UDP packets processed", - "Number of inbound UDP packets processed", + "Number of inbound LL protocol packets processed", + "Number of inbound LL protocol packets processed", "", "clientstack", scene.Name, @@ -79,6 +92,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP MeasuresOfInterest.AverageChangeOverTime, stat => stat.Value = m_udpServer.IncomingPacketsProcessed, StatVerbosity.Debug)); + + StatsManager.RegisterStat( + new Stat( + "OutgoingUDPSendsCount", + "Number of UDP sends performed", + "Number of UDP sends performed", + "", + "clientstack", + scene.Name, + StatType.Pull, + MeasuresOfInterest.AverageChangeOverTime, + stat => stat.Value = m_udpServer.UdpSends, + StatVerbosity.Debug)); + + StatsManager.RegisterStat( + new Stat( + "AverageUDPProcessTime", + "Average number of milliseconds taken to process each incoming UDP packet in a sample.", + "This is for initial receive processing which is separate from the later client LL packet processing stage.", + "ms", + "clientstack", + scene.Name, + StatType.Pull, + MeasuresOfInterest.None, + stat => stat.Value = m_udpServer.AverageReceiveTicksForLastSamplePeriod / TimeSpan.TicksPerMillisecond, +// stat => +// stat.Value = Math.Round(m_udpServer.AverageReceiveTicksForLastSamplePeriod / TimeSpan.TicksPerMillisecond, 7), + StatVerbosity.Debug)); } public bool HandlesRegion(Location x) @@ -185,6 +226,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected bool m_sendPing; private ExpiringCache> m_pendingCache = new ExpiringCache>(); + + /// + /// Event used to signal when queued packets are available for sending. + /// + /// + /// This allows the outbound loop to only operate when there is data to send rather than continuously polling. + /// Some data is sent immediately and not queued. That data would not trigger this event. + /// + private AutoResetEvent m_dataPresentEvent = new AutoResetEvent(false); + private Pool m_incomingPacketPool; /// @@ -462,6 +513,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_scene = (Scene)scene; m_location = new Location(m_scene.RegionInfo.RegionHandle); + StatsManager.RegisterStat( + new Stat( + "InboxPacketsCount", + "Number of LL protocol packets waiting for the second stage of processing after initial receive.", + "Number of LL protocol packets waiting for the second stage of processing after initial receive.", + "", + "clientstack", + scene.Name, + StatType.Pull, + MeasuresOfInterest.AverageChangeOverTime, + stat => stat.Value = packetInbox.Count, + StatVerbosity.Debug)); + // XXX: These stats are also pool stats but we register them separately since they are currently not // turned on and off by EnablePools()/DisablePools() StatsManager.RegisterStat( @@ -575,6 +639,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP "debug lludp status", "Return status of LLUDP packet processing.", HandleStatusCommand); + + MainConsole.Instance.Commands.AddCommand( + "Debug", + false, + "debug lludp toggle agentupdate", + "debug lludp toggle agentupdate", + "Toggle whether agentupdate packets are processed or simply discarded.", + HandleAgentUpdateCommand); } private void HandlePacketCommand(string module, string[] args) @@ -709,6 +781,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } + bool m_discardAgentUpdates; + + private void HandleAgentUpdateCommand(string module, string[] args) + { + if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_scene) + return; + + m_discardAgentUpdates = !m_discardAgentUpdates; + + MainConsole.Instance.OutputFormat( + "Discard AgentUpdates now {0} for {1}", m_discardAgentUpdates, m_scene.Name); + } + private void HandleStatusCommand(string module, string[] args) { if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_scene) @@ -809,12 +894,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP } PacketPool.Instance.ReturnPacket(packet); + m_dataPresentEvent.Set(); - } - private AutoResetEvent m_dataPresentEvent = new AutoResetEvent(false); - /// /// Start the process of sending a packet to the client. /// @@ -1325,6 +1408,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP LogPacketHeader(true, udpClient.CircuitCode, 0, packet.Type, (ushort)packet.Length); #endregion BinaryStats + if (packet.Type == PacketType.AgentUpdate) + { + if (m_discardAgentUpdates) + return; + + ((LLClientView)client).TotalAgentUpdates++; + + AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet; + + LLClientView llClient = client as LLClientView; + if (agentUpdate.AgentData.SessionID != client.SessionId + || agentUpdate.AgentData.AgentID != client.AgentId + || !(llClient == null || llClient.CheckAgentUpdateSignificance(agentUpdate.AgentData)) ) + { + PacketPool.Instance.ReturnPacket(packet); + return; + } + } + #region Ping Check Handling if (packet.Type == PacketType.StartPingCheck) @@ -1734,7 +1836,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Action generic every round Action clientPacketHandler = ClientOutgoingPacketHandler; -// while (true) while (base.IsRunningOutbound) { m_scene.ThreadAlive(2); @@ -1798,6 +1899,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP // token bucket could get more tokens //if (!m_packetSent) // Thread.Sleep((int)TickCountResolution); + // + // Instead, now wait for data present to be explicitly signalled. Evidence so far is that with + // modern mono it reduces CPU base load since there is no more continuous polling. m_dataPresentEvent.WaitOne(100); Watchdog.UpdateThread(); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs index 7035e38cc9..48c5b37510 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs @@ -77,6 +77,36 @@ namespace OpenMetaverse /// If IsRunningOut = false, then any request to send a packet is simply dropped. public bool IsRunningOutbound { get; private set; } + /// + /// Number of UDP receives. + /// + public int UdpReceives { get; private set; } + + /// + /// Number of UDP sends + /// + public int UdpSends { get; private set; } + + /// + /// Number of receives over which to establish a receive time average. + /// + private readonly static int s_receiveTimeSamples = 500; + + /// + /// Current number of samples taken to establish a receive time average. + /// + private int m_currentReceiveTimeSamples; + + /// + /// Cumulative receive time for the sample so far. + /// + private int m_receiveTicksInCurrentSamplePeriod; + + /// + /// The average time taken for each require receive in the last sample. + /// + public float AverageReceiveTicksForLastSamplePeriod { get; private set; } + /// /// Default constructor /// @@ -111,6 +141,8 @@ namespace OpenMetaverse if (!IsRunningInbound) { + m_log.DebugFormat("[UDPBASE]: Starting inbound UDP loop"); + const int SIO_UDP_CONNRESET = -1744830452; IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort); @@ -151,6 +183,8 @@ namespace OpenMetaverse /// public void StartOutbound() { + m_log.DebugFormat("[UDPBASE]: Starting outbound UDP loop"); + IsRunningOutbound = true; } @@ -158,10 +192,8 @@ namespace OpenMetaverse { if (IsRunningInbound) { - // wait indefinitely for a writer lock. Once this is called, the .NET runtime - // will deny any more reader locks, in effect blocking all other send/receive - // threads. Once we have the lock, we set IsRunningInbound = false to inform the other - // threads that the socket is closed. + m_log.DebugFormat("[UDPBASE]: Stopping inbound UDP loop"); + IsRunningInbound = false; m_udpSocket.Close(); } @@ -169,6 +201,8 @@ namespace OpenMetaverse public void StopOutbound() { + m_log.DebugFormat("[UDPBASE]: Stopping outbound UDP loop"); + IsRunningOutbound = false; } @@ -267,6 +301,8 @@ namespace OpenMetaverse // to AsyncBeginReceive if (IsRunningInbound) { + UdpReceives++; + // Asynchronous mode will start another receive before the // callback for this packet is even fired. Very parallel :-) if (m_asyncPacketHandling) @@ -278,6 +314,8 @@ namespace OpenMetaverse try { + int startTick = Util.EnvironmentTickCount(); + // get the length of data actually read from the socket, store it with the // buffer buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); @@ -285,6 +323,23 @@ namespace OpenMetaverse // call the abstract method PacketReceived(), passing the buffer that // has just been filled from the socket read. PacketReceived(buffer); + + // If more than one thread can be calling AsyncEndReceive() at once (e.g. if m_asyncPacketHandler) + // then a particular stat may be inaccurate due to a race condition. We won't worry about this + // since this should be rare and won't cause a runtime problem. + if (m_currentReceiveTimeSamples >= s_receiveTimeSamples) + { + AverageReceiveTicksForLastSamplePeriod + = (float)m_receiveTicksInCurrentSamplePeriod / s_receiveTimeSamples; + + m_receiveTicksInCurrentSamplePeriod = 0; + m_currentReceiveTimeSamples = 0; + } + else + { + m_receiveTicksInCurrentSamplePeriod += Util.EnvironmentTickCountSubtract(startTick); + m_currentReceiveTimeSamples++; + } } catch (SocketException) { } catch (ObjectDisposedException) { } @@ -298,14 +353,13 @@ namespace OpenMetaverse if (!m_asyncPacketHandling) AsyncBeginReceive(); } - } } public void AsyncBeginSend(UDPPacketBuffer buf) { - if (IsRunningOutbound) - { +// if (IsRunningOutbound) +// { try { m_udpSocket.BeginSendTo( @@ -319,7 +373,7 @@ namespace OpenMetaverse } catch (SocketException) { } catch (ObjectDisposedException) { } - } +// } } void AsyncEndSend(IAsyncResult result) @@ -328,6 +382,8 @@ namespace OpenMetaverse { // UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState; m_udpSocket.EndSendTo(result); + + UdpSends++; } catch (SocketException) { } catch (ObjectDisposedException) { } diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs index e0c8ea68a4..8f9800f681 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs @@ -185,8 +185,11 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess } } - public void UploadInventoryItem(UUID avatarID, UUID assetID, string name, int userlevel) + public void UploadInventoryItem(UUID avatarID, AssetType type, UUID assetID, string name, int userlevel) { + if (type == AssetType.Link) + return; + string userAssetServer = string.Empty; if (IsForeignUser(avatarID, out userAssetServer) && userAssetServer != string.Empty && m_OutboundPermission) { @@ -221,7 +224,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess { UUID newAssetID = base.CapsUpdateInventoryItemAsset(remoteClient, itemID, data); - UploadInventoryItem(remoteClient.AgentId, newAssetID, "", 0); + UploadInventoryItem(remoteClient.AgentId, AssetType.Unknown, newAssetID, "", 0); return newAssetID; } @@ -232,7 +235,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess protected override void ExportAsset(UUID agentID, UUID assetID) { if (!assetID.Equals(UUID.Zero)) - UploadInventoryItem(agentID, assetID, "", 0); + UploadInventoryItem(agentID, AssetType.Unknown, assetID, "", 0); else m_log.Debug("[HGScene]: Scene.Inventory did not create asset"); } @@ -348,7 +351,15 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess InventoryFolderBase root = m_Scene.InventoryService.GetRootFolder(client.AgentId); InventoryCollection content = m_Scene.InventoryService.GetFolderContent(client.AgentId, root.ID); - inv.SendBulkUpdateInventory(content.Folders.ToArray(), content.Items.ToArray()); + List keep = new List(); + + foreach (InventoryFolderBase f in content.Folders) + { + if (f.Name != "My Suitcase" && f.Name != "Current Outfit") + keep.Add(f); + } + + inv.SendBulkUpdateInventory(keep.ToArray(), content.Items.ToArray()); } } } @@ -381,7 +392,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess foreach (InventoryFolderBase f in content.Folders) { - if (f.Name != "My Suitcase") + if (f.Name != "My Suitcase" && f.Name != "Current Outfit") { f.Name = f.Name + " (Unavailable)"; keep.Add(f); diff --git a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs index a91adfab13..53bd2e2de2 100644 --- a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs +++ b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs @@ -389,7 +389,7 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement } names[0] = "Unknown"; - names[1] = "UserUMMTGUN7"; + names[1] = "UserUMMTGUN8"; return false; } @@ -601,7 +601,7 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement // TODO: Can be removed when GUN* unknown users have definitely dropped significantly or // disappeared. user.FirstName = "Unknown"; - user.LastName = "UserUMMAU3"; + user.LastName = "UserUMMAU4"; } AddUserInternal(user); diff --git a/OpenSim/Region/Framework/Scenes/EventManager.cs b/OpenSim/Region/Framework/Scenes/EventManager.cs index 1018b54adb..6e8eb91b90 100644 --- a/OpenSim/Region/Framework/Scenes/EventManager.cs +++ b/OpenSim/Region/Framework/Scenes/EventManager.cs @@ -743,7 +743,7 @@ namespace OpenSim.Region.Framework.Scenes public event OnIncomingSceneObjectDelegate OnIncomingSceneObject; public delegate void OnIncomingSceneObjectDelegate(SceneObjectGroup so); - public delegate void NewInventoryItemUploadComplete(UUID avatarID, UUID assetID, string name, int userlevel); + public delegate void NewInventoryItemUploadComplete(UUID avatarID, AssetType type, UUID assetID, string name, int userlevel); public event NewInventoryItemUploadComplete OnNewInventoryItemUploadComplete; @@ -2172,7 +2172,7 @@ namespace OpenSim.Region.Framework.Scenes } } - public void TriggerOnNewInventoryItemUploadComplete(UUID agentID, UUID AssetID, String AssetName, int userlevel) + public void TriggerOnNewInventoryItemUploadComplete(UUID agentID, AssetType type, UUID AssetID, String AssetName, int userlevel) { NewInventoryItemUploadComplete handlerNewInventoryItemUpdateComplete = OnNewInventoryItemUploadComplete; if (handlerNewInventoryItemUpdateComplete != null) @@ -2181,7 +2181,7 @@ namespace OpenSim.Region.Framework.Scenes { try { - d(agentID, AssetID, AssetName, userlevel); + d(agentID, type, AssetID, AssetName, userlevel); } catch (Exception e) { diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 70018c8d96..8f6073af28 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -139,7 +139,7 @@ namespace OpenSim.Region.Framework.Scenes { userlevel = 1; } - EventManager.TriggerOnNewInventoryItemUploadComplete(item.Owner, item.AssetID, item.Name, userlevel); + EventManager.TriggerOnNewInventoryItemUploadComplete(item.Owner, (AssetType)item.AssetType, item.AssetID, item.Name, userlevel); return true; } @@ -178,7 +178,7 @@ namespace OpenSim.Region.Framework.Scenes { userlevel = 1; } - EventManager.TriggerOnNewInventoryItemUploadComplete(item.Owner, item.AssetID, item.Name, userlevel); + EventManager.TriggerOnNewInventoryItemUploadComplete(item.Owner, (AssetType)item.AssetType, item.AssetID, item.Name, userlevel); if (originalFolder != UUID.Zero) { diff --git a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs index ce6415a8b9..421cb08492 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs @@ -416,6 +416,7 @@ namespace OpenSim.Region.Framework.Scenes void ProcessViewerEffect(IClientAPI remoteClient, List args) { // TODO: don't create new blocks if recycling an old packet + bool discardableEffects = true; ViewerEffectPacket.EffectBlock[] effectBlockArray = new ViewerEffectPacket.EffectBlock[args.Count]; for (int i = 0; i < args.Count; i++) { @@ -427,17 +428,34 @@ namespace OpenSim.Region.Framework.Scenes effect.Type = args[i].Type; effect.TypeData = args[i].TypeData; effectBlockArray[i] = effect; + + if ((EffectType)effect.Type != EffectType.LookAt && (EffectType)effect.Type != EffectType.Beam) + discardableEffects = false; + + //m_log.DebugFormat("[YYY]: VE {0} {1} {2}", effect.AgentID, effect.Duration, (EffectType)effect.Type); } - ForEachClient( - delegate(IClientAPI client) + ForEachScenePresence(sp => { - if (client.AgentId != remoteClient.AgentId) - client.SendViewerEffect(effectBlockArray); - } - ); + if (sp.ControllingClient.AgentId != remoteClient.AgentId) + { + if (!discardableEffects || + (discardableEffects && ShouldSendDiscardableEffect(remoteClient, sp))) + { + //m_log.DebugFormat("[YYY]: Sending to {0}", sp.UUID); + sp.ControllingClient.SendViewerEffect(effectBlockArray); + } + //else + // m_log.DebugFormat("[YYY]: Not sending to {0}", sp.UUID); + } + }); } + private bool ShouldSendDiscardableEffect(IClientAPI thisClient, ScenePresence other) + { + return Vector3.Distance(other.CameraPosition, thisClient.SceneAgent.AbsolutePosition) < 10; + } + private class DescendentsRequestData { public IClientAPI RemoteClient; diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 6e4ac98005..1225c2e1df 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -142,6 +142,8 @@ namespace OpenSim.Region.Framework.Scenes private Vector3 m_lastVelocity; private Vector3 m_lastSize = new Vector3(0.45f,0.6f,1.9f); + private bool m_followCamAuto = false; + private Vector3? m_forceToApply; private int m_userFlags; @@ -874,6 +876,7 @@ namespace OpenSim.Region.Framework.Scenes { ControllingClient.OnCompleteMovementToRegion += CompleteMovement; ControllingClient.OnAgentUpdate += HandleAgentUpdate; + ControllingClient.OnAgentCameraUpdate += HandleAgentCamerasUpdate; ControllingClient.OnAgentRequestSit += HandleAgentRequestSit; ControllingClient.OnAgentSit += HandleAgentSit; ControllingClient.OnSetAlwaysRun += HandleSetAlwaysRun; @@ -1306,7 +1309,26 @@ namespace OpenSim.Region.Framework.Scenes public void StopFlying() { - ControllingClient.StopFlying(this); + Vector3 pos = AbsolutePosition; + if (Appearance.AvatarHeight != 127.0f) + pos += new Vector3(0f, 0f, (Appearance.AvatarHeight / 6f)); + else + pos += new Vector3(0f, 0f, (1.56f / 6f)); + + AbsolutePosition = pos; + + // attach a suitable collision plane regardless of the actual situation to force the LLClient to land. + // Collision plane below the avatar's position a 6th of the avatar's height is suitable. + // Mind you, that this method doesn't get called if the avatar's velocity magnitude is greater then a + // certain amount.. because the LLClient wouldn't land in that situation anyway. + + // why are we still testing for this really old height value default??? + if (Appearance.AvatarHeight != 127.0f) + CollisionPlane = new Vector4(0, 0, 0, pos.Z - Appearance.AvatarHeight / 6f); + else + CollisionPlane = new Vector4(0, 0, 0, pos.Z - (1.56f / 6f)); + + ControllingClient.SendAgentTerseUpdate(this); } /// @@ -1662,9 +1684,9 @@ namespace OpenSim.Region.Framework.Scenes /// public void HandleAgentUpdate(IClientAPI remoteClient, AgentUpdateArgs agentData) { -// m_log.DebugFormat( -// "[SCENE PRESENCE]: In {0} received agent update from {1}, flags {2}", -// Scene.RegionInfo.RegionName, remoteClient.Name, (AgentManager.ControlFlags)agentData.ControlFlags); + //m_log.DebugFormat( + // "[SCENE PRESENCE]: In {0} received agent update from {1}, flags {2}", + // Scene.RegionInfo.RegionName, remoteClient.Name, (AgentManager.ControlFlags)agentData.ControlFlags); if (IsChildAgent) { @@ -1672,10 +1694,6 @@ namespace OpenSim.Region.Framework.Scenes return; } - ++m_movementUpdateCount; - if (m_movementUpdateCount < 1) - m_movementUpdateCount = 1; - #region Sanity Checking // This is irritating. Really. @@ -1706,21 +1724,6 @@ namespace OpenSim.Region.Framework.Scenes AgentManager.ControlFlags flags = (AgentManager.ControlFlags)agentData.ControlFlags; - // Camera location in world. We'll need to raytrace - // from this location from time to time. - CameraPosition = agentData.CameraCenter; - if (Vector3.Distance(m_lastCameraPosition, CameraPosition) >= Scene.RootReprioritizationDistance) - { - ReprioritizeUpdates(); - m_lastCameraPosition = CameraPosition; - } - - // Use these three vectors to figure out what the agent is looking at - // Convert it to a Matrix and/or Quaternion - CameraAtAxis = agentData.CameraAtAxis; - CameraLeftAxis = agentData.CameraLeftAxis; - CameraUpAxis = agentData.CameraUpAxis; - // The Agent's Draw distance setting // When we get to the point of re-computing neighbors everytime this // changes, then start using the agent's drawdistance rather than the @@ -2005,10 +2008,79 @@ namespace OpenSim.Region.Framework.Scenes SendControlsToScripts(flagsForScripts); } + // We need to send this back to the client in order to see the edit beams + if ((State & (uint)AgentState.Editing) != 0) + ControllingClient.SendAgentTerseUpdate(this); + m_scene.EventManager.TriggerOnClientMovement(this); - TriggerScenePresenceUpdated(); } + + /// + /// This is the event handler for client cameras. If a client is moving, or moving the camera, this event is triggering. + /// + private void HandleAgentCamerasUpdate(IClientAPI remoteClient, AgentUpdateArgs agentData) + { + //m_log.DebugFormat( + // "[SCENE PRESENCE]: In {0} received agent camera update from {1}, flags {2}", + // Scene.RegionInfo.RegionName, remoteClient.Name, (AgentManager.ControlFlags)agentData.ControlFlags); + + if (IsChildAgent) + { + // // m_log.Debug("DEBUG: HandleAgentUpdate: child agent"); + return; + } + + ++m_movementUpdateCount; + if (m_movementUpdateCount < 1) + m_movementUpdateCount = 1; + + + AgentManager.ControlFlags flags = (AgentManager.ControlFlags)agentData.ControlFlags; + + // Camera location in world. We'll need to raytrace + // from this location from time to time. + CameraPosition = agentData.CameraCenter; + if (Vector3.Distance(m_lastCameraPosition, CameraPosition) >= Scene.RootReprioritizationDistance) + { + ReprioritizeUpdates(); + m_lastCameraPosition = CameraPosition; + } + + // Use these three vectors to figure out what the agent is looking at + // Convert it to a Matrix and/or Quaternion + CameraAtAxis = agentData.CameraAtAxis; + CameraLeftAxis = agentData.CameraLeftAxis; + CameraUpAxis = agentData.CameraUpAxis; + + // The Agent's Draw distance setting + // When we get to the point of re-computing neighbors everytime this + // changes, then start using the agent's drawdistance rather than the + // region's draw distance. + // DrawDistance = agentData.Far; + DrawDistance = Scene.DefaultDrawDistance; + + // Check if Client has camera in 'follow cam' or 'build' mode. + Vector3 camdif = (Vector3.One * Rotation - Vector3.One * CameraRotation); + + m_followCamAuto = ((CameraUpAxis.Z > 0.959f && CameraUpAxis.Z < 0.98f) + && (Math.Abs(camdif.X) < 0.4f && Math.Abs(camdif.Y) < 0.4f)) ? true : false; + + + //m_log.DebugFormat("[FollowCam]: {0}", m_followCamAuto); + // Raycast from the avatar's head to the camera to see if there's anything blocking the view + if ((m_movementUpdateCount % NumMovementsBetweenRayCast) == 0 && m_scene.PhysicsScene.SupportsRayCast()) + { + if (m_followCamAuto) + { + Vector3 posAdjusted = m_pos + HEAD_ADJUSTMENT; + m_scene.PhysicsScene.RaycastWorld(m_pos, Vector3.Normalize(CameraPosition - posAdjusted), Vector3.Distance(CameraPosition, posAdjusted) + 0.3f, RayCastCameraCallback); + } + } + + TriggerScenePresenceUpdated(); + } + /// /// Calculate an update to move the presence to the set target. /// diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index b28d96b18a..a96c8b4e08 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -688,6 +688,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server public event Action OnCompleteMovementToRegion; public event UpdateAgent OnPreAgentUpdate; public event UpdateAgent OnAgentUpdate; + public event UpdateAgent OnAgentCameraUpdate; public event AgentRequestSit OnAgentRequestSit; public event AgentSit OnAgentSit; public event AvatarPickerRequest OnAvatarPickerRequest; @@ -1687,7 +1688,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server { } - public void StopFlying(ISceneEntity presence) + public void SendAgentTerseUpdate(ISceneEntity presence) { } diff --git a/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs b/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs index 79509ab0a0..15dea1742d 100644 --- a/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs +++ b/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs @@ -611,7 +611,7 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden // if (showParams.Length <= 4) { - m_log.InfoFormat("[INFO]: {0,-12} {1,20} {2,6} {3,11} {4, 10}", "Region", "Name", "Root", "Time", "Reqs/min"); + m_log.InfoFormat("[INFO]: {0,-12} {1,-20} {2,-6} {3,-11} {4,-11} {5,-16}", "Region", "Name", "Root", "Time", "Reqs/min", "AgentUpdates"); foreach (Scene scene in m_scenes.Values) { scene.ForEachClient( @@ -624,9 +624,15 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden int avg_reqs = cinfo.AsyncRequests.Values.Sum() + cinfo.GenericRequests.Values.Sum() + cinfo.SyncRequests.Values.Sum(); avg_reqs = avg_reqs / ((DateTime.Now - cinfo.StartedTime).Minutes + 1); - m_log.InfoFormat("[INFO]: {0,-12} {1,20} {2,4} {3,9}min {4,10}", + m_log.InfoFormat("[INFO]: {0,-12} {1,-20} {2,-6} {3,-11} {4,-11} {5,-16}", scene.RegionInfo.RegionName, llClient.Name, - (llClient.SceneAgent.IsChildAgent ? "N" : "Y"), (DateTime.Now - cinfo.StartedTime).Minutes, avg_reqs); + llClient.SceneAgent.IsChildAgent ? "N" : "Y", + (DateTime.Now - cinfo.StartedTime).Minutes, + avg_reqs, + string.Format( + "{0} ({1:0.00}%)", + llClient.TotalAgentUpdates, + (float)cinfo.SyncRequests["AgentUpdate"] / llClient.TotalAgentUpdates * 100)); } }); } diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index c8aab54093..f2355e25f9 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -259,6 +259,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC public event Action OnCompleteMovementToRegion; public event UpdateAgent OnPreAgentUpdate; public event UpdateAgent OnAgentUpdate; + public event UpdateAgent OnAgentCameraUpdate; public event AgentRequestSit OnAgentRequestSit; public event AgentSit OnAgentSit; public event AvatarPickerRequest OnAvatarPickerRequest; @@ -1242,7 +1243,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC { } - public void StopFlying(ISceneEntity presence) + public void SendAgentTerseUpdate(ISceneEntity presence) { } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActors.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActors.cs index fff63e4eb2..e0ccc501ca 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActors.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActors.cs @@ -69,7 +69,7 @@ public class BSActorCollection { lock (m_actors) { - Release(); + ForEachActor(a => a.Dispose()); m_actors.Clear(); } } @@ -98,10 +98,6 @@ public class BSActorCollection { ForEachActor(a => a.SetEnabled(enabl)); } - public void Release() - { - ForEachActor(a => a.Dispose()); - } public void Refresh() { ForEachActor(a => a.Refresh()); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index 0204967368..82d7c44253 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -45,7 +45,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin private static string LogHeader = "[BULLETSIM VEHICLE]"; // the prim this dynamic controller belongs to - private BSPrim ControllingPrim { get; set; } + private BSPrimLinkable ControllingPrim { get; set; } private bool m_haveRegisteredForSceneEvents; @@ -128,9 +128,15 @@ namespace OpenSim.Region.Physics.BulletSPlugin public BSDynamics(BSScene myScene, BSPrim myPrim, string actorName) : base(myScene, myPrim, actorName) { - ControllingPrim = myPrim; Type = Vehicle.TYPE_NONE; m_haveRegisteredForSceneEvents = false; + + ControllingPrim = myPrim as BSPrimLinkable; + if (ControllingPrim == null) + { + // THIS CANNOT HAPPEN!! + } + VDetailLog("{0},Creation", ControllingPrim.LocalID); } // Return 'true' if this vehicle is doing vehicle things @@ -585,6 +591,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Friction affects are handled by this vehicle code m_physicsScene.PE.SetFriction(ControllingPrim.PhysBody, BSParam.VehicleFriction); m_physicsScene.PE.SetRestitution(ControllingPrim.PhysBody, BSParam.VehicleRestitution); + // ControllingPrim.Linkset.SetPhysicalFriction(BSParam.VehicleFriction); + // ControllingPrim.Linkset.SetPhysicalRestitution(BSParam.VehicleRestitution); // Moderate angular movement introduced by Bullet. // TODO: possibly set AngularFactor and LinearFactor for the type of vehicle. @@ -595,17 +603,20 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Vehicles report collision events so we know when it's on the ground m_physicsScene.PE.AddToCollisionFlags(ControllingPrim.PhysBody, CollisionFlags.BS_VEHICLE_COLLISIONS); + // ControllingPrim.Linkset.SetPhysicalCollisionFlags(CollisionFlags.BS_VEHICLE_COLLISIONS); Vector3 inertia = m_physicsScene.PE.CalculateLocalInertia(ControllingPrim.PhysShape.physShapeInfo, m_vehicleMass); ControllingPrim.Inertia = inertia * BSParam.VehicleInertiaFactor; m_physicsScene.PE.SetMassProps(ControllingPrim.PhysBody, m_vehicleMass, ControllingPrim.Inertia); m_physicsScene.PE.UpdateInertiaTensor(ControllingPrim.PhysBody); + // ControllingPrim.Linkset.ComputeLocalInertia(BSParam.VehicleInertiaFactor); // Set the gravity for the vehicle depending on the buoyancy // TODO: what should be done if prim and vehicle buoyancy differ? m_VehicleGravity = ControllingPrim.ComputeGravity(m_VehicleBuoyancy); // The actual vehicle gravity is set to zero in Bullet so we can do all the application of same. m_physicsScene.PE.SetGravity(ControllingPrim.PhysBody, Vector3.Zero); + // ControllingPrim.Linkset.SetPhysicalGravity(Vector3.Zero); VDetailLog("{0},BSDynamics.SetPhysicalParameters,mass={1},inert={2},vehGrav={3},aDamp={4},frict={5},rest={6},lFact={7},aFact={8}", ControllingPrim.LocalID, m_vehicleMass, ControllingPrim.Inertia, m_VehicleGravity, @@ -617,6 +628,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin { if (ControllingPrim.PhysBody.HasPhysicalBody) m_physicsScene.PE.RemoveFromCollisionFlags(ControllingPrim.PhysBody, CollisionFlags.BS_VEHICLE_COLLISIONS); + // ControllingPrim.Linkset.RemoveFromPhysicalCollisionFlags(CollisionFlags.BS_VEHICLE_COLLISIONS); } } @@ -629,6 +641,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // BSActor.Release() public override void Dispose() { + VDetailLog("{0},Dispose", ControllingPrim.LocalID); UnregisterForSceneEvents(); Type = Vehicle.TYPE_NONE; Enabled = false; @@ -1001,7 +1014,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin else if (newVelocityLengthSq < 0.001f) VehicleVelocity = Vector3.Zero; - VDetailLog("{0}, MoveLinear,done,isColl={1},newVel={2}", ControllingPrim.LocalID, ControllingPrim.IsColliding, VehicleVelocity ); + VDetailLog("{0}, MoveLinear,done,isColl={1},newVel={2}", ControllingPrim.LocalID, ControllingPrim.HasSomeCollision, VehicleVelocity ); } // end MoveLinear() @@ -1029,8 +1042,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Add this correction to the velocity to make it faster/slower. VehicleVelocity += linearMotorVelocityW; - VDetailLog("{0}, MoveLinear,velocity,origVelW={1},velV={2},correctV={3},correctW={4},newVelW={5},fricFact={6}", - ControllingPrim.LocalID, origVelW, currentVelV, linearMotorCorrectionV, + VDetailLog("{0}, MoveLinear,velocity,origVelW={1},velV={2},tgt={3},correctV={4},correctW={5},newVelW={6},fricFact={7}", + ControllingPrim.LocalID, origVelW, currentVelV, m_linearMotor.TargetValue, linearMotorCorrectionV, linearMotorVelocityW, VehicleVelocity, frictionFactorV); } @@ -1062,7 +1075,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin Vector3 linearDeflectionW = linearDeflectionV * VehicleOrientation; // Optionally, if not colliding, don't effect world downward velocity. Let falling things fall. - if (BSParam.VehicleLinearDeflectionNotCollidingNoZ && !m_controllingPrim.IsColliding) + if (BSParam.VehicleLinearDeflectionNotCollidingNoZ && !m_controllingPrim.HasSomeCollision) { linearDeflectionW.Z = 0f; } @@ -1222,7 +1235,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin float targetHeight = Type == Vehicle.TYPE_BOAT ? GetWaterLevel(VehiclePosition) : GetTerrainHeight(VehiclePosition); distanceAboveGround = VehiclePosition.Z - targetHeight; // Not colliding if the vehicle is off the ground - if (!Prim.IsColliding) + if (!Prim.HasSomeCollision) { // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale); VehicleVelocity += new Vector3(0, 0, -distanceAboveGround); @@ -1233,12 +1246,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin // be computed with a motor. // TODO: add interaction with banking. VDetailLog("{0}, MoveLinear,limitMotorUp,distAbove={1},colliding={2},ret={3}", - Prim.LocalID, distanceAboveGround, Prim.IsColliding, ret); + Prim.LocalID, distanceAboveGround, Prim.HasSomeCollision, ret); */ // Another approach is to measure if we're going up. If going up and not colliding, // the vehicle is in the air. Fix that by pushing down. - if (!ControllingPrim.IsColliding && VehicleVelocity.Z > 0.1) + if (!ControllingPrim.HasSomeCollision && VehicleVelocity.Z > 0.1) { // Get rid of any of the velocity vector that is pushing us up. float upVelocity = VehicleVelocity.Z; @@ -1260,7 +1273,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin } */ VDetailLog("{0}, MoveLinear,limitMotorUp,collide={1},upVel={2},newVel={3}", - ControllingPrim.LocalID, ControllingPrim.IsColliding, upVelocity, VehicleVelocity); + ControllingPrim.LocalID, ControllingPrim.HasSomeCollision, upVelocity, VehicleVelocity); } } } @@ -1270,14 +1283,14 @@ namespace OpenSim.Region.Physics.BulletSPlugin Vector3 appliedGravity = m_VehicleGravity * m_vehicleMass; // Hack to reduce downward force if the vehicle is probably sitting on the ground - if (ControllingPrim.IsColliding && IsGroundVehicle) + if (ControllingPrim.HasSomeCollision && IsGroundVehicle) appliedGravity *= BSParam.VehicleGroundGravityFudge; VehicleAddForce(appliedGravity); VDetailLog("{0}, MoveLinear,applyGravity,vehGrav={1},collid={2},fudge={3},mass={4},appliedForce={5}", ControllingPrim.LocalID, m_VehicleGravity, - ControllingPrim.IsColliding, BSParam.VehicleGroundGravityFudge, m_vehicleMass, appliedGravity); + ControllingPrim.HasSomeCollision, BSParam.VehicleGroundGravityFudge, m_vehicleMass, appliedGravity); } // ======================================================================= diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs index ad8e10f2f5..960c0b4d2b 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs @@ -203,6 +203,33 @@ public abstract class BSLinkset return ret; } + // Called after a simulation step to post a collision with this object. + // Return 'true' if linkset processed the collision. 'false' says the linkset didn't have + // anything to add for the collision and it should be passed through normal processing. + // Default processing for a linkset. + public virtual bool HandleCollide(uint collidingWith, BSPhysObject collidee, + OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) + { + bool ret = false; + + // prims in the same linkset cannot collide with each other + BSPrimLinkable convCollidee = collidee as BSPrimLinkable; + if (convCollidee != null && (LinksetID == convCollidee.Linkset.LinksetID)) + { + // By returning 'true', we tell the caller the collision has been 'handled' so it won't + // do anything about this collision and thus, effectivily, ignoring the collision. + ret = true; + } + else + { + // Not a collision between members of the linkset. Must be a real collision. + // So the linkset root can know if there is a collision anywhere in the linkset. + LinksetRoot.SomeCollisionSimulationStep = m_physicsScene.SimulationStep; + } + + return ret; + } + // I am the root of a linkset and a new child is being added // Called while LinkActivity is locked. protected abstract void AddChildToLinkset(BSPrimLinkable child); @@ -250,6 +277,73 @@ public abstract class BSLinkset // Called at taint-time!! public abstract bool RemoveDependencies(BSPrimLinkable child); + // ================================================================ + // Some physical setting happen to all members of the linkset + public virtual void SetPhysicalFriction(float friction) + { + ForEachMember((member) => + { + if (member.PhysBody.HasPhysicalBody) + m_physicsScene.PE.SetFriction(member.PhysBody, friction); + return false; // 'false' says to continue looping + } + ); + } + public virtual void SetPhysicalRestitution(float restitution) + { + ForEachMember((member) => + { + if (member.PhysBody.HasPhysicalBody) + m_physicsScene.PE.SetRestitution(member.PhysBody, restitution); + return false; // 'false' says to continue looping + } + ); + } + public virtual void SetPhysicalGravity(OMV.Vector3 gravity) + { + ForEachMember((member) => + { + if (member.PhysBody.HasPhysicalBody) + m_physicsScene.PE.SetGravity(member.PhysBody, gravity); + return false; // 'false' says to continue looping + } + ); + } + public virtual void ComputeLocalInertia(OMV.Vector3 inertiaFactor) + { + ForEachMember((member) => + { + if (member.PhysBody.HasPhysicalBody) + { + OMV.Vector3 inertia = m_physicsScene.PE.CalculateLocalInertia(member.PhysShape.physShapeInfo, member.Mass); + member.Inertia = inertia * inertiaFactor; + m_physicsScene.PE.SetMassProps(member.PhysBody, member.Mass, member.Inertia); + m_physicsScene.PE.UpdateInertiaTensor(member.PhysBody); + } + return false; // 'false' says to continue looping + } + ); + } + public virtual void SetPhysicalCollisionFlags(CollisionFlags collFlags) + { + ForEachMember((member) => + { + if (member.PhysBody.HasPhysicalBody) + m_physicsScene.PE.SetCollisionFlags(member.PhysBody, collFlags); + return false; // 'false' says to continue looping + } + ); + } + public virtual void RemoveFromPhysicalCollisionFlags(CollisionFlags collFlags) + { + ForEachMember((member) => + { + if (member.PhysBody.HasPhysicalBody) + m_physicsScene.PE.RemoveFromCollisionFlags(member.PhysBody, collFlags); + return false; // 'false' says to continue looping + } + ); + } // ================================================================ protected virtual float ComputeLinksetMass() { @@ -311,6 +405,5 @@ public abstract class BSLinkset if (m_physicsScene.PhysicsLogging.Enabled) m_physicsScene.DetailLog(msg, args); } - } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs index 1d941426a8..33ae5a503c 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs @@ -44,6 +44,42 @@ public sealed class BSLinksetCompound : BSLinkset { } + // ================================================================ + // Changing the physical property of the linkset only needs to change the root + public override void SetPhysicalFriction(float friction) + { + if (LinksetRoot.PhysBody.HasPhysicalBody) + m_physicsScene.PE.SetFriction(LinksetRoot.PhysBody, friction); + } + public override void SetPhysicalRestitution(float restitution) + { + if (LinksetRoot.PhysBody.HasPhysicalBody) + m_physicsScene.PE.SetRestitution(LinksetRoot.PhysBody, restitution); + } + public override void SetPhysicalGravity(OMV.Vector3 gravity) + { + if (LinksetRoot.PhysBody.HasPhysicalBody) + m_physicsScene.PE.SetGravity(LinksetRoot.PhysBody, gravity); + } + public override void ComputeLocalInertia(OMV.Vector3 inertiaFactor) + { + OMV.Vector3 inertia = m_physicsScene.PE.CalculateLocalInertia(LinksetRoot.PhysShape.physShapeInfo, LinksetRoot.Mass); + LinksetRoot.Inertia = inertia * inertiaFactor; + m_physicsScene.PE.SetMassProps(LinksetRoot.PhysBody, LinksetRoot.Mass, LinksetRoot.Inertia); + m_physicsScene.PE.UpdateInertiaTensor(LinksetRoot.PhysBody); + } + public override void SetPhysicalCollisionFlags(CollisionFlags collFlags) + { + if (LinksetRoot.PhysBody.HasPhysicalBody) + m_physicsScene.PE.SetCollisionFlags(LinksetRoot.PhysBody, collFlags); + } + public override void RemoveFromPhysicalCollisionFlags(CollisionFlags collFlags) + { + if (LinksetRoot.PhysBody.HasPhysicalBody) + m_physicsScene.PE.RemoveFromCollisionFlags(LinksetRoot.PhysBody, collFlags); + } + // ================================================================ + // When physical properties are changed the linkset needs to recalculate // its internal properties. public override void Refresh(BSPrimLinkable requestor) @@ -59,6 +95,7 @@ public sealed class BSLinksetCompound : BSLinkset { DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1},hasChildren={2},actuallyScheduling={3}", requestor.LocalID, Rebuilding, HasAnyChildren, (!Rebuilding && HasAnyChildren)); + // When rebuilding, it is possible to set properties that would normally require a rebuild. // If already rebuilding, don't request another rebuild. // If a linkset with just a root prim (simple non-linked prim) don't bother rebuilding. diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs index a06a44d33f..f17d698462 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs @@ -48,12 +48,22 @@ public sealed class BSLinksetConstraints : BSLinkset { base.Refresh(requestor); - if (HasAnyChildren && IsRoot(requestor)) + } + + private void ScheduleRebuild(BSPrimLinkable requestor) + { + DetailLog("{0},BSLinksetConstraint.ScheduleRebuild,,rebuilding={1},hasChildren={2},actuallyScheduling={3}", + requestor.LocalID, Rebuilding, HasAnyChildren, (!Rebuilding && HasAnyChildren)); + + // When rebuilding, it is possible to set properties that would normally require a rebuild. + // If already rebuilding, don't request another rebuild. + // If a linkset with just a root prim (simple non-linked prim) don't bother rebuilding. + if (!Rebuilding && HasAnyChildren) { // Queue to happen after all the other taint processing m_physicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate() { - if (HasAnyChildren && IsRoot(requestor)) + if (HasAnyChildren) RecomputeLinksetConstraints(); }); } @@ -67,8 +77,14 @@ public sealed class BSLinksetConstraints : BSLinkset // Called at taint-time! public override bool MakeDynamic(BSPrimLinkable child) { - // What is done for each object in BSPrim is what we want. - return false; + bool ret = false; + DetailLog("{0},BSLinksetConstraints.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child)); + if (IsRoot(child)) + { + // The root is going dynamic. Rebuild the linkset so parts and mass get computed properly. + ScheduleRebuild(LinksetRoot); + } + return ret; } // The object is going static (non-physical). Do any setup necessary for a static linkset. @@ -78,8 +94,16 @@ public sealed class BSLinksetConstraints : BSLinkset // Called at taint-time! public override bool MakeStatic(BSPrimLinkable child) { - // What is done for each object in BSPrim is what we want. - return false; + bool ret = false; + + DetailLog("{0},BSLinksetConstraint.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child)); + child.ClearDisplacement(); + if (IsRoot(child)) + { + // Schedule a rebuild to verify that the root shape is set to the real shape. + ScheduleRebuild(LinksetRoot); + } + return ret; } // Called at taint-time!! @@ -105,7 +129,7 @@ public sealed class BSLinksetConstraints : BSLinkset // Just undo all the constraints for this linkset. Rebuild at the end of the step. ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot); // Cause the constraints, et al to be rebuilt before the next simulation step. - Refresh(LinksetRoot); + ScheduleRebuild(LinksetRoot); } return ret; } @@ -123,7 +147,7 @@ public sealed class BSLinksetConstraints : BSLinkset DetailLog("{0},BSLinksetConstraints.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); // Cause constraints and assorted properties to be recomputed before the next simulation step. - Refresh(LinksetRoot); + ScheduleRebuild(LinksetRoot); } return; } @@ -147,7 +171,7 @@ public sealed class BSLinksetConstraints : BSLinkset PhysicallyUnlinkAChildFromRoot(rootx, childx); }); // See that the linkset parameters are recomputed at the end of the taint time. - Refresh(LinksetRoot); + ScheduleRebuild(LinksetRoot); } else { @@ -165,6 +189,7 @@ public sealed class BSLinksetConstraints : BSLinkset Refresh(rootPrim); } + // Create a static constraint between the two passed objects private BSConstraint BuildConstraint(BSPrimLinkable rootPrim, BSPrimLinkable childPrim) { // Zero motion for children so they don't interpolate @@ -281,24 +306,39 @@ public sealed class BSLinksetConstraints : BSLinkset DetailLog("{0},BSLinksetConstraint.RecomputeLinksetConstraints,set,rBody={1},linksetMass={2}", LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString, linksetMass); - foreach (BSPrimLinkable child in m_children) + try { - // A child in the linkset physically shows the mass of the whole linkset. - // This allows Bullet to apply enough force on the child to move the whole linkset. - // (Also do the mass stuff before recomputing the constraint so mass is not zero.) - child.UpdatePhysicalMassProperties(linksetMass, true); + Rebuilding = true; - BSConstraint constrain; - if (!m_physicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, child.PhysBody, out constrain)) + // There is no reason to build all this physical stuff for a non-physical linkset. + if (!LinksetRoot.IsPhysicallyActive) { - // If constraint doesn't exist yet, create it. - constrain = BuildConstraint(LinksetRoot, child); + DetailLog("{0},BSLinksetConstraint.RecomputeLinksetCompound,notPhysical", LinksetRoot.LocalID); + return; // Note the 'finally' clause at the botton which will get executed. } - constrain.RecomputeConstraintVariables(linksetMass); - // PhysicsScene.PE.DumpConstraint(PhysicsScene.World, constrain.Constraint); // DEBUG DEBUG + foreach (BSPrimLinkable child in m_children) + { + // A child in the linkset physically shows the mass of the whole linkset. + // This allows Bullet to apply enough force on the child to move the whole linkset. + // (Also do the mass stuff before recomputing the constraint so mass is not zero.) + child.UpdatePhysicalMassProperties(linksetMass, true); + + BSConstraint constrain; + if (!m_physicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, child.PhysBody, out constrain)) + { + // If constraint doesn't exist yet, create it. + constrain = BuildConstraint(LinksetRoot, child); + } + constrain.RecomputeConstraintVariables(linksetMass); + + // PhysicsScene.PE.DumpConstraint(PhysicsScene.World, constrain.Constraint); // DEBUG DEBUG + } + } + finally + { + Rebuilding = false; } - } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs index dcf1e830ef..0bdb5f1031 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs @@ -461,8 +461,9 @@ public static class BSParam (s) => { return MaxAddForceMagnitude; }, (s,v) => { MaxAddForceMagnitude = v; MaxAddForceMagnitudeSquared = v * v; } ), // Density is passed around as 100kg/m3. This scales that to 1kg/m3. + // Reduce by power of 100 because Bullet doesn't seem to handle objects with large mass very well new ParameterDefn("DensityScaleFactor", "Conversion for simulator/viewer density (100kg/m3) to physical density (1kg/m3)", - 0.01f ), + 0.0001f ), new ParameterDefn("PID_D", "Derivitive factor for motion smoothing", 2200f ), @@ -607,8 +608,9 @@ public static class BSParam 0.0f ), new ParameterDefn("VehicleRestitution", "Bouncyness factor for vehicles (0.0 - 1.0)", 0.0f ), + // Turn off fudge with DensityScaleFactor = 0.0001. Value used to be 0.2f; new ParameterDefn("VehicleGroundGravityFudge", "Factor to multiply gravity if a ground vehicle is probably on the ground (0.0 - 1.0)", - 0.2f ), + 1.0f ), new ParameterDefn("VehicleAngularBankingTimescaleFudge", "Factor to multiple angular banking timescale. Tune to increase realism.", 60.0f ), new ParameterDefn("VehicleEnableLinearDeflection", "Turn on/off vehicle linear deflection effect", @@ -701,7 +703,7 @@ public static class BSParam new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)", (float)BSLinkset.LinksetImplementation.Compound ), new ParameterDefn("LinksetOffsetCenterOfMass", "If 'true', compute linkset center-of-mass and offset linkset position to account for same", - false ), + true ), new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.", false ), new ParameterDefn("LinkConstraintEnableTransMotor", "Whether to enable translational motor on linkset constraints", diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index fc4545f4e3..d34b7975f3 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -353,6 +353,16 @@ public abstract class BSPhysObject : PhysicsActor CollidingStep = BSScene.NotASimulationStep; } } + // Complex objects (like linksets) need to know if there is a collision on any part of + // their shape. 'IsColliding' has an existing definition of reporting a collision on + // only this specific prim or component of linksets. + // 'HasSomeCollision' is defined as reporting if there is a collision on any part of + // the complex body that this prim is the root of. + public virtual bool HasSomeCollision + { + get { return IsColliding; } + set { IsColliding = value; } + } public override bool CollidingGround { get { return (CollidingGroundStep == PhysScene.SimulationStep); } set @@ -386,6 +396,7 @@ public abstract class BSPhysObject : PhysicsActor // Return 'true' if a collision was processed and should be sent up. // Return 'false' if this object is not enabled/subscribed/appropriate for or has already seen this collision. // Called at taint time from within the Step() function + public delegate bool CollideCall(uint collidingWith, BSPhysObject collidee, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth); public virtual bool Collide(uint collidingWith, BSPhysObject collidee, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) { diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index d43448e50d..e92a1d2bc5 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -96,7 +96,7 @@ public class BSPrim : BSPhysObject _isVolumeDetect = false; // Add a dynamic vehicle to our set of actors that can move this prim. - PhysicalActors.Add(VehicleActorName, new BSDynamics(PhysScene, this, VehicleActorName)); + // PhysicalActors.Add(VehicleActorName, new BSDynamics(PhysScene, this, VehicleActorName)); _mass = CalculateMass(); @@ -440,8 +440,8 @@ public class BSPrim : BSPhysObject Gravity = ComputeGravity(Buoyancy); PhysScene.PE.SetGravity(PhysBody, Gravity); - OMV.Vector3 currentScale = PhysScene.PE.GetLocalScaling(PhysShape.physShapeInfo); // DEBUG DEBUG - DetailLog("{0},BSPrim.UpdateMassProperties,currentScale{1},shape={2}", LocalID, currentScale, PhysShape.physShapeInfo); // DEBUG DEBUG + // OMV.Vector3 currentScale = PhysScene.PE.GetLocalScaling(PhysShape.physShapeInfo); // DEBUG DEBUG + // DetailLog("{0},BSPrim.UpdateMassProperties,currentScale{1},shape={2}", LocalID, currentScale, PhysShape.physShapeInfo); // DEBUG DEBUG Inertia = PhysScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass); PhysScene.PE.SetMassProps(PhysBody, physMass, Inertia); @@ -495,9 +495,9 @@ public class BSPrim : BSPhysObject } } - // Find and return a handle to the current vehicle actor. - // Return 'null' if there is no vehicle actor. - public BSDynamics GetVehicleActor() + // Find and return a handle to the current vehicle actor. + // Return 'null' if there is no vehicle actor. + public BSDynamics GetVehicleActor(bool createIfNone) { BSDynamics ret = null; BSActor actor; @@ -505,12 +505,21 @@ public class BSPrim : BSPhysObject { ret = actor as BSDynamics; } + else + { + if (createIfNone) + { + ret = new BSDynamics(PhysScene, this, VehicleActorName); + PhysicalActors.Add(ret.ActorName, ret); + } + } return ret; } + public override int VehicleType { get { int ret = (int)Vehicle.TYPE_NONE; - BSDynamics vehicleActor = GetVehicleActor(); + BSDynamics vehicleActor = GetVehicleActor(false /* createIfNone */); if (vehicleActor != null) ret = (int)vehicleActor.Type; return ret; @@ -524,11 +533,24 @@ public class BSPrim : BSPhysObject // change all the parameters. Like a plane changing to CAR when on the // ground. In this case, don't want to zero motion. // ZeroMotion(true /* inTaintTime */); - BSDynamics vehicleActor = GetVehicleActor(); - if (vehicleActor != null) + if (type == Vehicle.TYPE_NONE) { - vehicleActor.ProcessTypeChange(type); - ActivateIfPhysical(false); + // Vehicle type is 'none' so get rid of any actor that may have been allocated. + BSDynamics vehicleActor = GetVehicleActor(false /* createIfNone */); + if (vehicleActor != null) + { + PhysicalActors.RemoveAndRelease(vehicleActor.ActorName); + } + } + else + { + // Vehicle type is not 'none' so create an actor and set it running. + BSDynamics vehicleActor = GetVehicleActor(true /* createIfNone */); + if (vehicleActor != null) + { + vehicleActor.ProcessTypeChange(type); + ActivateIfPhysical(false); + } } }); } @@ -537,7 +559,7 @@ public class BSPrim : BSPhysObject { PhysScene.TaintedObject("BSPrim.VehicleFloatParam", delegate() { - BSDynamics vehicleActor = GetVehicleActor(); + BSDynamics vehicleActor = GetVehicleActor(true /* createIfNone */); if (vehicleActor != null) { vehicleActor.ProcessFloatVehicleParam((Vehicle)param, value); @@ -549,7 +571,7 @@ public class BSPrim : BSPhysObject { PhysScene.TaintedObject("BSPrim.VehicleVectorParam", delegate() { - BSDynamics vehicleActor = GetVehicleActor(); + BSDynamics vehicleActor = GetVehicleActor(true /* createIfNone */); if (vehicleActor != null) { vehicleActor.ProcessVectorVehicleParam((Vehicle)param, value); @@ -561,7 +583,7 @@ public class BSPrim : BSPhysObject { PhysScene.TaintedObject("BSPrim.VehicleRotationParam", delegate() { - BSDynamics vehicleActor = GetVehicleActor(); + BSDynamics vehicleActor = GetVehicleActor(true /* createIfNone */); if (vehicleActor != null) { vehicleActor.ProcessRotationVehicleParam((Vehicle)param, rotation); @@ -573,7 +595,7 @@ public class BSPrim : BSPhysObject { PhysScene.TaintedObject("BSPrim.VehicleFlags", delegate() { - BSDynamics vehicleActor = GetVehicleActor(); + BSDynamics vehicleActor = GetVehicleActor(true /* createIfNone */); if (vehicleActor != null) { vehicleActor.ProcessVehicleFlags(param, remove); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs index 1fbcfcc6f0..2f392da971 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs @@ -200,20 +200,38 @@ public class BSPrimLinkable : BSPrimDisplaced } // Called after a simulation step to post a collision with this object. + // This returns 'true' if the collision has been queued and the SendCollisions call must + // be made at the end of the simulation step. public override bool Collide(uint collidingWith, BSPhysObject collidee, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) { - // prims in the same linkset cannot collide with each other - BSPrimLinkable convCollidee = collidee as BSPrimLinkable; - if (convCollidee != null && (this.Linkset.LinksetID == convCollidee.Linkset.LinksetID)) + bool ret = false; + // Ask the linkset if it wants to handle the collision + if (!Linkset.HandleCollide(collidingWith, collidee, contactPoint, contactNormal, pentrationDepth)) { - return false; + // The linkset didn't handle it so pass the collision through normal processing + ret = base.Collide(collidingWith, collidee, contactPoint, contactNormal, pentrationDepth); } + return ret; + } - // TODO: handle collisions of other objects with with children of linkset. - // This is a problem for LinksetCompound since the children are packed into the root. + // A linkset reports any collision on any part of the linkset. + public long SomeCollisionSimulationStep = 0; + public override bool HasSomeCollision + { + get + { + return (SomeCollisionSimulationStep == PhysScene.SimulationStep) || base.IsColliding; + } + set + { + if (value) + SomeCollisionSimulationStep = PhysScene.SimulationStep; + else + SomeCollisionSimulationStep = 0; - return base.Collide(collidingWith, collidee, contactPoint, contactNormal, pentrationDepth); + base.HasSomeCollision = value; + } } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/Tests/BasicVehicles.cs b/OpenSim/Region/Physics/BulletSPlugin/Tests/BasicVehicles.cs index 48d37428da..48e74eb9d4 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/Tests/BasicVehicles.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/Tests/BasicVehicles.cs @@ -116,7 +116,7 @@ public class BasicVehicles : OpenSimTestCase // Instead the appropriate values are set and calls are made just the parts of the // controller we want to exercise. Stepping the physics engine then applies // the actions of that one feature. - BSDynamics vehicleActor = TestVehicle.GetVehicleActor(); + BSDynamics vehicleActor = TestVehicle.GetVehicleActor(true /* createIfNone */); if (vehicleActor != null) { vehicleActor.ProcessFloatVehicleParam(Vehicle.VERTICAL_ATTRACTION_EFFICIENCY, efficiency); diff --git a/OpenSim/Region/Physics/Manager/PhysicsActor.cs b/OpenSim/Region/Physics/Manager/PhysicsActor.cs index 7cd364b248..739e9848ca 100644 --- a/OpenSim/Region/Physics/Manager/PhysicsActor.cs +++ b/OpenSim/Region/Physics/Manager/PhysicsActor.cs @@ -415,6 +415,11 @@ namespace OpenSim.Region.Physics.Manager public virtual PhysicsActor ParentActor { get { return this; } } + // Extendable interface for new, physics engine specific operations + public virtual object Extension(string pFunct, params object[] pParams) + { + throw new NotImplementedException(); + } } public class NullPhysicsActor : PhysicsActor diff --git a/OpenSim/Region/Physics/Manager/PhysicsScene.cs b/OpenSim/Region/Physics/Manager/PhysicsScene.cs index 20a70b41e5..52f2809fc0 100644 --- a/OpenSim/Region/Physics/Manager/PhysicsScene.cs +++ b/OpenSim/Region/Physics/Manager/PhysicsScene.cs @@ -25,10 +25,13 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +using System; using System.Collections.Generic; using System.Reflection; + using log4net; using Nini.Config; + using OpenSim.Framework; using OpenMetaverse; @@ -386,5 +389,11 @@ namespace OpenSim.Region.Physics.Manager { return 0; } + + // Extendable interface for new, physics engine specific operations + public virtual object Extension(string pFunct, params object[] pParams) + { + throw new NotImplementedException(); + } } } diff --git a/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs b/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs index 2567c8f799..0601ece9ad 100644 --- a/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs +++ b/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs @@ -493,6 +493,22 @@ namespace OpenSim.Services.HypergridService return null; } + private XInventoryFolder GetCurrentOutfitXFolder(UUID userID) + { + XInventoryFolder root = GetRootXFolder(userID); + if (root == null) + return null; + + XInventoryFolder[] folders = m_Database.GetFolders( + new string[] { "agentID", "type", "parentFolderID" }, + new string[] { userID.ToString(), ((int)AssetType.CurrentOutfitFolder).ToString(), root.folderID.ToString() }); + + if (folders.Length == 0) + return null; + + return folders[0]; + } + private XInventoryFolder GetSuitcaseXFolder(UUID principalID) { // Warp! Root folder for travelers @@ -531,6 +547,7 @@ namespace OpenSim.Services.HypergridService if (m_SuitcaseTrees.TryGetValue(principalID, out t)) return t; + // Get the tree of the suitcase folder t = GetFolderTreeRecursive(folder); m_SuitcaseTrees.AddOrUpdate(principalID, t, 5*60); // 5minutes return t; @@ -577,6 +594,9 @@ namespace OpenSim.Services.HypergridService List tree = new List(); tree.Add(suitcase); // Warp! the tree is the real root folder plus the children of the suitcase folder tree.AddRange(GetFolderTree(principalID, suitcase.folderID)); + // Also add the Current Outfit folder to the list of available folders + tree.Add(GetCurrentOutfitXFolder(principalID)); + XInventoryFolder f = tree.Find(delegate(XInventoryFolder fl) { if (fl.folderID == folderID) return true; diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index 6528d9ad37..df8cf2752e 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -107,6 +107,7 @@ namespace OpenSim.Tests.Common.Mock public event Action OnCompleteMovementToRegion; public event UpdateAgent OnPreAgentUpdate; public event UpdateAgent OnAgentUpdate; + public event UpdateAgent OnAgentCameraUpdate; public event AgentRequestSit OnAgentRequestSit; public event AgentSit OnAgentSit; public event AvatarPickerRequest OnAvatarPickerRequest; @@ -1266,7 +1267,7 @@ namespace OpenSim.Tests.Common.Mock { } - public void StopFlying(ISceneEntity presence) + public void SendAgentTerseUpdate(ISceneEntity presence) { }