From 3a04d706c9ebd13214e62fb944b4a8da6860bb91 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Thu, 8 Oct 2009 17:34:51 -0700 Subject: Fear the lockless LLUDP implementation! --- .../ClientStack/LindenUDP/LLUDPClientCollection.cs | 252 +++++---------------- ThirdPartyLicenses/BclExtras.txt | 60 +++++ ThirdPartyLicenses/CSJ2K.txt | 28 +++ bin/BclExtras.dll | Bin 0 -> 178688 bytes prebuild.xml | 1 + 5 files changed, 149 insertions(+), 192 deletions(-) create mode 100644 ThirdPartyLicenses/BclExtras.txt create mode 100644 ThirdPartyLicenses/CSJ2K.txt create mode 100644 bin/BclExtras.dll diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs index dbb9861..06fa3e2 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs @@ -30,6 +30,7 @@ using System.Collections.Generic; using System.Net; using OpenSim.Framework; using OpenMetaverse; +using BclExtras.Collections; using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim; @@ -37,246 +38,113 @@ namespace OpenSim.Region.ClientStack.LindenUDP { public sealed class UDPClientCollection { - Dictionary Dictionary1; - Dictionary Dictionary2; - LLUDPClient[] Array; - ReaderWriterLockImpl rwLock = new ReaderWriterLockImpl(); - object m_sync = new object(); + #region IComparers - public UDPClientCollection() - { - Dictionary1 = new Dictionary(); - Dictionary2 = new Dictionary(); - Array = new LLUDPClient[0]; - } - - public UDPClientCollection(int capacity) + private sealed class UUIDComparer : IComparer { - Dictionary1 = new Dictionary(capacity); - Dictionary2 = new Dictionary(capacity); - Array = new LLUDPClient[0]; + public int Compare(UUID x, UUID y) + { + return x.Guid.CompareTo(y.Guid); + } } - public void Add(UUID key1, IPEndPoint key2, LLUDPClient value) + private sealed class IPEndPointComparer : IComparer { - //rwLock.EnterWriteLock(); - - //try - //{ - // if (Dictionary1.ContainsKey(key1)) - // { - // if (!Dictionary2.ContainsKey(key2)) - // throw new ArgumentException("key1 exists in the dictionary but not key2"); - // } - // else if (Dictionary2.ContainsKey(key2)) - // { - // if (!Dictionary1.ContainsKey(key1)) - // throw new ArgumentException("key2 exists in the dictionary but not key1"); - // } - - // Dictionary1[key1] = value; - // Dictionary2[key2] = value; - - // LLUDPClient[] oldArray = Array; - // int oldLength = oldArray.Length; - - // LLUDPClient[] newArray = new LLUDPClient[oldLength + 1]; - // for (int i = 0; i < oldLength; i++) - // newArray[i] = oldArray[i]; - // newArray[oldLength] = value; - - // Array = newArray; - //} - //finally { rwLock.ExitWriteLock(); } - - lock (m_sync) + public int Compare(IPEndPoint x, IPEndPoint y) { - if (Dictionary1.ContainsKey(key1)) - { - if (!Dictionary2.ContainsKey(key2)) - throw new ArgumentException("key1 exists in the dictionary but not key2"); - } - else if (Dictionary2.ContainsKey(key2)) - { - if (!Dictionary1.ContainsKey(key1)) - throw new ArgumentException("key2 exists in the dictionary but not key1"); - } - - Dictionary1[key1] = value; - Dictionary2[key2] = value; - - LLUDPClient[] oldArray = Array; - int oldLength = oldArray.Length; + int result = x.Address.Address.CompareTo(y.Address.Address); + if (result == 0) result = x.Port.CompareTo(y.Port); + return result; + } + } - LLUDPClient[] newArray = new LLUDPClient[oldLength + 1]; - for (int i = 0; i < oldLength; i++) - newArray[i] = oldArray[i]; - newArray[oldLength] = value; + #endregion IComparers - Array = newArray; - } + private ImmutableMap m_dict1; + private ImmutableMap m_dict2; + private LLUDPClient[] m_array; + public UDPClientCollection() + { + m_dict1 = new ImmutableMap(new UUIDComparer()); + m_dict2 = new ImmutableMap(new IPEndPointComparer()); + m_array = new LLUDPClient[0]; } - public bool Remove(UUID key1, IPEndPoint key2) + public void Add(UUID key1, IPEndPoint key2, LLUDPClient value) { - //rwLock.EnterWriteLock(); - - //try - //{ - // LLUDPClient value; - // if (Dictionary1.TryGetValue(key1, out value)) - // { - // Dictionary1.Remove(key1); - // Dictionary2.Remove(key2); + m_dict1 = m_dict1.Add(key1, value); + m_dict2 = m_dict2.Add(key2, value); + + // Copy the array by hand + LLUDPClient[] oldArray = m_array; + int oldLength = oldArray.Length; + LLUDPClient[] newArray = new LLUDPClient[oldLength + 1]; + + for (int i = 0; i < oldLength; i++) + newArray[i] = oldArray[i]; + newArray[oldLength] = value; + + m_array = newArray; + } - // LLUDPClient[] oldArray = Array; - // int oldLength = oldArray.Length; + public void Remove(UUID key1, IPEndPoint key2) + { + m_dict1 = m_dict1.Delete(key1); + m_dict2 = m_dict2.Delete(key2); - // LLUDPClient[] newArray = new LLUDPClient[oldLength - 1]; - // int j = 0; - // for (int i = 0; i < oldLength; i++) - // { - // if (oldArray[i] != value) - // newArray[j++] = oldArray[i]; - // } + LLUDPClient[] oldArray = m_array; + int oldLength = oldArray.Length; - // Array = newArray; - // return true; - // } - //} - //finally { rwLock.ExitWriteLock(); } + // Copy the array by hand - //return false; + LLUDPClient[] newArray = new LLUDPClient[oldLength - 1]; + int j = 0; - lock (m_sync) + for (int i = 0; i < oldLength; i++) { - LLUDPClient value; - if (Dictionary1.TryGetValue(key1, out value)) - { - Dictionary1.Remove(key1); - Dictionary2.Remove(key2); - - LLUDPClient[] oldArray = Array; - int oldLength = oldArray.Length; - - LLUDPClient[] newArray = new LLUDPClient[oldLength - 1]; - int j = 0; - for (int i = 0; i < oldLength; i++) - { - if (oldArray[i] != value) - newArray[j++] = oldArray[i]; - } - - Array = newArray; - return true; - } + if (oldArray[i].AgentID != key1) + newArray[j++] = oldArray[i]; } - return false; - + m_array = newArray; } public void Clear() { - //rwLock.EnterWriteLock(); - - //try - //{ - // Dictionary1.Clear(); - // Dictionary2.Clear(); - // Array = new LLUDPClient[0]; - //} - //finally { rwLock.ExitWriteLock(); } - - lock (m_sync) - { - Dictionary1.Clear(); - Dictionary2.Clear(); - Array = new LLUDPClient[0]; - } - + m_dict1 = new ImmutableMap(new UUIDComparer()); + m_dict2 = new ImmutableMap(new IPEndPointComparer()); + m_array = new LLUDPClient[0]; } public int Count { - get { return Array.Length; } + get { return m_array.Length; } } public bool ContainsKey(UUID key) { - return Dictionary1.ContainsKey(key); + return m_dict1.ContainsKey(key); } public bool ContainsKey(IPEndPoint key) { - return Dictionary2.ContainsKey(key); + return m_dict2.ContainsKey(key); } public bool TryGetValue(UUID key, out LLUDPClient value) { - ////bool success; - ////bool doLock = !rwLock.IsUpgradeableReadLockHeld; - ////if (doLock) rwLock.EnterReadLock(); - - ////try { success = Dictionary1.TryGetValue(key, out value); } - ////finally { if (doLock) rwLock.ExitReadLock(); } - - ////return success; - - lock (m_sync) - return Dictionary1.TryGetValue(key, out value); - - //try - //{ - // return Dictionary1.TryGetValue(key, out value); - //} - //catch { } - //value = null; - //return false; + return m_dict1.TryGetValue(key, out value); } public bool TryGetValue(IPEndPoint key, out LLUDPClient value) { - ////bool success; - ////bool doLock = !rwLock.IsUpgradeableReadLockHeld; - ////if (doLock) rwLock.EnterReadLock(); - - ////try { success = Dictionary2.TryGetValue(key, out value); } - ////finally { if (doLock) rwLock.ExitReadLock(); } - - ////return success; - - lock (m_sync) - return Dictionary2.TryGetValue(key, out value); - - //try - //{ - // return Dictionary2.TryGetValue(key, out value); - //} - //catch { } - //value = null; - //return false; - + return m_dict2.TryGetValue(key, out value); } public void ForEach(Action action) { - //bool doLock = !rwLock.IsUpgradeableReadLockHeld; - //if (doLock) rwLock.EnterUpgradeableReadLock(); - - //try { Parallel.ForEach(Array, action); } - //finally { if (doLock) rwLock.ExitUpgradeableReadLock(); } - - LLUDPClient[] localArray = null; - lock (m_sync) - { - localArray = new LLUDPClient[Array.Length]; - Array.CopyTo(localArray, 0); - } - - Parallel.ForEach(localArray, action); - + Parallel.ForEach(m_array, action); } } } diff --git a/ThirdPartyLicenses/BclExtras.txt b/ThirdPartyLicenses/BclExtras.txt new file mode 100644 index 0000000..a8a0292 --- /dev/null +++ b/ThirdPartyLicenses/BclExtras.txt @@ -0,0 +1,60 @@ +MICROSOFT PUBLIC LICENSE (Ms-PL) + +This license governs use of the accompanying software. If you use the software, +you accept this license. If you do not accept the license, do not use the +software. + +1. Definitions + +The terms "reproduce," "reproduction," "derivative works," and "distribution" +have the same meaning here as under U.S. copyright law. + +A "contribution" is the original software, or any additions or changes to the +software. + +A "contributor" is any person that distributes its contribution under this +license. + +"Licensed patents" are a contributor's patent claims that read directly on its +contribution. + +2. Grant of Rights + +(A) Copyright Grant- Subject to the terms of this license, including the license +conditions and limitations in section 3, each contributor grants you a +non-exclusive, worldwide, royalty-free copyright license to reproduce its +contribution, prepare derivative works of its contribution, and distribute its +contribution or any derivative works that you create. + +(B) Patent Grant- Subject to the terms of this license, including the license +conditions and limitations in section 3, each contributor grants you a +non-exclusive, worldwide, royalty-free license under its licensed patents to +make, have made, use, sell, offer for sale, import, and/or otherwise dispose of +its contribution in the software or derivative works of the contribution in the +software. + +3. Conditions and Limitations + +(A) No Trademark License- This license does not grant you rights to use any +contributors' name, logo, or trademarks. + +(B) If you bring a patent claim against any contributor over patents that you +claim are infringed by the software, your patent license from such contributor +to the software ends automatically. + +(C) If you distribute any portion of the software, you must retain all +copyright, patent, trademark, and attribution notices that are present in the +software. + +(D) If you distribute any portion of the software in source code form, you may +do so only under this license by including a complete copy of this license with +your distribution. If you distribute any portion of the software in compiled or +object code form, you may only do so under a license that complies with this +license. + +(E) The software is licensed "as-is." You bear the risk of using it. The +contributors give no express warranties, guarantees or conditions. You may have +additional consumer rights under your local laws which this license cannot +change. To the extent permitted under your local laws, the contributors exclude +the implied warranties of merchantability, fitness for a particular purpose and +non-infringement. diff --git a/ThirdPartyLicenses/CSJ2K.txt b/ThirdPartyLicenses/CSJ2K.txt new file mode 100644 index 0000000..3032548 --- /dev/null +++ b/ThirdPartyLicenses/CSJ2K.txt @@ -0,0 +1,28 @@ +Copyright (c) 1999/2000 JJ2000 Partners. + +This software module was originally developed by Raphaël Grosbois and +Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel +Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David +Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research +Centre France S.A) in the course of development of the JPEG2000 +standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This +software module is an implementation of a part of the JPEG 2000 +Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio +Systems AB and Canon Research Centre France S.A (collectively JJ2000 +Partners) agree not to assert against ISO/IEC and users of the JPEG +2000 Standard (Users) any of their rights under the copyright, not +including other intellectual property rights, for this software module +with respect to the usage by ISO/IEC and Users of this software module +or modifications thereof for use in hardware or software products +claiming conformance to the JPEG 2000 Standard. Those intending to use +this software module in hardware or software products are advised that +their use may infringe existing patents. The original developers of +this software module, JJ2000 Partners and ISO/IEC assume no liability +for use of this software module or modifications thereof. No license +or right to this software module is granted for non JPEG 2000 Standard +conforming products. JJ2000 Partners have full right to use this +software module for his/her own purpose, assign or donate this +software module to any third party and to inhibit third parties from +using this software module for non JPEG 2000 Standard conforming +products. This copyright notice must be included in all copies or +derivative works of this software module. diff --git a/bin/BclExtras.dll b/bin/BclExtras.dll new file mode 100644 index 0000000..505cf01 Binary files /dev/null and b/bin/BclExtras.dll differ diff --git a/prebuild.xml b/prebuild.xml index bba54f3..028e5e7 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -1767,6 +1767,7 @@ + -- cgit v1.1 From 56a27c37d3e84495988e423be7b52007cea595cc Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Thu, 8 Oct 2009 21:51:53 -0700 Subject: Simplified LLUDPClientCollection from three collections down to one. This will prevent any potential problems from inconsistency between the internal collections --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 3 +- .../ClientStack/LindenUDP/LLUDPClientCollection.cs | 75 ++++------------------ .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 46 +++++-------- 3 files changed, 29 insertions(+), 95 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 84e705a..139dc3b 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -119,6 +119,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Properties + public LLUDPClient UDPClient { get { return m_udpClient; } } public UUID SecureSessionId { get { return m_secureSessionId; } } public IScene Scene { get { return m_scene; } } public UUID SessionId { get { return m_sessionId; } } @@ -504,7 +505,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP + "Any further actions taken will not be processed.\n" + "Please relog", true); - m_udpServer.SendPacket(m_agentId, packet, ThrottleOutPacketType.Unknown, false); + OutPacket(packet, ThrottleOutPacketType.Unknown); // There may be a better way to do this. Perhaps kick? Not sure this propogates notifications to // listeners yet, though. diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs index 06fa3e2..abf3882 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs @@ -40,14 +40,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP { #region IComparers - private sealed class UUIDComparer : IComparer - { - public int Compare(UUID x, UUID y) - { - return x.Guid.CompareTo(y.Guid); - } - } - private sealed class IPEndPointComparer : IComparer { public int Compare(IPEndPoint x, IPEndPoint y) @@ -60,91 +52,46 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion IComparers - private ImmutableMap m_dict1; - private ImmutableMap m_dict2; - private LLUDPClient[] m_array; + private ImmutableMap m_dict; public UDPClientCollection() { - m_dict1 = new ImmutableMap(new UUIDComparer()); - m_dict2 = new ImmutableMap(new IPEndPointComparer()); - m_array = new LLUDPClient[0]; + m_dict = new ImmutableMap(new IPEndPointComparer()); } - public void Add(UUID key1, IPEndPoint key2, LLUDPClient value) + public void Add(IPEndPoint key, LLUDPClient value) { - m_dict1 = m_dict1.Add(key1, value); - m_dict2 = m_dict2.Add(key2, value); - - // Copy the array by hand - LLUDPClient[] oldArray = m_array; - int oldLength = oldArray.Length; - LLUDPClient[] newArray = new LLUDPClient[oldLength + 1]; - - for (int i = 0; i < oldLength; i++) - newArray[i] = oldArray[i]; - newArray[oldLength] = value; - - m_array = newArray; + m_dict = m_dict.Add(key, value); } - public void Remove(UUID key1, IPEndPoint key2) + public void Remove(IPEndPoint key) { - m_dict1 = m_dict1.Delete(key1); - m_dict2 = m_dict2.Delete(key2); - - LLUDPClient[] oldArray = m_array; - int oldLength = oldArray.Length; - - // Copy the array by hand - - LLUDPClient[] newArray = new LLUDPClient[oldLength - 1]; - int j = 0; - - for (int i = 0; i < oldLength; i++) - { - if (oldArray[i].AgentID != key1) - newArray[j++] = oldArray[i]; - } - - m_array = newArray; + m_dict = m_dict.Delete(key); } public void Clear() { - m_dict1 = new ImmutableMap(new UUIDComparer()); - m_dict2 = new ImmutableMap(new IPEndPointComparer()); - m_array = new LLUDPClient[0]; + m_dict = new ImmutableMap(new IPEndPointComparer()); } public int Count { - get { return m_array.Length; } - } - - public bool ContainsKey(UUID key) - { - return m_dict1.ContainsKey(key); + get { return m_dict.Count; } } public bool ContainsKey(IPEndPoint key) { - return m_dict2.ContainsKey(key); - } - - public bool TryGetValue(UUID key, out LLUDPClient value) - { - return m_dict1.TryGetValue(key, out value); + return m_dict.ContainsKey(key); } public bool TryGetValue(IPEndPoint key, out LLUDPClient value) { - return m_dict2.TryGetValue(key, out value); + return m_dict.TryGetValue(key, out value); } public void ForEach(Action action) { - Parallel.ForEach(m_array, action); + Parallel.ForEach(m_dict.Values, action); } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 1e6927f..a6aa048 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -181,22 +181,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP return x == m_location; } - public void RemoveClient(IClientAPI client) + public void RemoveClient(LLUDPClient udpClient) { - m_scene.ClientManager.Remove(client.CircuitCode); - client.Close(false); + m_log.Debug("[LLUDPSERVER]: Removing LLUDPClient for " + udpClient.ClientAPI.Name); - LLUDPClient udpClient; - if (clients.TryGetValue(client.AgentId, out udpClient)) - { - m_log.Debug("[LLUDPSERVER]: Removing LLUDPClient for " + client.Name); - udpClient.Shutdown(); - clients.Remove(client.AgentId, udpClient.RemoteEndPoint); - } - else - { - m_log.Warn("[LLUDPSERVER]: Failed to remove LLUDPClient for " + client.Name); - } + m_scene.ClientManager.Remove(udpClient.CircuitCode); + udpClient.ClientAPI.Close(false); + udpClient.Shutdown(); + clients.Remove(udpClient.RemoteEndPoint); } public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting) @@ -230,15 +222,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public void SendPacket(UUID agentID, Packet packet, ThrottleOutPacketType category, bool allowSplitting) - { - LLUDPClient client; - if (clients.TryGetValue(agentID, out client)) - SendPacket(client, packet, category, allowSplitting); - else - m_log.Warn("[LLUDPSERVER]: Attempted to send a packet to unknown agentID " + agentID); - } - public void SendPacket(LLUDPClient client, Packet packet, ThrottleOutPacketType category, bool allowSplitting) { // CoarseLocationUpdate packets cannot be split in an automated way @@ -391,7 +374,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + client.ClientAPI.Name); - RemoveClient(client.ClientAPI); + RemoveClient(client); return; } } @@ -647,23 +630,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void AddClient(uint circuitCode, UUID agentID, UUID sessionID, IPEndPoint remoteEndPoint, AuthenticateResponse sessionInfo) { // Create the LLUDPClient - LLUDPClient client = new LLUDPClient(this, m_throttleRates, m_throttle, circuitCode, agentID, remoteEndPoint); + LLUDPClient udpClient = new LLUDPClient(this, m_throttleRates, m_throttle, circuitCode, agentID, remoteEndPoint); // Create the LLClientView - LLClientView clientApi = new LLClientView(remoteEndPoint, m_scene, this, client, sessionInfo, agentID, sessionID, circuitCode); + LLClientView clientApi = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); clientApi.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler; clientApi.OnLogout += LogoutHandler; - clientApi.OnConnectionClosed += RemoveClient; + clientApi.OnConnectionClosed += + delegate(IClientAPI client) + { if (client is LLClientView) RemoveClient(((LLClientView)client).UDPClient); }; // Start the IClientAPI m_scene.ClientManager.Add(circuitCode, clientApi); clientApi.Start(); // Give LLUDPClient a reference to IClientAPI - client.ClientAPI = clientApi; + udpClient.ClientAPI = clientApi; // Add the new client to our list of tracked clients - clients.Add(agentID, client.RemoteEndPoint, client); + clients.Add(udpClient.RemoteEndPoint, udpClient); } private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend) @@ -798,7 +783,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void LogoutHandler(IClientAPI client) { client.SendLogoutPacket(); - RemoveClient(client); + if (client is LLClientView) + RemoveClient(((LLClientView)client).UDPClient); } } } -- cgit v1.1 From a5b9971fd77c0c4bf70656be7f3e7999f59d9f85 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 9 Oct 2009 01:53:06 -0700 Subject: * Added a lock object for the write functions in LLUDPClientCollection (immutable != concurrent write safety) * Allow the UDP server to bind to a user-specified port again * Updated to a newer version of OpenSimUDPBase that streamlines the code even more. This also reintroduces the highly concurrent packet handling which needs more testing --- .../ClientStack/LindenUDP/LLUDPClientCollection.cs | 56 +++- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 20 +- .../Region/ClientStack/LindenUDP/OpenSimUDPBase.cs | 303 ++++++++------------- 3 files changed, 169 insertions(+), 210 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs index abf3882..2222a33 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs @@ -36,6 +36,9 @@ using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim; namespace OpenSim.Region.ClientStack.LindenUDP { + /// + /// A thread safe mapping from endpoints to client references + /// public sealed class UDPClientCollection { #region IComparers @@ -52,43 +55,80 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion IComparers + /// An immutable dictionary mapping from + /// to references private ImmutableMap m_dict; + /// Immutability grants thread safety for concurrent reads and + /// read-writes, but not concurrent writes + private object m_writeLock; + /// Number of clients in the collection + public int Count { get { return m_dict.Count; } } + + /// + /// Default constructor + /// public UDPClientCollection() { m_dict = new ImmutableMap(new IPEndPointComparer()); } + /// + /// Add a client reference to the collection + /// + /// Remote endpoint of the client + /// Reference to the client object public void Add(IPEndPoint key, LLUDPClient value) { - m_dict = m_dict.Add(key, value); + lock (m_writeLock) + m_dict = m_dict.Add(key, value); } + /// + /// Remove a client from the collection + /// + /// Remote endpoint of the client public void Remove(IPEndPoint key) { - m_dict = m_dict.Delete(key); + lock (m_writeLock) + m_dict = m_dict.Delete(key); } + /// + /// Resets the client collection + /// public void Clear() { - m_dict = new ImmutableMap(new IPEndPointComparer()); - } - - public int Count - { - get { return m_dict.Count; } + lock (m_writeLock) + m_dict = new ImmutableMap(new IPEndPointComparer()); } + /// + /// Checks if an endpoint is in the collection + /// + /// Endpoint to check for + /// True if the endpoint was found in the collection, otherwise false public bool ContainsKey(IPEndPoint key) { return m_dict.ContainsKey(key); } + /// + /// Attempts to fetch a value out of the collection + /// + /// Endpoint of the client to retrieve + /// Retrieved client, or null on lookup failure + /// True if the lookup succeeded, otherwise false public bool TryGetValue(IPEndPoint key, out LLUDPClient value) { return m_dict.TryGetValue(key, out value); } + /// + /// Performs a given task in parallel for each of the elements in the + /// collection + /// + /// Action to perform on each element public void ForEach(Action action) { Parallel.ForEach(m_dict.Values, action); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index a6aa048..9aeea9a 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -96,7 +96,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Incoming packets that are awaiting handling private OpenMetaverse.BlockingQueue packetInbox = new OpenMetaverse.BlockingQueue(); /// - private UDPClientCollection clients = new UDPClientCollection(); + private UDPClientCollection m_clients = new UDPClientCollection(); /// Bandwidth throttle for this UDP server private TokenBucket m_throttle; /// Bandwidth throttle rates for this UDP server @@ -115,7 +115,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public Socket Server { get { return null; } } public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) - : base((int)port) + : base(listenIP, (int)port) { #region Environment.TickCount Measurement @@ -143,7 +143,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public new void Start() { if (m_scene == null) - throw new InvalidOperationException("Cannot LLUDPServer.Start() without an IScene reference"); + throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); base.Start(); @@ -188,7 +188,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_scene.ClientManager.Remove(udpClient.CircuitCode); udpClient.ClientAPI.Close(false); udpClient.Shutdown(); - clients.Remove(udpClient.RemoteEndPoint); + m_clients.Remove(udpClient.RemoteEndPoint); } public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting) @@ -208,7 +208,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < packetCount; i++) { byte[] data = datas[i]; - clients.ForEach( + m_clients.ForEach( delegate(LLUDPClient client) { SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); }); } @@ -216,7 +216,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP else { byte[] data = packet.ToBytes(); - clients.ForEach( + m_clients.ForEach( delegate(LLUDPClient client) { SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); }); } @@ -502,7 +502,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } // Determine which agent this packet came from - if (!clients.TryGetValue(address, out client)) + if (!m_clients.TryGetValue(address, out client)) { m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address); return; @@ -606,7 +606,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_scene.RegionStatus != RegionStatus.SlaveScene) { AuthenticateResponse sessionInfo; - bool isNewCircuit = !clients.ContainsKey(remoteEndPoint); + bool isNewCircuit = !m_clients.ContainsKey(remoteEndPoint); if (!IsClientAuthorized(useCircuitCode, out sessionInfo)) { @@ -648,7 +648,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP udpClient.ClientAPI = clientApi; // Add the new client to our list of tracked clients - clients.Add(udpClient.RemoteEndPoint, udpClient); + m_clients.Add(udpClient.RemoteEndPoint, udpClient); } private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend) @@ -726,7 +726,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP elapsed500MS = 0; } - clients.ForEach( + m_clients.ForEach( delegate(LLUDPClient client) { if (client.DequeueOutgoing()) diff --git a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs index 218aaac..9b1751d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs @@ -29,101 +29,90 @@ using System; using System.Net; using System.Net.Sockets; using System.Threading; -using OpenMetaverse; +using log4net; -namespace OpenSim.Region.ClientStack.LindenUDP +namespace OpenMetaverse { /// - /// + /// Base UDP server /// public abstract class OpenSimUDPBase { - // these abstract methods must be implemented in a derived class to actually do - // something with the packets that are sent and received. + private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// This method is called when an incoming packet is received + /// + /// Incoming packet buffer protected abstract void PacketReceived(UDPPacketBuffer buffer); + + /// + /// This method is called when an outgoing packet is sent + /// + /// Outgoing packet buffer + /// Number of bytes written to the wire protected abstract void PacketSent(UDPPacketBuffer buffer, int bytesSent); - // the port to listen on - internal int udpPort; - - // the UDP socket - private Socket udpSocket; + /// UDP port to bind to in server mode + protected int m_udpPort; - // the ReaderWriterLock is used solely for the purposes of shutdown (Stop()). - // since there are potentially many "reader" threads in the internal .NET IOCP - // thread pool, this is a cheaper synchronization primitive than using - // a Mutex object. This allows many UDP socket "reads" concurrently - when - // Stop() is called, it attempts to obtain a writer lock which will then - // wait until all outstanding operations are completed before shutting down. - // this avoids the problem of closing the socket with outstanding operations - // and trying to catch the inevitable ObjectDisposedException. - private ReaderWriterLock rwLock = new ReaderWriterLock(); + /// Local IP address to bind to in server mode + protected IPAddress m_localBindAddress; - // number of outstanding operations. This is a reference count - // which we use to ensure that the threads exit cleanly. Note that - // we need this because the threads will potentially still need to process - // data even after the socket is closed. - private int rwOperationCount = 0; + /// UDP socket, used in either client or server mode + private Socket m_udpSocket; - // the all important shutdownFlag. This is synchronized through the ReaderWriterLock. - private volatile bool shutdownFlag = true; - - // the remote endpoint to communicate with - protected IPEndPoint remoteEndPoint = null; + /// The all important shutdown flag + private volatile bool m_shutdownFlag = true; + /// Returns true if the server is currently listening, otherwise false + public bool IsRunning { get { return !m_shutdownFlag; } } /// - /// Initialize the UDP packet handler in server mode + /// Default constructor /// + /// Local IP address to bind the server to /// Port to listening for incoming UDP packets on - public OpenSimUDPBase(int port) - { - udpPort = port; - } - - /// - /// Initialize the UDP packet handler in client mode - /// - /// Remote UDP server to connect to - public OpenSimUDPBase(IPEndPoint endPoint) + public OpenSimUDPBase(IPAddress bindAddress, int port) { - remoteEndPoint = endPoint; - udpPort = 0; + m_localBindAddress = bindAddress; + m_udpPort = port; } /// - /// + /// Start the UDP server /// + /// This method will attempt to set the SIO_UDP_CONNRESET flag + /// on the socket to get newer versions of Windows to behave in a sane + /// manner (not throwing an exception when the remote side resets the + /// connection). This call is ignored on Mono where the flag is not + /// necessary public void Start() { - if (shutdownFlag) + if (m_shutdownFlag) { - if (remoteEndPoint == null) - { - // Server mode + const int SIO_UDP_CONNRESET = -1744830452; - // create and bind the socket - IPEndPoint ipep = new IPEndPoint(Settings.BIND_ADDR, udpPort); - udpSocket = new Socket( - AddressFamily.InterNetwork, - SocketType.Dgram, - ProtocolType.Udp); - udpSocket.Bind(ipep); + IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort); + m_udpSocket = new Socket( + AddressFamily.InterNetwork, + SocketType.Dgram, + ProtocolType.Udp); + try + { + // this udp socket flag is not supported under mono, + // so we'll catch the exception and continue + m_udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null); + m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag set"); } - else + catch (SocketException) { - // Client mode - IPEndPoint ipep = new IPEndPoint(Settings.BIND_ADDR, udpPort); - udpSocket = new Socket( - AddressFamily.InterNetwork, - SocketType.Dgram, - ProtocolType.Udp); - udpSocket.Bind(ipep); - //udpSocket.Connect(remoteEndPoint); + m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring"); } + m_udpSocket.Bind(ipep); // we're not shutting down, we're starting up - shutdownFlag = false; + m_shutdownFlag = false; // kick off an async receive. The Start() method will return, the // actual receives will occur asynchronously and will be caught in @@ -133,104 +122,85 @@ namespace OpenSim.Region.ClientStack.LindenUDP } /// - /// + /// Stops the UDP server /// public void Stop() { - if (!shutdownFlag) + if (!m_shutdownFlag) { // 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 shutdownFlag to inform the other // threads that the socket is closed. - rwLock.AcquireWriterLock(-1); - shutdownFlag = true; - udpSocket.Close(); - rwLock.ReleaseWriterLock(); - - // wait for any pending operations to complete on other - // threads before exiting. - const int FORCE_STOP = 100; - int i = 0; - while (rwOperationCount > 0 && i < FORCE_STOP) - { - Thread.Sleep(10); - ++i; - } - - if (i >= FORCE_STOP) - { - Logger.Log("UDPBase.Stop() forced shutdown while waiting on pending operations", - Helpers.LogLevel.Warning); - } + m_shutdownFlag = true; + m_udpSocket.Close(); } } - /// - /// - /// - public bool IsRunning - { - get { return !shutdownFlag; } - } - private void AsyncBeginReceive() { - // this method actually kicks off the async read on the socket. - // we aquire a reader lock here to ensure that no other thread - // is trying to set shutdownFlag and close the socket. - rwLock.AcquireReaderLock(-1); + // allocate a packet buffer + //WrappedObject wrappedBuffer = Pool.CheckOut(); + UDPPacketBuffer buf = new UDPPacketBuffer(); - if (!shutdownFlag) + if (!m_shutdownFlag) { - // increment the count of pending operations - Interlocked.Increment(ref rwOperationCount); - - // allocate a packet buffer - //WrappedObject wrappedBuffer = Pool.CheckOut(); - UDPPacketBuffer buf = new UDPPacketBuffer(); - try { // kick off an async read - udpSocket.BeginReceiveFrom( + m_udpSocket.BeginReceiveFrom( //wrappedBuffer.Instance.Data, buf.Data, 0, UDPPacketBuffer.BUFFER_SIZE, SocketFlags.None, - //ref wrappedBuffer.Instance.RemoteEndPoint, ref buf.RemoteEndPoint, - new AsyncCallback(AsyncEndReceive), + AsyncEndReceive, //wrappedBuffer); buf); } - catch (SocketException) + catch (SocketException e) { - // something bad happened - //Logger.Log( - // "A SocketException occurred in UDPServer.AsyncBeginReceive()", - // Helpers.LogLevel.Error, se); - - // an error occurred, therefore the operation is void. Decrement the reference count. - Interlocked.Decrement(ref rwOperationCount); + if (e.SocketErrorCode == SocketError.ConnectionReset) + { + m_log.Warn("[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort); + bool salvaged = false; + while (!salvaged) + { + try + { + m_udpSocket.BeginReceiveFrom( + //wrappedBuffer.Instance.Data, + buf.Data, + 0, + UDPPacketBuffer.BUFFER_SIZE, + SocketFlags.None, + ref buf.RemoteEndPoint, + AsyncEndReceive, + //wrappedBuffer); + buf); + salvaged = true; + } + catch (SocketException) { } + catch (ObjectDisposedException) { return; } + } + + m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); + } } + catch (ObjectDisposedException) { } } - - // we're done with the socket for now, release the reader lock. - rwLock.ReleaseReaderLock(); } private void AsyncEndReceive(IAsyncResult iar) { // Asynchronous receive operations will complete here through the call // to AsyncBeginReceive - - // aquire a reader lock - rwLock.AcquireReaderLock(-1); - - if (!shutdownFlag) + if (!m_shutdownFlag) { + // start another receive - this keeps the server going! + AsyncBeginReceive(); + // get the buffer that was created in AsyncBeginReceive // this is the received data //WrappedObject wrappedBuffer = (WrappedObject)iar.AsyncState; @@ -241,100 +211,49 @@ namespace OpenSim.Region.ClientStack.LindenUDP { // get the length of data actually read from the socket, store it with the // buffer - buffer.DataLength = udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); - - // this operation is now complete, decrement the reference count - Interlocked.Decrement(ref rwOperationCount); - - // we're done with the socket, release the reader lock - rwLock.ReleaseReaderLock(); + buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); // call the abstract method PacketReceived(), passing the buffer that // has just been filled from the socket read. PacketReceived(buffer); } - catch (SocketException) - { - // an error occurred, therefore the operation is void. Decrement the reference count. - Interlocked.Decrement(ref rwOperationCount); - - // we're done with the socket for now, release the reader lock. - rwLock.ReleaseReaderLock(); - } - finally - { - // start another receive - this keeps the server going! - AsyncBeginReceive(); - - //wrappedBuffer.Dispose(); - } - } - else - { - // nothing bad happened, but we are done with the operation - // decrement the reference count and release the reader lock - Interlocked.Decrement(ref rwOperationCount); - rwLock.ReleaseReaderLock(); + catch (SocketException) { } + catch (ObjectDisposedException) { } + //finally { wrappedBuffer.Dispose(); } } } public void AsyncBeginSend(UDPPacketBuffer buf) { - rwLock.AcquireReaderLock(-1); - - if (!shutdownFlag) + if (!m_shutdownFlag) { try { - Interlocked.Increment(ref rwOperationCount); - udpSocket.BeginSendTo( + m_udpSocket.BeginSendTo( buf.Data, 0, buf.DataLength, SocketFlags.None, buf.RemoteEndPoint, - new AsyncCallback(AsyncEndSend), + AsyncEndSend, buf); } - catch (SocketException) - { - //Logger.Log( - // "A SocketException occurred in UDPServer.AsyncBeginSend()", - // Helpers.LogLevel.Error, se); - } + catch (SocketException) { } + catch (ObjectDisposedException) { } } - - rwLock.ReleaseReaderLock(); } - private void AsyncEndSend(IAsyncResult iar) + void AsyncEndSend(IAsyncResult result) { - rwLock.AcquireReaderLock(-1); - - if (!shutdownFlag) + try { - UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; + UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState; + int bytesSent = m_udpSocket.EndSendTo(result); - try - { - int bytesSent = udpSocket.EndSendTo(iar); - - // note that call to the abstract PacketSent() method - we are passing the number - // of bytes sent in a separate parameter, since we can't use buffer.DataLength which - // is the number of bytes to send (or bytes received depending upon whether this - // buffer was part of a send or a receive). - PacketSent(buffer, bytesSent); - } - catch (SocketException) - { - //Logger.Log( - // "A SocketException occurred in UDPServer.AsyncEndSend()", - // Helpers.LogLevel.Error, se); - } + PacketSent(buf, bytesSent); } - - Interlocked.Decrement(ref rwOperationCount); - rwLock.ReleaseReaderLock(); + catch (SocketException) { } + catch (ObjectDisposedException) { } } } } -- cgit v1.1 From 494a1e922dd5c86ef54d7a83347ec8ba255e0e83 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 9 Oct 2009 02:10:53 -0700 Subject: Calling .Close() on AutoResetEvent and ManualResetEvent (those classes contain an unmanaged resource that will not automatically be disposed when they are GCed), and commenting out some ManualResetEvents that are not in use yet --- OpenSim/Framework/Communications/GenericAsyncResult.cs | 1 + OpenSim/Framework/Communications/RestClient.cs | 6 +++--- OpenSim/Framework/Parallel.cs | 3 +++ OpenSim/Region/Application/OpenSimBackground.cs | 1 + OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs | 4 ++-- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/OpenSim/Framework/Communications/GenericAsyncResult.cs b/OpenSim/Framework/Communications/GenericAsyncResult.cs index efd2f43..8e3f62b 100644 --- a/OpenSim/Framework/Communications/GenericAsyncResult.cs +++ b/OpenSim/Framework/Communications/GenericAsyncResult.cs @@ -146,6 +146,7 @@ namespace OpenSim.Framework.Communications // If the operation isn't done, wait for it AsyncWaitHandle.WaitOne(); AsyncWaitHandle.Close(); + m_waitHandle.Close(); m_waitHandle = null; // Allow early GC } diff --git a/OpenSim/Framework/Communications/RestClient.cs b/OpenSim/Framework/Communications/RestClient.cs index d98f47d..a74169e 100644 --- a/OpenSim/Framework/Communications/RestClient.cs +++ b/OpenSim/Framework/Communications/RestClient.cs @@ -105,7 +105,7 @@ namespace OpenSim.Framework.Communications /// /// This flag will help block the main synchroneous method, in case we run in synchroneous mode /// - public static ManualResetEvent _allDone = new ManualResetEvent(false); + //public static ManualResetEvent _allDone = new ManualResetEvent(false); /// /// Default time out period @@ -282,12 +282,12 @@ namespace OpenSim.Framework.Communications else { s.Close(); - _allDone.Set(); + //_allDone.Set(); } } catch (Exception e) { - _allDone.Set(); + //_allDone.Set(); _asyncException = e; } } diff --git a/OpenSim/Framework/Parallel.cs b/OpenSim/Framework/Parallel.cs index 74537ba..6efdad0 100644 --- a/OpenSim/Framework/Parallel.cs +++ b/OpenSim/Framework/Parallel.cs @@ -89,6 +89,7 @@ namespace OpenSim.Framework } threadFinishEvent.WaitOne(); + threadFinishEvent.Close(); if (exception != null) throw new Exception(exception.Message, exception); @@ -148,6 +149,7 @@ namespace OpenSim.Framework } threadFinishEvent.WaitOne(); + threadFinishEvent.Close(); if (exception != null) throw new Exception(exception.Message, exception); @@ -199,6 +201,7 @@ namespace OpenSim.Framework } threadFinishEvent.WaitOne(); + threadFinishEvent.Close(); if (exception != null) throw new Exception(exception.Message, exception); diff --git a/OpenSim/Region/Application/OpenSimBackground.cs b/OpenSim/Region/Application/OpenSimBackground.cs index ac5e241..008c6b0 100644 --- a/OpenSim/Region/Application/OpenSimBackground.cs +++ b/OpenSim/Region/Application/OpenSimBackground.cs @@ -58,6 +58,7 @@ namespace OpenSim m_clientServers.Count.ToString(), m_clientServers.Count > 1 ? "s" : ""); WorldHasComeToAnEnd.WaitOne(); + WorldHasComeToAnEnd.Close(); } /// diff --git a/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs b/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs index b885420..9b565ed 100644 --- a/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs +++ b/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs @@ -57,7 +57,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp public string body; public int responseCode; public string responseBody; - public ManualResetEvent ev; + //public ManualResetEvent ev; public bool requestDone; public int startTime; public string uri; @@ -456,7 +456,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp requestData.headers["x-query-string"] = queryString; requestData.headers["x-script-url"] = url.url; - requestData.ev = new ManualResetEvent(false); + //requestData.ev = new ManualResetEvent(false); lock (url.requests) { url.requests.Add(requestID, requestData); -- cgit v1.1 From a3e31cdeafbcfd371291213413b6d0d97a71c13b Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 9 Oct 2009 02:13:21 -0700 Subject: Added more debugging output to the "unrecognized source" warning --- OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 9aeea9a..7052e0e 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -504,7 +504,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Determine which agent this packet came from if (!m_clients.TryGetValue(address, out client)) { - m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address); + m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + + ", currently tracking " + m_clients.Count + " clients"); return; } -- cgit v1.1 From 76a5cae0b4170b191e370a663bb6355b4dc38fa2 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 9 Oct 2009 02:18:25 -0700 Subject: Forgot to initialize m_writeLock --- OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs index 2222a33..2972d46 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs @@ -60,7 +60,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private ImmutableMap m_dict; /// Immutability grants thread safety for concurrent reads and /// read-writes, but not concurrent writes - private object m_writeLock; + private object m_writeLock = new object(); /// Number of clients in the collection public int Count { get { return m_dict.Count; } } -- cgit v1.1 From 77e48a6725ccc30d33596b4f8c3dcdc956b7d1ba Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 9 Oct 2009 02:49:55 -0700 Subject: Change the backup thread to run on a BackgroundWorker instead of a Thread. I don't have an explanation, but this seems to stop a slow but steady memory leak I was experiencing --- OpenSim/Region/Framework/Scenes/Scene.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index ceff28b..6a550fa 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -1186,10 +1186,10 @@ namespace OpenSim.Region.Framework.Scenes if (!m_backingup) { m_backingup = true; - Thread backupthread = new Thread(Backup); - backupthread.Name = "BackupWriter"; - backupthread.IsBackground = true; - backupthread.Start(); + + System.ComponentModel.BackgroundWorker backupWorker = new System.ComponentModel.BackgroundWorker(); + backupWorker.DoWork += delegate(object sender, System.ComponentModel.DoWorkEventArgs e) { Backup(); }; + backupWorker.RunWorkerAsync(); } } -- cgit v1.1 From 23586b69a16c75b8c7cc9a99d1a7693686285ff7 Mon Sep 17 00:00:00 2001 From: Melanie Date: Fri, 9 Oct 2009 12:17:55 +0100 Subject: Slow down the packet receiving code again after new reports of thread storms. --- OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs index 9b1751d..e78a4fe 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs @@ -198,9 +198,6 @@ namespace OpenMetaverse // to AsyncBeginReceive if (!m_shutdownFlag) { - // start another receive - this keeps the server going! - AsyncBeginReceive(); - // get the buffer that was created in AsyncBeginReceive // this is the received data //WrappedObject wrappedBuffer = (WrappedObject)iar.AsyncState; @@ -219,7 +216,14 @@ namespace OpenMetaverse } catch (SocketException) { } catch (ObjectDisposedException) { } - //finally { wrappedBuffer.Dispose(); } + finally + { + // wrappedBuffer.Dispose(); + + // start another receive - this keeps the server going! + AsyncBeginReceive(); + } + } } -- cgit v1.1 From 29543514e6a8ad91a90e244a9488e9d0408f45bf Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 9 Oct 2009 16:33:50 -0700 Subject: * Changed the "Packet exceeded buffer size" log line to debug and include the packet type. This message is normal, but could be evidence of a message marked for zerocoding that probably shouldn't be * Changing OpenSimUDPBase back to high concurrency. High concurrency mode seems to make other problems happen faster, so it's helpful for working out other bugs and will probably --- OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 2 +- OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 7052e0e..fcb2cd0 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -268,7 +268,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // The packet grew larger than the bufferSize while zerocoding. // Remove the MSG_ZEROCODED flag and send the unencoded data // instead - m_log.Info("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding. Removing MSG_ZEROCODED flag"); + m_log.Debug("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + type + ". Removing MSG_ZEROCODED flag"); data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs index e78a4fe..fad2ea8 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs @@ -198,6 +198,9 @@ namespace OpenMetaverse // to AsyncBeginReceive if (!m_shutdownFlag) { + // start another receive - this keeps the server going! + AsyncBeginReceive(); + // get the buffer that was created in AsyncBeginReceive // this is the received data //WrappedObject wrappedBuffer = (WrappedObject)iar.AsyncState; @@ -216,13 +219,7 @@ namespace OpenMetaverse } catch (SocketException) { } catch (ObjectDisposedException) { } - finally - { - // wrappedBuffer.Dispose(); - - // start another receive - this keeps the server going! - AsyncBeginReceive(); - } + //finally { wrappedBuffer.Dispose(); } } } -- cgit v1.1 From 09cd2ac4437e3905ba48b84e04559a0e606aa679 Mon Sep 17 00:00:00 2001 From: Melanie Date: Mon, 12 Oct 2009 20:46:19 +0100 Subject: Stop null values from being returned on database queries --- OpenSim/Data/MySQL/MySQLRegionData.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/OpenSim/Data/MySQL/MySQLRegionData.cs b/OpenSim/Data/MySQL/MySQLRegionData.cs index 3b561d1..f514076 100644 --- a/OpenSim/Data/MySQL/MySQLRegionData.cs +++ b/OpenSim/Data/MySQL/MySQLRegionData.cs @@ -151,7 +151,10 @@ namespace OpenSim.Data.MySQL DataTable schemaTable = result.GetSchemaTable(); foreach (DataRow row in schemaTable.Rows) - m_ColumnNames.Add(row["ColumnName"].ToString()); + { + if (row["ColumnName"] != null) + m_ColumnNames.Add(row["ColumnName"].ToString()); + } } foreach (string s in m_ColumnNames) -- cgit v1.1 From bf68dad64398f7bd4ae2992d345071d261c32e80 Mon Sep 17 00:00:00 2001 From: Melanie Date: Mon, 12 Oct 2009 23:21:32 +0100 Subject: 0004246: [Patch] FlotsamAssetCache deep scan & cache Thank you, mcortez. --- .../Region/CoreModules/Asset/FlotsamAssetCache.cs | 307 ++++++++++++++++++--- bin/config-include/FlotsamCache.ini.example | 7 + 2 files changed, 277 insertions(+), 37 deletions(-) diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs index b81ab41..4041b63 100644 --- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs @@ -43,6 +43,7 @@ using Mono.Addins; using OpenMetaverse; using OpenSim.Framework; +using OpenSim.Framework.Console; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Services.Interfaces; @@ -54,7 +55,7 @@ using OpenSim.Services.Interfaces; namespace Flotsam.RegionModules.AssetCache { [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] - public class FlotsamAssetCache : ISharedRegionModule, IImprovedAssetCache + public class FlotsamAssetCache : ISharedRegionModule, IImprovedAssetCache, IAssetService { private static readonly ILog m_log = LogManager.GetLogger( @@ -102,6 +103,11 @@ namespace Flotsam.RegionModules.AssetCache private System.Timers.Timer m_CachCleanTimer = new System.Timers.Timer(); + private IAssetService m_AssetService = null; + private List m_Scenes = new List(); + + private bool m_DeepScanBeforePurge = false; + public FlotsamAssetCache() { m_InvalidChars.AddRange(Path.GetInvalidPathChars()); @@ -121,6 +127,7 @@ namespace Flotsam.RegionModules.AssetCache public void Initialise(IConfigSource source) { IConfig moduleConfig = source.Configs["Modules"]; + if (moduleConfig != null) { @@ -195,7 +202,13 @@ namespace Flotsam.RegionModules.AssetCache m_CacheWarnAt = assetConfig.GetInt("CacheWarnAt", 30000); - + m_DeepScanBeforePurge = assetConfig.GetBoolean("DeepScanBeforePurge", false); + + MainConsole.Instance.Commands.AddCommand(this.Name, true, "fcache status", "fcache status", "Display cache status", HandleConsoleCommand); + MainConsole.Instance.Commands.AddCommand(this.Name, true, "fcache clear", "fcache clear [file] [memory]", "Remove all assets in the file and/or memory cache", HandleConsoleCommand); + MainConsole.Instance.Commands.AddCommand(this.Name, true, "fcache assets", "fcache assets", "Attempt a deep scan and cache of all assets in all scenes", HandleConsoleCommand); + MainConsole.Instance.Commands.AddCommand(this.Name, true, "fcache expire", "fcache expire ", "Purge cached assets older then the specified date/time", HandleConsoleCommand); + } } } @@ -213,16 +226,23 @@ namespace Flotsam.RegionModules.AssetCache if (m_Enabled) { scene.RegisterModuleInterface(this); + m_Scenes.Add(scene); - //scene.AddCommand(this, "flotsamcache", "", "Display a list of console commands for the Flotsam Asset Cache", HandleConsoleCommand); - scene.AddCommand(this, "flotsamcache counts", "flotsamcache counts", "Display the number of cached assets", HandleConsoleCommand); - scene.AddCommand(this, "flotsamcache clearmem", "flotsamcache clearmem", "Remove all assets cached in memory", HandleConsoleCommand); - scene.AddCommand(this, "flotsamcache clearfile", "flotsamcache clearfile", "Remove all assets cached on disk", HandleConsoleCommand); + if (m_AssetService == null) + { + m_AssetService = scene.RequestModuleInterface(); + + } } } public void RemoveRegion(Scene scene) { + if (m_Enabled) + { + scene.UnregisterModuleInterface(this); + m_Scenes.Remove(scene); + } } public void RegionLoaded(Scene scene) @@ -442,31 +462,47 @@ namespace Flotsam.RegionModules.AssetCache if (m_LogLevel >= 2) m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Checking for expired files older then {0}.", m_FileExpiration.ToString()); + // Purge all files last accessed prior to this point + DateTime purgeLine = DateTime.Now - m_FileExpiration; + + // An optional deep scan at this point will ensure assets present in scenes, + // or referenced by objects in the scene, but not recently accessed + // are not purged. + if (m_DeepScanBeforePurge) + { + CacheScenes(); + } + foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) { - CleanExpiredFiles(dir); + CleanExpiredFiles(dir, purgeLine); } } /// - /// Recurses through specified directory checking for expired asset files and deletes them. Also removes empty directories. + /// Recurses through specified directory checking for asset files last + /// accessed prior to the specified purge line and deletes them. Also + /// removes empty tier directories. /// /// - private void CleanExpiredFiles(string dir) + private void CleanExpiredFiles(string dir, DateTime purgeLine) { + foreach (string file in Directory.GetFiles(dir)) { - if (DateTime.Now - File.GetLastAccessTime(file) > m_FileExpiration) + if (File.GetLastAccessTime(file) < purgeLine) { File.Delete(file); } } + // Recurse into lower tiers foreach (string subdir in Directory.GetDirectories(dir)) { - CleanExpiredFiles(subdir); + CleanExpiredFiles(subdir, purgeLine); } + // Check if a tier directory is empty, if so, delete it int dirSize = Directory.GetFiles(dir).Length + Directory.GetDirectories(dir).Length; if (dirSize == 0) { @@ -478,6 +514,11 @@ namespace Flotsam.RegionModules.AssetCache } } + /// + /// Determines the filename for an AssetID stored in the file cache + /// + /// + /// private string GetFileName(string id) { // Would it be faster to just hash the darn thing? @@ -496,14 +537,23 @@ namespace Flotsam.RegionModules.AssetCache return Path.Combine(path, id); } + /// + /// Writes a file to the file cache, creating any nessesary + /// tier directories along the way + /// + /// + /// private void WriteFileCache(string filename, AssetBase asset) { Stream stream = null; + // Make sure the target cache directory exists string directory = Path.GetDirectoryName(filename); + // Write file first to a temp name, so that it doesn't look // like it's already cached while it's still writing. string tempname = Path.Combine(directory, Path.GetRandomFileName()); + try { if (!Directory.Exists(directory)) @@ -563,6 +613,11 @@ namespace Flotsam.RegionModules.AssetCache } } + /// + /// Scan through the file cache, and return number of assets currently cached. + /// + /// + /// private int GetFileCacheCount(string dir) { int count = Directory.GetFiles(dir).Length; @@ -575,55 +630,180 @@ namespace Flotsam.RegionModules.AssetCache return count; } + /// + /// This notes the last time the Region had a deep asset scan performed on it. + /// + /// + private void StampRegionStatusFile(UUID RegionID) + { + string RegionCacheStatusFile = Path.Combine(m_CacheDirectory, "RegionStatus_" + RegionID.ToString() + ".fac"); + if (File.Exists(RegionCacheStatusFile)) + { + File.SetLastWriteTime(RegionCacheStatusFile, DateTime.Now); + } + else + { + File.WriteAllText(RegionCacheStatusFile, "Please do not delete this file unless you are manually clearing your Flotsam Asset Cache."); + } + } + + /// + /// Iterates through all Scenes, doing a deep scan through assets + /// to cache all assets present in the scene or referenced by assets + /// in the scene + /// + /// + private int CacheScenes() + { + UuidGatherer gatherer = new UuidGatherer(m_AssetService); + + Dictionary assets = new Dictionary(); + foreach (Scene s in m_Scenes) + { + StampRegionStatusFile(s.RegionInfo.RegionID); + + s.ForEachSOG(delegate(SceneObjectGroup e) + { + gatherer.GatherAssetUuids(e, assets); + } + ); + } + + foreach (UUID assetID in assets.Keys) + { + string filename = GetFileName(assetID.ToString()); + + if (File.Exists(filename)) + { + File.SetLastAccessTime(filename, DateTime.Now); + } + else + { + m_AssetService.Get(assetID.ToString()); + } + } + + return assets.Keys.Count; + } + + /// + /// Deletes all cache contents + /// + private void ClearFileCache() + { + foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) + { + try + { + Directory.Delete(dir, true); + } + catch (Exception e) + { + LogException(e); + } + } + + foreach (string file in Directory.GetFiles(m_CacheDirectory)) + { + try + { + File.Delete(file); + } + catch (Exception e) + { + LogException(e); + } + } + } + #region Console Commands private void HandleConsoleCommand(string module, string[] cmdparams) { - if (cmdparams.Length == 2) + if (cmdparams.Length >= 2) { string cmd = cmdparams[1]; switch (cmd) { - case "count": - case "counts": - m_log.InfoFormat("[FLOTSAM ASSET CACHE] Memory Cache : {0}", m_MemoryCache.Count); + case "status": + m_log.InfoFormat("[FLOTSAM ASSET CACHE] Memory Cache : {0} assets", m_MemoryCache.Count); int fileCount = GetFileCacheCount(m_CacheDirectory); - m_log.InfoFormat("[FLOTSAM ASSET CACHE] File Cache : {0}", fileCount); + m_log.InfoFormat("[FLOTSAM ASSET CACHE] File Cache : {0} assets", fileCount); - break; + foreach ( string s in Directory.GetFiles(m_CacheDirectory, "*.fac" ) ) + { + m_log.Info("[FLOTSAM ASSET CACHE] Deep Scans were performed on the following regions:"); + + string RegionID = s.Remove(0,s.IndexOf("_")).Replace(".fac",""); + DateTime RegionDeepScanTMStamp = File.GetLastWriteTime(s); + m_log.InfoFormat("[FLOTSAM ASSET CACHE] Region: {0}, {1}", RegionID, RegionDeepScanTMStamp.ToString("MM/dd/yyyy hh:mm:ss")); + } - case "clearmem": - m_MemoryCache.Clear(); - m_log.InfoFormat("[FLOTSAM ASSET CACHE] Memory Cache Cleared, there are now {0} items in the memory cache", m_MemoryCache.Count); break; - case "clearfile": - foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) + case "clear": + if (cmdparams.Length < 3) { - try - { - Directory.Delete(dir, true); - } - catch (Exception e) - { - LogException(e); - } + m_log.Warn("[FLOTSAM ASSET CACHE] Please specify memory and/or file cache."); + break; } - - foreach (string file in Directory.GetFiles(m_CacheDirectory)) + foreach (string s in cmdparams) { - try + if (s.ToLower() == "memory") { - File.Delete(file); + m_MemoryCache.Clear(); + m_log.Info("[FLOTSAM ASSET CACHE] Memory cache cleared."); } - catch (Exception e) + else if (s.ToLower() == "file") { - LogException(e); + ClearFileCache(); + m_log.Info("[FLOTSAM ASSET CACHE] File cache cleared."); } } + break; + + + case "assets": + m_log.Info("[FLOTSAM ASSET CACHE] Caching all assets, in all scenes."); + + Util.FireAndForget(delegate { + int assetsCached = CacheScenes(); + m_log.InfoFormat("[FLOTSAM ASSET CACHE] Completed Scene Caching, {0} assets found.", assetsCached); + + }); break; + case "expire": + + + if (cmdparams.Length >= 3) + { + m_log.InfoFormat("[FLOTSAM ASSET CACHE] Invalid parameters for Expire, please specify a valid date & time", cmd); + break; + } + + string s_expirationDate = ""; + DateTime expirationDate; + + if (cmdparams.Length > 3) + { + s_expirationDate = string.Join(" ", cmdparams, 2, cmdparams.Length - 2); + } + else + { + s_expirationDate = cmdparams[2]; + } + + if (!DateTime.TryParse(s_expirationDate, out expirationDate)) + { + m_log.InfoFormat("[FLOTSAM ASSET CACHE] {0} is not a valid date & time", cmd); + break; + } + + CleanExpiredFiles(m_CacheDirectory, expirationDate); + + break; default: m_log.InfoFormat("[FLOTSAM ASSET CACHE] Unknown command {0}", cmd); break; @@ -631,13 +811,66 @@ namespace Flotsam.RegionModules.AssetCache } else if (cmdparams.Length == 1) { - m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache counts - Display the number of cached assets"); + m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache status - Display cache status"); m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache clearmem - Remove all assets cached in memory"); m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache clearfile - Remove all assets cached on disk"); + m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache cachescenes - Attempt a deep cache of all assets in all scenes"); + m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache - Purge assets older then the specified date & time"); } } #endregion + + #region IAssetService Members + + + public AssetMetadata GetMetadata(string id) + { + AssetBase asset = Get(id); + return asset.Metadata; + } + + public byte[] GetData(string id) + { + AssetBase asset = Get(id); + return asset.Data; + } + + public bool Get(string id, object sender, AssetRetrieved handler) + { + AssetBase asset = Get(id); + handler(id, sender, asset); + return true; + } + + public string Store(AssetBase asset) + { + if ((asset.FullID == null) || (asset.FullID == UUID.Zero)) + { + asset.FullID = UUID.Random(); + } + + Cache(asset); + + return asset.ID; + + } + + public bool UpdateContent(string id, byte[] data) + { + AssetBase asset = Get(id); + asset.Data = data; + Cache(asset); + return true; + } + + public bool Delete(string id) + { + Expire(id); + return true; + } + + #endregion } } \ No newline at end of file diff --git a/bin/config-include/FlotsamCache.ini.example b/bin/config-include/FlotsamCache.ini.example index abb3b9a..b50d7ec 100644 --- a/bin/config-include/FlotsamCache.ini.example +++ b/bin/config-include/FlotsamCache.ini.example @@ -50,3 +50,10 @@ ; Warning level for cache directory size ;CacheWarnAt = 30000 + + ; Perform a deep scan of all assets within all regions, looking for all assets + ; present or referenced. Mark all assets found that are already present in the + ; cache, and request all assets that are found that are not already cached (this + ; will cause those assets to be cached) + ; + ; DeepScanBeforePurge = false -- cgit v1.1 From c0beeb929e22509329781cdf85f7a5d90c4b0e36 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 12 Oct 2009 17:00:01 -0700 Subject: * Fixes http://opensimulator.org/mantis/view.php?id=4225 * Fixes http://opensimulator.org/mantis/view.php?id=3959 * Allows for viewing inventory textures outside home grid --- OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs | 14 ++++ .../Region/ClientStack/LindenUDP/LLClientView.cs | 21 +++-- .../Region/ClientStack/LindenUDP/LLImageManager.cs | 5 ++ .../ServiceConnectorsOut/Asset/HGAssetBroker.cs | 44 +++++++++- .../ServiceConnectorsOut/Grid/HGGridConnector.cs | 1 + .../Inventory/BaseInventoryConnector.cs | 3 + .../Inventory/HGInventoryBroker.cs | 2 + .../Framework/Scenes/Hypergrid/HGAssetMapper.cs | 98 ++++++++++------------ .../Scenes/Hypergrid/HGScene.Inventory.cs | 26 ++++++ OpenSim/Region/Framework/Scenes/Scene.Inventory.cs | 9 +- .../Inventory/HGInventoryServiceConnector.cs | 1 + 11 files changed, 162 insertions(+), 62 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs index 5877779..1bbe00f 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs @@ -31,6 +31,7 @@ using OpenMetaverse; using OpenMetaverse.Imaging; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes.Hypergrid; using OpenSim.Services.Interfaces; using log4net; using System.Reflection; @@ -54,6 +55,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public UUID TextureID; public IJ2KDecoder J2KDecoder; public IAssetService AssetService; + public UUID AgentID; + public IHyperAssetService HyperAssets; public OpenJPEG.J2KLayerInfo[] Layers; public bool IsDecoded; public bool HasAsset; @@ -370,6 +373,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP UUID assetID = UUID.Zero; if (asset != null) assetID = asset.FullID; + else if (HyperAssets != null) + { + // Try the user's inventory, but only if it's different from the regions' + string userAssets = HyperAssets.GetUserAssetServer(AgentID); + if ((userAssets != string.Empty) && (userAssets != HyperAssets.GetSimAssetServer())) + { + m_log.DebugFormat("[J2KIMAGE]: texture {0} not found in local asset storage. Trying user's storage.", id); + AssetService.Get(userAssets + "/" + id, this, AssetReceived); + return; + } + } AssetDataCallback(assetID, asset); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 84e705a..25eb5cd 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -43,6 +43,7 @@ using OpenSim.Framework.Communications.Cache; using OpenSim.Framework.Statistics; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Scenes.Hypergrid; using OpenSim.Services.Interfaces; using Timer=System.Timers.Timer; using AssetLandmark = OpenSim.Framework.AssetLandmark; @@ -117,6 +118,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected int m_packetMTU = 1400; protected IAssetService m_assetService; + #region Properties public UUID SecureSessionId { get { return m_secureSessionId; } } @@ -7013,7 +7015,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion //handlerTextureRequest = null; - for (int i = 0; i < imageRequest.RequestImage.Length; i++) { if (OnRequestTexture != null) @@ -7024,7 +7025,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP args.PacketNumber = imageRequest.RequestImage[i].Packet; args.Priority = imageRequest.RequestImage[i].DownloadPriority; args.requestSequence = imageRequest.Header.Sequence; - //handlerTextureRequest = OnRequestTexture; //if (handlerTextureRequest != null) @@ -7047,10 +7047,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Validate inventory transfers // Has to be done here, because AssetCache can't do it // - + UUID taskID = UUID.Zero; if (transfer.TransferInfo.SourceType == 3) { - UUID taskID = new UUID(transfer.TransferInfo.Params, 48); + taskID = new UUID(transfer.TransferInfo.Params, 48); UUID itemID = new UUID(transfer.TransferInfo.Params, 64); UUID requestID = new UUID(transfer.TransferInfo.Params, 80); if (!(((Scene)m_scene).Permissions.BypassPermissions())) @@ -7121,7 +7121,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP //m_assetCache.AddAssetRequest(this, transfer); - MakeAssetRequest(transfer); + MakeAssetRequest(transfer, taskID); /* RequestAsset = OnRequestAsset; if (RequestAsset != null) @@ -10330,7 +10330,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return String.Empty; } - public void MakeAssetRequest(TransferRequestPacket transferRequest) + public void MakeAssetRequest(TransferRequestPacket transferRequest, UUID taskID) { UUID requestID = UUID.Zero; if (transferRequest.TransferInfo.SourceType == 2) @@ -10343,11 +10343,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP //inventory asset request requestID = new UUID(transferRequest.TransferInfo.Params, 80); //m_log.Debug("asset request " + requestID); + if (taskID == UUID.Zero) // Agent + if (m_scene is HGScene) + { + // We may need to fetch the asset from the user's asset server into the local asset server + HGAssetMapper mapper = ((HGScene)m_scene).AssetMapper; + mapper.Get(requestID, AgentId); + } } //check to see if asset is in local cache, if not we need to request it from asset server. //m_log.Debug("asset request " + requestID); - + m_assetService.Get(requestID.ToString(), transferRequest, AssetReceived); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index 343f537..56d34e6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -59,6 +59,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private C5.IntervalHeap m_priorityQueue = new C5.IntervalHeap(10, new J2KImageComparer()); private object m_syncRoot = new object(); + private IHyperAssetService m_hyperAssets; + public LLClientView Client { get { return m_client; } } public AssetBase MissingImage { get { return m_missingImage; } } @@ -74,6 +76,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.Error("[ClientView] - Couldn't set missing image asset, falling back to missing image packet. This is known to crash the client"); m_j2kDecodeModule = pJ2kDecodeModule; + m_hyperAssets = client.Scene.RequestModuleInterface(); } /// @@ -146,6 +149,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP imgrequest = new J2KImage(this); imgrequest.J2KDecoder = m_j2kDecodeModule; imgrequest.AssetService = m_assetCache; + imgrequest.AgentID = m_client.AgentId; + imgrequest.HyperAssets = m_hyperAssets; imgrequest.DiscardLevel = newRequest.DiscardLevel; imgrequest.StartPacket = Math.Max(1, newRequest.PacketNumber); imgrequest.Priority = newRequest.Priority; diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/HGAssetBroker.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/HGAssetBroker.cs index 7b4e374..0aa753d 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/HGAssetBroker.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/HGAssetBroker.cs @@ -31,6 +31,7 @@ using System; using System.Collections.Generic; using System.Reflection; using OpenSim.Framework; +using OpenSim.Framework.Communications.Cache; using OpenSim.Server.Base; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; @@ -40,7 +41,7 @@ using OpenMetaverse; namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset { public class HGAssetBroker : - ISharedRegionModule, IAssetService + ISharedRegionModule, IAssetService, IHyperAssetService { private static readonly ILog m_log = LogManager.GetLogger( @@ -50,6 +51,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset private IAssetService m_GridService; private IAssetService m_HGService; + private Scene m_aScene; + private string m_LocalAssetServiceURI; + private bool m_Enabled = false; public Type ReplaceableInterface @@ -114,6 +118,16 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset return; } + m_LocalAssetServiceURI = assetConfig.GetString("AssetServerURI", string.Empty); + if (m_LocalAssetServiceURI == string.Empty) + { + IConfig netConfig = source.Configs["Network"]; + m_LocalAssetServiceURI = netConfig.GetString("asset_server_url", string.Empty); + } + + if (m_LocalAssetServiceURI != string.Empty) + m_LocalAssetServiceURI = m_LocalAssetServiceURI.Trim('/'); + m_Enabled = true; m_log.Info("[HG ASSET CONNECTOR]: HG asset broker enabled"); } @@ -132,8 +146,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset { if (!m_Enabled) return; + + m_aScene = scene; scene.RegisterModuleInterface(this); + scene.RegisterModuleInterface(this); } public void RemoveRegion(Scene scene) @@ -344,5 +361,30 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset else return m_GridService.Delete(id); } + + #region IHyperAssetService + + public string GetUserAssetServer(UUID userID) + { + CachedUserInfo uinfo = m_aScene.CommsManager.UserProfileCacheService.GetUserDetails(userID); + if ((uinfo != null) && (uinfo.UserProfile != null)) + { + if ((uinfo.UserProfile.UserAssetURI == string.Empty) || (uinfo.UserProfile.UserAssetURI == "")) + return m_LocalAssetServiceURI; + return uinfo.UserProfile.UserAssetURI.Trim('/'); + } + else + { + // we don't know anyting about this user + return string.Empty; + } + } + + public string GetSimAssetServer() + { + return m_LocalAssetServiceURI; + } + + #endregion } } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/HGGridConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/HGGridConnector.cs index 148331b..1422add 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/HGGridConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/HGGridConnector.cs @@ -759,6 +759,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid } + protected bool IsLocalRegion(ulong handle) { return m_LocalScenes.ContainsKey(handle); diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/BaseInventoryConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/BaseInventoryConnector.cs index bd32f3b..811569f 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/BaseInventoryConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/BaseInventoryConnector.cs @@ -159,6 +159,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory /// true if the item was successfully added public bool AddItem(InventoryItemBase item) { + if (item == null) + return false; + if (item.Folder == UUID.Zero) { InventoryFolderBase f = GetFolderForType(item.Owner, (AssetType)item.AssetType); diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker.cs index fd1a759..f073f32 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker.cs @@ -386,7 +386,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory return false; if (IsLocalGridUser(item.Owner)) + { return m_GridService.AddItem(item); + } else { UUID sessionID = GetSessionID(item.Owner); diff --git a/OpenSim/Region/Framework/Scenes/Hypergrid/HGAssetMapper.cs b/OpenSim/Region/Framework/Scenes/Hypergrid/HGAssetMapper.cs index b6fa41d..244ac3b 100644 --- a/OpenSim/Region/Framework/Scenes/Hypergrid/HGAssetMapper.cs +++ b/OpenSim/Region/Framework/Scenes/Hypergrid/HGAssetMapper.cs @@ -35,6 +35,7 @@ using OpenSim.Framework; using OpenSim.Framework.Communications.Cache; using OpenSim.Framework.Communications.Clients; using OpenSim.Region.Framework.Scenes.Serialization; +using OpenSim.Region.Framework.Interfaces; using OpenSim.Services.Interfaces; //using HyperGrid.Framework; @@ -52,13 +53,13 @@ namespace OpenSim.Region.Framework.Scenes.Hypergrid private Scene m_scene; - private IHyperlinkService m_hyper; - IHyperlinkService HyperlinkService + private IHyperAssetService m_hyper; + IHyperAssetService HyperlinkAssets { get { if (m_hyper == null) - m_hyper = m_scene.RequestModuleInterface(); + m_hyper = m_scene.RequestModuleInterface(); return m_hyper; } } @@ -99,7 +100,7 @@ namespace OpenSim.Region.Framework.Scenes.Hypergrid if (asset != null) { - m_log.Debug("[HGScene]: Asset made it to asset cache. " + asset.Name + " " + assetID); + m_log.DebugFormat("[HGScene]: Copied asset {0} from {1} to local asset server. ", asset.ID, url); return asset; } return null; @@ -129,6 +130,7 @@ namespace OpenSim.Region.Framework.Scenes.Hypergrid } m_scene.AssetService.Store(asset1); + m_log.DebugFormat("[HGScene]: Posted copy of asset {0} from local asset server to {1}", asset1.ID, url); } return true; } @@ -167,34 +169,32 @@ namespace OpenSim.Region.Framework.Scenes.Hypergrid public void Get(UUID assetID, UUID ownerID) { - if (!HyperlinkService.IsLocalUser(ownerID)) + // Get the item from the remote asset server onto the local AssetCache + // and place an entry in m_assetMap + + string userAssetURL = HyperlinkAssets.GetUserAssetServer(ownerID); + if ((userAssetURL != string.Empty) && (userAssetURL != HyperlinkAssets.GetSimAssetServer())) { - // Get the item from the remote asset server onto the local AssetCache - // and place an entry in m_assetMap + m_log.Debug("[HGScene]: Fetching object " + assetID + " from asset server " + userAssetURL); + AssetBase asset = FetchAsset(userAssetURL, assetID); - string userAssetURL = UserAssetURL(ownerID); - if (userAssetURL != null) + if (asset != null) { - m_log.Debug("[HGScene]: Fetching object " + assetID + " to asset server " + userAssetURL); - AssetBase asset = FetchAsset(userAssetURL, assetID); + // OK, now fetch the inside. + Dictionary ids = new Dictionary(); + HGUuidGatherer uuidGatherer = new HGUuidGatherer(this, m_scene.AssetService, userAssetURL); + uuidGatherer.GatherAssetUuids(asset.FullID, (AssetType)asset.Type, ids); + foreach (UUID uuid in ids.Keys) + FetchAsset(userAssetURL, uuid); + + m_log.DebugFormat("[HGScene]: Successfully fetched asset {0} from asset server {1}", asset.ID, userAssetURL); - if (asset != null) - { - m_log.Debug("[HGScene]: Successfully fetched item from remote asset server " + userAssetURL); - - // OK, now fetch the inside. - Dictionary ids = new Dictionary(); - HGUuidGatherer uuidGatherer = new HGUuidGatherer(this, m_scene.AssetService, userAssetURL); - uuidGatherer.GatherAssetUuids(asset.FullID, (AssetType)asset.Type, ids); - foreach (UUID uuid in ids.Keys) - FetchAsset(userAssetURL, uuid); - } - else - m_log.Warn("[HGScene]: Could not fetch asset from remote asset server " + userAssetURL); } else - m_log.Warn("[HGScene]: Unable to locate foreign user's asset server"); + m_log.Warn("[HGScene]: Could not fetch asset from remote asset server " + userAssetURL); } + else + m_log.Debug("[HGScene]: user's asset server is the local region's asset server"); } //public InventoryItemBase Get(InventoryItemBase item, UUID rootFolder, CachedUserInfo userInfo) @@ -225,44 +225,38 @@ namespace OpenSim.Region.Framework.Scenes.Hypergrid public void Post(UUID assetID, UUID ownerID) { - if (!HyperlinkService.IsLocalUser(ownerID)) - { // Post the item from the local AssetCache onto the remote asset server // and place an entry in m_assetMap - string userAssetURL = UserAssetURL(ownerID); - if (userAssetURL != null) + string userAssetURL = HyperlinkAssets.GetUserAssetServer(ownerID); + if ((userAssetURL != string.Empty) && (userAssetURL != HyperlinkAssets.GetSimAssetServer())) + { + m_log.Debug("[HGScene]: Posting object " + assetID + " to asset server " + userAssetURL); + AssetBase asset = m_scene.AssetService.Get(assetID.ToString()); + if (asset != null) { - m_log.Debug("[HGScene]: Posting object " + assetID + " to asset server " + userAssetURL); - AssetBase asset = m_scene.AssetService.Get(assetID.ToString()); - if (asset != null) + Dictionary ids = new Dictionary(); + HGUuidGatherer uuidGatherer = new HGUuidGatherer(this, m_scene.AssetService, string.Empty); + uuidGatherer.GatherAssetUuids(asset.FullID, (AssetType)asset.Type, ids); + foreach (UUID uuid in ids.Keys) { - Dictionary ids = new Dictionary(); - HGUuidGatherer uuidGatherer = new HGUuidGatherer(this, m_scene.AssetService, string.Empty); - uuidGatherer.GatherAssetUuids(asset.FullID, (AssetType)asset.Type, ids); - foreach (UUID uuid in ids.Keys) - { - asset = m_scene.AssetService.Get(uuid.ToString()); - if (asset != null) - m_log.DebugFormat("[HGScene]: Posting {0} {1}", asset.Type.ToString(), asset.Name); - else - m_log.DebugFormat("[HGScene]: Could not find asset {0}", uuid); + asset = m_scene.AssetService.Get(uuid.ToString()); + if (asset == null) + m_log.DebugFormat("[HGScene]: Could not find asset {0}", uuid); + else PostAsset(userAssetURL, asset); - } + } - if (ids.Count > 0) // maybe it succeeded... - m_log.DebugFormat("[HGScene]: Successfully posted item {0} to remote asset server {1}", assetID, userAssetURL); - else - m_log.WarnFormat("[HGScene]: Could not post asset {0} to remote asset server {1}", assetID, userAssetURL); + // maybe all pieces got there... + m_log.DebugFormat("[HGScene]: Successfully posted item {0} to asset server {1}", assetID, userAssetURL); - } - else - m_log.Debug("[HGScene]: Something wrong with asset, it could not be found"); } else - m_log.Warn("[HGScene]: Unable to locate foreign user's asset server"); - + m_log.DebugFormat("[HGScene]: Something wrong with asset {0}, it could not be found", assetID); } + else + m_log.Debug("[HGScene]: user's asset server is local region's asset server"); + } #endregion diff --git a/OpenSim/Region/Framework/Scenes/Hypergrid/HGScene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Hypergrid/HGScene.Inventory.cs index 8fe3565..6f7f34f 100644 --- a/OpenSim/Region/Framework/Scenes/Hypergrid/HGScene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Hypergrid/HGScene.Inventory.cs @@ -32,6 +32,7 @@ using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Communications; using OpenSim.Framework.Communications.Cache; +using OpenSim.Region.Framework.Interfaces; namespace OpenSim.Region.Framework.Scenes.Hypergrid { @@ -41,6 +42,21 @@ namespace OpenSim.Region.Framework.Scenes.Hypergrid private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private HGAssetMapper m_assMapper; + public HGAssetMapper AssetMapper + { + get { return m_assMapper; } + } + + private IHyperAssetService m_hyper; + private IHyperAssetService HyperAssets + { + get + { + if (m_hyper == null) + m_hyper = RequestModuleInterface(); + return m_hyper; + } + } #endregion @@ -140,6 +156,16 @@ namespace OpenSim.Region.Framework.Scenes.Hypergrid } + protected override void TransferInventoryAssets(InventoryItemBase item, UUID sender, UUID receiver) + { + string userAssetServer = HyperAssets.GetUserAssetServer(sender); + if ((userAssetServer != string.Empty) && (userAssetServer != HyperAssets.GetSimAssetServer())) + m_assMapper.Get(item.AssetID, sender); + + userAssetServer = HyperAssets.GetUserAssetServer(receiver); + if ((userAssetServer != string.Empty) && (userAssetServer != HyperAssets.GetSimAssetServer())) + m_assMapper.Post(item.AssetID, receiver); + } #endregion diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 1917228..0cb5682 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -408,7 +408,7 @@ namespace OpenSim.Region.Framework.Scenes public virtual InventoryItemBase GiveInventoryItem( UUID recipient, UUID senderId, UUID itemId, UUID recipientFolderId) { - Console.WriteLine("Scene.Inventory.cs: GiveInventoryItem"); + //Console.WriteLine("Scene.Inventory.cs: GiveInventoryItem"); InventoryItemBase item = new InventoryItemBase(itemId, senderId); item = InventoryService.GetItem(item); @@ -472,7 +472,8 @@ namespace OpenSim.Region.Framework.Scenes itemCopy.SalePrice = item.SalePrice; itemCopy.SaleType = item.SaleType; - InventoryService.AddItem(itemCopy); + if (InventoryService.AddItem(itemCopy)) + TransferInventoryAssets(itemCopy, senderId, recipient); if (!Permissions.BypassPermissions()) { @@ -494,6 +495,10 @@ namespace OpenSim.Region.Framework.Scenes } + protected virtual void TransferInventoryAssets(InventoryItemBase item, UUID sender, UUID receiver) + { + } + /// /// Give an entire inventory folder from one user to another. The entire contents (including all descendent /// folders) is given. diff --git a/OpenSim/Services/Connectors/Inventory/HGInventoryServiceConnector.cs b/OpenSim/Services/Connectors/Inventory/HGInventoryServiceConnector.cs index 1004fb9..9878855 100644 --- a/OpenSim/Services/Connectors/Inventory/HGInventoryServiceConnector.cs +++ b/OpenSim/Services/Connectors/Inventory/HGInventoryServiceConnector.cs @@ -299,6 +299,7 @@ namespace OpenSim.Services.Connectors.Inventory if (StringToUrlAndUserID(id, out url, out userID)) { + //m_log.DebugFormat("[HGInventory CONNECTOR]: calling {0}", url); ISessionAuthInventoryService connector = GetConnector(url); return connector.QueryItem(userID, item, sessionID); } -- cgit v1.1 From 0cfbdf3894756654465c0a2e1b8e796fcdb37baa Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 12 Oct 2009 17:01:03 -0700 Subject: Added this one file for the previous commit to work. --- .../Region/Framework/Interfaces/IHyperService.cs | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 OpenSim/Region/Framework/Interfaces/IHyperService.cs diff --git a/OpenSim/Region/Framework/Interfaces/IHyperService.cs b/OpenSim/Region/Framework/Interfaces/IHyperService.cs new file mode 100644 index 0000000..51ea28a --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IHyperService.cs @@ -0,0 +1,37 @@ +/* + * 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 OpenMetaverse; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IHyperAssetService + { + string GetUserAssetServer(UUID userID); + string GetSimAssetServer(); + } +} -- cgit v1.1 From 63ed605eba7d4655bbbf956cef0d6e6b02b4a64e Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 12 Oct 2009 17:36:13 -0700 Subject: Stop the recurring texture requests for textures that truly don't exist. --- OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs index 1bbe00f..bb98f24 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs @@ -373,14 +373,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP UUID assetID = UUID.Zero; if (asset != null) assetID = asset.FullID; - else if (HyperAssets != null) + else if ((HyperAssets != null) && (sender != HyperAssets)) { // Try the user's inventory, but only if it's different from the regions' string userAssets = HyperAssets.GetUserAssetServer(AgentID); if ((userAssets != string.Empty) && (userAssets != HyperAssets.GetSimAssetServer())) { m_log.DebugFormat("[J2KIMAGE]: texture {0} not found in local asset storage. Trying user's storage.", id); - AssetService.Get(userAssets + "/" + id, this, AssetReceived); + AssetService.Get(userAssets + "/" + id, HyperAssets, AssetReceived); return; } } -- cgit v1.1 From f3d2192cd486cf32517e6c4549a3a691422a5f88 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 12 Oct 2009 18:30:06 -0700 Subject: Better handling of missing assets. --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 32 ++++++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 25eb5cd..ad92494 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -117,6 +117,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected int m_avatarTerseUpdatesPerPacket = 5; protected int m_packetMTU = 1400; protected IAssetService m_assetService; + private IHyperAssetService m_hyperAssets; #region Properties @@ -172,6 +173,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_scene = scene; m_assetService = m_scene.RequestModuleInterface(); + m_hyperAssets = m_scene.RequestModuleInterface(); m_GroupsModule = scene.RequestModuleInterface(); m_imageManager = new LLImageManager(this, m_assetService, Scene.RequestModuleInterface()); m_channelVersion = Utils.StringToBytes(scene.GetSimulatorVersion()); @@ -10342,14 +10344,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP { //inventory asset request requestID = new UUID(transferRequest.TransferInfo.Params, 80); - //m_log.Debug("asset request " + requestID); - if (taskID == UUID.Zero) // Agent - if (m_scene is HGScene) - { - // We may need to fetch the asset from the user's asset server into the local asset server - HGAssetMapper mapper = ((HGScene)m_scene).AssetMapper; - mapper.Get(requestID, AgentId); - } + //m_log.Debug("[XXX] inventory asset request " + requestID); + //if (taskID == UUID.Zero) // Agent + // if (m_scene is HGScene) + // { + // m_log.Debug("[XXX] hg asset request " + requestID); + // // We may need to fetch the asset from the user's asset server into the local asset server + // HGAssetMapper mapper = ((HGScene)m_scene).AssetMapper; + // mapper.Get(requestID, AgentId); + // } } //check to see if asset is in local cache, if not we need to request it from asset server. @@ -10378,10 +10381,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP //m_log.Debug("asset request " + requestID); } - // FIXME: We never tell the client about assets which do not exist when requested by this transfer mechanism, which can't be right. if (null == asset) { + // Try the user's inventory, but only if it's different from the regions' + string userAssets = m_hyperAssets.GetUserAssetServer(AgentId); + if ((userAssets != string.Empty) && (userAssets != m_hyperAssets.GetSimAssetServer())) + { + m_log.DebugFormat("[CLIENT]: asset {0} not found in local asset storage. Trying user's storage.", id); + transferRequest.TransferInfo.SourceType = 9999; // marker + m_assetService.Get(userAssets + "/" + id, transferRequest, AssetReceived); + return; + } + //m_log.DebugFormat("[ASSET CACHE]: Asset transfer request for asset which is {0} already known to be missing. Dropping", requestID); + + // FIXME: We never tell the client about assets which do not exist when requested by this transfer mechanism, which can't be right. return; } -- cgit v1.1 From e3d5beebfb44aee8a9bd30526be8d4e65adf632b Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Tue, 13 Oct 2009 06:39:11 -0700 Subject: Better handling of missing assets. --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 25 ++++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index ad92494..d64f655 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -10368,12 +10368,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP UUID requestID = UUID.Zero; byte source = 2; - if (transferRequest.TransferInfo.SourceType == 2) + if ((transferRequest.TransferInfo.SourceType == 2) || (transferRequest.TransferInfo.SourceType == 2222)) { //direct asset request requestID = new UUID(transferRequest.TransferInfo.Params, 0); } - else if (transferRequest.TransferInfo.SourceType == 3) + else if ((transferRequest.TransferInfo.SourceType == 3) || (transferRequest.TransferInfo.SourceType == 3333)) { //inventory asset request requestID = new UUID(transferRequest.TransferInfo.Params, 80); @@ -10383,14 +10383,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (null == asset) { - // Try the user's inventory, but only if it's different from the regions' - string userAssets = m_hyperAssets.GetUserAssetServer(AgentId); - if ((userAssets != string.Empty) && (userAssets != m_hyperAssets.GetSimAssetServer())) + if ((m_hyperAssets != null) && (transferRequest.TransferInfo.SourceType < 2000)) { - m_log.DebugFormat("[CLIENT]: asset {0} not found in local asset storage. Trying user's storage.", id); - transferRequest.TransferInfo.SourceType = 9999; // marker - m_assetService.Get(userAssets + "/" + id, transferRequest, AssetReceived); - return; + // Try the user's inventory, but only if it's different from the regions' + string userAssets = m_hyperAssets.GetUserAssetServer(AgentId); + if ((userAssets != string.Empty) && (userAssets != m_hyperAssets.GetSimAssetServer())) + { + m_log.DebugFormat("[CLIENT]: asset {0} not found in local asset storage. Trying user's storage.", id); + if (transferRequest.TransferInfo.SourceType == 2) + transferRequest.TransferInfo.SourceType = 2222; // marker + else if (transferRequest.TransferInfo.SourceType == 3) + transferRequest.TransferInfo.SourceType = 3333; // marker + + m_assetService.Get(userAssets + "/" + id, transferRequest, AssetReceived); + return; + } } //m_log.DebugFormat("[ASSET CACHE]: Asset transfer request for asset which is {0} already known to be missing. Dropping", requestID); -- cgit v1.1 From 3828b3c0e86bbb444dabadcc6e509675b058639e Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 10:38:35 -0700 Subject: * Eliminated unnecessary parameters from LLUDPServer.SendPacketData() * Changed PrimMesher's Quat.Identity to return <0,0,0,1> instead of <0,0,0,1.1> --- OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 13 ++++++++----- OpenSim/Region/Physics/Meshing/PrimMesher.cs | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index fcb2cd0..07764cb 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -210,7 +210,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP byte[] data = datas[i]; m_clients.ForEach( delegate(LLUDPClient client) - { SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); }); + { SendPacketData(client, data, packet.Type, category); }); } } else @@ -218,7 +218,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP byte[] data = packet.ToBytes(); m_clients.ForEach( delegate(LLUDPClient client) - { SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); }); + { SendPacketData(client, data, packet.Type, category); }); } } @@ -239,18 +239,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < packetCount; i++) { byte[] data = datas[i]; - SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); + SendPacketData(client, data, packet.Type, category); } } else { byte[] data = packet.ToBytes(); - SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); + SendPacketData(client, data, packet.Type, category); } } - public void SendPacketData(LLUDPClient client, byte[] data, int dataLength, PacketType type, bool doZerocode, ThrottleOutPacketType category) + public void SendPacketData(LLUDPClient client, byte[] data, PacketType type, ThrottleOutPacketType category) { + int dataLength = data.Length; + bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; + // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum. // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here diff --git a/OpenSim/Region/Physics/Meshing/PrimMesher.cs b/OpenSim/Region/Physics/Meshing/PrimMesher.cs index a283840..47ce615 100644 --- a/OpenSim/Region/Physics/Meshing/PrimMesher.cs +++ b/OpenSim/Region/Physics/Meshing/PrimMesher.cs @@ -69,7 +69,7 @@ namespace PrimMesher public Quat Identity() { - return new Quat(0.0f, 0.0f, 0.0f, 1.1f); + return new Quat(0.0f, 0.0f, 0.0f, 1.0f); } public float Length() -- cgit v1.1 From 82ace481c9ea180e7e5b9d6d7c7c191e438fdbe1 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 11:14:45 -0700 Subject: * Broke the circular reference between LLClientView and LLUDPClient. This should speed up garbage collection on the large LLClientView objects, and also prevents handling packets for disconnected clients * Renamed local LLUDPClient variables to udpClient to avoid naming confusion between LLUDPClient and LLClientView --- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 2 - .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 134 ++++++++++++--------- 2 files changed, 77 insertions(+), 59 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 871e8e8..10e22d5 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -82,8 +82,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// ACKs that are queued up, waiting to be sent to the client public readonly LocklessQueue PendingAcks = new LocklessQueue(); - /// Reference to the IClientAPI for this client - public LLClientView ClientAPI; /// Current packet sequence number public int CurrentSequence; /// Current ping sequence number diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 07764cb..0b5b51d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -183,10 +183,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void RemoveClient(LLUDPClient udpClient) { - m_log.Debug("[LLUDPSERVER]: Removing LLUDPClient for " + udpClient.ClientAPI.Name); + m_log.Debug("[LLUDPSERVER]: Removing LLUDPClient for " + udpClient.AgentID); - m_scene.ClientManager.Remove(udpClient.CircuitCode); - udpClient.ClientAPI.Close(false); + // Shut down the IClientAPI and remove it from the scene + IClientAPI client; + if (m_scene.ClientManager.TryGetClient(udpClient.CircuitCode, out client)) + { + client.Close(false); + m_scene.ClientManager.Remove(udpClient.CircuitCode); + } + + // Shut down the LLUDPClient and remove it from the list of UDP clients udpClient.Shutdown(); m_clients.Remove(udpClient.RemoteEndPoint); } @@ -222,7 +229,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public void SendPacket(LLUDPClient client, Packet packet, ThrottleOutPacketType category, bool allowSplitting) + public void SendPacket(LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting) { // CoarseLocationUpdate packets cannot be split in an automated way if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting) @@ -239,17 +246,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < packetCount; i++) { byte[] data = datas[i]; - SendPacketData(client, data, packet.Type, category); + SendPacketData(udpClient, data, packet.Type, category); } } else { byte[] data = packet.ToBytes(); - SendPacketData(client, data, packet.Type, category); + SendPacketData(udpClient, data, packet.Type, category); } } - public void SendPacketData(LLUDPClient client, byte[] data, PacketType type, ThrottleOutPacketType category) + public void SendPacketData(LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category) { int dataLength = data.Length; bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; @@ -260,7 +267,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // to accomodate for both common scenarios and provide ample room for ACK appending in both int bufferSize = (dataLength > 180) ? Packet.MTU : 200; - UDPPacketBuffer buffer = new UDPPacketBuffer(client.RemoteEndPoint, bufferSize); + UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); // Zerocode if needed if (doZerocode) @@ -285,7 +292,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Queue or Send // Look up the UDPClient this is going to - OutgoingPacket outgoingPacket = new OutgoingPacket(client, buffer, category); + OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category); if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) SendPacketFinal(outgoingPacket); @@ -293,18 +300,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Queue or Send } - public void SendAcks(LLUDPClient client) + public void SendAcks(LLUDPClient udpClient) { uint ack; - if (client.PendingAcks.Dequeue(out ack)) + if (udpClient.PendingAcks.Dequeue(out ack)) { List blocks = new List(); PacketAckPacket.PacketsBlock block = new PacketAckPacket.PacketsBlock(); block.ID = ack; blocks.Add(block); - while (client.PendingAcks.Dequeue(out ack)) + while (udpClient.PendingAcks.Dequeue(out ack)) { block = new PacketAckPacket.PacketsBlock(); block.ID = ack; @@ -315,22 +322,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.Header.Reliable = false; packet.Packets = blocks.ToArray(); - SendPacket(client, packet, ThrottleOutPacketType.Unknown, true); + SendPacket(udpClient, packet, ThrottleOutPacketType.Unknown, true); } } - public void SendPing(LLUDPClient client) + public void SendPing(LLUDPClient udpClient) { - IClientAPI api = client.ClientAPI; - if (api != null) - api.SendStartPingCheck(client.CurrentPingSequence++); + StartPingCheckPacket pc = (StartPingCheckPacket)PacketPool.Instance.GetPacket(PacketType.StartPingCheck); + pc.Header.Reliable = false; + + OutgoingPacket oldestPacket = udpClient.NeedAcks.GetOldest(); + + pc.PingID.PingID = (byte)udpClient.CurrentPingSequence++; + pc.PingID.OldestUnacked = (oldestPacket != null) ? oldestPacket.SequenceNumber : 0; + + SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false); } - public void ResendUnacked(LLUDPClient client) + public void ResendUnacked(LLUDPClient udpClient) { - if (client.NeedAcks.Count > 0) + if (udpClient.NeedAcks.Count > 0) { - List expiredPackets = client.NeedAcks.GetExpiredPackets(client.RTO); + List expiredPackets = udpClient.NeedAcks.GetExpiredPackets(udpClient.RTO); if (expiredPackets != null) { @@ -366,18 +379,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.DebugFormat("[LLUDPSERVER]: Dropping packet #{0} for agent {1} after {2} failed attempts", outgoingPacket.SequenceNumber, outgoingPacket.Client.RemoteEndPoint, outgoingPacket.ResendCount); - lock (client.NeedAcks.SyncRoot) - client.NeedAcks.RemoveUnsafe(outgoingPacket.SequenceNumber); + lock (udpClient.NeedAcks.SyncRoot) + udpClient.NeedAcks.RemoveUnsafe(outgoingPacket.SequenceNumber); //Interlocked.Increment(ref Stats.DroppedPackets); // Disconnect an agent if no packets are received for some time //FIXME: Make 60 an .ini setting - if (Environment.TickCount - client.TickLastPacketReceived > 1000 * 60) + if (Environment.TickCount - udpClient.TickLastPacketReceived > 1000 * 60) { - m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + client.ClientAPI.Name); + m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID); - RemoveClient(client); + RemoveClient(udpClient); return; } } @@ -397,7 +410,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP byte flags = buffer.Data[0]; bool isResend = (flags & Helpers.MSG_RESENT) != 0; bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0; - LLUDPClient client = outgoingPacket.Client; + LLUDPClient udpClient = outgoingPacket.Client; // Keep track of when this packet was sent out (right now) outgoingPacket.TickCount = Environment.TickCount; @@ -410,7 +423,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // no more ACKs to append uint ackCount = 0; uint ack; - while (dataLength + 5 < buffer.Data.Length && client.PendingAcks.Dequeue(out ack)) + while (dataLength + 5 < buffer.Data.Length && udpClient.PendingAcks.Dequeue(out ack)) { Utils.UIntToBytesBig(ack, buffer.Data, dataLength); dataLength += 4; @@ -429,24 +442,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion ACK Appending + #region Sequence Number Assignment + if (!isResend) { // Not a resend, assign a new sequence number - uint sequenceNumber = (uint)Interlocked.Increment(ref client.CurrentSequence); + uint sequenceNumber = (uint)Interlocked.Increment(ref udpClient.CurrentSequence); Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1); outgoingPacket.SequenceNumber = sequenceNumber; if (isReliable) { // Add this packet to the list of ACK responses we are waiting on from the server - client.NeedAcks.Add(outgoingPacket); + udpClient.NeedAcks.Add(outgoingPacket); } } + #endregion Sequence Number Assignment + // Stats tracking - Interlocked.Increment(ref client.PacketsSent); + Interlocked.Increment(ref udpClient.PacketsSent); if (isReliable) - Interlocked.Add(ref client.UnackedBytes, outgoingPacket.Buffer.DataLength); + Interlocked.Add(ref udpClient.UnackedBytes, outgoingPacket.Buffer.DataLength); // Put the UDP payload on the wire AsyncBeginSend(buffer); @@ -455,7 +472,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected override void PacketReceived(UDPPacketBuffer buffer) { // Debugging/Profiling - //try { Thread.CurrentThread.Name = "PacketReceived (" + scene.RegionName + ")"; } + //try { Thread.CurrentThread.Name = "PacketReceived (" + m_scene.RegionInfo.RegionName + ")"; } //catch (Exception) { } LLUDPClient client = null; @@ -484,9 +501,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP return; } - //Stats.RecvBytes += (ulong)buffer.DataLength; - //++Stats.RecvPackets; - #endregion Decoding #region UseCircuitCode Handling @@ -508,7 +522,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (!m_clients.TryGetValue(address, out client)) { m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + - ", currently tracking " + m_clients.Count + " clients"); + " in " + m_scene.RegionInfo.RegionName + ", currently tracking " + m_clients.Count + " clients"); return; } @@ -549,7 +563,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region ACK Sending if (packet.Header.Reliable) - client.PendingAcks.Enqueue((uint)packet.Header.Sequence); + client.PendingAcks.Enqueue(packet.Header.Sequence); // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove @@ -648,9 +662,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_scene.ClientManager.Add(circuitCode, clientApi); clientApi.Start(); - // Give LLUDPClient a reference to IClientAPI - udpClient.ClientAPI = clientApi; - // Add the new client to our list of tracked clients m_clients.Add(udpClient.RemoteEndPoint, udpClient); } @@ -756,31 +767,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP { IncomingPacket incomingPacket = (IncomingPacket)state; Packet packet = incomingPacket.Packet; - LLUDPClient client = incomingPacket.Client; + LLUDPClient udpClient = incomingPacket.Client; + IClientAPI client; // Sanity check - if (packet == null || client == null || client.ClientAPI == null) + if (packet == null || udpClient == null) { - m_log.WarnFormat("[LLUDPSERVER]: Processing a packet with incomplete state. Packet=\"{0}\", Client=\"{1}\", Client.ClientAPI=\"{2}\"", - packet, client, (client != null) ? client.ClientAPI : null); + m_log.WarnFormat("[LLUDPSERVER]: Processing a packet with incomplete state. Packet=\"{0}\", UDPClient=\"{1}\"", + packet, udpClient); } - try + // Make sure this client is still alive + if (m_scene.ClientManager.TryGetClient(udpClient.CircuitCode, out client)) { - // Process this packet - client.ClientAPI.ProcessInPacket(packet); - } - catch (ThreadAbortException) - { - // If something is trying to abort the packet processing thread, take that as a hint that it's time to shut down - m_log.Info("[LLUDPSERVER]: Caught a thread abort, shutting down the LLUDP server"); - Stop(); + try + { + // Process this packet + client.ProcessInPacket(packet); + } + catch (ThreadAbortException) + { + // If something is trying to abort the packet processing thread, take that as a hint that it's time to shut down + m_log.Info("[LLUDPSERVER]: Caught a thread abort, shutting down the LLUDP server"); + Stop(); + } + catch (Exception e) + { + // Don't let a failure in an individual client thread crash the whole sim. + m_log.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", udpClient.AgentID, packet.Type); + m_log.Error(e.Message, e); + } } - catch (Exception e) + else { - // Don't let a failure in an individual client thread crash the whole sim. - m_log.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", client.AgentID, packet.Type); - m_log.Error(e.Message, e); + m_log.DebugFormat("[LLUDPSERVER]: Dropping incoming {0} packet for dead client {1}", packet.Type, udpClient.AgentID); } } -- cgit v1.1 From f55b282078e4e7c5ee7d0ca613891302d2b9957d Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 11:28:08 -0700 Subject: Avoid checking m_clients collection twice when a UseCircuitCode packet is received --- .../ClientStack/LindenUDP/LLUDPClientCollection.cs | 2 +- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 56 +++++++++++----------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs index 2972d46..4f375e4 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs @@ -131,7 +131,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Action to perform on each element public void ForEach(Action action) { - Parallel.ForEach(m_dict.Values, action); + Parallel.ForEach(m_dict.Values, action); } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 0b5b51d..2228f39 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -503,19 +503,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Decoding - #region UseCircuitCode Handling + #region Packet to Client Mapping + // UseCircuitCode handling if (packet.Type == PacketType.UseCircuitCode) { - UseCircuitCodePacket useCircuitCode = (UseCircuitCodePacket)packet; - IClientAPI newuser; - uint circuitCode = useCircuitCode.CircuitCode.Code; - - // Check if the client is already established - if (!m_scene.ClientManager.TryGetClient(circuitCode, out newuser)) - { - AddNewClient(useCircuitCode, (IPEndPoint)buffer.RemoteEndPoint); - } + AddNewClient((UseCircuitCodePacket)packet, (IPEndPoint)buffer.RemoteEndPoint); } // Determine which agent this packet came from @@ -526,7 +519,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return; } - #endregion UseCircuitCode Handling + #endregion Packet to Client Mapping // Stats tracking Interlocked.Increment(ref client.PacketsReceived); @@ -620,29 +613,38 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void AddNewClient(UseCircuitCodePacket useCircuitCode, IPEndPoint remoteEndPoint) { - //Slave regions don't accept new clients if (m_scene.RegionStatus != RegionStatus.SlaveScene) { - AuthenticateResponse sessionInfo; - bool isNewCircuit = !m_clients.ContainsKey(remoteEndPoint); - - if (!IsClientAuthorized(useCircuitCode, out sessionInfo)) + if (!m_clients.ContainsKey(remoteEndPoint)) { - m_log.WarnFormat( - "[CONNECTION FAILURE]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}", - useCircuitCode.CircuitCode.ID, useCircuitCode.CircuitCode.Code, remoteEndPoint); - return; - } + AuthenticateResponse sessionInfo; + if (IsClientAuthorized(useCircuitCode, out sessionInfo)) + { + UUID agentID = useCircuitCode.CircuitCode.ID; + UUID sessionID = useCircuitCode.CircuitCode.SessionID; + uint circuitCode = useCircuitCode.CircuitCode.Code; - if (isNewCircuit) + AddClient(circuitCode, agentID, sessionID, remoteEndPoint, sessionInfo); + } + else + { + // Don't create circuits for unauthorized clients + m_log.WarnFormat( + "[LLUDPSERVER]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}", + useCircuitCode.CircuitCode.ID, useCircuitCode.CircuitCode.Code, remoteEndPoint); + } + } + else { - UUID agentID = useCircuitCode.CircuitCode.ID; - UUID sessionID = useCircuitCode.CircuitCode.SessionID; - uint circuitCode = useCircuitCode.CircuitCode.Code; - - AddClient(circuitCode, agentID, sessionID, remoteEndPoint, sessionInfo); + // Ignore repeated UseCircuitCode packets + m_log.Debug("[LLUDPSERVER]: Ignoring UseCircuitCode for already established circuit " + useCircuitCode.CircuitCode.Code); } } + else + { + // Slave regions don't accept new clients + m_log.Debug("[LLUDPSERVER]: Slave region " + m_scene.RegionInfo.RegionName + " ignoring UseCircuitCode packet"); + } } private void AddClient(uint circuitCode, UUID agentID, UUID sessionID, IPEndPoint remoteEndPoint, AuthenticateResponse sessionInfo) -- cgit v1.1 From c893761319f7e61d13b2d2301180d0f227fde1a9 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 12:50:59 -0700 Subject: * Unregister event handlers in LLUDPServer when a client logs out and disconnects * Move ViewerEffect handling to Scene.PacketHandlers * Removing the unused CloseAllAgents function * Trimming ClientManager down. This class needs to be reworked to keep LLUDP circuit codes from intruding into the abstract OpenSim core code --- OpenSim/Framework/ClientManager.cs | 163 +------- OpenSim/Framework/IScene.cs | 1 - .../Region/ClientStack/LindenUDP/LLClientView.cs | 442 ++++++++++----------- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 18 +- .../Framework/Scenes/Scene.PacketHandlers.cs | 26 ++ OpenSim/Region/Framework/Scenes/Scene.cs | 15 +- OpenSim/Region/Framework/Scenes/SceneBase.cs | 2 - .../Framework/Scenes/Tests/SceneBaseTests.cs | 5 - 8 files changed, 262 insertions(+), 410 deletions(-) diff --git a/OpenSim/Framework/ClientManager.cs b/OpenSim/Framework/ClientManager.cs index 094a3ff..5ebbbc1 100644 --- a/OpenSim/Framework/ClientManager.cs +++ b/OpenSim/Framework/ClientManager.cs @@ -34,120 +34,33 @@ using OpenMetaverse.Packets; namespace OpenSim.Framework { - public delegate void ForEachClientDelegate(IClientAPI client); - public class ClientManager { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private Dictionary m_clients; - - public ClientManager() - { - m_clients = new Dictionary(); - } + private Dictionary m_clients = new Dictionary(); - public void ForEachClient(ForEachClientDelegate whatToDo) + public void Add(uint circuitCode, IClientAPI client) { - IClientAPI[] LocalClients; lock (m_clients) - { - LocalClients = new IClientAPI[m_clients.Count]; - m_clients.Values.CopyTo(LocalClients, 0); - } - - for (int i = 0; i < LocalClients.Length; i++) - { - try - { - whatToDo(LocalClients[i]); - } - catch (Exception e) - { - m_log.Warn("[CLIENT]: Unable to do ForEachClient for one of the clients" + "\n Reason: " + e.ToString()); - } - } + m_clients.Add(circuitCode, client); } - public void Remove(uint id) + public bool Remove(uint circuitCode) { lock (m_clients) - { - m_clients.Remove(id); - } - } - - public void Add(uint id, IClientAPI client) - { - lock (m_clients) - { - m_clients.Add(id, client); - } - } - - /// - /// Pass incoming packet to client. - /// - /// uint identifying the connection/client. - /// object containing the packet. - public void InPacket(uint circuitCode, object packet) - { - IClientAPI client; - bool tryGetRet = false; - - lock (m_clients) - tryGetRet = m_clients.TryGetValue(circuitCode, out client); - - if (tryGetRet) - { - client.InPacket(packet); - } + return m_clients.Remove(circuitCode); } - public void CloseAllAgents(uint circuitCode) + public bool TryGetClient(uint circuitCode, out IClientAPI user) { - IClientAPI client; - bool tryGetRet = false; lock (m_clients) - tryGetRet = m_clients.TryGetValue(circuitCode, out client); - if (tryGetRet) - { - CloseAllCircuits(client.AgentId); - } - } - - public void CloseAllCircuits(UUID agentId) - { - uint[] circuits = GetAllCircuits(agentId); - // We're using a for loop here so changes to the circuits don't cause it to completely fail. - - for (int i = 0; i < circuits.Length; i++) - { - IClientAPI client; - try - { - bool tryGetRet = false; - lock (m_clients) - tryGetRet = m_clients.TryGetValue(circuits[i], out client); - if (tryGetRet) - { - Remove(client.CircuitCode); - client.Close(false); - } - } - catch (Exception e) - { - m_log.Error(string.Format("[CLIENT]: Unable to shutdown circuit for: {0}\n Reason: {1}", agentId, e)); - } - } + return m_clients.TryGetValue(circuitCode, out user); } - // [Obsolete("Using Obsolete to drive development is invalid. Obsolete presumes that something new has already been created to replace this.")] - public uint[] GetAllCircuits(UUID agentId) + public void ForEachClient(Action action) { - List circuits = new List(); - // Wasteful, I know - IClientAPI[] LocalClients = new IClientAPI[0]; + IClientAPI[] LocalClients; lock (m_clients) { LocalClients = new IClientAPI[m_clients.Count]; @@ -156,65 +69,15 @@ namespace OpenSim.Framework for (int i = 0; i < LocalClients.Length; i++) { - if (LocalClients[i].AgentId == agentId) + try { - circuits.Add(LocalClients[i].CircuitCode); + action(LocalClients[i]); } - } - return circuits.ToArray(); - } - - public List GetAllCircuitCodes() - { - List circuits; - - lock (m_clients) - { - circuits = new List(m_clients.Keys); - } - - return circuits; - } - - public void ViewerEffectHandler(IClientAPI sender, List args) - { - // TODO: don't create new blocks if recycling an old packet - List effectBlock = new List(); - for (int i = 0; i < args.Count; i++) - { - ViewerEffectPacket.EffectBlock effect = new ViewerEffectPacket.EffectBlock(); - effect.AgentID = args[i].AgentID; - effect.Color = args[i].Color; - effect.Duration = args[i].Duration; - effect.ID = args[i].ID; - effect.Type = args[i].Type; - effect.TypeData = args[i].TypeData; - effectBlock.Add(effect); - } - ViewerEffectPacket.EffectBlock[] effectBlockArray = effectBlock.ToArray(); - - IClientAPI[] LocalClients; - lock (m_clients) - { - LocalClients = new IClientAPI[m_clients.Count]; - m_clients.Values.CopyTo(LocalClients, 0); - } - - for (int i = 0; i < LocalClients.Length; i++) - { - if (LocalClients[i].AgentId != sender.AgentId) + catch (Exception e) { - LocalClients[i].SendViewerEffect(effectBlockArray); + m_log.Warn("[CLIENT]: Unable to do ForEachClient for one of the clients" + "\n Reason: " + e.ToString()); } } } - - public bool TryGetClient(uint circuitId, out IClientAPI user) - { - lock (m_clients) - { - return m_clients.TryGetValue(circuitId, out user); - } - } } } diff --git a/OpenSim/Framework/IScene.cs b/OpenSim/Framework/IScene.cs index 489653f..f34027d 100644 --- a/OpenSim/Framework/IScene.cs +++ b/OpenSim/Framework/IScene.cs @@ -71,7 +71,6 @@ namespace OpenSim.Framework void AddNewClient(IClientAPI client); void RemoveClient(UUID agentID); - void CloseAllAgents(uint circuitcode); void Restart(int seconds); //RegionInfo OtherRegionUp(RegionInfo thisRegion); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 139dc3b..bc9cfcf 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -58,6 +58,209 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public class LLClientView : IClientAPI, IClientCore, IClientIM, IClientChat, IClientIPEndpoint, IStatsCollector { + #region Events + + public event GenericMessage OnGenericMessage; + public event BinaryGenericMessage OnBinaryGenericMessage; + public event Action OnLogout; + public event ObjectPermissions OnObjectPermissions; + public event Action OnConnectionClosed; + public event ViewerEffectEventHandler OnViewerEffect; + public event ImprovedInstantMessage OnInstantMessage; + public event ChatMessage OnChatFromClient; + public event TextureRequest OnRequestTexture; + public event RezObject OnRezObject; + public event DeRezObject OnDeRezObject; + public event ModifyTerrain OnModifyTerrain; + public event Action OnRegionHandShakeReply; + public event GenericCall2 OnRequestWearables; + public event SetAppearance OnSetAppearance; + public event AvatarNowWearing OnAvatarNowWearing; + public event RezSingleAttachmentFromInv OnRezSingleAttachmentFromInv; + public event RezMultipleAttachmentsFromInv OnRezMultipleAttachmentsFromInv; + public event UUIDNameRequest OnDetachAttachmentIntoInv; + public event ObjectAttach OnObjectAttach; + public event ObjectDeselect OnObjectDetach; + public event ObjectDrop OnObjectDrop; + public event GenericCall2 OnCompleteMovementToRegion; + public event UpdateAgent OnAgentUpdate; + public event AgentRequestSit OnAgentRequestSit; + public event AgentSit OnAgentSit; + public event AvatarPickerRequest OnAvatarPickerRequest; + public event StartAnim OnStartAnim; + public event StopAnim OnStopAnim; + public event Action OnRequestAvatarsData; + public event LinkObjects OnLinkObjects; + public event DelinkObjects OnDelinkObjects; + public event GrabObject OnGrabObject; + public event DeGrabObject OnDeGrabObject; + public event SpinStart OnSpinStart; + public event SpinStop OnSpinStop; + public event ObjectDuplicate OnObjectDuplicate; + public event ObjectDuplicateOnRay OnObjectDuplicateOnRay; + public event MoveObject OnGrabUpdate; + public event SpinObject OnSpinUpdate; + public event AddNewPrim OnAddPrim; + public event RequestGodlikePowers OnRequestGodlikePowers; + public event GodKickUser OnGodKickUser; + public event ObjectExtraParams OnUpdateExtraParams; + public event UpdateShape OnUpdatePrimShape; + public event ObjectRequest OnObjectRequest; + public event ObjectSelect OnObjectSelect; + public event ObjectDeselect OnObjectDeselect; + public event GenericCall7 OnObjectDescription; + public event GenericCall7 OnObjectName; + public event GenericCall7 OnObjectClickAction; + public event GenericCall7 OnObjectMaterial; + public event ObjectIncludeInSearch OnObjectIncludeInSearch; + public event RequestObjectPropertiesFamily OnRequestObjectPropertiesFamily; + public event UpdatePrimFlags OnUpdatePrimFlags; + public event UpdatePrimTexture OnUpdatePrimTexture; + public event UpdateVector OnUpdatePrimGroupPosition; + public event UpdateVector OnUpdatePrimSinglePosition; + public event UpdatePrimRotation OnUpdatePrimGroupRotation; + public event UpdatePrimSingleRotation OnUpdatePrimSingleRotation; + public event UpdatePrimSingleRotationPosition OnUpdatePrimSingleRotationPosition; + public event UpdatePrimGroupRotation OnUpdatePrimGroupMouseRotation; + public event UpdateVector OnUpdatePrimScale; + public event UpdateVector OnUpdatePrimGroupScale; + public event StatusChange OnChildAgentStatus; + public event GenericCall2 OnStopMovement; + public event Action OnRemoveAvatar; + public event RequestMapBlocks OnRequestMapBlocks; + public event RequestMapName OnMapNameRequest; + public event TeleportLocationRequest OnTeleportLocationRequest; + public event TeleportLandmarkRequest OnTeleportLandmarkRequest; + public event DisconnectUser OnDisconnectUser; + public event RequestAvatarProperties OnRequestAvatarProperties; + public event SetAlwaysRun OnSetAlwaysRun; + public event FetchInventory OnAgentDataUpdateRequest; + public event TeleportLocationRequest OnSetStartLocationRequest; + public event UpdateAvatarProperties OnUpdateAvatarProperties; + public event CreateNewInventoryItem OnCreateNewInventoryItem; + public event CreateInventoryFolder OnCreateNewInventoryFolder; + public event UpdateInventoryFolder OnUpdateInventoryFolder; + public event MoveInventoryFolder OnMoveInventoryFolder; + public event FetchInventoryDescendents OnFetchInventoryDescendents; + public event PurgeInventoryDescendents OnPurgeInventoryDescendents; + public event FetchInventory OnFetchInventory; + public event RequestTaskInventory OnRequestTaskInventory; + public event UpdateInventoryItem OnUpdateInventoryItem; + public event CopyInventoryItem OnCopyInventoryItem; + public event MoveInventoryItem OnMoveInventoryItem; + public event RemoveInventoryItem OnRemoveInventoryItem; + public event RemoveInventoryFolder OnRemoveInventoryFolder; + public event UDPAssetUploadRequest OnAssetUploadRequest; + public event XferReceive OnXferReceive; + public event RequestXfer OnRequestXfer; + public event ConfirmXfer OnConfirmXfer; + public event AbortXfer OnAbortXfer; + public event RequestTerrain OnRequestTerrain; + public event RezScript OnRezScript; + public event UpdateTaskInventory OnUpdateTaskInventory; + public event MoveTaskInventory OnMoveTaskItem; + public event RemoveTaskInventory OnRemoveTaskItem; + public event RequestAsset OnRequestAsset; + public event UUIDNameRequest OnNameFromUUIDRequest; + public event ParcelAccessListRequest OnParcelAccessListRequest; + public event ParcelAccessListUpdateRequest OnParcelAccessListUpdateRequest; + public event ParcelPropertiesRequest OnParcelPropertiesRequest; + public event ParcelDivideRequest OnParcelDivideRequest; + public event ParcelJoinRequest OnParcelJoinRequest; + public event ParcelPropertiesUpdateRequest OnParcelPropertiesUpdateRequest; + public event ParcelSelectObjects OnParcelSelectObjects; + public event ParcelObjectOwnerRequest OnParcelObjectOwnerRequest; + public event ParcelAbandonRequest OnParcelAbandonRequest; + public event ParcelGodForceOwner OnParcelGodForceOwner; + public event ParcelReclaim OnParcelReclaim; + public event ParcelReturnObjectsRequest OnParcelReturnObjectsRequest; + public event ParcelDeedToGroup OnParcelDeedToGroup; + public event RegionInfoRequest OnRegionInfoRequest; + public event EstateCovenantRequest OnEstateCovenantRequest; + public event FriendActionDelegate OnApproveFriendRequest; + public event FriendActionDelegate OnDenyFriendRequest; + public event FriendshipTermination OnTerminateFriendship; + public event MoneyTransferRequest OnMoneyTransferRequest; + public event EconomyDataRequest OnEconomyDataRequest; + public event MoneyBalanceRequest OnMoneyBalanceRequest; + public event ParcelBuy OnParcelBuy; + public event UUIDNameRequest OnTeleportHomeRequest; + public event UUIDNameRequest OnUUIDGroupNameRequest; + public event ScriptAnswer OnScriptAnswer; + public event RequestPayPrice OnRequestPayPrice; + public event ObjectSaleInfo OnObjectSaleInfo; + public event ObjectBuy OnObjectBuy; + public event BuyObjectInventory OnBuyObjectInventory; + public event AgentSit OnUndo; + public event ForceReleaseControls OnForceReleaseControls; + public event GodLandStatRequest OnLandStatRequest; + public event RequestObjectPropertiesFamily OnObjectGroupRequest; + public event DetailedEstateDataRequest OnDetailedEstateDataRequest; + public event SetEstateFlagsRequest OnSetEstateFlagsRequest; + public event SetEstateTerrainBaseTexture OnSetEstateTerrainBaseTexture; + public event SetEstateTerrainDetailTexture OnSetEstateTerrainDetailTexture; + public event SetEstateTerrainTextureHeights OnSetEstateTerrainTextureHeights; + public event CommitEstateTerrainTextureRequest OnCommitEstateTerrainTextureRequest; + public event SetRegionTerrainSettings OnSetRegionTerrainSettings; + public event BakeTerrain OnBakeTerrain; + public event RequestTerrain OnUploadTerrain; + public event EstateChangeInfo OnEstateChangeInfo; + public event EstateRestartSimRequest OnEstateRestartSimRequest; + public event EstateChangeCovenantRequest OnEstateChangeCovenantRequest; + public event UpdateEstateAccessDeltaRequest OnUpdateEstateAccessDeltaRequest; + public event SimulatorBlueBoxMessageRequest OnSimulatorBlueBoxMessageRequest; + public event EstateBlueBoxMessageRequest OnEstateBlueBoxMessageRequest; + public event EstateDebugRegionRequest OnEstateDebugRegionRequest; + public event EstateTeleportOneUserHomeRequest OnEstateTeleportOneUserHomeRequest; + public event EstateTeleportAllUsersHomeRequest OnEstateTeleportAllUsersHomeRequest; + public event RegionHandleRequest OnRegionHandleRequest; + public event ParcelInfoRequest OnParcelInfoRequest; + public event ScriptReset OnScriptReset; + public event GetScriptRunning OnGetScriptRunning; + public event SetScriptRunning OnSetScriptRunning; + public event UpdateVector OnAutoPilotGo; + public event TerrainUnacked OnUnackedTerrain; + public event ActivateGesture OnActivateGesture; + public event DeactivateGesture OnDeactivateGesture; + public event ObjectOwner OnObjectOwner; + public event DirPlacesQuery OnDirPlacesQuery; + public event DirFindQuery OnDirFindQuery; + public event DirLandQuery OnDirLandQuery; + public event DirPopularQuery OnDirPopularQuery; + public event DirClassifiedQuery OnDirClassifiedQuery; + public event EventInfoRequest OnEventInfoRequest; + public event ParcelSetOtherCleanTime OnParcelSetOtherCleanTime; + public event MapItemRequest OnMapItemRequest; + public event OfferCallingCard OnOfferCallingCard; + public event AcceptCallingCard OnAcceptCallingCard; + public event DeclineCallingCard OnDeclineCallingCard; + public event SoundTrigger OnSoundTrigger; + public event StartLure OnStartLure; + public event TeleportLureRequest OnTeleportLureRequest; + public event NetworkStats OnNetworkStatsUpdate; + public event ClassifiedInfoRequest OnClassifiedInfoRequest; + public event ClassifiedInfoUpdate OnClassifiedInfoUpdate; + public event ClassifiedDelete OnClassifiedDelete; + public event ClassifiedDelete OnClassifiedGodDelete; + public event EventNotificationAddRequest OnEventNotificationAddRequest; + public event EventNotificationRemoveRequest OnEventNotificationRemoveRequest; + public event EventGodDelete OnEventGodDelete; + public event ParcelDwellRequest OnParcelDwellRequest; + public event UserInfoRequest OnUserInfoRequest; + public event UpdateUserInfo OnUpdateUserInfo; + public event RetrieveInstantMessages OnRetrieveInstantMessages; + public event PickDelete OnPickDelete; + public event PickGodDelete OnPickGodDelete; + public event PickInfoUpdate OnPickInfoUpdate; + public event AvatarNotesUpdate OnAvatarNotesUpdate; + public event MuteListRequest OnMuteListRequest; + public event AvatarInterestUpdate OnAvatarInterestUpdate; + public event PlacesQuery OnPlacesQuery; + + #endregion Events + + #region Class Members + // LLClientView Only public delegate void BinaryGenericMessage(Object sender, string method, byte[][] args); @@ -114,9 +317,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected int m_textureDataLimit = 10; protected int m_avatarTerseUpdateRate = 50; protected int m_avatarTerseUpdatesPerPacket = 5; - protected int m_packetMTU = 1400; protected IAssetService m_assetService; + #endregion Class Members + #region Properties public LLUDPClient UDPClient { get { return m_udpClient; } } @@ -413,34 +617,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP return result; } - /*protected void DebugPacket(string direction, Packet packet) - { - string info; - - if (m_debugPacketLevel < 255 && packet.Type == PacketType.AgentUpdate) - return; - if (m_debugPacketLevel < 254 && packet.Type == PacketType.ViewerEffect) - return; - if (m_debugPacketLevel < 253 && ( - packet.Type == PacketType.CompletePingCheck || - packet.Type == PacketType.StartPingCheck - )) - return; - if (m_debugPacketLevel < 252 && packet.Type == PacketType.PacketAck) - return; - - if (m_debugPacketLevel > 1) - { - info = packet.ToString(); - } - else - { - info = packet.Type.ToString(); - } - - Console.WriteLine(m_circuitCode + ":" + direction + ": " + info); - }*/ - #endregion Packet Handling # region Setup @@ -523,207 +699,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP # endregion - #region Events - - public event GenericMessage OnGenericMessage; - public event BinaryGenericMessage OnBinaryGenericMessage; - public event Action OnLogout; - public event ObjectPermissions OnObjectPermissions; - public event Action OnConnectionClosed; - public event ViewerEffectEventHandler OnViewerEffect; - public event ImprovedInstantMessage OnInstantMessage; - public event ChatMessage OnChatFromClient; - public event TextureRequest OnRequestTexture; - public event RezObject OnRezObject; - public event DeRezObject OnDeRezObject; - public event ModifyTerrain OnModifyTerrain; - public event Action OnRegionHandShakeReply; - public event GenericCall2 OnRequestWearables; - public event SetAppearance OnSetAppearance; - public event AvatarNowWearing OnAvatarNowWearing; - public event RezSingleAttachmentFromInv OnRezSingleAttachmentFromInv; - public event RezMultipleAttachmentsFromInv OnRezMultipleAttachmentsFromInv; - public event UUIDNameRequest OnDetachAttachmentIntoInv; - public event ObjectAttach OnObjectAttach; - public event ObjectDeselect OnObjectDetach; - public event ObjectDrop OnObjectDrop; - public event GenericCall2 OnCompleteMovementToRegion; - public event UpdateAgent OnAgentUpdate; - public event AgentRequestSit OnAgentRequestSit; - public event AgentSit OnAgentSit; - public event AvatarPickerRequest OnAvatarPickerRequest; - public event StartAnim OnStartAnim; - public event StopAnim OnStopAnim; - public event Action OnRequestAvatarsData; - public event LinkObjects OnLinkObjects; - public event DelinkObjects OnDelinkObjects; - public event GrabObject OnGrabObject; - public event DeGrabObject OnDeGrabObject; - public event SpinStart OnSpinStart; - public event SpinStop OnSpinStop; - public event ObjectDuplicate OnObjectDuplicate; - public event ObjectDuplicateOnRay OnObjectDuplicateOnRay; - public event MoveObject OnGrabUpdate; - public event SpinObject OnSpinUpdate; - public event AddNewPrim OnAddPrim; - public event RequestGodlikePowers OnRequestGodlikePowers; - public event GodKickUser OnGodKickUser; - public event ObjectExtraParams OnUpdateExtraParams; - public event UpdateShape OnUpdatePrimShape; - public event ObjectRequest OnObjectRequest; - public event ObjectSelect OnObjectSelect; - public event ObjectDeselect OnObjectDeselect; - public event GenericCall7 OnObjectDescription; - public event GenericCall7 OnObjectName; - public event GenericCall7 OnObjectClickAction; - public event GenericCall7 OnObjectMaterial; - public event ObjectIncludeInSearch OnObjectIncludeInSearch; - public event RequestObjectPropertiesFamily OnRequestObjectPropertiesFamily; - public event UpdatePrimFlags OnUpdatePrimFlags; - public event UpdatePrimTexture OnUpdatePrimTexture; - public event UpdateVector OnUpdatePrimGroupPosition; - public event UpdateVector OnUpdatePrimSinglePosition; - public event UpdatePrimRotation OnUpdatePrimGroupRotation; - public event UpdatePrimSingleRotation OnUpdatePrimSingleRotation; - public event UpdatePrimSingleRotationPosition OnUpdatePrimSingleRotationPosition; - public event UpdatePrimGroupRotation OnUpdatePrimGroupMouseRotation; - public event UpdateVector OnUpdatePrimScale; - public event UpdateVector OnUpdatePrimGroupScale; - public event StatusChange OnChildAgentStatus; - public event GenericCall2 OnStopMovement; - public event Action OnRemoveAvatar; - public event RequestMapBlocks OnRequestMapBlocks; - public event RequestMapName OnMapNameRequest; - public event TeleportLocationRequest OnTeleportLocationRequest; - public event TeleportLandmarkRequest OnTeleportLandmarkRequest; - public event DisconnectUser OnDisconnectUser; - public event RequestAvatarProperties OnRequestAvatarProperties; - public event SetAlwaysRun OnSetAlwaysRun; - public event FetchInventory OnAgentDataUpdateRequest; - public event TeleportLocationRequest OnSetStartLocationRequest; - public event UpdateAvatarProperties OnUpdateAvatarProperties; - public event CreateNewInventoryItem OnCreateNewInventoryItem; - public event CreateInventoryFolder OnCreateNewInventoryFolder; - public event UpdateInventoryFolder OnUpdateInventoryFolder; - public event MoveInventoryFolder OnMoveInventoryFolder; - public event FetchInventoryDescendents OnFetchInventoryDescendents; - public event PurgeInventoryDescendents OnPurgeInventoryDescendents; - public event FetchInventory OnFetchInventory; - public event RequestTaskInventory OnRequestTaskInventory; - public event UpdateInventoryItem OnUpdateInventoryItem; - public event CopyInventoryItem OnCopyInventoryItem; - public event MoveInventoryItem OnMoveInventoryItem; - public event RemoveInventoryItem OnRemoveInventoryItem; - public event RemoveInventoryFolder OnRemoveInventoryFolder; - public event UDPAssetUploadRequest OnAssetUploadRequest; - public event XferReceive OnXferReceive; - public event RequestXfer OnRequestXfer; - public event ConfirmXfer OnConfirmXfer; - public event AbortXfer OnAbortXfer; - public event RequestTerrain OnRequestTerrain; - public event RezScript OnRezScript; - public event UpdateTaskInventory OnUpdateTaskInventory; - public event MoveTaskInventory OnMoveTaskItem; - public event RemoveTaskInventory OnRemoveTaskItem; - public event RequestAsset OnRequestAsset; - public event UUIDNameRequest OnNameFromUUIDRequest; - public event ParcelAccessListRequest OnParcelAccessListRequest; - public event ParcelAccessListUpdateRequest OnParcelAccessListUpdateRequest; - public event ParcelPropertiesRequest OnParcelPropertiesRequest; - public event ParcelDivideRequest OnParcelDivideRequest; - public event ParcelJoinRequest OnParcelJoinRequest; - public event ParcelPropertiesUpdateRequest OnParcelPropertiesUpdateRequest; - public event ParcelSelectObjects OnParcelSelectObjects; - public event ParcelObjectOwnerRequest OnParcelObjectOwnerRequest; - public event ParcelAbandonRequest OnParcelAbandonRequest; - public event ParcelGodForceOwner OnParcelGodForceOwner; - public event ParcelReclaim OnParcelReclaim; - public event ParcelReturnObjectsRequest OnParcelReturnObjectsRequest; - public event ParcelDeedToGroup OnParcelDeedToGroup; - public event RegionInfoRequest OnRegionInfoRequest; - public event EstateCovenantRequest OnEstateCovenantRequest; - public event FriendActionDelegate OnApproveFriendRequest; - public event FriendActionDelegate OnDenyFriendRequest; - public event FriendshipTermination OnTerminateFriendship; - public event MoneyTransferRequest OnMoneyTransferRequest; - public event EconomyDataRequest OnEconomyDataRequest; - public event MoneyBalanceRequest OnMoneyBalanceRequest; - public event ParcelBuy OnParcelBuy; - public event UUIDNameRequest OnTeleportHomeRequest; - public event UUIDNameRequest OnUUIDGroupNameRequest; - public event ScriptAnswer OnScriptAnswer; - public event RequestPayPrice OnRequestPayPrice; - public event ObjectSaleInfo OnObjectSaleInfo; - public event ObjectBuy OnObjectBuy; - public event BuyObjectInventory OnBuyObjectInventory; - public event AgentSit OnUndo; - public event ForceReleaseControls OnForceReleaseControls; - public event GodLandStatRequest OnLandStatRequest; - public event RequestObjectPropertiesFamily OnObjectGroupRequest; - public event DetailedEstateDataRequest OnDetailedEstateDataRequest; - public event SetEstateFlagsRequest OnSetEstateFlagsRequest; - public event SetEstateTerrainBaseTexture OnSetEstateTerrainBaseTexture; - public event SetEstateTerrainDetailTexture OnSetEstateTerrainDetailTexture; - public event SetEstateTerrainTextureHeights OnSetEstateTerrainTextureHeights; - public event CommitEstateTerrainTextureRequest OnCommitEstateTerrainTextureRequest; - public event SetRegionTerrainSettings OnSetRegionTerrainSettings; - public event BakeTerrain OnBakeTerrain; - public event RequestTerrain OnUploadTerrain; - public event EstateChangeInfo OnEstateChangeInfo; - public event EstateRestartSimRequest OnEstateRestartSimRequest; - public event EstateChangeCovenantRequest OnEstateChangeCovenantRequest; - public event UpdateEstateAccessDeltaRequest OnUpdateEstateAccessDeltaRequest; - public event SimulatorBlueBoxMessageRequest OnSimulatorBlueBoxMessageRequest; - public event EstateBlueBoxMessageRequest OnEstateBlueBoxMessageRequest; - public event EstateDebugRegionRequest OnEstateDebugRegionRequest; - public event EstateTeleportOneUserHomeRequest OnEstateTeleportOneUserHomeRequest; - public event EstateTeleportAllUsersHomeRequest OnEstateTeleportAllUsersHomeRequest; - public event RegionHandleRequest OnRegionHandleRequest; - public event ParcelInfoRequest OnParcelInfoRequest; - public event ScriptReset OnScriptReset; - public event GetScriptRunning OnGetScriptRunning; - public event SetScriptRunning OnSetScriptRunning; - public event UpdateVector OnAutoPilotGo; - public event TerrainUnacked OnUnackedTerrain; - public event ActivateGesture OnActivateGesture; - public event DeactivateGesture OnDeactivateGesture; - public event ObjectOwner OnObjectOwner; - public event DirPlacesQuery OnDirPlacesQuery; - public event DirFindQuery OnDirFindQuery; - public event DirLandQuery OnDirLandQuery; - public event DirPopularQuery OnDirPopularQuery; - public event DirClassifiedQuery OnDirClassifiedQuery; - public event EventInfoRequest OnEventInfoRequest; - public event ParcelSetOtherCleanTime OnParcelSetOtherCleanTime; - public event MapItemRequest OnMapItemRequest; - public event OfferCallingCard OnOfferCallingCard; - public event AcceptCallingCard OnAcceptCallingCard; - public event DeclineCallingCard OnDeclineCallingCard; - public event SoundTrigger OnSoundTrigger; - public event StartLure OnStartLure; - public event TeleportLureRequest OnTeleportLureRequest; - public event NetworkStats OnNetworkStatsUpdate; - public event ClassifiedInfoRequest OnClassifiedInfoRequest; - public event ClassifiedInfoUpdate OnClassifiedInfoUpdate; - public event ClassifiedDelete OnClassifiedDelete; - public event ClassifiedDelete OnClassifiedGodDelete; - public event EventNotificationAddRequest OnEventNotificationAddRequest; - public event EventNotificationRemoveRequest OnEventNotificationRemoveRequest; - public event EventGodDelete OnEventGodDelete; - public event ParcelDwellRequest OnParcelDwellRequest; - public event UserInfoRequest OnUserInfoRequest; - public event UpdateUserInfo OnUpdateUserInfo; - public event RetrieveInstantMessages OnRetrieveInstantMessages; - public event PickDelete OnPickDelete; - public event PickGodDelete OnPickGodDelete; - public event PickInfoUpdate OnPickInfoUpdate; - public event AvatarNotesUpdate OnAvatarNotesUpdate; - public event MuteListRequest OnMuteListRequest; - public event AvatarInterestUpdate OnAvatarInterestUpdate; - public event PlacesQuery OnPlacesQuery; - - #endregion Events - public void ActivateGesture(UUID assetId, UUID gestureId) { } @@ -3367,7 +3342,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP int length = 0; m_avatarTerseUpdates[count].ToBytes(blockbuffer, ref length); length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); - if (size + length > m_packetMTU) + if (size + length > Packet.MTU) break; size += length; } @@ -3611,7 +3586,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP int length = 0; m_primFullUpdates[count].ToBytes(blockbuffer, ref length); length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); - if (size + length > m_packetMTU) + if (size + length > Packet.MTU) break; size += length; } @@ -3699,7 +3674,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP int length = 0; m_primTerseUpdates[count].ToBytes(blockbuffer, ref length); length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); - if (size + length > m_packetMTU) + if (size + length > Packet.MTU) break; size += length; } @@ -4802,7 +4777,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP public bool HandleObjectGroupRequest(IClientAPI sender, Packet Pack) { - ObjectGroupPacket ogpack = (ObjectGroupPacket)Pack; if (ogpack.AgentData.SessionID != SessionId) return false; diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 2228f39..04c9cb1 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -419,7 +419,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP int dataLength = buffer.DataLength; - // Keep appending ACKs until there is no room left in the packet or there are + // Keep appending ACKs until there is no room left in the buffer or there are // no more ACKs to append uint ackCount = 0; uint ack; @@ -654,11 +654,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Create the LLClientView LLClientView clientApi = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); - clientApi.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler; clientApi.OnLogout += LogoutHandler; - clientApi.OnConnectionClosed += - delegate(IClientAPI client) - { if (client is LLClientView) RemoveClient(((LLClientView)client).UDPClient); }; + clientApi.OnConnectionClosed += ConnectionClosedHandler; // Start the IClientAPI m_scene.ClientManager.Add(circuitCode, clientApi); @@ -808,7 +805,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void LogoutHandler(IClientAPI client) { + client.OnLogout -= LogoutHandler; + client.SendLogoutPacket(); + + if (client is LLClientView) + RemoveClient(((LLClientView)client).UDPClient); + } + + private void ConnectionClosedHandler(IClientAPI client) + { + client.OnConnectionClosed -= ConnectionClosedHandler; + if (client is LLClientView) RemoveClient(((LLClientView)client).UDPClient); } diff --git a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs index 4ae4dc3..dbbf679 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs @@ -390,6 +390,32 @@ namespace OpenSim.Region.Framework.Scenes EventManager.TriggerScriptReset(part.LocalId, itemID); } } + + void ProcessViewerEffect(IClientAPI remoteClient, List args) + { + // TODO: don't create new blocks if recycling an old packet + List effectBlock = new List(); + for (int i = 0; i < args.Count; i++) + { + ViewerEffectPacket.EffectBlock effect = new ViewerEffectPacket.EffectBlock(); + effect.AgentID = args[i].AgentID; + effect.Color = args[i].Color; + effect.Duration = args[i].Duration; + effect.ID = args[i].ID; + effect.Type = args[i].Type; + effect.TypeData = args[i].TypeData; + effectBlock.Add(effect); + } + ViewerEffectPacket.EffectBlock[] effectBlockArray = effectBlock.ToArray(); + + ClientManager.ForEachClient( + delegate(IClientAPI client) + { + if (client.AgentId != remoteClient.AgentId) + client.SendViewerEffect(effectBlockArray); + } + ); + } /// /// Handle a fetch inventory request from the client diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index cffb23c..e81b07b 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -2572,6 +2572,7 @@ namespace OpenSim.Region.Framework.Scenes public virtual void SubscribeToClientNetworkEvents(IClientAPI client) { client.OnNetworkStatsUpdate += StatsReporter.AddPacketsStats; + client.OnViewerEffect += ProcessViewerEffect; } protected virtual void UnsubscribeToClientEvents(IClientAPI client) @@ -2726,11 +2727,9 @@ namespace OpenSim.Region.Framework.Scenes public virtual void UnSubscribeToClientNetworkEvents(IClientAPI client) { client.OnNetworkStatsUpdate -= StatsReporter.AddPacketsStats; + client.OnViewerEffect -= ProcessViewerEffect; } - - - /// /// Teleport an avatar to their home region /// @@ -3053,16 +3052,6 @@ namespace OpenSim.Region.Framework.Scenes } /// - /// Closes all endpoints with the circuitcode provided. - /// - /// Circuit Code of the endpoint to close - public override void CloseAllAgents(uint circuitcode) - { - // Called by ClientView to kill all circuit codes - ClientManager.CloseAllAgents(circuitcode); - } - - /// /// Inform all other ScenePresences on this Scene that someone else has changed position on the minimap. /// public void NotifyMyCoarseLocationChange() diff --git a/OpenSim/Region/Framework/Scenes/SceneBase.cs b/OpenSim/Region/Framework/Scenes/SceneBase.cs index 0ac4ed4..cf5c3c8 100644 --- a/OpenSim/Region/Framework/Scenes/SceneBase.cs +++ b/OpenSim/Region/Framework/Scenes/SceneBase.cs @@ -196,8 +196,6 @@ namespace OpenSim.Region.Framework.Scenes /// public abstract void RemoveClient(UUID agentID); - public abstract void CloseAllAgents(uint circuitcode); - #endregion /// diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneBaseTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneBaseTests.cs index 5c9e66f..8230f32 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/SceneBaseTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/SceneBaseTests.cs @@ -61,11 +61,6 @@ namespace OpenSim.Region.Framework.Scenes.Tests throw new NotImplementedException(); } - public override void CloseAllAgents(uint circuitcode) - { - throw new NotImplementedException(); - } - public override void OtherRegionUp(GridRegion otherRegion) { throw new NotImplementedException(); -- cgit v1.1 From 23a334b9f54a1ef5df3b503c165e7b76b746a2b1 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 14:50:03 -0700 Subject: * Rewrote ClientManager to remove Lindenisms from OpenSim core, improve performance by removing locks, and replace LLUDPClientCollection * Removed the confusing (and LL-specific) shutdowncircuit parameter from IClientAPI.Close() * Updated the LLUDP code to only use ClientManager instead of trying to synchronize ClientManager and m_clients * Remove clients asynchronously since it is a very slow operation (including a 2000ms sleep) --- OpenSim/Client/MXP/ClientStack/MXPClientView.cs | 17 ++- .../Client/MXP/PacketHandler/MXPPacketServer.cs | 2 +- .../Client/VWoHTTP/ClientStack/VWHClientView.cs | 2 +- OpenSim/Framework/ClientManager.cs | 170 +++++++++++++++++---- OpenSim/Framework/IClientAPI.cs | 2 +- .../Region/ClientStack/LindenUDP/LLClientView.cs | 77 +++------- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 1 - .../ClientStack/LindenUDP/LLUDPClientCollection.cs | 137 ----------------- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 163 ++++++++++---------- .../Region/CoreModules/Avatar/Gods/GodsModule.cs | 18 ++- .../InterGrid/OpenGridProtocolModule.cs | 2 +- .../Region/Examples/SimpleModule/MyNpcCharacter.cs | 2 +- .../Framework/Scenes/Scene.PacketHandlers.cs | 2 +- OpenSim/Region/Framework/Scenes/Scene.cs | 6 +- .../InternetRelayClientView/IRCStackModule.cs | 2 +- .../Server/IRCClientView.cs | 8 +- .../ContentManagementSystem/MetaEntity.cs | 10 +- .../Region/OptionalModules/World/NPC/NPCAvatar.cs | 14 +- .../Region/OptionalModules/World/NPC/NPCModule.cs | 2 +- OpenSim/Tests/Common/Mock/TestClient.cs | 2 +- prebuild.xml | 1 + 21 files changed, 311 insertions(+), 329 deletions(-) delete mode 100644 OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs index bc1b2e5..bc02bc4 100644 --- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs +++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs @@ -774,6 +774,11 @@ namespace OpenSim.Client.MXP.ClientStack get { return m_sessionID.CRC(); } } + public IPEndPoint RemoteEndPoint + { + get { return Session.RemoteEndPoint; } + } + public void SetDebugPacketLevel(int newDebug) { //m_debugLevel = newDebug; @@ -798,9 +803,9 @@ namespace OpenSim.Client.MXP.ClientStack OnConnectionClosed(this); } - public void Close(bool ShutdownCircuit) + public void Close() { - m_log.Info("[MXP ClientStack] Close Called with SC=" + ShutdownCircuit); + m_log.Info("[MXP ClientStack] Close Called"); // Tell the client to go SendLogoutPacket(); @@ -815,7 +820,7 @@ namespace OpenSim.Client.MXP.ClientStack public void Kick(string message) { - Close(false); + Close(); } public void Start() @@ -1448,7 +1453,7 @@ namespace OpenSim.Client.MXP.ClientStack public void Terminate() { - Close(false); + Close(); } public void SendSetFollowCamProperties(UUID objectID, SortedDictionary parameters) @@ -1615,12 +1620,12 @@ namespace OpenSim.Client.MXP.ClientStack public void Disconnect(string reason) { Kick(reason); - Close(true); + Close(); } public void Disconnect() { - Close(true); + Close(); } #endregion diff --git a/OpenSim/Client/MXP/PacketHandler/MXPPacketServer.cs b/OpenSim/Client/MXP/PacketHandler/MXPPacketServer.cs index 4910ab1..d5dc18f 100644 --- a/OpenSim/Client/MXP/PacketHandler/MXPPacketServer.cs +++ b/OpenSim/Client/MXP/PacketHandler/MXPPacketServer.cs @@ -341,7 +341,7 @@ namespace OpenSim.Client.MXP.PacketHandler m_log.Debug("[MXP ClientStack]: Adding ClientView to Scene..."); - scene.ClientManager.Add(client.CircuitCode, client); + scene.ClientManager.Add(client.AgentId, client.RemoteEndPoint, client); m_log.Debug("[MXP ClientStack]: Added ClientView to Scene."); diff --git a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs index c649c5a..9c24da5 100644 --- a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs +++ b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs @@ -415,7 +415,7 @@ namespace OpenSim.Client.VWoHTTP.ClientStack throw new System.NotImplementedException(); } - public void Close(bool ShutdownCircuit) + public void Close() { throw new System.NotImplementedException(); } diff --git a/OpenSim/Framework/ClientManager.cs b/OpenSim/Framework/ClientManager.cs index 5ebbbc1..4edfabe 100644 --- a/OpenSim/Framework/ClientManager.cs +++ b/OpenSim/Framework/ClientManager.cs @@ -28,56 +28,174 @@ using System; using System.Collections.Generic; using System.Reflection; -using log4net; +using System.Net; +using BclExtras.Collections; using OpenMetaverse; using OpenMetaverse.Packets; namespace OpenSim.Framework { + /// + /// Maps from client AgentID and RemoteEndPoint values to IClientAPI + /// references for all of the connected clients + /// public class ClientManager { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + #region IComparers - private Dictionary m_clients = new Dictionary(); - - public void Add(uint circuitCode, IClientAPI client) + private sealed class UUIDComparer : IComparer { - lock (m_clients) - m_clients.Add(circuitCode, client); + public int Compare(UUID x, UUID y) + { + return x.CompareTo(y); + } } - public bool Remove(uint circuitCode) + private sealed class IPEndPointComparer : IComparer { - lock (m_clients) - return m_clients.Remove(circuitCode); + public int Compare(IPEndPoint x, IPEndPoint y) + { + if (x == null && y == null) + return 0; + else if (x == null) + return -1; + else if (y == null) + return 1; + + int result = x.Address.Address.CompareTo(y.Address.Address); + if (result == 0) result = x.Port.CompareTo(y.Port); + + return result; + } } - public bool TryGetClient(uint circuitCode, out IClientAPI user) + #endregion IComparers + + /// An immutable dictionary mapping from + /// to references + private ImmutableMap m_dict; + /// An immutable dictionary mapping from + /// to references + private ImmutableMap m_dict2; + /// Immutability grants thread safety for concurrent reads and + /// read-writes, but not concurrent writes + private object m_writeLock = new object(); + + /// Number of clients in the collection + public int Count { get { return m_dict.Count; } } + + /// + /// Default constructor + /// + public ClientManager() { - lock (m_clients) - return m_clients.TryGetValue(circuitCode, out user); + m_dict = new ImmutableMap(new UUIDComparer()); + m_dict2 = new ImmutableMap(new IPEndPointComparer()); } - public void ForEachClient(Action action) + /// + /// Add a client reference to the collection if it does not already + /// exist + /// + /// UUID of the client + /// Remote endpoint of the client + /// Reference to the client object + /// True if the client reference was successfully added, + /// otherwise false if the given key already existed in the collection + public bool Add(UUID key, IPEndPoint key2, IClientAPI value) { - IClientAPI[] LocalClients; - lock (m_clients) + lock (m_writeLock) { - LocalClients = new IClientAPI[m_clients.Count]; - m_clients.Values.CopyTo(LocalClients, 0); - } - - for (int i = 0; i < LocalClients.Length; i++) - { - try + if (!m_dict.ContainsKey(key) && !m_dict2.ContainsKey(key2)) { - action(LocalClients[i]); + m_dict = m_dict.Add(key, value); + m_dict2 = m_dict2.Add(key2, value); + + return true; } - catch (Exception e) + else { - m_log.Warn("[CLIENT]: Unable to do ForEachClient for one of the clients" + "\n Reason: " + e.ToString()); + return false; } } } + + /// + /// Remove a client from the collection + /// + /// UUID of the client + /// Remote endpoint of the client + public void Remove(UUID key, IPEndPoint key2) + { + lock (m_writeLock) + { + m_dict = m_dict.Delete(key); + m_dict2 = m_dict2.Delete(key2); + } + } + + /// + /// Resets the client collection + /// + public void Clear() + { + lock (m_writeLock) + { + m_dict = new ImmutableMap(new UUIDComparer()); + m_dict2 = new ImmutableMap(new IPEndPointComparer()); + } + } + + /// + /// Checks if a UUID is in the collection + /// + /// UUID to check for + /// True if the UUID was found in the collection, otherwise false + public bool ContainsKey(UUID key) + { + return m_dict.ContainsKey(key); + } + + /// + /// Checks if an endpoint is in the collection + /// + /// Endpoint to check for + /// True if the endpoint was found in the collection, otherwise false + public bool ContainsKey(IPEndPoint key) + { + return m_dict2.ContainsKey(key); + } + + /// + /// Attempts to fetch a value out of the collection + /// + /// UUID of the client to retrieve + /// Retrieved client, or null on lookup failure + /// True if the lookup succeeded, otherwise false + public bool TryGetValue(UUID key, out IClientAPI value) + { + return m_dict.TryGetValue(key, out value); + } + + /// + /// Attempts to fetch a value out of the collection + /// + /// Endpoint of the client to retrieve + /// Retrieved client, or null on lookup failure + /// True if the lookup succeeded, otherwise false + public bool TryGetValue(IPEndPoint key, out IClientAPI value) + { + return m_dict2.TryGetValue(key, out value); + } + + /// + /// Performs a given task in parallel for each of the elements in the + /// collection + /// + /// Action to perform on each element + public void ForEach(Action action) + { + Parallel.ForEach(m_dict.Values, action); + } } } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index d3bd9e7..99ea0d5 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -802,7 +802,7 @@ namespace OpenSim.Framework void InPacket(object NewPack); void ProcessInPacket(Packet NewPack); - void Close(bool ShutdownCircuit); + void Close(); void Kick(string message); /// diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index bc9cfcf..86d0112 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -403,39 +403,35 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Client Methods /// - /// Close down the client view. This *must* be the last method called, since the last # - /// statement of CloseCleanup() aborts the thread. + /// Shut down the client view /// - /// - public void Close(bool shutdownCircuit) + public void Close() { m_log.DebugFormat( - "[CLIENT]: Close has been called with shutdownCircuit = {0} for {1} attached to scene {2}", - shutdownCircuit, Name, m_scene.RegionInfo.RegionName); + "[CLIENT]: Close has been called for {0} attached to scene {1}", + Name, m_scene.RegionInfo.RegionName); + + // Remove ourselves from the scene + m_scene.ClientManager.Remove(m_agentId, m_udpClient.RemoteEndPoint); if (m_imageManager != null) + { m_imageManager.Close(); + m_imageManager = null; + } if (m_udpServer != null) - m_udpServer.Flush(); - - // raise an event on the packet server to Shutdown the circuit - // Now, if we raise the event then the packet server will call this method itself, so don't try cleanup - // here otherwise we'll end up calling it twice. - // FIXME: In truth, I might be wrong but this whole business of calling this method twice (with different args) looks - // horribly tangly. Hopefully it should be possible to greatly simplify it. - if (shutdownCircuit) { - if (OnConnectionClosed != null) - OnConnectionClosed(this); - } - else - { - CloseCleanup(shutdownCircuit); + m_udpServer.Flush(); } + + if (OnConnectionClosed != null) + OnConnectionClosed(this); + + CloseCleanup(); } - private void CloseCleanup(bool shutdownCircuit) + private void CloseCleanup() { m_scene.RemoveClient(AgentId); @@ -459,43 +455,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP lock (m_primFullUpdateTimer) m_primFullUpdateTimer.Stop(); - // This is just to give the client a reasonable chance of - // flushing out all it's packets. There should probably - // be a better mechanism here - // We can't reach into other scenes and close the connection // We need to do this over grid communications //m_scene.CloseAllAgents(CircuitCode); - // If we're not shutting down the circuit, then this is the last time we'll go here. - // If we are shutting down the circuit, the UDP Server will come back here with - // ShutDownCircuit = false - if (!(shutdownCircuit)) - { - GC.Collect(); - m_imageManager = null; - // Sends a KillPacket object, with which, the - // blockingqueue dequeues and sees it's a killpacket - // and terminates within the context of the client thread. - // This ensures that it's done from within the context - // of the client thread regardless of where Close() is called. - KillEndDone(); - } - IsActive = false; - m_avatarTerseUpdateTimer.Close(); - m_primTerseUpdateTimer.Close(); - m_primFullUpdateTimer.Close(); + m_avatarTerseUpdateTimer.Dispose(); + m_primTerseUpdateTimer.Dispose(); + m_primFullUpdateTimer.Dispose(); - //m_udpServer.OnPacketStats -= PopulateStats; + // Disable UDP handling for this client m_udpClient.Shutdown(); - - // wait for thread stoped - // m_clientThread.Join(); - - // delete circuit code - //m_networkServer.CloseClient(this); } public void Kick(string message) @@ -10225,7 +10196,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void KillEndDone() { - m_udpClient.Shutdown(); } #region IClientCore @@ -10268,15 +10238,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP { Kick(reason); Thread.Sleep(1000); - Close(true); + Close(); } public void Disconnect() { - Close(true); + Close(); } - #endregion public void RefreshGroupMembership() diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 10e22d5..e5b2594 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -181,7 +181,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public void Shutdown() { - // TODO: Do we need to invalidate the circuit? IsConnected = false; } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs deleted file mode 100644 index 4f375e4..0000000 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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 System.Net; -using OpenSim.Framework; -using OpenMetaverse; -using BclExtras.Collections; - -using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim; - -namespace OpenSim.Region.ClientStack.LindenUDP -{ - /// - /// A thread safe mapping from endpoints to client references - /// - public sealed class UDPClientCollection - { - #region IComparers - - private sealed class IPEndPointComparer : IComparer - { - public int Compare(IPEndPoint x, IPEndPoint y) - { - int result = x.Address.Address.CompareTo(y.Address.Address); - if (result == 0) result = x.Port.CompareTo(y.Port); - return result; - } - } - - #endregion IComparers - - /// An immutable dictionary mapping from - /// to references - private ImmutableMap m_dict; - /// Immutability grants thread safety for concurrent reads and - /// read-writes, but not concurrent writes - private object m_writeLock = new object(); - - /// Number of clients in the collection - public int Count { get { return m_dict.Count; } } - - /// - /// Default constructor - /// - public UDPClientCollection() - { - m_dict = new ImmutableMap(new IPEndPointComparer()); - } - - /// - /// Add a client reference to the collection - /// - /// Remote endpoint of the client - /// Reference to the client object - public void Add(IPEndPoint key, LLUDPClient value) - { - lock (m_writeLock) - m_dict = m_dict.Add(key, value); - } - - /// - /// Remove a client from the collection - /// - /// Remote endpoint of the client - public void Remove(IPEndPoint key) - { - lock (m_writeLock) - m_dict = m_dict.Delete(key); - } - - /// - /// Resets the client collection - /// - public void Clear() - { - lock (m_writeLock) - m_dict = new ImmutableMap(new IPEndPointComparer()); - } - - /// - /// Checks if an endpoint is in the collection - /// - /// Endpoint to check for - /// True if the endpoint was found in the collection, otherwise false - public bool ContainsKey(IPEndPoint key) - { - return m_dict.ContainsKey(key); - } - - /// - /// Attempts to fetch a value out of the collection - /// - /// Endpoint of the client to retrieve - /// Retrieved client, or null on lookup failure - /// True if the lookup succeeded, otherwise false - public bool TryGetValue(IPEndPoint key, out LLUDPClient value) - { - return m_dict.TryGetValue(key, out value); - } - - /// - /// Performs a given task in parallel for each of the elements in the - /// collection - /// - /// Action to perform on each element - public void ForEach(Action action) - { - Parallel.ForEach(m_dict.Values, action); - } - } -} diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 04c9cb1..8ec143a 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -96,7 +96,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Incoming packets that are awaiting handling private OpenMetaverse.BlockingQueue packetInbox = new OpenMetaverse.BlockingQueue(); /// - private UDPClientCollection m_clients = new UDPClientCollection(); + //private UDPClientCollection m_clients = new UDPClientCollection(); /// Bandwidth throttle for this UDP server private TokenBucket m_throttle; /// Bandwidth throttle rates for this UDP server @@ -181,23 +181,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP return x == m_location; } - public void RemoveClient(LLUDPClient udpClient) - { - m_log.Debug("[LLUDPSERVER]: Removing LLUDPClient for " + udpClient.AgentID); - - // Shut down the IClientAPI and remove it from the scene - IClientAPI client; - if (m_scene.ClientManager.TryGetClient(udpClient.CircuitCode, out client)) - { - client.Close(false); - m_scene.ClientManager.Remove(udpClient.CircuitCode); - } - - // Shut down the LLUDPClient and remove it from the list of UDP clients - udpClient.Shutdown(); - m_clients.Remove(udpClient.RemoteEndPoint); - } - public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting) { // CoarseLocationUpdate packets cannot be split in an automated way @@ -215,17 +198,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < packetCount; i++) { byte[] data = datas[i]; - m_clients.ForEach( - delegate(LLUDPClient client) - { SendPacketData(client, data, packet.Type, category); }); + m_scene.ClientManager.ForEach( + delegate(IClientAPI client) + { + if (client is LLClientView) + SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category); + } + ); } } else { byte[] data = packet.ToBytes(); - m_clients.ForEach( - delegate(LLUDPClient client) - { SendPacketData(client, data, packet.Type, category); }); + m_scene.ClientManager.ForEach( + delegate(IClientAPI client) + { + if (client is LLClientView) + SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category); + } + ); } } @@ -475,7 +466,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP //try { Thread.CurrentThread.Name = "PacketReceived (" + m_scene.RegionInfo.RegionName + ")"; } //catch (Exception) { } - LLUDPClient client = null; + LLUDPClient udpClient = null; Packet packet = null; int packetEnd = buffer.DataLength - 1; IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint; @@ -512,30 +503,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP } // Determine which agent this packet came from - if (!m_clients.TryGetValue(address, out client)) + IClientAPI client; + if (!m_scene.ClientManager.TryGetValue(address, out client) || !(client is LLClientView)) { m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + - " in " + m_scene.RegionInfo.RegionName + ", currently tracking " + m_clients.Count + " clients"); + " in " + m_scene.RegionInfo.RegionName + ", currently tracking " + m_scene.ClientManager.Count + " clients"); return; } + udpClient = ((LLClientView)client).UDPClient; + #endregion Packet to Client Mapping // Stats tracking - Interlocked.Increment(ref client.PacketsReceived); + Interlocked.Increment(ref udpClient.PacketsReceived); #region ACK Receiving int now = Environment.TickCount; - client.TickLastPacketReceived = now; + udpClient.TickLastPacketReceived = now; // Handle appended ACKs if (packet.Header.AppendedAcks && packet.Header.AckList != null) { - lock (client.NeedAcks.SyncRoot) + lock (udpClient.NeedAcks.SyncRoot) { for (int i = 0; i < packet.Header.AckList.Length; i++) - AcknowledgePacket(client, packet.Header.AckList[i], now, packet.Header.Resent); + AcknowledgePacket(udpClient, packet.Header.AckList[i], now, packet.Header.Resent); } } @@ -544,10 +538,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP { PacketAckPacket ackPacket = (PacketAckPacket)packet; - lock (client.NeedAcks.SyncRoot) + lock (udpClient.NeedAcks.SyncRoot) { for (int i = 0; i < ackPacket.Packets.Length; i++) - AcknowledgePacket(client, ackPacket.Packets[i].ID, now, packet.Header.Resent); + AcknowledgePacket(udpClient, ackPacket.Packets[i].ID, now, packet.Header.Resent); } } @@ -556,27 +550,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region ACK Sending if (packet.Header.Reliable) - client.PendingAcks.Enqueue(packet.Header.Sequence); + udpClient.PendingAcks.Enqueue(packet.Header.Sequence); // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove // 2*MTU bytes from the value and send ACKs, and finally add the local value back to // client.BytesSinceLastACK. Lockless thread safety - int bytesSinceLastACK = Interlocked.Exchange(ref client.BytesSinceLastACK, 0); + int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0); bytesSinceLastACK += buffer.DataLength; if (bytesSinceLastACK > Packet.MTU * 2) { bytesSinceLastACK -= Packet.MTU * 2; - SendAcks(client); + SendAcks(udpClient); } - Interlocked.Add(ref client.BytesSinceLastACK, bytesSinceLastACK); + Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK); #endregion ACK Sending #region Incoming Packet Accounting // Check the archive of received reliable packet IDs to see whether we already received this packet - if (packet.Header.Reliable && !client.PacketArchive.TryEnqueue(packet.Header.Sequence)) + if (packet.Header.Reliable && !udpClient.PacketArchive.TryEnqueue(packet.Header.Sequence)) { if (packet.Header.Resent) m_log.Debug("[LLUDPSERVER]: Received a resend of already processed packet #" + packet.Header.Sequence + ", type: " + packet.Type); @@ -593,7 +587,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (packet.Type != PacketType.PacketAck) { // Inbox insertion - packetInbox.Enqueue(new IncomingPacket(client, packet)); + packetInbox.Enqueue(new IncomingPacket(udpClient, packet)); } } @@ -613,31 +607,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void AddNewClient(UseCircuitCodePacket useCircuitCode, IPEndPoint remoteEndPoint) { + UUID agentID = useCircuitCode.CircuitCode.ID; + UUID sessionID = useCircuitCode.CircuitCode.SessionID; + uint circuitCode = useCircuitCode.CircuitCode.Code; + if (m_scene.RegionStatus != RegionStatus.SlaveScene) { - if (!m_clients.ContainsKey(remoteEndPoint)) + AuthenticateResponse sessionInfo; + if (IsClientAuthorized(useCircuitCode, out sessionInfo)) { - AuthenticateResponse sessionInfo; - if (IsClientAuthorized(useCircuitCode, out sessionInfo)) - { - UUID agentID = useCircuitCode.CircuitCode.ID; - UUID sessionID = useCircuitCode.CircuitCode.SessionID; - uint circuitCode = useCircuitCode.CircuitCode.Code; - - AddClient(circuitCode, agentID, sessionID, remoteEndPoint, sessionInfo); - } - else - { - // Don't create circuits for unauthorized clients - m_log.WarnFormat( - "[LLUDPSERVER]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}", - useCircuitCode.CircuitCode.ID, useCircuitCode.CircuitCode.Code, remoteEndPoint); - } + AddClient(circuitCode, agentID, sessionID, remoteEndPoint, sessionInfo); } else { - // Ignore repeated UseCircuitCode packets - m_log.Debug("[LLUDPSERVER]: Ignoring UseCircuitCode for already established circuit " + useCircuitCode.CircuitCode.Code); + // Don't create circuits for unauthorized clients + m_log.WarnFormat( + "[LLUDPSERVER]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}", + useCircuitCode.CircuitCode.ID, useCircuitCode.CircuitCode.Code, remoteEndPoint); } } else @@ -652,17 +638,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Create the LLUDPClient LLUDPClient udpClient = new LLUDPClient(this, m_throttleRates, m_throttle, circuitCode, agentID, remoteEndPoint); - // Create the LLClientView - LLClientView clientApi = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); - clientApi.OnLogout += LogoutHandler; - clientApi.OnConnectionClosed += ConnectionClosedHandler; + if (!m_scene.ClientManager.ContainsKey(agentID)) + { + // Create the LLClientView + LLClientView client = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); + client.OnLogout += LogoutHandler; + client.OnConnectionClosed += ConnectionClosedHandler; - // Start the IClientAPI - m_scene.ClientManager.Add(circuitCode, clientApi); - clientApi.Start(); + m_scene.ClientManager.Add(agentID, remoteEndPoint, client); - // Add the new client to our list of tracked clients - m_clients.Add(udpClient.RemoteEndPoint, udpClient); + // Start the IClientAPI + m_scene.ClientManager.Add(agentID, remoteEndPoint, client); + client.Start(); + } + else + { + m_log.Debug("[LLUDPSERVER]: Ignoring a repeated UseCircuitCode from " + udpClient.AgentID); + } + } + + private void RemoveClient(LLUDPClient udpClient) + { + // Remove this client from the scene ClientManager + IClientAPI client; + if (m_scene.ClientManager.TryGetValue(udpClient.AgentID, out client)) + Util.FireAndForget(delegate(object o) { client.Close(); }); } private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend) @@ -740,20 +740,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP elapsed500MS = 0; } - m_clients.ForEach( - delegate(LLUDPClient client) + m_scene.ClientManager.ForEach( + delegate(IClientAPI client) { - if (client.DequeueOutgoing()) - packetSent = true; - if (resendUnacked) - ResendUnacked(client); - if (sendAcks) + if (client is LLClientView) { - SendAcks(client); - client.SendPacketStats(); + LLUDPClient udpClient = ((LLClientView)client).UDPClient; + + if (udpClient.DequeueOutgoing()) + packetSent = true; + if (resendUnacked) + ResendUnacked(udpClient); + if (sendAcks) + { + SendAcks(udpClient); + udpClient.SendPacketStats(); + } + if (sendPings) + SendPing(udpClient); } - if (sendPings) - SendPing(client); } ); @@ -777,7 +782,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } // Make sure this client is still alive - if (m_scene.ClientManager.TryGetClient(udpClient.CircuitCode, out client)) + if (m_scene.ClientManager.TryGetValue(udpClient.AgentID, out client)) { try { diff --git a/OpenSim/Region/CoreModules/Avatar/Gods/GodsModule.cs b/OpenSim/Region/CoreModules/Avatar/Gods/GodsModule.cs index f941728..7855862 100644 --- a/OpenSim/Region/CoreModules/Avatar/Gods/GodsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Gods/GodsModule.cs @@ -36,6 +36,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Gods { public class GodsModule : IRegionModule, IGodsModule { + /// Special UUID for actions that apply to all agents + private static readonly UUID ALL_AGENTS = new UUID("44e87126-e794-4ded-05b3-7c42da3d5cdb"); + protected Scene m_scene; protected IDialogModule m_dialogModule; @@ -99,8 +102,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Gods /// The message to send to the user after it's been turned into a field public void KickUser(UUID godID, UUID sessionID, UUID agentID, uint kickflags, byte[] reason) { - // For some reason the client sends this seemingly hard coded UUID for kicking everyone. Dun-know. - UUID kickUserID = new UUID("44e87126e7944ded05b37c42da3d5cdb"); + UUID kickUserID = ALL_AGENTS; ScenePresence sp = m_scene.GetScenePresence(agentID); @@ -110,15 +112,17 @@ namespace OpenSim.Region.CoreModules.Avatar.Gods { if (agentID == kickUserID) { - m_scene.ClientManager.ForEachClient( + string reasonStr = Utils.BytesToString(reason); + + m_scene.ClientManager.ForEach( delegate(IClientAPI controller) { if (controller.AgentId != godID) - controller.Kick(Utils.BytesToString(reason)); + controller.Kick(reasonStr); } ); - // This is a bit crude. It seems the client will be null before it actually stops the thread + // This is a bit crude. It seems the client will be null before it actually stops the thread // The thread will kill itself eventually :/ // Is there another way to make sure *all* clients get this 'inter region' message? m_scene.ForEachScenePresence( @@ -128,7 +132,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Gods { // Possibly this should really be p.Close() though that method doesn't send a close // to the client - p.ControllingClient.Close(true); + p.ControllingClient.Close(); } } ); @@ -138,7 +142,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Gods m_scene.SceneGraph.removeUserCount(!sp.IsChildAgent); sp.ControllingClient.Kick(Utils.BytesToString(reason)); - sp.ControllingClient.Close(true); + sp.ControllingClient.Close(); } } else diff --git a/OpenSim/Region/CoreModules/InterGrid/OpenGridProtocolModule.cs b/OpenSim/Region/CoreModules/InterGrid/OpenGridProtocolModule.cs index 7d6f150..d636b1c 100644 --- a/OpenSim/Region/CoreModules/InterGrid/OpenGridProtocolModule.cs +++ b/OpenSim/Region/CoreModules/InterGrid/OpenGridProtocolModule.cs @@ -1267,7 +1267,7 @@ namespace OpenSim.Region.CoreModules.InterGrid if (avToBeKilled.IsChildAgent) { m_mod.DeleteOGPState(avUUID); - avToBeKilled.ControllingClient.Close(true); + avToBeKilled.ControllingClient.Close(); } } } diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index 8ad4844..dcee824 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -813,7 +813,7 @@ namespace OpenSim.Region.Examples.SimpleModule { } - public void Close(bool ShutdownCircuit) + public void Close() { } diff --git a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs index dbbf679..ac89f7b 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs @@ -408,7 +408,7 @@ namespace OpenSim.Region.Framework.Scenes } ViewerEffectPacket.EffectBlock[] effectBlockArray = effectBlock.ToArray(); - ClientManager.ForEachClient( + ClientManager.ForEach( delegate(IClientAPI client) { if (client.AgentId != remoteClient.AgentId) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index e81b07b..bb71896 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -867,7 +867,7 @@ namespace OpenSim.Region.Framework.Scenes Thread.Sleep(500); // Stop all client threads. - ForEachScenePresence(delegate(ScenePresence avatar) { avatar.ControllingClient.Close(true); }); + ForEachScenePresence(delegate(ScenePresence avatar) { avatar.ControllingClient.Close(); }); // Stop updating the scene objects and agents. //m_heartbeatTimer.Close(); @@ -3372,7 +3372,7 @@ namespace OpenSim.Region.Framework.Scenes loggingOffUser.ControllingClient.Kick(message); // Give them a second to receive the message! Thread.Sleep(1000); - loggingOffUser.ControllingClient.Close(true); + loggingOffUser.ControllingClient.Close(); } else { @@ -3543,7 +3543,7 @@ namespace OpenSim.Region.Framework.Scenes presence.ControllingClient.SendShutdownConnectionNotice(); } - presence.ControllingClient.Close(true); + presence.ControllingClient.Close(); return true; } diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/IRCStackModule.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/IRCStackModule.cs index 4b199ac..c962329 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/IRCStackModule.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/IRCStackModule.cs @@ -64,7 +64,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView void user_OnIRCReady(IRCClientView cv) { m_log.Info("[IRCd] Adding user..."); - m_scene.ClientManager.Add(cv.CircuitCode, cv); + m_scene.ClientManager.Add(cv.AgentId, cv.RemoteEndPoint, cv); cv.Start(); m_log.Info("[IRCd] Added user to Scene"); } diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 4364627..a8acf0d 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -634,6 +634,12 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server { get { return (uint)Util.RandomClass.Next(0,int.MaxValue); } } + + public IPEndPoint RemoteEndPoint + { + get { return (IPEndPoint)m_client.Client.RemoteEndPoint; } + } + #pragma warning disable 67 public event GenericMessage OnGenericMessage; public event ImprovedInstantMessage OnInstantMessage; @@ -843,7 +849,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server } - public void Close(bool ShutdownCircuit) + public void Close() { Disconnect(); } diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/MetaEntity.cs b/OpenSim/Region/OptionalModules/ContentManagementSystem/MetaEntity.cs index d6dacbc..b6513e2 100644 --- a/OpenSim/Region/OptionalModules/ContentManagementSystem/MetaEntity.cs +++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/MetaEntity.cs @@ -183,8 +183,9 @@ namespace OpenSim.Region.OptionalModules.ContentManagement public virtual void HideFromAll() { foreach (SceneObjectPart part in m_Entity.Children.Values) - m_Entity.Scene.ClientManager.ForEachClient(delegate(IClientAPI controller) - { controller.SendKillObject(m_Entity.RegionHandle, part.LocalId); } + m_Entity.Scene.ClientManager.ForEach( + delegate(IClientAPI controller) + { controller.SendKillObject(m_Entity.RegionHandle, part.LocalId); } ); } @@ -201,8 +202,9 @@ namespace OpenSim.Region.OptionalModules.ContentManagement public void SendFullUpdateToAll() { - m_Entity.Scene.ClientManager.ForEachClient(delegate(IClientAPI controller) - { m_Entity.SendFullUpdateToClient(controller); } + m_Entity.Scene.ClientManager.ForEach( + delegate(IClientAPI controller) + { m_Entity.SendFullUpdateToClient(controller); } ); } diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index ac8b98c..f7c63ac 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -825,7 +825,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC { } - public void Close(bool ShutdownCircuit) + public void Close() { } @@ -838,11 +838,21 @@ namespace OpenSim.Region.OptionalModules.World.NPC } private uint m_circuitCode; + private IPEndPoint m_remoteEndPoint; public uint CircuitCode { get { return m_circuitCode; } - set { m_circuitCode = value; } + set + { + m_circuitCode = value; + m_remoteEndPoint = new IPEndPoint(IPAddress.Loopback, (ushort)m_circuitCode); + } + } + + public IPEndPoint RemoteEndPoint + { + get { return m_remoteEndPoint; } } public void SendBlueBoxMessage(UUID FromAvatarID, String FromAvatarName, String Message) diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs index 30a2675..eb7b0d7 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs @@ -155,7 +155,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC NPCAvatar npcAvatar = new NPCAvatar(p_firstname, p_lastname, p_position, p_scene); npcAvatar.CircuitCode = (uint) Util.RandomClass.Next(0, int.MaxValue); - p_scene.ClientManager.Add(npcAvatar.CircuitCode, npcAvatar); + p_scene.ClientManager.Add(npcAvatar.AgentId, npcAvatar.RemoteEndPoint, npcAvatar); p_scene.AddNewClient(npcAvatar); ScenePresence sp; diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index 2b54890..ad90817 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -865,7 +865,7 @@ namespace OpenSim.Tests.Common.Mock { } - public void Close(bool ShutdownCircuit) + public void Close() { m_scene.RemoveClient(AgentId); } diff --git a/prebuild.xml b/prebuild.xml index 028e5e7..f15fadf 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -135,6 +135,7 @@ + -- cgit v1.1 From 395a8680c3633ca131e7481f765517311ef51710 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 16:53:19 -0700 Subject: * Fixed a bug where clients were being added to ClientManager twice * Changed the ClientManager interface to reduce potential errors with duplicate or mismatched keys * Added IClientAPI.RemoteEndPoint, which can (hopefully) eventually replace IClientAPI.CircuitCode * Changed the order of operations during client shutdown --- OpenSim/Framework/ClientManager.cs | 22 +++++------ OpenSim/Framework/IClientAPI.cs | 2 + .../Region/ClientStack/LindenUDP/LLClientView.cs | 36 ++++++++---------- .../Region/ClientStack/LindenUDP/LLImageManager.cs | 44 +++++++++++----------- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 9 ++--- 5 files changed, 54 insertions(+), 59 deletions(-) diff --git a/OpenSim/Framework/ClientManager.cs b/OpenSim/Framework/ClientManager.cs index 4edfabe..aefe425 100644 --- a/OpenSim/Framework/ClientManager.cs +++ b/OpenSim/Framework/ClientManager.cs @@ -97,19 +97,17 @@ namespace OpenSim.Framework /// Add a client reference to the collection if it does not already /// exist /// - /// UUID of the client - /// Remote endpoint of the client /// Reference to the client object /// True if the client reference was successfully added, /// otherwise false if the given key already existed in the collection - public bool Add(UUID key, IPEndPoint key2, IClientAPI value) + public bool Add(IClientAPI value) { lock (m_writeLock) { - if (!m_dict.ContainsKey(key) && !m_dict2.ContainsKey(key2)) + if (!m_dict.ContainsKey(value.AgentId) && !m_dict2.ContainsKey(value.RemoteEndPoint)) { - m_dict = m_dict.Add(key, value); - m_dict2 = m_dict2.Add(key2, value); + m_dict = m_dict.Add(value.AgentId, value); + m_dict2 = m_dict2.Add(value.RemoteEndPoint, value); return true; } @@ -123,14 +121,16 @@ namespace OpenSim.Framework /// /// Remove a client from the collection /// - /// UUID of the client - /// Remote endpoint of the client - public void Remove(UUID key, IPEndPoint key2) + /// Reference to the client object + public void Remove(IClientAPI value) { lock (m_writeLock) { - m_dict = m_dict.Delete(key); - m_dict2 = m_dict2.Delete(key2); + if (m_dict.ContainsKey(value.AgentId)) + m_dict = m_dict.Delete(value.AgentId); + + if (m_dict2.ContainsKey(value.RemoteEndPoint)) + m_dict2 = m_dict2.Delete(value.RemoteEndPoint); } } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 99ea0d5..29846f5 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -561,6 +561,8 @@ namespace OpenSim.Framework // [Obsolete("LLClientView Specific - Circuits are unique to LLClientView")] uint CircuitCode { get; } + IPEndPoint RemoteEndPoint { get; } + event GenericMessage OnGenericMessage; // [Obsolete("LLClientView Specific - Replace with more bare-bones arguments.")] diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 86d0112..aecb362 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -411,38 +411,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP "[CLIENT]: Close has been called for {0} attached to scene {1}", Name, m_scene.RegionInfo.RegionName); - // Remove ourselves from the scene - m_scene.ClientManager.Remove(m_agentId, m_udpClient.RemoteEndPoint); + // Send the STOP packet + DisableSimulatorPacket disable = (DisableSimulatorPacket)PacketPool.Instance.GetPacket(PacketType.DisableSimulator); + OutPacket(disable, ThrottleOutPacketType.Unknown); + + IsActive = false; + // Shutdown the image manager if (m_imageManager != null) - { m_imageManager.Close(); - m_imageManager = null; - } - - if (m_udpServer != null) - { - m_udpServer.Flush(); - } + // Fire the callback for this connection closing if (OnConnectionClosed != null) OnConnectionClosed(this); - CloseCleanup(); - } + // Flush all of the packets out of the UDP server for this client + if (m_udpServer != null) + m_udpServer.Flush(m_udpClient); - private void CloseCleanup() - { + // Remove ourselves from the scene m_scene.RemoveClient(AgentId); + m_scene.ClientManager.Remove(this); //m_log.InfoFormat("[CLIENTVIEW] Memory pre GC {0}", System.GC.GetTotalMemory(false)); + //GC.Collect(); //m_log.InfoFormat("[CLIENTVIEW] Memory post GC {0}", System.GC.GetTotalMemory(true)); - // Send the STOP packet - DisableSimulatorPacket disable = (DisableSimulatorPacket)PacketPool.Instance.GetPacket(PacketType.DisableSimulator); - OutPacket(disable, ThrottleOutPacketType.Unknown); - - Thread.Sleep(2000); + // FIXME: Is this still necessary? + //Thread.Sleep(2000); // Shut down timers. Thread Context of this method is murky. Lock all timers if (m_avatarTerseUpdateTimer.Enabled) @@ -459,8 +455,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // We need to do this over grid communications //m_scene.CloseAllAgents(CircuitCode); - IsActive = false; - m_avatarTerseUpdateTimer.Dispose(); m_primTerseUpdateTimer.Dispose(); m_primFullUpdateTimer.Dispose(); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index 343f537..8469ba6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -167,39 +167,39 @@ namespace OpenSim.Region.ClientStack.LindenUDP J2KImage imagereq; int numCollected = 0; - //lock (m_syncRoot) - //{ - m_lastloopprocessed = DateTime.Now.Ticks; - - // This can happen during Close() - if (m_client == null) - return false; - - while ((imagereq = GetHighestPriorityImage()) != null) + m_lastloopprocessed = DateTime.Now.Ticks; + + // This can happen during Close() + if (m_client == null) + return false; + + while ((imagereq = GetHighestPriorityImage()) != null) + { + if (imagereq.IsDecoded == true) { - if (imagereq.IsDecoded == true) - { - ++numCollected; + ++numCollected; - if (imagereq.SendPackets(m_client, maxpack)) - { - // Send complete. Destroy any knowledge of this transfer - RemoveImageFromQueue(imagereq); - } + if (imagereq.SendPackets(m_client, maxpack)) + { + // Send complete. Destroy any knowledge of this transfer + RemoveImageFromQueue(imagereq); } - - if (numCollected == count) - break; } - //} + + if (numCollected == count) + break; + } return m_priorityQueue.Count > 0; } - //Faux destructor + /// + /// Faux destructor + /// public void Close() { m_shuttingdown = true; + m_priorityQueue = null; m_j2kDecodeModule = null; m_assetCache = null; m_client = null; diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 8ec143a..22c275c 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -390,7 +390,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public void Flush() + public void Flush(LLUDPClient udpClient) { // FIXME: Implement? } @@ -645,15 +645,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP client.OnLogout += LogoutHandler; client.OnConnectionClosed += ConnectionClosedHandler; - m_scene.ClientManager.Add(agentID, remoteEndPoint, client); - // Start the IClientAPI - m_scene.ClientManager.Add(agentID, remoteEndPoint, client); + m_scene.ClientManager.Add(client); client.Start(); } else { - m_log.Debug("[LLUDPSERVER]: Ignoring a repeated UseCircuitCode from " + udpClient.AgentID); + m_log.WarnFormat("[LLUDPSERVER]: Ignoring a repeated UseCircuitCode from {0} at {1} for circuit {2}", + udpClient.AgentID, remoteEndPoint, circuitCode); } } -- cgit v1.1 From dc11643c007adf7a640ec4fbabe25995352aaa18 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 17:33:45 -0700 Subject: * Consolidated adding / removing ClientManager IClientAPIs to two places in Scene * Added some missing implementations of IClientAPI.RemoteEndPoint * Added a ClientManager.Remove(UUID) overload * Removed a reference to a missing project from prebuild.xml --- .../Client/MXP/PacketHandler/MXPPacketServer.cs | 6 --- .../Client/VWoHTTP/ClientStack/VWHClientView.cs | 5 ++ OpenSim/Framework/ClientManager.cs | 14 +++++ .../Region/ClientStack/LindenUDP/LLClientView.cs | 61 +--------------------- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 13 ++--- .../Region/Examples/SimpleModule/MyNpcCharacter.cs | 5 ++ OpenSim/Region/Framework/Scenes/Scene.cs | 4 ++ .../InternetRelayClientView/IRCStackModule.cs | 1 - .../Region/OptionalModules/World/NPC/NPCModule.cs | 1 - OpenSim/Tests/Common/Mock/TestClient.cs | 5 ++ prebuild.xml | 1 - 11 files changed, 38 insertions(+), 78 deletions(-) diff --git a/OpenSim/Client/MXP/PacketHandler/MXPPacketServer.cs b/OpenSim/Client/MXP/PacketHandler/MXPPacketServer.cs index d5dc18f..ba9c653 100644 --- a/OpenSim/Client/MXP/PacketHandler/MXPPacketServer.cs +++ b/OpenSim/Client/MXP/PacketHandler/MXPPacketServer.cs @@ -339,12 +339,6 @@ namespace OpenSim.Client.MXP.PacketHandler m_clients.Add(client); m_log.Debug("[MXP ClientStack]: Created ClientView."); - - m_log.Debug("[MXP ClientStack]: Adding ClientView to Scene..."); - scene.ClientManager.Add(client.AgentId, client.RemoteEndPoint, client); - m_log.Debug("[MXP ClientStack]: Added ClientView to Scene."); - - client.MXPSendSynchronizationBegin(m_scenes[new UUID(joinRequestMessage.BubbleId)].SceneContents.GetTotalObjectsCount()); m_log.Debug("[MXP ClientStack]: Starting ClientView..."); diff --git a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs index 9c24da5..e3abcf5 100644 --- a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs +++ b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs @@ -207,6 +207,11 @@ namespace OpenSim.Client.VWoHTTP.ClientStack get { throw new System.NotImplementedException(); } } + public IPEndPoint RemoteEndPoint + { + get { throw new System.NotImplementedException(); } + } + public event GenericMessage OnGenericMessage = delegate { }; public event ImprovedInstantMessage OnInstantMessage = delegate { }; public event ChatMessage OnChatFromClient = delegate { }; diff --git a/OpenSim/Framework/ClientManager.cs b/OpenSim/Framework/ClientManager.cs index aefe425..fd5f87f 100644 --- a/OpenSim/Framework/ClientManager.cs +++ b/OpenSim/Framework/ClientManager.cs @@ -134,6 +134,20 @@ namespace OpenSim.Framework } } + public void Remove(UUID key) + { + lock (m_writeLock) + { + IClientAPI client; + + if (m_dict.TryGetValue(key, out client)) + { + m_dict = m_dict.Delete(key); + m_dict2 = m_dict2.Delete(client.RemoteEndPoint); + } + } + } + /// /// Resets the client collection /// diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index aecb362..0acf6e8 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -324,6 +324,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Properties public LLUDPClient UDPClient { get { return m_udpClient; } } + public IPEndPoint RemoteEndPoint { get { return m_udpClient.RemoteEndPoint; } } public UUID SecureSessionId { get { return m_secureSessionId; } } public IScene Scene { get { return m_scene; } } public UUID SessionId { get { return m_sessionId; } } @@ -431,7 +432,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Remove ourselves from the scene m_scene.RemoveClient(AgentId); - m_scene.ClientManager.Remove(this); //m_log.InfoFormat("[CLIENTVIEW] Memory pre GC {0}", System.GC.GetTotalMemory(false)); //GC.Collect(); @@ -586,11 +586,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP # region Setup - /// - /// Starts up the timers to check the client and resend unacked packets - /// Adds the client to the OpenSim.Region.Framework.Scenes.Scene - /// - protected virtual void InitNewClient() + public virtual void Start() { m_avatarTerseUpdateTimer = new Timer(m_avatarTerseUpdateRate); m_avatarTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates); @@ -609,59 +605,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP RefreshGroupMembership(); } - public virtual void Start() - { - // This sets up all the timers - InitNewClient(); - } - - /// - /// Run a user session. This method lies at the base of the entire client thread. - /// - protected void RunUserSession() - { - try - { - - } - catch (Exception e) - { - if (e is ThreadAbortException) - throw; - - if (StatsManager.SimExtraStats != null) - StatsManager.SimExtraStats.AddAbnormalClientThreadTermination(); - - // Don't let a failure in an individual client thread crash the whole sim. - m_log.ErrorFormat( - "[CLIENT]: Client thread for {0} {1} crashed. Logging them out.", Name, AgentId); - m_log.Error(e.ToString()); - - try - { - // Make an attempt to alert the user that their session has crashed - AgentAlertMessagePacket packet - = BuildAgentAlertPacket( - "Unfortunately the session for this client on the server has crashed.\n" - + "Any further actions taken will not be processed.\n" - + "Please relog", true); - - OutPacket(packet, ThrottleOutPacketType.Unknown); - - // There may be a better way to do this. Perhaps kick? Not sure this propogates notifications to - // listeners yet, though. - Logout(this); - } - catch (Exception e2) - { - if (e2 is ThreadAbortException) - throw; - - m_log.ErrorFormat("[CLIENT]: Further exception thrown on forced session logout. {0}", e2); - } - } - } - # endregion public void ActivateGesture(UUID assetId, UUID gestureId) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 22c275c..8689af3 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -646,7 +646,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP client.OnConnectionClosed += ConnectionClosedHandler; // Start the IClientAPI - m_scene.ClientManager.Add(client); client.Start(); } else @@ -658,10 +657,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void RemoveClient(LLUDPClient udpClient) { - // Remove this client from the scene ClientManager + // Remove this client from the scene IClientAPI client; if (m_scene.ClientManager.TryGetValue(udpClient.AgentID, out client)) - Util.FireAndForget(delegate(object o) { client.Close(); }); + client.Close(); } private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend) @@ -810,19 +809,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void LogoutHandler(IClientAPI client) { client.OnLogout -= LogoutHandler; - client.SendLogoutPacket(); - - if (client is LLClientView) - RemoveClient(((LLClientView)client).UDPClient); } private void ConnectionClosedHandler(IClientAPI client) { client.OnConnectionClosed -= ConnectionClosedHandler; - - if (client is LLClientView) - RemoveClient(((LLClientView)client).UDPClient); + RemoveClient(((LLClientView)client).UDPClient); } } } diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index dcee824..d651fd4 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -833,6 +833,11 @@ namespace OpenSim.Region.Examples.SimpleModule set { m_circuitCode = value; } } + public IPEndPoint RemoteEndPoint + { + get { return new IPEndPoint(IPAddress.Loopback, (ushort)m_circuitCode); } + } + public void SendBlueBoxMessage(UUID FromAvatarID, String FromAvatarName, String Message) { diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index bb71896..7944548 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -2363,6 +2363,8 @@ namespace OpenSim.Region.Framework.Scenes /// public override void AddNewClient(IClientAPI client) { + ClientManager.Add(client); + CheckHeartbeat(); SubscribeToClientEvents(client); ScenePresence presence; @@ -3002,7 +3004,9 @@ namespace OpenSim.Region.Framework.Scenes agentTransactions.RemoveAgentAssetTransactions(agentID); } + // Remove the avatar from the scene m_sceneGraph.RemoveScenePresence(agentID); + ClientManager.Remove(agentID); try { diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/IRCStackModule.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/IRCStackModule.cs index c962329..4c2a4b9 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/IRCStackModule.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/IRCStackModule.cs @@ -64,7 +64,6 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView void user_OnIRCReady(IRCClientView cv) { m_log.Info("[IRCd] Adding user..."); - m_scene.ClientManager.Add(cv.AgentId, cv.RemoteEndPoint, cv); cv.Start(); m_log.Info("[IRCd] Added user to Scene"); } diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs index eb7b0d7..41a6255 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs @@ -155,7 +155,6 @@ namespace OpenSim.Region.OptionalModules.World.NPC NPCAvatar npcAvatar = new NPCAvatar(p_firstname, p_lastname, p_position, p_scene); npcAvatar.CircuitCode = (uint) Util.RandomClass.Next(0, int.MaxValue); - p_scene.ClientManager.Add(npcAvatar.AgentId, npcAvatar.RemoteEndPoint, npcAvatar); p_scene.AddNewClient(npcAvatar); ScenePresence sp; diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index ad90817..5c838c5 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -393,6 +393,11 @@ namespace OpenSim.Tests.Common.Mock set { m_circuitCode = value; } } + public IPEndPoint RemoteEndPoint + { + get { return new IPEndPoint(IPAddress.Loopback, (ushort)m_circuitCode); } + } + /// /// Constructor /// diff --git a/prebuild.xml b/prebuild.xml index f15fadf..1a491a7 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -3497,7 +3497,6 @@ - -- cgit v1.1 From e8c1e69a0dbab1a7db894eeff6b052bbd350a8f5 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 18:56:54 -0700 Subject: * Copied LocklessQueue.cs into OpenSim.Framework and added the .Count property and .Clear() method * Changed the way the QueueEmpty callback is fired. It will be fired asynchronously as soon as an empty queue is detected (this can happen immediately following a dequeue), and will not be fired again until at least one packet is dequeued from that queue. This will give callbacks advanced notice of an empty queue and prevent callbacks from stacking up while the queue is empty * Added LLUDPClient.IsConnected checks in several places to prevent unwanted network activity after a client disconnects * Prevent LLClientView.Close() from being called twice every disconnect * Removed the packet resend limit and improved the client timeout check --- OpenSim/Framework/ClientManager.cs | 23 ++-- OpenSim/Framework/LocklessQueue.cs | 130 +++++++++++++++++++++ .../Region/ClientStack/LindenUDP/LLClientView.cs | 11 +- .../Region/ClientStack/LindenUDP/LLImageManager.cs | 6 +- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 50 ++++++-- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 107 ++++++++--------- .../LindenUDP/UnackedPacketCollection.cs | 9 ++ 7 files changed, 242 insertions(+), 94 deletions(-) create mode 100644 OpenSim/Framework/LocklessQueue.cs diff --git a/OpenSim/Framework/ClientManager.cs b/OpenSim/Framework/ClientManager.cs index fd5f87f..367bc6a 100644 --- a/OpenSim/Framework/ClientManager.cs +++ b/OpenSim/Framework/ClientManager.cs @@ -121,20 +121,10 @@ namespace OpenSim.Framework /// /// Remove a client from the collection /// - /// Reference to the client object - public void Remove(IClientAPI value) - { - lock (m_writeLock) - { - if (m_dict.ContainsKey(value.AgentId)) - m_dict = m_dict.Delete(value.AgentId); - - if (m_dict2.ContainsKey(value.RemoteEndPoint)) - m_dict2 = m_dict2.Delete(value.RemoteEndPoint); - } - } - - public void Remove(UUID key) + /// UUID of the client to remove + /// True if a client was removed, or false if the given UUID + /// was not present in the collection + public bool Remove(UUID key) { lock (m_writeLock) { @@ -144,6 +134,11 @@ namespace OpenSim.Framework { m_dict = m_dict.Delete(key); m_dict2 = m_dict2.Delete(client.RemoteEndPoint); + return true; + } + else + { + return false; } } } diff --git a/OpenSim/Framework/LocklessQueue.cs b/OpenSim/Framework/LocklessQueue.cs new file mode 100644 index 0000000..dd3d201 --- /dev/null +++ b/OpenSim/Framework/LocklessQueue.cs @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2009, openmetaverse.org + * All rights reserved. + * + * - 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. + * - Neither the name of the openmetaverse.org 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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.Threading; + +namespace OpenSim.Framework +{ + public sealed class LocklessQueue + { + private sealed class SingleLinkNode + { + public SingleLinkNode Next; + public T Item; + } + + SingleLinkNode head; + SingleLinkNode tail; + int count; + + public int Count { get { return count; } } + + public LocklessQueue() + { + Init(); + } + + public void Enqueue(T item) + { + SingleLinkNode oldTail = null; + SingleLinkNode oldTailNext; + + SingleLinkNode newNode = new SingleLinkNode(); + newNode.Item = item; + + bool newNodeWasAdded = false; + + while (!newNodeWasAdded) + { + oldTail = tail; + oldTailNext = oldTail.Next; + + if (tail == oldTail) + { + if (oldTailNext == null) + newNodeWasAdded = CAS(ref tail.Next, null, newNode); + else + CAS(ref tail, oldTail, oldTailNext); + } + } + + CAS(ref tail, oldTail, newNode); + Interlocked.Increment(ref count); + } + + public bool Dequeue(out T item) + { + item = default(T); + SingleLinkNode oldHead = null; + bool haveAdvancedHead = false; + + while (!haveAdvancedHead) + { + oldHead = head; + SingleLinkNode oldTail = tail; + SingleLinkNode oldHeadNext = oldHead.Next; + + if (oldHead == head) + { + if (oldHead == oldTail) + { + if (oldHeadNext == null) + return false; + + CAS(ref tail, oldTail, oldHeadNext); + } + else + { + item = oldHeadNext.Item; + haveAdvancedHead = CAS(ref head, oldHead, oldHeadNext); + } + } + } + + Interlocked.Decrement(ref count); + return true; + } + + public void Clear() + { + Init(); + } + + private void Init() + { + count = 0; + head = tail = new SingleLinkNode(); + } + + private static bool CAS(ref SingleLinkNode location, SingleLinkNode comparand, SingleLinkNode newValue) + { + return + (object)comparand == + (object)Interlocked.CompareExchange(ref location, newValue, comparand); + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 0acf6e8..ac558ff 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -433,13 +433,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Remove ourselves from the scene m_scene.RemoveClient(AgentId); - //m_log.InfoFormat("[CLIENTVIEW] Memory pre GC {0}", System.GC.GetTotalMemory(false)); - //GC.Collect(); - //m_log.InfoFormat("[CLIENTVIEW] Memory post GC {0}", System.GC.GetTotalMemory(true)); - - // FIXME: Is this still necessary? - //Thread.Sleep(2000); - // Shut down timers. Thread Context of this method is murky. Lock all timers if (m_avatarTerseUpdateTimer.Enabled) lock (m_avatarTerseUpdateTimer) @@ -461,6 +454,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Disable UDP handling for this client m_udpClient.Shutdown(); + + //m_log.InfoFormat("[CLIENTVIEW] Memory pre GC {0}", System.GC.GetTotalMemory(false)); + //GC.Collect(); + //m_log.InfoFormat("[CLIENTVIEW] Memory post GC {0}", System.GC.GetTotalMemory(true)); } public void Kick(string message) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index 8469ba6..8410ee9 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -213,13 +213,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP lock (m_syncRoot) { - if (m_priorityQueue.Count > 0) { - try - { - image = m_priorityQueue.FindMax(); - } + try { image = m_priorityQueue.FindMax(); } catch (Exception) { } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index e5b2594..b27d8d6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -80,7 +80,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Packets we have sent that need to be ACKed by the client public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection(); /// ACKs that are queued up, waiting to be sent to the client - public readonly LocklessQueue PendingAcks = new LocklessQueue(); + public readonly OpenSim.Framework.LocklessQueue PendingAcks = new OpenSim.Framework.LocklessQueue(); /// Current packet sequence number public int CurrentSequence; @@ -127,13 +127,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Throttle rate defaults and limits private readonly ThrottleRates defaultThrottleRates; /// Outgoing queues for throttled packets - private readonly LocklessQueue[] packetOutboxes = new LocklessQueue[THROTTLE_CATEGORY_COUNT]; + private readonly OpenSim.Framework.LocklessQueue[] packetOutboxes = new OpenSim.Framework.LocklessQueue[THROTTLE_CATEGORY_COUNT]; /// A container that can hold one packet for each outbox, used to store /// dequeued packets that are being held for throttling private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; /// An optimization to store the length of dequeued packets being held /// for throttling. This avoids expensive calls to Packet.Length private readonly int[] nextPacketLengths = new int[THROTTLE_CATEGORY_COUNT]; + /// Flags to prevent queue empty callbacks from repeatedly firing + /// before the callbacks have a chance to put packets in the queue + private readonly bool[] queueEmptySent = new bool[THROTTLE_CATEGORY_COUNT]; /// A reference to the LLUDPServer that is managing this client private readonly LLUDPServer udpServer; @@ -156,7 +159,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP defaultThrottleRates = rates; for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) - packetOutboxes[i] = new LocklessQueue(); + packetOutboxes[i] = new OpenSim.Framework.LocklessQueue(); throttle = new TokenBucket(parentThrottle, 0, 0); throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; @@ -182,6 +185,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void Shutdown() { IsConnected = false; + NeedAcks.Clear(); + for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) + { + packetOutboxes[i].Clear(); + nextPackets[i] = null; + } + OnPacketStats = null; + OnQueueEmpty = null; } /// @@ -322,7 +333,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (category >= 0 && category < packetOutboxes.Length) { - LocklessQueue queue = packetOutboxes[category]; + OpenSim.Framework.LocklessQueue queue = packetOutboxes[category]; TokenBucket bucket = throttleCategories[category]; if (throttleCategories[category].RemoveTokens(packet.Buffer.DataLength)) @@ -354,7 +365,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public bool DequeueOutgoing() { OutgoingPacket packet; - LocklessQueue queue; + OpenSim.Framework.LocklessQueue queue; TokenBucket bucket; bool packetSent = false; @@ -382,6 +393,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP queue = packetOutboxes[i]; if (queue.Dequeue(out packet)) { + // Reset the flag for firing this queue's OnQueueEmpty callback + // now that we have dequeued a packet + queueEmptySent[i] = false; + // A packet was pulled off the queue. See if we have // enough tokens in the bucket to send it out if (bucket.RemoveTokens(packet.Buffer.DataLength)) @@ -397,13 +412,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP nextPackets[i] = packet; nextPacketLengths[i] = packet.Buffer.DataLength; } + + // If the queue is empty after this dequeue, fire the queue + // empty callback now so it has a chance to fill before we + // get back here + if (queue.Count == 0) + FireQueueEmpty(i); } else { // No packets in this queue. Fire the queue empty callback - QueueEmpty callback = OnQueueEmpty; - if (callback != null) - callback((ThrottleOutPacketType)i); + // if it has not been called recently + FireQueueEmpty(i); } } } @@ -432,8 +452,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Always round retransmission timeout up to two seconds RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR))); - //Logger.Debug("Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " + + //m_log.Debug("[LLUDPCLIENT]: Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " + // RTTVAR + " based on new RTT of " + r + "ms"); } + + private void FireQueueEmpty(int queueIndex) + { + if (!queueEmptySent[queueIndex]) + { + queueEmptySent[queueIndex] = true; + + QueueEmpty callback = OnQueueEmpty; + if (callback != null) + Util.FireAndForget(delegate(object o) { callback((ThrottleOutPacketType)(int)o); }, queueIndex); + } + } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 8689af3..57fee59 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -332,8 +332,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void ResendUnacked(LLUDPClient udpClient) { - if (udpClient.NeedAcks.Count > 0) + if (udpClient.IsConnected && udpClient.NeedAcks.Count > 0) { + // Disconnect an agent if no packets are received for some time + //FIXME: Make 60 an .ini setting + if (Environment.TickCount - udpClient.TickLastPacketReceived > 1000 * 60) + { + m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID); + + RemoveClient(udpClient); + return; + } + + // Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO List expiredPackets = udpClient.NeedAcks.GetExpiredPackets(udpClient.RTO); if (expiredPackets != null) @@ -343,48 +354,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP { OutgoingPacket outgoingPacket = expiredPackets[i]; - // FIXME: Make this an .ini setting - if (outgoingPacket.ResendCount < 3) - { - //Logger.Debug(String.Format("Resending packet #{0} (attempt {1}), {2}ms have passed", - // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount)); + //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed", + // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount); - // Set the resent flag - outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); - outgoingPacket.Category = ThrottleOutPacketType.Resend; + // Set the resent flag + outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); + outgoingPacket.Category = ThrottleOutPacketType.Resend; - // The TickCount will be set to the current time when the packet - // is actually sent out again - outgoingPacket.TickCount = 0; + // The TickCount will be set to the current time when the packet + // is actually sent out again + outgoingPacket.TickCount = 0; - // Bump up the resend count on this packet - Interlocked.Increment(ref outgoingPacket.ResendCount); - //Interlocked.Increment(ref Stats.ResentPackets); + // Bump up the resend count on this packet + Interlocked.Increment(ref outgoingPacket.ResendCount); + //Interlocked.Increment(ref Stats.ResentPackets); - // Queue or (re)send the packet - if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) - SendPacketFinal(outgoingPacket); - } - else - { - m_log.DebugFormat("[LLUDPSERVER]: Dropping packet #{0} for agent {1} after {2} failed attempts", - outgoingPacket.SequenceNumber, outgoingPacket.Client.RemoteEndPoint, outgoingPacket.ResendCount); - - lock (udpClient.NeedAcks.SyncRoot) - udpClient.NeedAcks.RemoveUnsafe(outgoingPacket.SequenceNumber); - - //Interlocked.Increment(ref Stats.DroppedPackets); - - // Disconnect an agent if no packets are received for some time - //FIXME: Make 60 an .ini setting - if (Environment.TickCount - udpClient.TickLastPacketReceived > 1000 * 60) - { - m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID); - - RemoveClient(udpClient); - return; - } - } + // Requeue or resend the packet + if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) + SendPacketFinal(outgoingPacket); } } } @@ -403,6 +390,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0; LLUDPClient udpClient = outgoingPacket.Client; + if (!udpClient.IsConnected) + return; + // Keep track of when this packet was sent out (right now) outgoingPacket.TickCount = Environment.TickCount; @@ -481,14 +471,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP } catch (MalformedDataException) { - m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse packet:\n{0}", - Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)); + m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse packet from {0}:\n{1}", + buffer.RemoteEndPoint, Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)); } // Fail-safe check if (packet == null) { - m_log.Warn("[LLUDPSERVER]: Couldn't build a message from the incoming data"); + m_log.Warn("[LLUDPSERVER]: Couldn't build a message from incoming data " + buffer.DataLength + + " bytes long from " + buffer.RemoteEndPoint); return; } @@ -513,6 +504,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP udpClient = ((LLClientView)client).UDPClient; + if (!udpClient.IsConnected) + return; + #endregion Packet to Client Mapping // Stats tracking @@ -643,7 +637,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Create the LLClientView LLClientView client = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); client.OnLogout += LogoutHandler; - client.OnConnectionClosed += ConnectionClosedHandler; // Start the IClientAPI client.Start(); @@ -745,17 +738,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP { LLUDPClient udpClient = ((LLClientView)client).UDPClient; - if (udpClient.DequeueOutgoing()) - packetSent = true; - if (resendUnacked) - ResendUnacked(udpClient); - if (sendAcks) + if (udpClient.IsConnected) { - SendAcks(udpClient); - udpClient.SendPacketStats(); + if (udpClient.DequeueOutgoing()) + packetSent = true; + if (resendUnacked) + ResendUnacked(udpClient); + if (sendAcks) + { + SendAcks(udpClient); + udpClient.SendPacketStats(); + } + if (sendPings) + SendPing(udpClient); } - if (sendPings) - SendPing(udpClient); } } ); @@ -808,14 +804,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void LogoutHandler(IClientAPI client) { - client.OnLogout -= LogoutHandler; client.SendLogoutPacket(); } - - private void ConnectionClosedHandler(IClientAPI client) - { - client.OnConnectionClosed -= ConnectionClosedHandler; - RemoveClient(((LLClientView)client).UDPClient); - } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index 195ca57..f3242c1 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -103,6 +103,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP } /// + /// Removes all elements from the collection + /// + public void Clear() + { + lock (SyncRoot) + packets.Clear(); + } + + /// /// Gets the packet with the lowest sequence number /// /// The packet with the lowest sequence number, or null if the -- cgit v1.1 From 31a61bbeec5aae6b679ffa53e2966420e774f301 Mon Sep 17 00:00:00 2001 From: Teravus Ovares (Dan Olivares) Date: Tue, 13 Oct 2009 22:03:00 -0400 Subject: * Fixes some prim crossings on megaregions with regions beyond the 512m mark * There's a slight chance that this could cause a problem with regular prim crossings.. but hopefully not. Revert if it does. --- .../CoreModules/World/Land/RegionCombinerModule.cs | 10 +- OpenSim/Region/Framework/Scenes/Scene.cs | 114 +++++++++++++++++---- 2 files changed, 99 insertions(+), 25 deletions(-) diff --git a/OpenSim/Region/CoreModules/World/Land/RegionCombinerModule.cs b/OpenSim/Region/CoreModules/World/Land/RegionCombinerModule.cs index 05d19a2..6499915 100644 --- a/OpenSim/Region/CoreModules/World/Land/RegionCombinerModule.cs +++ b/OpenSim/Region/CoreModules/World/Land/RegionCombinerModule.cs @@ -343,7 +343,9 @@ namespace OpenSim.Region.CoreModules.World.Land lock (scene.WestBorders) { - scene.WestBorders[0].BorderLine.Z += (int) Constants.RegionSize; //auto teleport West + + + scene.WestBorders[0].BorderLine.Z = (int)((scene.RegionInfo.RegionLocX - conn.RegionScene.RegionInfo.RegionLocX) * (int)Constants.RegionSize); //auto teleport West // Trigger auto teleport to root region scene.WestBorders[0].TriggerRegionX = conn.RegionScene.RegionInfo.RegionLocX; @@ -410,7 +412,7 @@ namespace OpenSim.Region.CoreModules.World.Land conn.RegionScene.WestBorders[0].BorderLine.Y += (int)Constants.RegionSize; lock (scene.SouthBorders) { - scene.SouthBorders[0].BorderLine.Z += (int) Constants.RegionSize; //auto teleport south + scene.SouthBorders[0].BorderLine.Z = (int)((scene.RegionInfo.RegionLocY - conn.RegionScene.RegionInfo.RegionLocY) * (int)Constants.RegionSize); //auto teleport south scene.SouthBorders[0].TriggerRegionX = conn.RegionScene.RegionInfo.RegionLocX; scene.SouthBorders[0].TriggerRegionY = conn.RegionScene.RegionInfo.RegionLocY; } @@ -481,7 +483,7 @@ namespace OpenSim.Region.CoreModules.World.Land lock (scene.SouthBorders) { - scene.SouthBorders[0].BorderLine.Z += (int) Constants.RegionSize; //auto teleport south + scene.SouthBorders[0].BorderLine.Z = (int)((scene.RegionInfo.RegionLocY - conn.RegionScene.RegionInfo.RegionLocY) * (int)Constants.RegionSize); //auto teleport south scene.SouthBorders[0].TriggerRegionX = conn.RegionScene.RegionInfo.RegionLocX; scene.SouthBorders[0].TriggerRegionY = conn.RegionScene.RegionInfo.RegionLocY; } @@ -503,7 +505,7 @@ namespace OpenSim.Region.CoreModules.World.Land lock (scene.WestBorders) { - scene.WestBorders[0].BorderLine.Z += (int) Constants.RegionSize; //auto teleport West + scene.WestBorders[0].BorderLine.Z = (int)((scene.RegionInfo.RegionLocX - conn.RegionScene.RegionInfo.RegionLocX) * (int)Constants.RegionSize); //auto teleport West scene.WestBorders[0].TriggerRegionX = conn.RegionScene.RegionInfo.RegionLocX; scene.WestBorders[0].TriggerRegionY = conn.RegionScene.RegionInfo.RegionLocY; } diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 949cf19..0f351ce 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -1780,36 +1780,86 @@ namespace OpenSim.Region.Framework.Scenes Vector3 pos = attemptedPosition; + int changeX = 1; + int changeY = 1; + if (TestBorderCross(attemptedPosition + WestCross, Cardinals.W)) { if (TestBorderCross(attemptedPosition + SouthCross, Cardinals.S)) { - //Border crossedBorderx = GetCrossedBorder(attemptedPosition,Cardinals.W); - //Border crossedBordery = GetCrossedBorder(attemptedPosition, Cardinals.S); + + Border crossedBorderx = GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W); + + if (crossedBorderx.BorderLine.Z > 0) + { + pos.X = ((pos.X + crossedBorderx.BorderLine.Z)); + changeX = (int)(crossedBorderx.BorderLine.Z /(int) Constants.RegionSize); + } + else + pos.X = ((pos.X + Constants.RegionSize)); + + Border crossedBordery = GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S); //(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize) - pos.X = ((pos.X + Constants.RegionSize)); - pos.Y = ((pos.Y + Constants.RegionSize)); + + if (crossedBordery.BorderLine.Z > 0) + { + pos.Y = ((pos.Y + crossedBordery.BorderLine.Z)); + } + else + pos.Y = ((pos.Y + Constants.RegionSize)); + + + newRegionHandle - = Util.UIntsToLong((uint)((thisx - 1) * Constants.RegionSize), - (uint)((thisy - 1) * Constants.RegionSize)); + = Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize), + (uint)((thisy - changeY) * Constants.RegionSize)); // x - 1 // y - 1 } else if (TestBorderCross(attemptedPosition + NorthCross, Cardinals.N)) { - pos.X = ((pos.X + Constants.RegionSize)); - pos.Y = ((pos.Y - Constants.RegionSize)); + Border crossedBorderx = GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W); + + if (crossedBorderx.BorderLine.Z > 0) + { + pos.X = ((pos.X + crossedBorderx.BorderLine.Z)); + changeX = (int)(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize); + } + else + pos.X = ((pos.X + Constants.RegionSize)); + + + Border crossedBordery = GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S); + //(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize) + + if (crossedBordery.BorderLine.Z > 0) + { + pos.Y = ((pos.Y + crossedBordery.BorderLine.Z)); + changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize); + } + else + pos.Y = ((pos.Y + Constants.RegionSize)); + newRegionHandle - = Util.UIntsToLong((uint)((thisx - 1) * Constants.RegionSize), - (uint)((thisy + 1) * Constants.RegionSize)); + = Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize), + (uint)((thisy + changeY) * Constants.RegionSize)); // x - 1 // y + 1 } else { - pos.X = ((pos.X + Constants.RegionSize)); + Border crossedBorderx = GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W); + + if (crossedBorderx.BorderLine.Z > 0) + { + pos.X = ((pos.X + crossedBorderx.BorderLine.Z)); + changeX = (int)(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize); + } + else + pos.X = ((pos.X + Constants.RegionSize)); + newRegionHandle - = Util.UIntsToLong((uint) ((thisx - 1)*Constants.RegionSize), + = Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize), (uint) (thisy*Constants.RegionSize)); // x - 1 } @@ -1818,11 +1868,23 @@ namespace OpenSim.Region.Framework.Scenes { if (TestBorderCross(attemptedPosition + SouthCross, Cardinals.S)) { + pos.X = ((pos.X - Constants.RegionSize)); - pos.Y = ((pos.Y + Constants.RegionSize)); + Border crossedBordery = GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S); + //(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize) + + if (crossedBordery.BorderLine.Z > 0) + { + pos.Y = ((pos.Y + crossedBordery.BorderLine.Z)); + changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize); + } + else + pos.Y = ((pos.Y + Constants.RegionSize)); + + newRegionHandle - = Util.UIntsToLong((uint)((thisx + 1) * Constants.RegionSize), - (uint)((thisy - 1) * Constants.RegionSize)); + = Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize), + (uint)((thisy - changeY) * Constants.RegionSize)); // x + 1 // y - 1 } @@ -1831,8 +1893,8 @@ namespace OpenSim.Region.Framework.Scenes pos.X = ((pos.X - Constants.RegionSize)); pos.Y = ((pos.Y - Constants.RegionSize)); newRegionHandle - = Util.UIntsToLong((uint)((thisx + 1) * Constants.RegionSize), - (uint)((thisy + 1) * Constants.RegionSize)); + = Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize), + (uint)((thisy + changeY) * Constants.RegionSize)); // x + 1 // y + 1 } @@ -1840,16 +1902,26 @@ namespace OpenSim.Region.Framework.Scenes { pos.X = ((pos.X - Constants.RegionSize)); newRegionHandle - = Util.UIntsToLong((uint) ((thisx + 1)*Constants.RegionSize), + = Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize), (uint) (thisy*Constants.RegionSize)); // x + 1 } } else if (TestBorderCross(attemptedPosition + SouthCross, Cardinals.S)) { - pos.Y = ((pos.Y + Constants.RegionSize)); + Border crossedBordery = GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S); + //(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize) + + if (crossedBordery.BorderLine.Z > 0) + { + pos.Y = ((pos.Y + crossedBordery.BorderLine.Z)); + changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize); + } + else + pos.Y = ((pos.Y + Constants.RegionSize)); + newRegionHandle - = Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy - 1) * Constants.RegionSize)); + = Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy - changeY) * Constants.RegionSize)); // y - 1 } else if (TestBorderCross(attemptedPosition + NorthCross, Cardinals.N)) @@ -1857,7 +1929,7 @@ namespace OpenSim.Region.Framework.Scenes pos.Y = ((pos.Y - Constants.RegionSize)); newRegionHandle - = Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy + 1) * Constants.RegionSize)); + = Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy + changeY) * Constants.RegionSize)); // y + 1 } -- cgit v1.1 From 5976ac16b0eedaeca2360010a3d6f7be4213700a Mon Sep 17 00:00:00 2001 From: Dan Lake Date: Tue, 13 Oct 2009 19:13:06 -0700 Subject: Optimized heartbeat by calling Update() only on updated objects. During the heartbeat loop, Update() is called on every SceneObjectGroup which in turn checks if any SceneObjectPart has changed. For large regions (> 100k prims) this work consumes 20-30% of a CPU even though there are only a few objects updating each frame. There is only one other reason to check every object on every frame, and that is the case where a script has registered the object with an "at target" listener. We can easily track when an object is registered or unregistered with an AtTarget, so this is not a reason to check every object every heartbeat. In the attached patch, I have added a dictionary to the scene which tracks the objects which have At Targets. Each heartbeat, the AtTarget() function will be called on every object registered with a listener for that event. Also, I added a dictionary to SceneGraph which stores references to objects which have been queued for updates during the heartbeat. At each heartbeat, Update() is called only on the objects which have generated updates during that beat. --- OpenSim/Region/Framework/Scenes/Scene.cs | 82 ++++++++++------------ OpenSim/Region/Framework/Scenes/SceneGraph.cs | 28 +++----- .../Region/Framework/Scenes/SceneObjectGroup.cs | 15 ++-- 3 files changed, 52 insertions(+), 73 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 7944548..1ca0267 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -117,6 +117,8 @@ namespace OpenSim.Region.Framework.Scenes private volatile bool m_backingup = false; private Dictionary m_returns = new Dictionary(); + + private Dictionary m_groupsWithTargets = new Dictionary(); protected string m_simulatorVersion = "OpenSimulator Server"; @@ -246,8 +248,7 @@ namespace OpenSim.Region.Framework.Scenes private int m_update_physics = 1; private int m_update_entitymovement = 1; - private int m_update_entities = 1; // Run through all objects checking for updates - private int m_update_entitiesquick = 200; // Run through objects that have scheduled updates checking for updates + private int m_update_objects = 1; // Update objects which have scheduled themselves for updates private int m_update_presences = 1; // Update scene presence movements private int m_update_events = 1; private int m_update_backup = 200; @@ -979,28 +980,7 @@ namespace OpenSim.Region.Framework.Scenes maintc = Environment.TickCount; TimeSpan SinceLastFrame = DateTime.Now - m_lastupdate; - // Aquire a lock so only one update call happens at once - //updateLock.WaitOne(); float physicsFPS = 0; - //m_log.Info("sadfadf" + m_neighbours.Count.ToString()); - int agentsInScene = m_sceneGraph.GetRootAgentCount() + m_sceneGraph.GetChildAgentCount(); - - if (agentsInScene > 21) - { - if (m_update_entities == 1) - { - m_update_entities = 5; - StatsReporter.SetUpdateMS(6000); - } - } - else - { - if (m_update_entities == 5) - { - m_update_entities = 1; - StatsReporter.SetUpdateMS(3000); - } - } frameMS = Environment.TickCount; try @@ -1013,30 +993,17 @@ namespace OpenSim.Region.Framework.Scenes m_frame = 0; otherMS = Environment.TickCount; - // run through all entities looking for updates (slow) - if (m_frame % m_update_entities == 0) - { - /* // Adam Experimental - if (m_updateEntitiesThread == null) - { - m_updateEntitiesThread = new Thread(m_sceneGraph.UpdateEntities); - ThreadTracker.Add(m_updateEntitiesThread); - } - - if (m_updateEntitiesThread.ThreadState == ThreadState.Stopped) - m_updateEntitiesThread.Start(); - */ - - m_sceneGraph.UpdateEntities(); - } + // Check if any objects have reached their targets + CheckAtTargets(); + + // Update SceneObjectGroups that have scheduled themselves for updates + // Objects queue their updates onto all scene presences + if (m_frame % m_update_objects == 0) + m_sceneGraph.UpdateObjectGroups(); - // run through entities that have scheduled themselves for - // updates looking for updates(faster) - if (m_frame % m_update_entitiesquick == 0) - m_sceneGraph.ProcessUpdates(); - - // Run through scenepresences looking for updates + // Run through all ScenePresences looking for updates + // Presence updates and queued object updates for each presence are sent to clients if (m_frame % m_update_presences == 0) m_sceneGraph.UpdatePresences(); @@ -1140,6 +1107,31 @@ namespace OpenSim.Region.Framework.Scenes } } + + public void AddGroupTarget(SceneObjectGroup grp) + { + lock(m_groupsWithTargets) + m_groupsWithTargets[grp.UUID] = grp; + } + + public void RemoveGroupTarget(SceneObjectGroup grp) + { + lock(m_groupsWithTargets) + m_groupsWithTargets.Remove(grp.UUID); + } + + private void CheckAtTargets() + { + lock (m_groupsWithTargets) + { + foreach (KeyValuePair kvp in m_groupsWithTargets) + { + kvp.Value.checkAtTargets(); + } + } + } + + /// /// Send out simstats data to all clients /// diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index 54ac792..9cd2247 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -77,7 +77,7 @@ namespace OpenSim.Region.Framework.Scenes protected RegionInfo m_regInfo; protected Scene m_parentScene; - protected Dictionary m_updateList = new Dictionary(); + protected Dictionary m_updateList = new Dictionary(); protected int m_numRootAgents = 0; protected int m_numPrim = 0; protected int m_numChildAgents = 0; @@ -155,16 +155,6 @@ namespace OpenSim.Region.Framework.Scenes } } - protected internal void UpdateEntities() - { - List updateEntities = GetEntities(); - - foreach (EntityBase entity in updateEntities) - { - entity.Update(); - } - } - protected internal void UpdatePresences() { List updateScenePresences = GetScenePresences(); @@ -365,12 +355,12 @@ namespace OpenSim.Region.Framework.Scenes } /// - /// Add an entity to the list of prims to process on the next update + /// Add an object to the list of prims to process on the next update /// /// - /// A + /// A /// - protected internal void AddToUpdateList(EntityBase obj) + protected internal void AddToUpdateList(SceneObjectGroup obj) { lock (m_updateList) { @@ -381,18 +371,18 @@ namespace OpenSim.Region.Framework.Scenes /// /// Process all pending updates /// - protected internal void ProcessUpdates() + protected internal void UpdateObjectGroups() { - Dictionary updates; + Dictionary updates; // Some updates add more updates to the updateList. // Get the current list of updates and clear the list before iterating lock (m_updateList) { - updates = new Dictionary(m_updateList); + updates = new Dictionary(m_updateList); m_updateList.Clear(); } - // Go through all timers - foreach (KeyValuePair kvp in updates) + // Go through all updates + foreach (KeyValuePair kvp in updates) { // Don't abort the whole update if one entity happens to give us an exception. try diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 6a10618..d4cef7d 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -1234,6 +1234,7 @@ namespace OpenSim.Region.Framework.Scenes { lock (m_targets) m_targets.Clear(); + m_scene.RemoveGroupTarget(this); } ScheduleGroupForFullUpdate(); @@ -1864,12 +1865,6 @@ namespace OpenSim.Region.Framework.Scenes m_rootPart.UpdateFlag = 1; lastPhysGroupPos = AbsolutePosition; } - //foreach (SceneObjectPart part in m_parts.Values) - //{ - //if (part.UpdateFlag == 0) part.UpdateFlag = 1; - //} - - checkAtTargets(); if (UsePhysics && ((Math.Abs(lastPhysGroupRot.W - GroupRotation.W) > 0.1) || (Math.Abs(lastPhysGroupRot.X - GroupRotation.X) > 0.1) @@ -3114,6 +3109,7 @@ namespace OpenSim.Region.Framework.Scenes { m_targets.Add(handle, waypoint); } + m_scene.AddGroupTarget(this); return (int)handle; } @@ -3121,12 +3117,13 @@ namespace OpenSim.Region.Framework.Scenes { lock (m_targets) { - if (m_targets.ContainsKey((uint)handle)) - m_targets.Remove((uint)handle); + m_targets.Remove((uint)handle); + if (m_targets.Count == 0) + m_scene.RemoveGroupTarget(this); } } - private void checkAtTargets() + public void checkAtTargets() { if (m_scriptListens_atTarget || m_scriptListens_notAtTarget) { -- cgit v1.1 From 4135b0c4dcd142fe43b4c1b020d41a72d9df63dd Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 19:45:38 -0700 Subject: * Split Task category into Task and State * Crude prioritization hack --- OpenSim/Framework/ThrottleOutPacketType.cs | 18 +++++-- .../Region/ClientStack/LindenUDP/LLClientView.cs | 13 ++--- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 62 +++++++++++++--------- 3 files changed, 59 insertions(+), 34 deletions(-) diff --git a/OpenSim/Framework/ThrottleOutPacketType.cs b/OpenSim/Framework/ThrottleOutPacketType.cs index fd490a5..eb42fea 100644 --- a/OpenSim/Framework/ThrottleOutPacketType.cs +++ b/OpenSim/Framework/ThrottleOutPacketType.cs @@ -31,13 +31,23 @@ namespace OpenSim.Framework { public enum ThrottleOutPacketType : int { - Unknown = -1, // Also doubles as 'do not throttle' + /// Unthrottled packets + Unknown = -1, + /// Packets that are being resent Resend = 0, + /// Terrain data Land = 1, + /// Wind data Wind = 2, + /// Cloud data Cloud = 3, - Task = 4, - Texture = 5, - Asset = 6, + /// Texture assets + Texture = 4, + /// Non-texture assets + Asset = 5, + /// Avatar and primitive data + State = 6, + /// Any packets that do not fit into the other throttles + Task = 7, } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index ac558ff..3b1a0bd 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -1223,7 +1223,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP kill.ObjectData[0].ID = localID; kill.Header.Reliable = true; kill.Header.Zerocoded = true; - OutPacket(kill, ThrottleOutPacketType.Task); + OutPacket(kill, ThrottleOutPacketType.State); } /// @@ -1817,7 +1817,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP sendXfer.XferID.ID = xferID; sendXfer.XferID.Packet = packet; sendXfer.DataPacket.Data = data; - OutPacket(sendXfer, ThrottleOutPacketType.Task); + OutPacket(sendXfer, ThrottleOutPacketType.Asset); } public void SendEconomyData(float EnergyEfficiency, int ObjectCapacity, int ObjectCount, int PriceEnergyUnit, @@ -2099,7 +2099,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.AgentData.SessionID = SessionId; packet.Header.Reliable = false; packet.Header.Zerocoded = true; - OutPacket(packet, ThrottleOutPacketType.Task); + OutPacket(packet, ThrottleOutPacketType.State); } public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, @@ -3122,7 +3122,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP avp.Sender.IsTrial = false; avp.Sender.ID = agentID; - OutPacket(avp, ThrottleOutPacketType.Task); + OutPacket(avp, ThrottleOutPacketType.State); } public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) @@ -3262,6 +3262,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP terse.Header.Reliable = false; terse.Header.Zerocoded = true; + // FIXME: Move this to ThrottleOutPacketType.State when the real prioritization code is committed OutPacket(terse, ThrottleOutPacketType.Task); if (m_avatarTerseUpdates.Count == 0) @@ -3506,7 +3507,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } outPacket.Header.Zerocoded = true; - OutPacket(outPacket, ThrottleOutPacketType.Task); + OutPacket(outPacket, ThrottleOutPacketType.State); if (m_primFullUpdates.Count == 0 && m_primFullUpdateTimer.Enabled) lock (m_primFullUpdateTimer) @@ -3596,7 +3597,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP outPacket.Header.Reliable = false; outPacket.Header.Zerocoded = true; - OutPacket(outPacket, ThrottleOutPacketType.Task); + OutPacket(outPacket, ThrottleOutPacketType.State); if (m_primTerseUpdates.Count == 0) lock (m_primTerseUpdateTimer) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index b27d8d6..e7707a9 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -59,9 +59,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public sealed class LLUDPClient { + // FIXME: Make this a config setting + /// Percentage of the task throttle category that is allocated to avatar and prim + /// state updates + const float STATE_TASK_PERCENTAGE = 0.8f; + /// The number of packet categories to throttle on. If a throttle category is added /// or removed, this number must also change - const int THROTTLE_CATEGORY_COUNT = 7; + const int THROTTLE_CATEGORY_COUNT = 8; /// Fired when updated networking stats are produced for this client public event PacketStats OnPacketStats; @@ -134,9 +139,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// An optimization to store the length of dequeued packets being held /// for throttling. This avoids expensive calls to Packet.Length private readonly int[] nextPacketLengths = new int[THROTTLE_CATEGORY_COUNT]; - /// Flags to prevent queue empty callbacks from repeatedly firing - /// before the callbacks have a chance to put packets in the queue - private readonly bool[] queueEmptySent = new bool[THROTTLE_CATEGORY_COUNT]; /// A reference to the LLUDPServer that is managing this client private readonly LLUDPServer udpServer; @@ -167,9 +169,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP throttleCategories[(int)ThrottleOutPacketType.Land] = new TokenBucket(throttle, rates.LandLimit, rates.Land); throttleCategories[(int)ThrottleOutPacketType.Wind] = new TokenBucket(throttle, rates.WindLimit, rates.Wind); throttleCategories[(int)ThrottleOutPacketType.Cloud] = new TokenBucket(throttle, rates.CloudLimit, rates.Cloud); - throttleCategories[(int)ThrottleOutPacketType.Task] = new TokenBucket(throttle, rates.TaskLimit, rates.Task); throttleCategories[(int)ThrottleOutPacketType.Texture] = new TokenBucket(throttle, rates.TextureLimit, rates.Texture); throttleCategories[(int)ThrottleOutPacketType.Asset] = new TokenBucket(throttle, rates.AssetLimit, rates.Asset); + // State and Transaction are actually sub-categories of the LLUDP generic "Task" category + TokenBucket stateBucket = new TokenBucket(throttle, (int)((float)rates.TaskLimit * STATE_TASK_PERCENTAGE), (int)((float)rates.Task * STATE_TASK_PERCENTAGE)); + throttleCategories[(int)ThrottleOutPacketType.State] = stateBucket; + throttleCategories[(int)ThrottleOutPacketType.Task] = new TokenBucket(throttle, rates.TaskLimit - stateBucket.MaxBurst, rates.Task - stateBucket.DripRate); // Set the granularity variable used for retransmission calculations to // the measured resolution of Environment.TickCount @@ -177,6 +182,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Default the retransmission timeout to three seconds RTO = 3000; + + // Initialize this to a sane value to prevent early disconnects + TickLastPacketReceived = Environment.TickCount; } /// @@ -212,7 +220,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP info.landThrottle = throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; info.windThrottle = throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; info.cloudThrottle = throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; - info.taskThrottle = throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; + info.taskThrottle = throttleCategories[(int)ThrottleOutPacketType.State].DripRate + throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; info.assetThrottle = throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; info.textureThrottle = throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + @@ -309,7 +317,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Task].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)(throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) + + throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4; Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4; @@ -318,12 +327,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SetThrottle(ThrottleOutPacketType category, int rate) { - int i = (int)category; - if (i >= 0 && i < throttleCategories.Length) + if (category == ThrottleOutPacketType.Task) + { + TokenBucket stateBucket = throttleCategories[(int)ThrottleOutPacketType.State]; + TokenBucket taskBucket = throttleCategories[(int)ThrottleOutPacketType.Task]; + + stateBucket.MaxBurst = (int)((float)rate * STATE_TASK_PERCENTAGE); + stateBucket.DripRate = (int)((float)rate * STATE_TASK_PERCENTAGE); + + taskBucket.MaxBurst = rate - stateBucket.MaxBurst; + taskBucket.DripRate = rate - stateBucket.DripRate; + } + else { - TokenBucket bucket = throttleCategories[(int)category]; - bucket.MaxBurst = rate; - bucket.DripRate = rate; + int i = (int)category; + if (i >= 0 && i < throttleCategories.Length) + { + TokenBucket bucket = throttleCategories[(int)category]; + bucket.MaxBurst = rate; + bucket.DripRate = rate; + } } } @@ -393,10 +416,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP queue = packetOutboxes[i]; if (queue.Dequeue(out packet)) { - // Reset the flag for firing this queue's OnQueueEmpty callback - // now that we have dequeued a packet - queueEmptySent[i] = false; - // A packet was pulled off the queue. See if we have // enough tokens in the bucket to send it out if (bucket.RemoveTokens(packet.Buffer.DataLength)) @@ -458,14 +477,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void FireQueueEmpty(int queueIndex) { - if (!queueEmptySent[queueIndex]) - { - queueEmptySent[queueIndex] = true; - - QueueEmpty callback = OnQueueEmpty; - if (callback != null) - Util.FireAndForget(delegate(object o) { callback((ThrottleOutPacketType)(int)o); }, queueIndex); - } + QueueEmpty callback = OnQueueEmpty; + if (callback != null) + Util.FireAndForget(delegate(object o) { callback((ThrottleOutPacketType)(int)o); }, queueIndex); } } } -- cgit v1.1 From db4cdc09614abc1c35a242f4c882b2e3285f0206 Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 14 Oct 2009 04:18:59 +0100 Subject: Set the estate owner to be the master avatar if it's not set. --- OpenSim/Services/Interfaces/IGridService.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/OpenSim/Services/Interfaces/IGridService.cs b/OpenSim/Services/Interfaces/IGridService.cs index 14560b1..e69e4cd 100644 --- a/OpenSim/Services/Interfaces/IGridService.cs +++ b/OpenSim/Services/Interfaces/IGridService.cs @@ -200,6 +200,12 @@ namespace OpenSim.Services.Interfaces Maturity = ConvertFrom.RegionSettings.Maturity; RegionSecret = ConvertFrom.regionSecret; EstateOwner = ConvertFrom.EstateSettings.EstateOwner; + if (EstateOwner == UUID.Zero) + { + EstateOwner = ConvertFrom.MasterAvatarAssignedUUID; + ConvertFrom.EstateSettings.EstateOwner = EstateOwner; + ConvertFrom.EstateSettings.Save(); + } } public GridRegion(GridRegion ConvertFrom) -- cgit v1.1 From 3795cface2c089c9148e727b086cb4cd554ff38e Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 14 Oct 2009 17:39:38 +0100 Subject: Enable LSL dialogs to display group names properly --- .../Shared/Api/Implementation/LSL_Api.cs | 10 +++++++ ...m.Region.ClientStack.LindenUDP.Tests.dll.config | 33 ---------------------- 2 files changed, 10 insertions(+), 33 deletions(-) delete mode 100644 bin/OpenSim.Region.ClientStack.LindenUDP.Tests.dll.config diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index b5f6721..e10e612 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -859,7 +859,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api World.Entities.TryGetValue(objecUUID, out SensedObject); if (SensedObject == null) + { + IGroupsModule groups = World.RequestModuleInterface(); + if (groups != null) + { + GroupRecord gr = groups.GetGroupRecord(objecUUID); + if (gr != null) + return gr.GroupName; + } return String.Empty; + } + return SensedObject.Name; } diff --git a/bin/OpenSim.Region.ClientStack.LindenUDP.Tests.dll.config b/bin/OpenSim.Region.ClientStack.LindenUDP.Tests.dll.config deleted file mode 100644 index a3f681d..0000000 --- a/bin/OpenSim.Region.ClientStack.LindenUDP.Tests.dll.config +++ /dev/null @@ -1,33 +0,0 @@ - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- cgit v1.1 From bea13e37090bd2daa44c2202634c8e2fa81c203b Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Wed, 14 Oct 2009 11:01:46 -0700 Subject: Setting changeY in border crossing. --- OpenSim/Region/Framework/Scenes/Scene.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 0f351ce..04eb93f 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -1804,6 +1804,7 @@ namespace OpenSim.Region.Framework.Scenes if (crossedBordery.BorderLine.Z > 0) { pos.Y = ((pos.Y + crossedBordery.BorderLine.Z)); + changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize); } else pos.Y = ((pos.Y + Constants.RegionSize)); -- cgit v1.1 From 0d2e6463d714bce8a6a628bd647c625feeeae8f6 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 14 Oct 2009 11:43:31 -0700 Subject: * Minimized the number of times textures are pulled off the priority queue * OnQueueEmpty is still called async, but will not be called for a given category if the previous callback for that category is still running. This is the most balanced behavior I could find, and seems to work well * Added support for the old [ClientStack.LindenUDP] settings (including setting the receive buffer size) and added the new token bucket and global throttle settings * Added the AssetLoaderEnabled config variable to optionally disable loading assets from XML every startup. This gives a dramatic improvement in startup times for those who don't need the functionality every startup --- OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs | 25 +++++---- .../Region/ClientStack/LindenUDP/LLClientView.cs | 7 ++- .../Region/ClientStack/LindenUDP/LLImageManager.cs | 48 ++++++++-------- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 65 +++++++++++++++++----- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 12 +++- .../Region/ClientStack/LindenUDP/OpenSimUDPBase.cs | 15 ++++- .../Region/ClientStack/LindenUDP/ThrottleRates.cs | 36 +++++++----- OpenSim/Services/AssetService/AssetService.cs | 17 ++++-- bin/OpenSim.ini.example | 62 ++++++++++++--------- 9 files changed, 189 insertions(+), 98 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs index 5877779..9ded390 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs @@ -72,14 +72,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_imageManager = imageManager; } - public bool SendPackets(LLClientView client, int maxpack) + /// + /// Sends packets for this texture to a client until packetsToSend is + /// hit or the transfer completes + /// + /// Reference to the client that the packets are destined for + /// Maximum number of packets to send during this call + /// Number of packets sent during this call + /// True if the transfer completes at the current discard level, otherwise false + public bool SendPackets(LLClientView client, int packetsToSend, out int packetsSent) { - if (client == null) - return false; + packetsSent = 0; if (m_currentPacket <= m_stopPacket) { - int count = 0; bool sendMore = true; if (!m_sentInfo || (m_currentPacket == 0)) @@ -88,25 +94,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_sentInfo = true; ++m_currentPacket; - ++count; + ++packetsSent; } if (m_currentPacket < 2) { m_currentPacket = 2; } - while (sendMore && count < maxpack && m_currentPacket <= m_stopPacket) + while (sendMore && packetsSent < packetsToSend && m_currentPacket <= m_stopPacket) { sendMore = SendPacket(client); ++m_currentPacket; - ++count; + ++packetsSent; } - - if (m_currentPacket > m_stopPacket) - return true; } - return false; + return (m_currentPacket > m_stopPacket); } public void RunUpdate() diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 3b1a0bd..9afff5a 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -313,10 +313,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected int m_primFullUpdatesPerPacket = 14; protected int m_primTerseUpdateRate = 10; protected int m_primFullUpdateRate = 14; - protected int m_textureSendLimit = 20; - protected int m_textureDataLimit = 10; protected int m_avatarTerseUpdateRate = 50; protected int m_avatarTerseUpdatesPerPacket = 5; + /// Number of texture packets to put on the queue each time the + /// OnQueueEmpty event is triggered for the texture category + protected int m_textureSendLimit = 20; protected IAssetService m_assetService; #endregion Class Members @@ -3453,7 +3454,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP void ProcessTextureRequests() { if (m_imageManager != null) - m_imageManager.ProcessImageQueue(m_textureSendLimit, m_textureDataLimit); + m_imageManager.ProcessImageQueue(m_textureSendLimit); } void ProcessPrimFullUpdates(object sender, ElapsedEventArgs e) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index 8410ee9..9d36cff 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -162,32 +162,38 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public bool ProcessImageQueue(int count, int maxpack) + public bool ProcessImageQueue(int packetsToSend) { - J2KImage imagereq; - int numCollected = 0; - m_lastloopprocessed = DateTime.Now.Ticks; + int packetsSent = 0; - // This can happen during Close() - if (m_client == null) - return false; - - while ((imagereq = GetHighestPriorityImage()) != null) + while (packetsSent < packetsToSend) { - if (imagereq.IsDecoded == true) + J2KImage image = GetHighestPriorityImage(); + + // If null was returned, the texture priority queue is currently empty + if (image == null) + return false; + + if (image.IsDecoded) { - ++numCollected; + int sent; + bool imageDone = image.SendPackets(m_client, packetsToSend - packetsSent, out sent); - if (imagereq.SendPackets(m_client, maxpack)) - { - // Send complete. Destroy any knowledge of this transfer - RemoveImageFromQueue(imagereq); - } - } + packetsSent += sent; - if (numCollected == count) - break; + // If the send is complete, destroy any knowledge of this transfer + if (imageDone) + RemoveImageFromQueue(image); + } + else + { + // TODO: This is a limitation of how LLImageManager is currently + // written. Undecoded textures should not be going into the priority + // queue, because a high priority undecoded texture will clog up the + // pipeline for a client + return true; + } } return m_priorityQueue.Count > 0; @@ -199,10 +205,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void Close() { m_shuttingdown = true; - m_priorityQueue = null; - m_j2kDecodeModule = null; - m_assetCache = null; - m_client = null; } #region Priority Queue Helpers diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index e7707a9..39472cb 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -28,6 +28,7 @@ using System; using System.Collections.Generic; using System.Net; +using log4net; using OpenSim.Framework; using OpenMetaverse; @@ -59,6 +60,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP ///
public sealed class LLUDPClient { + private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + // FIXME: Make this a config setting /// Percentage of the task throttle category that is allocated to avatar and prim /// state updates @@ -136,9 +139,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// A container that can hold one packet for each outbox, used to store /// dequeued packets that are being held for throttling private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; - /// An optimization to store the length of dequeued packets being held - /// for throttling. This avoids expensive calls to Packet.Length - private readonly int[] nextPacketLengths = new int[THROTTLE_CATEGORY_COUNT]; + /// Flags to prevent queue empty callbacks from stacking up on + /// top of each other + private readonly bool[] onQueueEmptyRunning = new bool[THROTTLE_CATEGORY_COUNT]; /// A reference to the LLUDPServer that is managing this client private readonly LLUDPServer udpServer; @@ -163,7 +166,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) packetOutboxes[i] = new OpenSim.Framework.LocklessQueue(); - throttle = new TokenBucket(parentThrottle, 0, 0); + throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; throttleCategories[(int)ThrottleOutPacketType.Resend] = new TokenBucket(throttle, rates.ResendLimit, rates.Resend); throttleCategories[(int)ThrottleOutPacketType.Land] = new TokenBucket(throttle, rates.LandLimit, rates.Land); @@ -401,10 +404,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP // This bucket was empty the last time we tried to send a packet, // leaving a dequeued packet still waiting to be sent out. Try to // send it again - if (bucket.RemoveTokens(nextPacketLengths[i])) + OutgoingPacket nextPacket = nextPackets[i]; + if (bucket.RemoveTokens(nextPacket.Buffer.DataLength)) { // Send the packet - udpServer.SendPacketFinal(nextPackets[i]); + udpServer.SendPacketFinal(nextPacket); nextPackets[i] = null; packetSent = true; } @@ -426,23 +430,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else { - // Save the dequeued packet and the length calculation for - // the next iteration + // Save the dequeued packet for the next iteration nextPackets[i] = packet; - nextPacketLengths[i] = packet.Buffer.DataLength; } // If the queue is empty after this dequeue, fire the queue // empty callback now so it has a chance to fill before we // get back here if (queue.Count == 0) - FireQueueEmpty(i); + BeginFireQueueEmpty(i); } else { // No packets in this queue. Fire the queue empty callback // if it has not been called recently - FireQueueEmpty(i); + BeginFireQueueEmpty(i); } } } @@ -450,6 +452,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP return packetSent; } + /// + /// Called when an ACK packet is received and a round-trip time for a + /// packet is calculated. This is used to calculate the smoothed + /// round-trip time, round trip time variance, and finally the + /// retransmission timeout + /// + /// Round-trip time of a single packet and its + /// acknowledgement public void UpdateRoundTrip(float r) { const float ALPHA = 0.125f; @@ -475,11 +485,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP // RTTVAR + " based on new RTT of " + r + "ms"); } - private void FireQueueEmpty(int queueIndex) + /// + /// Does an early check to see if this queue empty callback is already + /// running, then asynchronously firing the event + /// + /// Throttle category to fire the callback + /// for + private void BeginFireQueueEmpty(int throttleIndex) { + if (!onQueueEmptyRunning[throttleIndex]) + Util.FireAndForget(FireQueueEmpty, throttleIndex); + } + + /// + /// Checks to see if this queue empty callback is already running, + /// then firing the event + /// + /// Throttle category to fire the callback for, stored + /// as an object to match the WaitCallback delegate signature + private void FireQueueEmpty(object o) + { + int i = (int)o; + ThrottleOutPacketType type = (ThrottleOutPacketType)i; QueueEmpty callback = OnQueueEmpty; + if (callback != null) - Util.FireAndForget(delegate(object o) { callback((ThrottleOutPacketType)(int)o); }, queueIndex); + { + if (!onQueueEmptyRunning[i]) + { + onQueueEmptyRunning[i] = true; + try { callback(type); } + catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); } + onQueueEmptyRunning[i] = false; + } + } } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 57fee59..1cfde91 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -109,6 +109,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP private Location m_location; /// The measured resolution of Environment.TickCount private float m_tickCountResolution; + /// The size of the receive buffer for the UDP socket. This value + /// is passed up to the operating system and used in the system networking + /// stack. Use zero to leave this value as the default + private int m_recvBufferSize; /// The measured resolution of Environment.TickCount public float TickCountResolution { get { return m_tickCountResolution; } } @@ -135,6 +139,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_circuitManager = circuitManager; + IConfig config = configSource.Configs["ClientStack.LindenUDP"]; + if (config != null) + { + m_recvBufferSize = config.GetInt("client_socket_rcvbuf_size", 0); + } + // TODO: Config support for throttling the entire connection m_throttle = new TokenBucket(null, 0, 0); m_throttleRates = new ThrottleRates(configSource); @@ -145,7 +155,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_scene == null) throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); - base.Start(); + base.Start(m_recvBufferSize); // Start the incoming packet processing thread Thread incomingThread = new Thread(IncomingPacketHandler); diff --git a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs index fad2ea8..44a6ed6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs @@ -73,6 +73,7 @@ namespace OpenMetaverse ///
/// Local IP address to bind the server to /// Port to listening for incoming UDP packets on + /// public OpenSimUDPBase(IPAddress bindAddress, int port) { m_localBindAddress = bindAddress; @@ -82,25 +83,31 @@ namespace OpenMetaverse /// /// Start the UDP server /// + /// The size of the receive buffer for + /// the UDP socket. This value is passed up to the operating system + /// and used in the system networking stack. Use zero to leave this + /// value as the default /// This method will attempt to set the SIO_UDP_CONNRESET flag /// on the socket to get newer versions of Windows to behave in a sane /// manner (not throwing an exception when the remote side resets the /// connection). This call is ignored on Mono where the flag is not /// necessary - public void Start() + public void Start(int recvBufferSize) { if (m_shutdownFlag) { const int SIO_UDP_CONNRESET = -1744830452; IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort); + m_udpSocket = new Socket( AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + try { - // this udp socket flag is not supported under mono, + // This udp socket flag is not supported under mono, // so we'll catch the exception and continue m_udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null); m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag set"); @@ -109,6 +116,10 @@ namespace OpenMetaverse { m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring"); } + + if (recvBufferSize != 0) + m_udpSocket.ReceiveBufferSize = recvBufferSize; + m_udpSocket.Bind(ipep); // we're not shutting down, we're starting up diff --git a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs index 858a03c..adad4c3 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs @@ -51,6 +51,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public int Texture; /// Drip rate for asset packets public int Asset; + /// Drip rate for the parent token bucket + public int Total; /// Maximum burst rate for resent packets public int ResendLimit; @@ -66,6 +68,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public int TextureLimit; /// Maximum burst rate for asset packets public int AssetLimit; + /// Burst rate for the parent token bucket + public int TotalLimit; /// /// Default constructor @@ -77,21 +81,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP { IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"]; - Resend = throttleConfig.GetInt("ResendDefault", 12500); - Land = throttleConfig.GetInt("LandDefault", 500); - Wind = throttleConfig.GetInt("WindDefault", 500); - Cloud = throttleConfig.GetInt("CloudDefault", 500); - Task = throttleConfig.GetInt("TaskDefault", 500); - Texture = throttleConfig.GetInt("TextureDefault", 500); - Asset = throttleConfig.GetInt("AssetDefault", 500); + Resend = throttleConfig.GetInt("resend_default", 12500); + Land = throttleConfig.GetInt("land_default", 500); + Wind = throttleConfig.GetInt("wind_default", 500); + Cloud = throttleConfig.GetInt("cloud_default", 500); + Task = throttleConfig.GetInt("task_default", 500); + Texture = throttleConfig.GetInt("texture_default", 500); + Asset = throttleConfig.GetInt("asset_default", 500); - ResendLimit = throttleConfig.GetInt("ResendLimit", 18750); - LandLimit = throttleConfig.GetInt("LandLimit", 29750); - WindLimit = throttleConfig.GetInt("WindLimit", 18750); - CloudLimit = throttleConfig.GetInt("CloudLimit", 18750); - TaskLimit = throttleConfig.GetInt("TaskLimit", 55750); - TextureLimit = throttleConfig.GetInt("TextureLimit", 55750); - AssetLimit = throttleConfig.GetInt("AssetLimit", 27500); + Total = throttleConfig.GetInt("client_throttle_max_bps", 0); + + ResendLimit = throttleConfig.GetInt("resend_limit", 18750); + LandLimit = throttleConfig.GetInt("land_limit", 29750); + WindLimit = throttleConfig.GetInt("wind_limit", 18750); + CloudLimit = throttleConfig.GetInt("cloud_limit", 18750); + TaskLimit = throttleConfig.GetInt("task_limit", 55750); + TextureLimit = throttleConfig.GetInt("texture_limit", 55750); + AssetLimit = throttleConfig.GetInt("asset_limit", 27500); + + TotalLimit = throttleConfig.GetInt("client_throttle_max_bps", 0); } catch (Exception) { } } diff --git a/OpenSim/Services/AssetService/AssetService.cs b/OpenSim/Services/AssetService/AssetService.cs index ebfd47a..df33db2 100644 --- a/OpenSim/Services/AssetService/AssetService.cs +++ b/OpenSim/Services/AssetService/AssetService.cs @@ -64,12 +64,17 @@ namespace OpenSim.Services.AssetService string loaderArgs = assetConfig.GetString("AssetLoaderArgs", String.Empty); - m_log.InfoFormat("[ASSET]: Loading default asset set from {0}", loaderArgs); - m_AssetLoader.ForEachDefaultXmlAsset(loaderArgs, - delegate(AssetBase a) - { - Store(a); - }); + bool assetLoaderEnabled = assetConfig.GetBoolean("AssetLoaderEnabled", true); + + if (assetLoaderEnabled) + { + m_log.InfoFormat("[ASSET]: Loading default asset set from {0}", loaderArgs); + m_AssetLoader.ForEachDefaultXmlAsset(loaderArgs, + delegate(AssetBase a) + { + Store(a); + }); + } m_log.Info("[ASSET CONNECTOR]: Local asset service enabled"); } diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 5cb51b2..0ab6257 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -341,25 +341,6 @@ [ClientStack.LindenUDP] - ; This is the multiplier applied to all client throttles for outgoing UDP network data - ; If it is set to 1, then we obey the throttle settings as given to us by the client. If it is set to 3, for example, then we - ; multiply that setting by 3 (e.g. if the client gives us a setting of 250 kilobits per second then we - ; will actually push down data at a maximum rate of 750 kilobits per second). - ; - ; In principle, setting a multiplier greater than 1 will allow data to be pushed down to a client much faster - ; than its UI allows the setting to go. This may be okay in some situations, such as standalone OpenSim - ; applications on a LAN. However, the greater the multipler, the higher the risk of packet drop, resulting - ; in symptoms such as missing terrain or objects. A much better solution is to change the client UI to allow - ; higher network bandwidth settings directly, though this isn't always possible. - ; - ; Currently this setting is 2 by default because we currently send much more texture data than is strictly - ; necessary. A setting of 1 could result in slow texture transfer. This will be fixed when the transfer - ; of textures at different levels of quality is improved. - ; - ; Pre r7113, this setting was not exposed but was effectively 8. You may want to try this if you encounter - ; unexpected difficulties - client_throttle_multiplier = 2; - ; the client socket receive buffer size determines how many ; incoming requests we can process; the default on .NET is 8192 ; which is about 2 4k-sized UDP datagrams. On mono this is @@ -374,12 +355,39 @@ ; by the system's settings for the maximum client receive buffer ; size (on linux systems you can set that with "sysctl -w ; net.core.rmem_max=X") - ; - ; client_socket_rcvbuf_size = 8388608 - - ; Maximum bits per second to send to any single client. This will override the user's viewer preference settings. - - ; client_throttle_max_bps = 1500000 + ;client_socket_rcvbuf_size = 8388608 + + ; Maximum outbound bits per second for a single scene. This can be used to + ; throttle total outbound UDP traffic for a simulator. The default value is + ; 0, meaning no throttling at the scene level. The example given here is + ; 20 megabits + ;scene_throttle_max_bps = 20971520 + + ; Maximum bits per second to send to any single client. This will override + ; the user's viewer preference settings. The default value is 0, meaning no + ; aggregate throttling on clients (only per-category throttling). The + ; example given here is 1.5 megabits + ;client_throttle_max_bps = 1572864 + + ; Per-client bits per second rates for the various throttle categories. + ; These are default values that will be overriden by clients + ;resend_default = 12500 + ;land_default = 500 + ;wind_default = 500 + ;cloud_default = 50 + ;task_default = 500 + ;texture_default = 500 + ;asset_default = 500 + + ; Per-client maximum burst rates in bits per second for the various throttle + ; categories. These are default values that will be overriden by clients + ;resend_limit = 18750 + ;land_limit = 29750 + ;wind_limit = 18750 + ;cloud_limit = 18750 + ;task_limit = 55750 + ;texture_limit = 55750 + ;asset_limit = 27500 [Chat] ; Controls whether the chat module is enabled. Default is true. @@ -1381,6 +1389,10 @@ [AssetService] DefaultAssetLoader = "OpenSim.Framework.AssetLoader.Filesystem.dll" AssetLoaderArgs = "assets/AssetSets.xml" + + ; Disable this to prevent the default asset set from being inserted into the + ; asset store each time the region starts + AssetLoaderEnabled = true [GridService] ;; default standalone, overridable in StandaloneCommon.ini -- cgit v1.1 From 0cb0a28fde4c1d870c157bd19675a0653b06bc80 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Wed, 14 Oct 2009 19:43:56 +0100 Subject: * minor: remove some mono compiler warnings --- OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs | 2 +- OpenSim/Services/Connectors/User/UserServiceConnector.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs b/OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs index 6f7c90f..ecda85a 100644 --- a/OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs +++ b/OpenSim/Services/Connectors/Asset/AssetServiceConnector.cs @@ -298,7 +298,7 @@ namespace OpenSim.Services.Connectors return; } - AssetBase asset = asset = m_Cache.Get(assetID.ToString()); + AssetBase asset = m_Cache.Get(assetID.ToString()); if (asset == null) { diff --git a/OpenSim/Services/Connectors/User/UserServiceConnector.cs b/OpenSim/Services/Connectors/User/UserServiceConnector.cs index d418938..683990f 100644 --- a/OpenSim/Services/Connectors/User/UserServiceConnector.cs +++ b/OpenSim/Services/Connectors/User/UserServiceConnector.cs @@ -45,7 +45,7 @@ namespace OpenSim.Services.Connectors LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType); - private string m_ServerURI = String.Empty; +// private string m_ServerURI = String.Empty; public UserServicesConnector() { @@ -53,7 +53,7 @@ namespace OpenSim.Services.Connectors public UserServicesConnector(string serverURI) { - m_ServerURI = serverURI.TrimEnd('/'); +// m_ServerURI = serverURI.TrimEnd('/'); } public UserServicesConnector(IConfigSource source) @@ -78,7 +78,7 @@ namespace OpenSim.Services.Connectors m_log.Error("[USER CONNECTOR]: No Server URI named in section UserService"); throw new Exception("User connector init error"); } - m_ServerURI = serviceURI; + //m_ServerURI = serviceURI; } public UserAccount GetUserAccount(UUID scopeID, string firstName, string lastName) -- cgit v1.1 From c033477d2faf1449685d471db243651f132a7632 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 14 Oct 2009 11:52:48 -0700 Subject: * Read scene_throttle_bps from the config file and use it * Minor formatting cleanup --- OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs | 11 ++--------- OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 4 +++- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index 25542ab..d25bf95 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -184,7 +184,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP { int sent; bool imageDone = image.SendPackets(m_client, packetsToSend - packetsSent, out sent); - packetsSent += sent; // If the send is complete, destroy any knowledge of this transfer @@ -234,20 +233,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP image.PriorityQueueHandle = null; lock (m_syncRoot) - try - { - m_priorityQueue.Add(ref image.PriorityQueueHandle, image); - } + try { m_priorityQueue.Add(ref image.PriorityQueueHandle, image); } catch (Exception) { } } void RemoveImageFromQueue(J2KImage image) { lock (m_syncRoot) - try - { - m_priorityQueue.Delete(image.PriorityQueueHandle); - } + try { m_priorityQueue.Delete(image.PriorityQueueHandle); } catch (Exception) { } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 1cfde91..384eda7 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -138,15 +138,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Environment.TickCount Measurement m_circuitManager = circuitManager; + int sceneThrottleBps = 0; IConfig config = configSource.Configs["ClientStack.LindenUDP"]; if (config != null) { m_recvBufferSize = config.GetInt("client_socket_rcvbuf_size", 0); + sceneThrottleBps = config.GetInt("scene_throttle_max_bps", 0); } // TODO: Config support for throttling the entire connection - m_throttle = new TokenBucket(null, 0, 0); + m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps); m_throttleRates = new ThrottleRates(configSource); } -- cgit v1.1 From 25878d68287d89f2d1ab1061319439d93e6119e5 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 14 Oct 2009 13:00:42 -0700 Subject: * Added the "show connections" command to print out all of the currently tracked IClientAPIs --- OpenSim/Region/Application/OpenSim.cs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 4d8409bb..0fcc21e 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -239,6 +239,10 @@ namespace OpenSim "show users [full]", "Show user data", HandleShow); + m_console.Commands.AddCommand("region", false, "show connections", + "show connections", + "Show connection data", HandleShow); + m_console.Commands.AddCommand("region", false, "show users full", "show users full", String.Empty, HandleShow); @@ -921,7 +925,25 @@ namespace OpenSim regionName)); } - m_log.Info(""); + m_log.Info(String.Empty); + break; + + case "connections": + System.Text.StringBuilder connections = new System.Text.StringBuilder("Connections:\n"); + m_sceneManager.ForEachScene( + delegate(Scene scene) + { + scene.ClientManager.ForEach( + delegate(IClientAPI client) + { + connections.AppendFormat("{0}: {1} ({2}) from {3} on circuit {4}\n", + scene.RegionInfo.RegionName, client.Name, client.AgentId, client.RemoteEndPoint, client.CircuitCode); + } + ); + } + ); + + m_log.Info(connections.ToString()); break; case "modules": -- cgit v1.1 From 1e9e9df0b3c2c6fad5e94db96c799bb31c193af1 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 14 Oct 2009 14:25:58 -0700 Subject: * Switched to a plain lock for the ClientManager collections and protected the TryGetValues with try/catch instead of a lock * Added ClientManager.ForEachSync() for operations that need to run synchronously, such as "show connections" --- OpenSim/Framework/ClientManager.cs | 150 +++++++++++---------- OpenSim/Region/Application/OpenSim.cs | 2 +- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 4 +- 3 files changed, 85 insertions(+), 71 deletions(-) diff --git a/OpenSim/Framework/ClientManager.cs b/OpenSim/Framework/ClientManager.cs index 367bc6a..61b59e7 100644 --- a/OpenSim/Framework/ClientManager.cs +++ b/OpenSim/Framework/ClientManager.cs @@ -29,7 +29,6 @@ using System; using System.Collections.Generic; using System.Reflection; using System.Net; -using BclExtras.Collections; using OpenMetaverse; using OpenMetaverse.Packets; @@ -41,56 +40,29 @@ namespace OpenSim.Framework /// public class ClientManager { - #region IComparers - - private sealed class UUIDComparer : IComparer - { - public int Compare(UUID x, UUID y) - { - return x.CompareTo(y); - } - } - - private sealed class IPEndPointComparer : IComparer - { - public int Compare(IPEndPoint x, IPEndPoint y) - { - if (x == null && y == null) - return 0; - else if (x == null) - return -1; - else if (y == null) - return 1; - - int result = x.Address.Address.CompareTo(y.Address.Address); - if (result == 0) result = x.Port.CompareTo(y.Port); - - return result; - } - } - - #endregion IComparers - - /// An immutable dictionary mapping from + /// A dictionary mapping from /// to references - private ImmutableMap m_dict; - /// An immutable dictionary mapping from + private Dictionary m_dict1; + /// A dictionary mapping from /// to references - private ImmutableMap m_dict2; - /// Immutability grants thread safety for concurrent reads and - /// read-writes, but not concurrent writes - private object m_writeLock = new object(); + private Dictionary m_dict2; + /// An immutable collection of + /// references + private IClientAPI[] m_array; + /// Synchronization object for writing to the collections + private object m_syncRoot = new object(); /// Number of clients in the collection - public int Count { get { return m_dict.Count; } } + public int Count { get { return m_dict1.Count; } } /// /// Default constructor /// public ClientManager() { - m_dict = new ImmutableMap(new UUIDComparer()); - m_dict2 = new ImmutableMap(new IPEndPointComparer()); + m_dict1 = new Dictionary(); + m_dict2 = new Dictionary(); + m_array = new IClientAPI[0]; } /// @@ -102,20 +74,26 @@ namespace OpenSim.Framework /// otherwise false if the given key already existed in the collection public bool Add(IClientAPI value) { - lock (m_writeLock) + lock (m_syncRoot) { - if (!m_dict.ContainsKey(value.AgentId) && !m_dict2.ContainsKey(value.RemoteEndPoint)) - { - m_dict = m_dict.Add(value.AgentId, value); - m_dict2 = m_dict2.Add(value.RemoteEndPoint, value); - - return true; - } - else - { + if (m_dict1.ContainsKey(value.AgentId) || m_dict2.ContainsKey(value.RemoteEndPoint)) return false; - } + + m_dict1[value.AgentId] = value; + m_dict2[value.RemoteEndPoint] = value; + + IClientAPI[] oldArray = m_array; + int oldLength = oldArray.Length; + + IClientAPI[] newArray = new IClientAPI[oldLength + 1]; + for (int i = 0; i < oldLength; i++) + newArray[i] = oldArray[i]; + newArray[oldLength] = value; + + m_array = newArray; } + + return true; } /// @@ -126,21 +104,31 @@ namespace OpenSim.Framework /// was not present in the collection public bool Remove(UUID key) { - lock (m_writeLock) + lock (m_syncRoot) { - IClientAPI client; - - if (m_dict.TryGetValue(key, out client)) + IClientAPI value; + if (m_dict1.TryGetValue(key, out value)) { - m_dict = m_dict.Delete(key); - m_dict2 = m_dict2.Delete(client.RemoteEndPoint); + m_dict1.Remove(key); + m_dict2.Remove(value.RemoteEndPoint); + + IClientAPI[] oldArray = m_array; + int oldLength = oldArray.Length; + + IClientAPI[] newArray = new IClientAPI[oldLength - 1]; + int j = 0; + for (int i = 0; i < oldLength; i++) + { + if (oldArray[i] != value) + newArray[j++] = oldArray[i]; + } + + m_array = newArray; return true; } - else - { - return false; - } } + + return false; } /// @@ -148,10 +136,11 @@ namespace OpenSim.Framework /// public void Clear() { - lock (m_writeLock) + lock (m_syncRoot) { - m_dict = new ImmutableMap(new UUIDComparer()); - m_dict2 = new ImmutableMap(new IPEndPointComparer()); + m_dict1.Clear(); + m_dict2.Clear(); + m_array = new IClientAPI[0]; } } @@ -162,7 +151,7 @@ namespace OpenSim.Framework /// True if the UUID was found in the collection, otherwise false public bool ContainsKey(UUID key) { - return m_dict.ContainsKey(key); + return m_dict1.ContainsKey(key); } /// @@ -183,7 +172,12 @@ namespace OpenSim.Framework /// True if the lookup succeeded, otherwise false public bool TryGetValue(UUID key, out IClientAPI value) { - return m_dict.TryGetValue(key, out value); + try { return m_dict1.TryGetValue(key, out value); } + catch (Exception) + { + value = null; + return false; + } } /// @@ -194,7 +188,12 @@ namespace OpenSim.Framework /// True if the lookup succeeded, otherwise false public bool TryGetValue(IPEndPoint key, out IClientAPI value) { - return m_dict2.TryGetValue(key, out value); + try { return m_dict2.TryGetValue(key, out value); } + catch (Exception) + { + value = null; + return false; + } } /// @@ -204,7 +203,20 @@ namespace OpenSim.Framework /// Action to perform on each element public void ForEach(Action action) { - Parallel.ForEach(m_dict.Values, action); + IClientAPI[] localArray = m_array; + Parallel.ForEach(localArray, action); + } + + /// + /// Performs a given task synchronously for each of the elements in + /// the collection + /// + /// Action to perform on each element + public void ForEachSync(Action action) + { + IClientAPI[] localArray = m_array; + for (int i = 0; i < localArray.Length; i++) + action(localArray[i]); } } } diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 0fcc21e..ca6a2a3 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -933,7 +933,7 @@ namespace OpenSim m_sceneManager.ForEachScene( delegate(Scene scene) { - scene.ClientManager.ForEach( + scene.ClientManager.ForEachSync( delegate(IClientAPI client) { connections.AppendFormat("{0}: {1} ({2}) from {3} on circuit {4}\n", diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 384eda7..09845d6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -152,7 +152,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_throttleRates = new ThrottleRates(configSource); } - public new void Start() + public void Start() { if (m_scene == null) throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); @@ -817,6 +817,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void LogoutHandler(IClientAPI client) { client.SendLogoutPacket(); + if (client.IsActive) + RemoveClient(((LLClientView)client).UDPClient); } } } -- cgit v1.1 From 82012ec4e3c441021795c66112a66e002d459e73 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 14 Oct 2009 16:21:48 -0700 Subject: * Clean up the SetThrottle() code and add a maxBurstRate parameter to allow more tweaking in the future --- OpenSim/Framework/ThrottleOutPacketType.cs | 11 +- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 188 ++++++++++----------- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 1 - .../Region/ClientStack/LindenUDP/ThrottleRates.cs | 63 ++++++- bin/OpenSim.ini.example | 17 +- 5 files changed, 171 insertions(+), 109 deletions(-) diff --git a/OpenSim/Framework/ThrottleOutPacketType.cs b/OpenSim/Framework/ThrottleOutPacketType.cs index eb42fea..e21ff32 100644 --- a/OpenSim/Framework/ThrottleOutPacketType.cs +++ b/OpenSim/Framework/ThrottleOutPacketType.cs @@ -41,13 +41,14 @@ namespace OpenSim.Framework Wind = 2, /// Cloud data Cloud = 3, + /// Any packets that do not fit into the other throttles + Task = 4, /// Texture assets - Texture = 4, + Texture = 5, /// Non-texture assets - Asset = 5, + Asset = 6, /// Avatar and primitive data - State = 6, - /// Any packets that do not fit into the other throttles - Task = 7, + /// This is a sub-category of Task + State = 7, } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 39472cb..4eee6b6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -31,6 +31,7 @@ using System.Net; using log4net; using OpenSim.Framework; using OpenMetaverse; +using OpenMetaverse.Packets; namespace OpenSim.Region.ClientStack.LindenUDP { @@ -60,13 +61,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public sealed class LLUDPClient { - private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - - // FIXME: Make this a config setting + // TODO: Make this a config setting /// Percentage of the task throttle category that is allocated to avatar and prim /// state updates const float STATE_TASK_PERCENTAGE = 0.8f; + private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + /// The number of packet categories to throttle on. If a throttle category is added /// or removed, this number must also change const int THROTTLE_CATEGORY_COUNT = 8; @@ -129,21 +130,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_packetsSentReported; /// Throttle bucket for this agent's connection - private readonly TokenBucket throttle; + private readonly TokenBucket m_throttle; /// Throttle buckets for each packet category - private readonly TokenBucket[] throttleCategories; + private readonly TokenBucket[] m_throttleCategories; /// Throttle rate defaults and limits - private readonly ThrottleRates defaultThrottleRates; + private readonly ThrottleRates m_defaultThrottleRates; /// Outgoing queues for throttled packets - private readonly OpenSim.Framework.LocklessQueue[] packetOutboxes = new OpenSim.Framework.LocklessQueue[THROTTLE_CATEGORY_COUNT]; + private readonly OpenSim.Framework.LocklessQueue[] m_packetOutboxes = new OpenSim.Framework.LocklessQueue[THROTTLE_CATEGORY_COUNT]; /// A container that can hold one packet for each outbox, used to store /// dequeued packets that are being held for throttling - private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; + private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; /// Flags to prevent queue empty callbacks from stacking up on /// top of each other - private readonly bool[] onQueueEmptyRunning = new bool[THROTTLE_CATEGORY_COUNT]; + private readonly bool[] m_onQueueEmptyRunning = new bool[THROTTLE_CATEGORY_COUNT]; /// A reference to the LLUDPServer that is managing this client - private readonly LLUDPServer udpServer; + private readonly LLUDPServer m_udpServer; /// /// Default constructor @@ -157,27 +158,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Remote endpoint for this connection public LLUDPClient(LLUDPServer server, ThrottleRates rates, TokenBucket parentThrottle, uint circuitCode, UUID agentID, IPEndPoint remoteEndPoint) { - udpServer = server; AgentID = agentID; RemoteEndPoint = remoteEndPoint; CircuitCode = circuitCode; - defaultThrottleRates = rates; + m_udpServer = server; + m_defaultThrottleRates = rates; + m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); + m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) - packetOutboxes[i] = new OpenSim.Framework.LocklessQueue(); - - throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); - throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; - throttleCategories[(int)ThrottleOutPacketType.Resend] = new TokenBucket(throttle, rates.ResendLimit, rates.Resend); - throttleCategories[(int)ThrottleOutPacketType.Land] = new TokenBucket(throttle, rates.LandLimit, rates.Land); - throttleCategories[(int)ThrottleOutPacketType.Wind] = new TokenBucket(throttle, rates.WindLimit, rates.Wind); - throttleCategories[(int)ThrottleOutPacketType.Cloud] = new TokenBucket(throttle, rates.CloudLimit, rates.Cloud); - throttleCategories[(int)ThrottleOutPacketType.Texture] = new TokenBucket(throttle, rates.TextureLimit, rates.Texture); - throttleCategories[(int)ThrottleOutPacketType.Asset] = new TokenBucket(throttle, rates.AssetLimit, rates.Asset); - // State and Transaction are actually sub-categories of the LLUDP generic "Task" category - TokenBucket stateBucket = new TokenBucket(throttle, (int)((float)rates.TaskLimit * STATE_TASK_PERCENTAGE), (int)((float)rates.Task * STATE_TASK_PERCENTAGE)); - throttleCategories[(int)ThrottleOutPacketType.State] = stateBucket; - throttleCategories[(int)ThrottleOutPacketType.Task] = new TokenBucket(throttle, rates.TaskLimit - stateBucket.MaxBurst, rates.Task - stateBucket.DripRate); + { + ThrottleOutPacketType type = (ThrottleOutPacketType)i; + + m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue(); + m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); + } // Set the granularity variable used for retransmission calculations to // the measured resolution of Environment.TickCount @@ -199,8 +194,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP NeedAcks.Clear(); for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) { - packetOutboxes[i].Clear(); - nextPackets[i] = null; + m_packetOutboxes[i].Clear(); + m_nextPackets[i] = null; } OnPacketStats = null; OnQueueEmpty = null; @@ -219,13 +214,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP info.pendingAcks = new Dictionary(); info.needAck = new Dictionary(); - info.resendThrottle = throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; - info.landThrottle = throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; - info.windThrottle = throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; - info.cloudThrottle = throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; - info.taskThrottle = throttleCategories[(int)ThrottleOutPacketType.State].DripRate + throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; - info.assetThrottle = throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; - info.textureThrottle = throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; + info.resendThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; + info.landThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; + info.windThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; + info.cloudThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; + info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; + info.assetThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; + info.textureThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + info.taskThrottle + info.assetThrottle + info.textureThrottle; @@ -286,6 +281,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP adjData = throttleData; } + // 0.125f converts from bits to bytes int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; @@ -293,22 +289,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); + // State is a subcategory of task that we allocate a percentage to + int state = (int)((float)task * STATE_TASK_PERCENTAGE); + task -= state; - resend = (resend <= defaultThrottleRates.ResendLimit) ? resend : defaultThrottleRates.ResendLimit; - land = (land <= defaultThrottleRates.LandLimit) ? land : defaultThrottleRates.LandLimit; - wind = (wind <= defaultThrottleRates.WindLimit) ? wind : defaultThrottleRates.WindLimit; - cloud = (cloud <= defaultThrottleRates.CloudLimit) ? cloud : defaultThrottleRates.CloudLimit; - task = (task <= defaultThrottleRates.TaskLimit) ? task : defaultThrottleRates.TaskLimit; - texture = (texture <= defaultThrottleRates.TextureLimit) ? texture : defaultThrottleRates.TextureLimit; - asset = (asset <= defaultThrottleRates.AssetLimit) ? asset : defaultThrottleRates.AssetLimit; - - SetThrottle(ThrottleOutPacketType.Resend, resend); - SetThrottle(ThrottleOutPacketType.Land, land); - SetThrottle(ThrottleOutPacketType.Wind, wind); - SetThrottle(ThrottleOutPacketType.Cloud, cloud); - SetThrottle(ThrottleOutPacketType.Task, task); - SetThrottle(ThrottleOutPacketType.Texture, texture); - SetThrottle(ThrottleOutPacketType.Asset, asset); + int ceiling = Int32.MaxValue; + if (m_defaultThrottleRates.Total != 0) + { + ceiling = m_defaultThrottleRates.Total; + if (ceiling < Packet.MTU) ceiling = Packet.MTU; + } + + resend = Utils.Clamp(resend, Packet.MTU, ceiling); + land = Utils.Clamp(land, Packet.MTU, ceiling); + wind = Utils.Clamp(wind, Packet.MTU, ceiling); + cloud = Utils.Clamp(cloud, Packet.MTU, ceiling); + task = Utils.Clamp(task, Packet.MTU, ceiling); + texture = Utils.Clamp(texture, Packet.MTU, ceiling); + asset = Utils.Clamp(asset, Packet.MTU, ceiling); + state = Utils.Clamp(state, Packet.MTU, ceiling); + + int total = resend + land + wind + cloud + task + texture + asset + state; + int taskTotal = task + state; + + m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, State={8}, Total={9}", + AgentID, resend, land, wind, cloud, task, texture, asset, state, total); + + SetThrottle(ThrottleOutPacketType.Resend, resend, resend); + SetThrottle(ThrottleOutPacketType.Land, land, land); + SetThrottle(ThrottleOutPacketType.Wind, wind, wind); + SetThrottle(ThrottleOutPacketType.Cloud, cloud, cloud); + SetThrottle(ThrottleOutPacketType.Task, task, taskTotal); + SetThrottle(ThrottleOutPacketType.Texture, texture, texture); + SetThrottle(ThrottleOutPacketType.Asset, asset, asset); + SetThrottle(ThrottleOutPacketType.State, state, taskTotal); } public byte[] GetThrottlesPacked() @@ -316,40 +330,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP byte[] data = new byte[7 * 4]; int i = 0; - Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)(throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) + - throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)(m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) + + m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4; return data; } - public void SetThrottle(ThrottleOutPacketType category, int rate) + public void SetThrottle(ThrottleOutPacketType category, int rate, int maxBurst) { - if (category == ThrottleOutPacketType.Task) - { - TokenBucket stateBucket = throttleCategories[(int)ThrottleOutPacketType.State]; - TokenBucket taskBucket = throttleCategories[(int)ThrottleOutPacketType.Task]; - - stateBucket.MaxBurst = (int)((float)rate * STATE_TASK_PERCENTAGE); - stateBucket.DripRate = (int)((float)rate * STATE_TASK_PERCENTAGE); - - taskBucket.MaxBurst = rate - stateBucket.MaxBurst; - taskBucket.DripRate = rate - stateBucket.DripRate; - } - else + int i = (int)category; + if (i >= 0 && i < m_throttleCategories.Length) { - int i = (int)category; - if (i >= 0 && i < throttleCategories.Length) - { - TokenBucket bucket = throttleCategories[(int)category]; - bucket.MaxBurst = rate; - bucket.DripRate = rate; - } + TokenBucket bucket = m_throttleCategories[(int)category]; + bucket.DripRate = rate; + bucket.MaxBurst = maxBurst; } } @@ -357,12 +357,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP { int category = (int)packet.Category; - if (category >= 0 && category < packetOutboxes.Length) + if (category >= 0 && category < m_packetOutboxes.Length) { - OpenSim.Framework.LocklessQueue queue = packetOutboxes[category]; - TokenBucket bucket = throttleCategories[category]; + OpenSim.Framework.LocklessQueue queue = m_packetOutboxes[category]; + TokenBucket bucket = m_throttleCategories[category]; - if (throttleCategories[category].RemoveTokens(packet.Buffer.DataLength)) + if (m_throttleCategories[category].RemoveTokens(packet.Buffer.DataLength)) { // Enough tokens were removed from the bucket, the packet will not be queued return false; @@ -397,19 +397,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) { - bucket = throttleCategories[i]; + bucket = m_throttleCategories[i]; - if (nextPackets[i] != null) + if (m_nextPackets[i] != null) { // This bucket was empty the last time we tried to send a packet, // leaving a dequeued packet still waiting to be sent out. Try to // send it again - OutgoingPacket nextPacket = nextPackets[i]; + OutgoingPacket nextPacket = m_nextPackets[i]; if (bucket.RemoveTokens(nextPacket.Buffer.DataLength)) { // Send the packet - udpServer.SendPacketFinal(nextPacket); - nextPackets[i] = null; + m_udpServer.SendPacketFinal(nextPacket); + m_nextPackets[i] = null; packetSent = true; } } @@ -417,7 +417,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { // No dequeued packet waiting to be sent, try to pull one off // this queue - queue = packetOutboxes[i]; + queue = m_packetOutboxes[i]; if (queue.Dequeue(out packet)) { // A packet was pulled off the queue. See if we have @@ -425,13 +425,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (bucket.RemoveTokens(packet.Buffer.DataLength)) { // Send the packet - udpServer.SendPacketFinal(packet); + m_udpServer.SendPacketFinal(packet); packetSent = true; } else { // Save the dequeued packet for the next iteration - nextPackets[i] = packet; + m_nextPackets[i] = packet; } // If the queue is empty after this dequeue, fire the queue @@ -493,7 +493,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// for private void BeginFireQueueEmpty(int throttleIndex) { - if (!onQueueEmptyRunning[throttleIndex]) + if (!m_onQueueEmptyRunning[throttleIndex]) Util.FireAndForget(FireQueueEmpty, throttleIndex); } @@ -511,12 +511,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (callback != null) { - if (!onQueueEmptyRunning[i]) + if (!m_onQueueEmptyRunning[i]) { - onQueueEmptyRunning[i] = true; + m_onQueueEmptyRunning[i] = true; try { callback(type); } catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); } - onQueueEmptyRunning[i] = false; + m_onQueueEmptyRunning[i] = false; } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 09845d6..890f701 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -147,7 +147,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP sceneThrottleBps = config.GetInt("scene_throttle_max_bps", 0); } - // TODO: Config support for throttling the entire connection m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps); m_throttleRates = new ThrottleRates(configSource); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs index adad4c3..008d827 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs @@ -26,6 +26,7 @@ */ using System; +using OpenSim.Framework; using Nini.Config; namespace OpenSim.Region.ClientStack.LindenUDP @@ -45,12 +46,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP public int Wind; /// Drip rate for cloud packets public int Cloud; - /// Drip rate for task (state and transaction) packets + /// Drip rate for task packets public int Task; /// Drip rate for texture packets public int Texture; /// Drip rate for asset packets public int Asset; + /// Drip rate for state packets + public int State; /// Drip rate for the parent token bucket public int Total; @@ -68,6 +71,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public int TextureLimit; /// Maximum burst rate for asset packets public int AssetLimit; + /// Maximum burst rate for state packets + public int StateLimit; /// Burst rate for the parent token bucket public int TotalLimit; @@ -88,6 +93,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP Task = throttleConfig.GetInt("task_default", 500); Texture = throttleConfig.GetInt("texture_default", 500); Asset = throttleConfig.GetInt("asset_default", 500); + State = throttleConfig.GetInt("state_default", 500); Total = throttleConfig.GetInt("client_throttle_max_bps", 0); @@ -95,13 +101,66 @@ namespace OpenSim.Region.ClientStack.LindenUDP LandLimit = throttleConfig.GetInt("land_limit", 29750); WindLimit = throttleConfig.GetInt("wind_limit", 18750); CloudLimit = throttleConfig.GetInt("cloud_limit", 18750); - TaskLimit = throttleConfig.GetInt("task_limit", 55750); + TaskLimit = throttleConfig.GetInt("task_limit", 18750); TextureLimit = throttleConfig.GetInt("texture_limit", 55750); AssetLimit = throttleConfig.GetInt("asset_limit", 27500); + State = throttleConfig.GetInt("state_limit", 37000); TotalLimit = throttleConfig.GetInt("client_throttle_max_bps", 0); } catch (Exception) { } } + + public int GetRate(ThrottleOutPacketType type) + { + switch (type) + { + case ThrottleOutPacketType.Resend: + return Resend; + case ThrottleOutPacketType.Land: + return Land; + case ThrottleOutPacketType.Wind: + return Wind; + case ThrottleOutPacketType.Cloud: + return Cloud; + case ThrottleOutPacketType.Task: + return Task; + case ThrottleOutPacketType.Texture: + return Texture; + case ThrottleOutPacketType.Asset: + return Asset; + case ThrottleOutPacketType.State: + return State; + case ThrottleOutPacketType.Unknown: + default: + return 0; + } + } + + public int GetLimit(ThrottleOutPacketType type) + { + switch (type) + { + case ThrottleOutPacketType.Resend: + return ResendLimit; + case ThrottleOutPacketType.Land: + return LandLimit; + case ThrottleOutPacketType.Wind: + return WindLimit; + case ThrottleOutPacketType.Cloud: + return CloudLimit; + case ThrottleOutPacketType.Task: + return TaskLimit; + case ThrottleOutPacketType.Texture: + return TextureLimit; + case ThrottleOutPacketType.Asset: + return AssetLimit; + case ThrottleOutPacketType.State: + return StateLimit; + case ThrottleOutPacketType.Unknown: + default: + return 0; + } + } } } diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 0ab6257..983a7e5 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -357,19 +357,19 @@ ; net.core.rmem_max=X") ;client_socket_rcvbuf_size = 8388608 - ; Maximum outbound bits per second for a single scene. This can be used to + ; Maximum outbound bytes per second for a single scene. This can be used to ; throttle total outbound UDP traffic for a simulator. The default value is ; 0, meaning no throttling at the scene level. The example given here is ; 20 megabits - ;scene_throttle_max_bps = 20971520 + ;scene_throttle_max_bps = 2621440 ; Maximum bits per second to send to any single client. This will override ; the user's viewer preference settings. The default value is 0, meaning no ; aggregate throttling on clients (only per-category throttling). The ; example given here is 1.5 megabits - ;client_throttle_max_bps = 1572864 + ;client_throttle_max_bps = 196608 - ; Per-client bits per second rates for the various throttle categories. + ; Per-client bytes per second rates for the various throttle categories. ; These are default values that will be overriden by clients ;resend_default = 12500 ;land_default = 500 @@ -378,16 +378,19 @@ ;task_default = 500 ;texture_default = 500 ;asset_default = 500 + ;state_default = 500 - ; Per-client maximum burst rates in bits per second for the various throttle - ; categories. These are default values that will be overriden by clients + ; Per-client maximum burst rates in bytes per second for the various + ; throttle categories. These are default values that will be overriden by + ; clients ;resend_limit = 18750 ;land_limit = 29750 ;wind_limit = 18750 ;cloud_limit = 18750 - ;task_limit = 55750 + ;task_limit = 18750 ;texture_limit = 55750 ;asset_limit = 27500 + ;state_limit = 37000 [Chat] ; Controls whether the chat module is enabled. Default is true. -- cgit v1.1 From 06990b074c17c2201ed379bf1ae4c7191ab3187f Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 14 Oct 2009 16:48:27 -0700 Subject: Allow the LLUDP server to run in either synchronous or asynchronous mode with a config setting, defaulting to synchronous mode --- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 7 +++++- .../Region/ClientStack/LindenUDP/OpenSimUDPBase.cs | 28 ++++++++++++++++++---- bin/OpenSim.ini.example | 8 ++++++- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 890f701..545a0bc 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -113,6 +113,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// is passed up to the operating system and used in the system networking /// stack. Use zero to leave this value as the default private int m_recvBufferSize; + /// Flag to process packets asynchronously or synchronously + private bool m_asyncPacketHandling; /// The measured resolution of Environment.TickCount public float TickCountResolution { get { return m_tickCountResolution; } } @@ -143,6 +145,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP IConfig config = configSource.Configs["ClientStack.LindenUDP"]; if (config != null) { + m_asyncPacketHandling = config.GetBoolean("async_packet_handling", false); m_recvBufferSize = config.GetInt("client_socket_rcvbuf_size", 0); sceneThrottleBps = config.GetInt("scene_throttle_max_bps", 0); } @@ -156,7 +159,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_scene == null) throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); - base.Start(m_recvBufferSize); + m_log.Info("[LLUDPSERVER]: Starting the LLUDP server in " + (m_asyncPacketHandling ? "asynchronous" : "synchronous") + " mode"); + + base.Start(m_recvBufferSize, m_asyncPacketHandling); // Start the incoming packet processing thread Thread incomingThread = new Thread(IncomingPacketHandler); diff --git a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs index 44a6ed6..d16837d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs @@ -62,6 +62,9 @@ namespace OpenMetaverse /// UDP socket, used in either client or server mode private Socket m_udpSocket; + /// Flag to process packets asynchronously or synchronously + private bool m_asyncPacketHandling; + /// The all important shutdown flag private volatile bool m_shutdownFlag = true; @@ -73,7 +76,6 @@ namespace OpenMetaverse /// /// Local IP address to bind the server to /// Port to listening for incoming UDP packets on - /// public OpenSimUDPBase(IPAddress bindAddress, int port) { m_localBindAddress = bindAddress; @@ -87,13 +89,19 @@ namespace OpenMetaverse /// the UDP socket. This value is passed up to the operating system /// and used in the system networking stack. Use zero to leave this /// value as the default + /// Set this to true to start + /// receiving more packets while current packet handler callbacks are + /// still running. Setting this to false will complete each packet + /// callback before the next packet is processed /// This method will attempt to set the SIO_UDP_CONNRESET flag /// on the socket to get newer versions of Windows to behave in a sane /// manner (not throwing an exception when the remote side resets the /// connection). This call is ignored on Mono where the flag is not /// necessary - public void Start(int recvBufferSize) + public void Start(int recvBufferSize, bool asyncPacketHandling) { + m_asyncPacketHandling = asyncPacketHandling; + if (m_shutdownFlag) { const int SIO_UDP_CONNRESET = -1744830452; @@ -209,8 +217,10 @@ namespace OpenMetaverse // to AsyncBeginReceive if (!m_shutdownFlag) { - // start another receive - this keeps the server going! - AsyncBeginReceive(); + // Asynchronous mode will start another receive before the + // callback for this packet is even fired. Very parallel :-) + if (m_asyncPacketHandling) + AsyncBeginReceive(); // get the buffer that was created in AsyncBeginReceive // this is the received data @@ -230,7 +240,15 @@ namespace OpenMetaverse } catch (SocketException) { } catch (ObjectDisposedException) { } - //finally { wrappedBuffer.Dispose(); } + finally + { + //wrappedBuffer.Dispose(); + + // Synchronous mode waits until the packet callback completes + // before starting the receive to fetch another packet + if (!m_asyncPacketHandling) + AsyncBeginReceive(); + } } } diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 983a7e5..002745d 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -341,7 +341,13 @@ [ClientStack.LindenUDP] - ; the client socket receive buffer size determines how many + ; Set this to true to process incoming packets asynchronously. Networking is + ; already separated from packet handling with a queue, so this will only + ; affect whether networking internals such as packet decoding and + ; acknowledgement accounting are done synchronously or asynchronously + async_packet_handling = false + + ; The client socket receive buffer size determines how many ; incoming requests we can process; the default on .NET is 8192 ; which is about 2 4k-sized UDP datagrams. On mono this is ; whatever the underlying operating system has as default; for -- cgit v1.1