From e7c877407f2a72a9519eb53debca5aeef20cded9 Mon Sep 17 00:00:00 2001
From: John Hurliman
Date: Tue, 6 Oct 2009 02:38:00 -0700
Subject: * Continued work on the new LLUDP implementation. Appears to be
functioning, although not everything is reimplemented yet * Replaced logic in
ThreadTracker with a call to System.Diagnostics that does the same thing *
Added Util.StringToBytes256() and Util.StringToBytes1024() to clamp output at
byte[256] and byte[1024], respectively * Fixed formatting for a
MySQLAssetData error logging line
---
OpenSim/Data/MySQL/MySQLAssetData.cs | 6 +-
OpenSim/Framework/Parallel.cs | 207 +++++
OpenSim/Framework/ThreadTracker.cs | 127 +--
OpenSim/Framework/Util.cs | 36 +
.../Region/ClientStack/LindenUDP/IncomingPacket.cs | 42 +
.../LindenUDP/IncomingPacketHistoryCollection.cs | 73 ++
OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs | 33 +-
OpenSim/Region/ClientStack/LindenUDP/KillPacket.cs | 71 --
.../Region/ClientStack/LindenUDP/LLClientView.cs | 284 +++----
.../ClientStack/LindenUDP/LLPacketHandler.cs | 875 ---------------------
.../Region/ClientStack/LindenUDP/LLPacketQueue.cs | 742 -----------------
.../Region/ClientStack/LindenUDP/LLPacketServer.cs | 206 -----
OpenSim/Region/ClientStack/LindenUDP/LLQueItem.cs | 49 --
.../Region/ClientStack/LindenUDP/LLUDPClient.cs | 370 +++++++++
.../ClientStack/LindenUDP/LLUDPClientCollection.cs | 185 +++++
.../Region/ClientStack/LindenUDP/LLUDPServer.cs | 138 +++-
OpenSim/Region/ClientStack/LindenUDP/LLUtil.cs | 56 --
.../Region/ClientStack/LindenUDP/OutgoingPacket.cs | 56 ++
.../Region/ClientStack/LindenUDP/ThrottleRates.cs | 76 ++
.../LindenUDP/UnackedPacketCollection.cs | 114 +++
20 files changed, 1391 insertions(+), 2355 deletions(-)
create mode 100644 OpenSim/Framework/Parallel.cs
create mode 100644 OpenSim/Region/ClientStack/LindenUDP/IncomingPacket.cs
create mode 100644 OpenSim/Region/ClientStack/LindenUDP/IncomingPacketHistoryCollection.cs
delete mode 100644 OpenSim/Region/ClientStack/LindenUDP/KillPacket.cs
delete mode 100644 OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
delete mode 100644 OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
delete mode 100644 OpenSim/Region/ClientStack/LindenUDP/LLPacketServer.cs
delete mode 100644 OpenSim/Region/ClientStack/LindenUDP/LLQueItem.cs
create mode 100644 OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
create mode 100644 OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs
delete mode 100644 OpenSim/Region/ClientStack/LindenUDP/LLUtil.cs
create mode 100644 OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs
create mode 100644 OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs
create mode 100644 OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
(limited to 'OpenSim')
diff --git a/OpenSim/Data/MySQL/MySQLAssetData.cs b/OpenSim/Data/MySQL/MySQLAssetData.cs
index 8f97440..259e186 100644
--- a/OpenSim/Data/MySQL/MySQLAssetData.cs
+++ b/OpenSim/Data/MySQL/MySQLAssetData.cs
@@ -239,10 +239,8 @@ namespace OpenSim.Data.MySQL
}
catch (Exception e)
{
- m_log.ErrorFormat(
- "[ASSETS DB]: " +
- "MySql failure creating asset {0} with name {1}" + Environment.NewLine + e.ToString()
- + Environment.NewLine + "Attempting reconnection", asset.FullID, asset.Name);
+ m_log.ErrorFormat("[ASSET DB]: MySQL failure creating asset {0} with name \"{1}\". Attempting reconnect. Error: {2}",
+ asset.FullID, asset.Name, e.Message);
_dbConnection.Reconnect();
}
}
diff --git a/OpenSim/Framework/Parallel.cs b/OpenSim/Framework/Parallel.cs
new file mode 100644
index 0000000..74537ba
--- /dev/null
+++ b/OpenSim/Framework/Parallel.cs
@@ -0,0 +1,207 @@
+/*
+ * 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.Threading;
+
+namespace OpenSim.Framework
+{
+ ///
+ /// Provides helper methods for parallelizing loops
+ ///
+ public static class Parallel
+ {
+ private static readonly int processorCount = System.Environment.ProcessorCount;
+
+ ///
+ /// Executes a for loop in which iterations may run in parallel
+ ///
+ /// The loop will be started at this index
+ /// The loop will be terminated before this index is reached
+ /// Method body to run for each iteration of the loop
+ public static void For(int fromInclusive, int toExclusive, Action body)
+ {
+ For(processorCount, fromInclusive, toExclusive, body);
+ }
+
+ ///
+ /// Executes a for loop in which iterations may run in parallel
+ ///
+ /// The number of concurrent execution threads to run
+ /// The loop will be started at this index
+ /// The loop will be terminated before this index is reached
+ /// Method body to run for each iteration of the loop
+ public static void For(int threadCount, int fromInclusive, int toExclusive, Action body)
+ {
+ int counter = threadCount;
+ AutoResetEvent threadFinishEvent = new AutoResetEvent(false);
+ Exception exception = null;
+
+ --fromInclusive;
+
+ for (int i = 0; i < threadCount; i++)
+ {
+ ThreadPool.QueueUserWorkItem(
+ delegate(object o)
+ {
+ int threadIndex = (int)o;
+
+ while (exception == null)
+ {
+ int currentIndex = Interlocked.Increment(ref fromInclusive);
+
+ if (currentIndex >= toExclusive)
+ break;
+
+ try { body(currentIndex); }
+ catch (Exception ex) { exception = ex; break; }
+ }
+
+ if (Interlocked.Decrement(ref counter) == 0)
+ threadFinishEvent.Set();
+ }, i
+ );
+ }
+
+ threadFinishEvent.WaitOne();
+
+ if (exception != null)
+ throw new Exception(exception.Message, exception);
+ }
+
+ ///
+ /// Executes a foreach loop in which iterations may run in parallel
+ ///
+ /// Object type that the collection wraps
+ /// An enumerable collection to iterate over
+ /// Method body to run for each object in the collection
+ public static void ForEach(IEnumerable enumerable, Action body)
+ {
+ ForEach(processorCount, enumerable, body);
+ }
+
+ ///
+ /// Executes a foreach loop in which iterations may run in parallel
+ ///
+ /// Object type that the collection wraps
+ /// The number of concurrent execution threads to run
+ /// An enumerable collection to iterate over
+ /// Method body to run for each object in the collection
+ public static void ForEach(int threadCount, IEnumerable enumerable, Action body)
+ {
+ int counter = threadCount;
+ AutoResetEvent threadFinishEvent = new AutoResetEvent(false);
+ IEnumerator enumerator = enumerable.GetEnumerator();
+ Exception exception = null;
+
+ for (int i = 0; i < threadCount; i++)
+ {
+ ThreadPool.QueueUserWorkItem(
+ delegate(object o)
+ {
+ int threadIndex = (int)o;
+
+ while (exception == null)
+ {
+ T entry;
+
+ lock (enumerator)
+ {
+ if (!enumerator.MoveNext())
+ break;
+ entry = (T)enumerator.Current; // Explicit typecast for Mono's sake
+ }
+
+ try { body(entry); }
+ catch (Exception ex) { exception = ex; break; }
+ }
+
+ if (Interlocked.Decrement(ref counter) == 0)
+ threadFinishEvent.Set();
+ }, i
+ );
+ }
+
+ threadFinishEvent.WaitOne();
+
+ if (exception != null)
+ throw new Exception(exception.Message, exception);
+ }
+
+ ///
+ /// Executes a series of tasks in parallel
+ ///
+ /// A series of method bodies to execute
+ public static void Invoke(params Action[] actions)
+ {
+ Invoke(processorCount, actions);
+ }
+
+ ///
+ /// Executes a series of tasks in parallel
+ ///
+ /// The number of concurrent execution threads to run
+ /// A series of method bodies to execute
+ public static void Invoke(int threadCount, params Action[] actions)
+ {
+ int counter = threadCount;
+ AutoResetEvent threadFinishEvent = new AutoResetEvent(false);
+ int index = -1;
+ Exception exception = null;
+
+ for (int i = 0; i < threadCount; i++)
+ {
+ ThreadPool.QueueUserWorkItem(
+ delegate(object o)
+ {
+ int threadIndex = (int)o;
+
+ while (exception == null)
+ {
+ int currentIndex = Interlocked.Increment(ref index);
+
+ if (currentIndex >= actions.Length)
+ break;
+
+ try { actions[currentIndex](); }
+ catch (Exception ex) { exception = ex; break; }
+ }
+
+ if (Interlocked.Decrement(ref counter) == 0)
+ threadFinishEvent.Set();
+ }, i
+ );
+ }
+
+ threadFinishEvent.WaitOne();
+
+ if (exception != null)
+ throw new Exception(exception.Message, exception);
+ }
+ }
+}
diff --git a/OpenSim/Framework/ThreadTracker.cs b/OpenSim/Framework/ThreadTracker.cs
index d3a239d..b68d9b3 100644
--- a/OpenSim/Framework/ThreadTracker.cs
+++ b/OpenSim/Framework/ThreadTracker.cs
@@ -26,138 +26,21 @@
*/
using System;
-using System.Collections;
using System.Collections.Generic;
using System.Reflection;
-using System.Threading;
+using System.Diagnostics;
using log4net;
namespace OpenSim.Framework
{
public static class ThreadTracker
{
- private static readonly ILog m_log
- = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
- private static readonly long ThreadTimeout = 30 * 10000000;
- public static List m_Threads;
- public static Thread ThreadTrackerThread;
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
- static ThreadTracker()
+ public static ProcessThreadCollection GetThreads()
{
-#if DEBUG
- m_Threads = new List();
- ThreadTrackerThread = new Thread(ThreadTrackerThreadLoop);
- ThreadTrackerThread.Name = "ThreadTrackerThread";
- ThreadTrackerThread.IsBackground = true;
- ThreadTrackerThread.Priority = ThreadPriority.BelowNormal;
- ThreadTrackerThread.Start();
- Add(ThreadTrackerThread);
-#endif
+ Process thisProc = Process.GetCurrentProcess();
+ return thisProc.Threads;
}
-
- private static void ThreadTrackerThreadLoop()
- {
- try
- {
- while (true)
- {
- Thread.Sleep(5000);
- CleanUp();
- }
- }
- catch (Exception e)
- {
- m_log.ErrorFormat(
- "[THREAD TRACKER]: Thread tracker cleanup thread terminating with exception. Please report this error. Exception is {0}",
- e);
- }
- }
-
- public static void Add(Thread thread)
- {
-#if DEBUG
- if (thread != null)
- {
- lock (m_Threads)
- {
- ThreadTrackerItem tti = new ThreadTrackerItem();
- tti.Thread = thread;
- tti.LastSeenActive = DateTime.Now.Ticks;
- m_Threads.Add(tti);
- }
- }
-#endif
- }
-
- public static void Remove(Thread thread)
- {
-#if DEBUG
- lock (m_Threads)
- {
- foreach (ThreadTrackerItem tti in new ArrayList(m_Threads))
- {
- if (tti.Thread == thread)
- m_Threads.Remove(tti);
- }
- }
-#endif
- }
-
- public static void CleanUp()
- {
- lock (m_Threads)
- {
- foreach (ThreadTrackerItem tti in new ArrayList(m_Threads))
- {
- try
- {
-
-
- if (tti.Thread.IsAlive)
- {
- // Its active
- tti.LastSeenActive = DateTime.Now.Ticks;
- }
- else
- {
- // Its not active -- if its expired then remove it
- if (tti.LastSeenActive + ThreadTimeout < DateTime.Now.Ticks)
- m_Threads.Remove(tti);
- }
- }
- catch (NullReferenceException)
- {
- m_Threads.Remove(tti);
- }
- }
- }
- }
-
- public static List GetThreads()
- {
- if (m_Threads == null)
- return null;
-
- List threads = new List();
- lock (m_Threads)
- {
- foreach (ThreadTrackerItem tti in new ArrayList(m_Threads))
- {
- threads.Add(tti.Thread);
- }
- }
- return threads;
- }
-
- #region Nested type: ThreadTrackerItem
-
- public class ThreadTrackerItem
- {
- public long LastSeenActive;
- public Thread Thread;
- }
-
- #endregion
}
}
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs
index 0851d26..189fa38 100644
--- a/OpenSim/Framework/Util.cs
+++ b/OpenSim/Framework/Util.cs
@@ -1231,6 +1231,42 @@ namespace OpenSim.Framework
return (ipaddr1 != null) ? "http://" + ipaddr1.ToString() + ":" + port1 : uri;
}
+ public static byte[] StringToBytes256(string str)
+ {
+ if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; }
+ if (str.Length > 254) str = str.Remove(254);
+ if (!str.EndsWith("\0")) { str += "\0"; }
+
+ // Because this is UTF-8 encoding and not ASCII, it's possible we
+ // might have gotten an oversized array even after the string trim
+ byte[] data = UTF8.GetBytes(str);
+ if (data.Length > 256)
+ {
+ Array.Resize(ref data, 256);
+ data[255] = 0;
+ }
+
+ return data;
+ }
+
+ public static byte[] StringToBytes1024(string str)
+ {
+ if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; }
+ if (str.Length > 1023) str = str.Remove(1023);
+ if (!str.EndsWith("\0")) { str += "\0"; }
+
+ // Because this is UTF-8 encoding and not ASCII, it's possible we
+ // might have gotten an oversized array even after the string trim
+ byte[] data = UTF8.GetBytes(str);
+ if (data.Length > 1024)
+ {
+ Array.Resize(ref data, 1024);
+ data[1023] = 0;
+ }
+
+ return data;
+ }
+
#region FireAndForget Threading Pattern
public static void FireAndForget(System.Threading.WaitCallback callback)
diff --git a/OpenSim/Region/ClientStack/LindenUDP/IncomingPacket.cs b/OpenSim/Region/ClientStack/LindenUDP/IncomingPacket.cs
new file mode 100644
index 0000000..dc0d62a
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/IncomingPacket.cs
@@ -0,0 +1,42 @@
+/*
+ * 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 OpenSim.Framework;
+using OpenMetaverse;
+using OpenMetaverse.Packets;
+
+namespace OpenSim.Region.ClientStack.LindenUDP
+{
+ public struct IncomingPacket
+ {
+ /// Client this packet came from
+ public LLUDPClient Client;
+ /// Packet data that has been received
+ public Packet Packet;
+ }
+}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/IncomingPacketHistoryCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/IncomingPacketHistoryCollection.cs
new file mode 100644
index 0000000..1f73a1d
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/IncomingPacketHistoryCollection.cs
@@ -0,0 +1,73 @@
+/*
+ * 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;
+
+namespace OpenSim.Region.ClientStack.LindenUDP
+{
+ ///
+ /// A circular buffer and hashset for tracking incoming packet sequence
+ /// numbers
+ ///
+ public sealed class IncomingPacketHistoryCollection
+ {
+ private readonly uint[] m_items;
+ private HashSet m_hashSet;
+ private int m_first;
+ private int m_next;
+ private int m_capacity;
+
+ public IncomingPacketHistoryCollection(int capacity)
+ {
+ this.m_capacity = capacity;
+ m_items = new uint[capacity];
+ m_hashSet = new HashSet();
+ }
+
+ public bool TryEnqueue(uint ack)
+ {
+ lock (m_hashSet)
+ {
+ if (m_hashSet.Add(ack))
+ {
+ m_items[m_next] = ack;
+ m_next = (m_next + 1) % m_capacity;
+ if (m_next == m_first)
+ {
+ m_hashSet.Remove(m_items[m_first]);
+ m_first = (m_first + 1) % m_capacity;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
index 000f455..2f1face 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
@@ -76,27 +76,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
if (m_currentPacket <= m_stopPacket)
{
- bool SendMore = true;
+ int count = 0;
+ bool sendMore = true;
+
if (!m_sentInfo || (m_currentPacket == 0))
{
- if (SendFirstPacket(client))
- {
- SendMore = false;
- }
+ sendMore = !SendFirstPacket(client);
+
m_sentInfo = true;
- m_currentPacket++;
+ ++m_currentPacket;
+ ++count;
}
if (m_currentPacket < 2)
{
m_currentPacket = 2;
}
-
- int count = 0;
- while (SendMore && count < maxpack && m_currentPacket <= m_stopPacket)
+
+ while (sendMore && count < maxpack && m_currentPacket <= m_stopPacket)
{
- count++;
- SendMore = SendPacket(client);
- m_currentPacket++;
+ sendMore = SendPacket(client);
+ ++m_currentPacket;
+ ++count;
}
if (m_currentPacket > m_stopPacket)
@@ -196,15 +196,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_currentPacket = StartPacket;
}
-
- if (m_imageManager != null && m_imageManager.Client != null)
- {
- if (m_imageManager.Client.IsThrottleEmpty(ThrottleOutPacketType.Texture))
- {
- //m_log.Debug("No textures queued, sending one packet to kickstart it");
- SendPacket(m_imageManager.Client);
- }
- }
}
}
}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/KillPacket.cs b/OpenSim/Region/ClientStack/LindenUDP/KillPacket.cs
deleted file mode 100644
index a80c1f0..0000000
--- a/OpenSim/Region/ClientStack/LindenUDP/KillPacket.cs
+++ /dev/null
@@ -1,71 +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 OpenMetaverse.Packets;
-
-namespace OpenSim.Region.ClientStack.LindenUDP
-{
- ///
- /// When packetqueue dequeues this packet in the outgoing stream, it thread aborts
- /// Ensures that the thread abort happens from within the client thread
- /// regardless of where the close method is called
- ///
- class KillPacket : Packet
- {
- public override int Length
- {
- get { return 0; }
- }
-
- public override void FromBytes(Header header, byte[] bytes, ref int i, ref int packetEnd)
- {
- }
-
- public override void FromBytes(byte[] bytes, ref int i, ref int packetEnd, byte[] zeroBuffer)
- {
- }
-
- public override byte[] ToBytes()
- {
- return new byte[0];
- }
-
- public override byte[][] ToBytesMultiple()
- {
- return new byte[][] { new byte[0] };
- }
-
- public KillPacket()
- {
- Type = PacketType.UseCircuitCode;
- Header = new Header();
- Header.Frequency = OpenMetaverse.PacketFrequency.Low;
- Header.ID = 65531;
- Header.Reliable = true;
- }
- }
-}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 31cd53f..767020f 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -70,28 +70,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private readonly LLUDPServer m_udpServer;
private readonly LLUDPClient m_udpClient;
private readonly UUID m_sessionId;
- private readonly UUID m_secureSessionId = UUID.Zero;
+ private readonly UUID m_secureSessionId;
private readonly UUID m_agentId;
private readonly uint m_circuitCode;
- private readonly byte[] m_channelVersion = Utils.StringToBytes("OpenSimulator Server"); // Dummy value needed by libSL
+ private readonly byte[] m_channelVersion = Utils.EmptyBytes;
private readonly Dictionary m_defaultAnimations = new Dictionary();
private readonly IGroupsModule m_GroupsModule;
- private int m_debugPacketLevel;
private int m_cachedTextureSerial;
- private Timer m_clientPingTimer;
private Timer m_avatarTerseUpdateTimer;
private List m_avatarTerseUpdates = new List();
private Timer m_primTerseUpdateTimer;
private List m_primTerseUpdates = new List();
private Timer m_primFullUpdateTimer;
private List m_primFullUpdates = new List();
- private bool m_clientBlocked;
- private int m_probesWithNoIngressPackets;
private int m_moneyBalance;
private int m_animationSequenceNumber = 1;
private bool m_SendLogoutPacketWhenClosing = true;
- private int m_inPacketsChecked;
private AgentUpdateArgs lastarg;
private bool m_IsActive = true;
@@ -170,58 +165,72 @@ namespace OpenSim.Region.ClientStack.LindenUDP
RegisterInterface(this);
RegisterInterface(this);
RegisterInterface(this);
- m_GroupsModule = scene.RequestModuleInterface();
-
- m_moneyBalance = 1000;
-
- m_channelVersion = Utils.StringToBytes(scene.GetSimulatorVersion());
-
+
InitDefaultAnimations();
m_scene = scene;
- //m_assetCache = assetCache;
-
m_assetService = m_scene.RequestModuleInterface();
-
- m_udpServer = udpServer;
- m_udpClient = udpClient;
-
+ m_GroupsModule = scene.RequestModuleInterface();
+ m_imageManager = new LLImageManager(this, m_assetService, Scene.RequestModuleInterface());
+ m_channelVersion = Utils.StringToBytes(scene.GetSimulatorVersion());
m_agentId = agentId;
m_sessionId = sessionId;
+ m_secureSessionId = sessionInfo.LoginInfo.SecureSession;
m_circuitCode = circuitCode;
-
m_userEndPoint = remoteEP;
-
m_firstName = sessionInfo.LoginInfo.First;
m_lastName = sessionInfo.LoginInfo.Last;
m_startpos = sessionInfo.LoginInfo.StartPos;
+ m_moneyBalance = 1000;
- if (sessionInfo.LoginInfo.SecureSession != UUID.Zero)
- {
- m_secureSessionId = sessionInfo.LoginInfo.SecureSession;
- }
-
- // While working on this, the BlockingQueue had me fooled for a bit.
- // The Blocking queue causes the thread to stop until there's something
- // in it to process. It's an on-purpose threadlock though because
- // without it, the clientloop will suck up all sim resources.
-
- //m_PacketHandler = new LLPacketHandler(this, m_networkServer, userSettings);
- //m_PacketHandler.SynchronizeClient = SynchronizeClient;
- //m_PacketHandler.OnPacketStats += PopulateStats;
- //m_PacketHandler.OnQueueEmpty += HandleQueueEmpty;
+ m_udpServer = udpServer;
+ m_udpClient = udpClient;
+ m_udpClient.OnQueueEmpty += HandleQueueEmpty;
+ // FIXME: Implement this
+ //m_udpClient.OnPacketStats += PopulateStats;
RegisterLocalPacketHandlers();
- m_imageManager = new LLImageManager(this, m_assetService, Scene.RequestModuleInterface());
}
- public void SetDebugPacketLevel(int newDebugPacketLevel)
+ public void SetDebugPacketLevel(int newDebug)
{
- m_debugPacketLevel = newDebugPacketLevel;
}
#region Client Methods
+ ///
+ /// Close down the client view. This *must* be the last method called, since the last #
+ /// statement of CloseCleanup() aborts the thread.
+ ///
+ ///
+ public void Close(bool shutdownCircuit)
+ {
+ m_log.DebugFormat(
+ "[CLIENT]: Close has been called with shutdownCircuit = {0} for {1} attached to scene {2}",
+ shutdownCircuit, Name, m_scene.RegionInfo.RegionName);
+
+ if (m_imageManager != null)
+ m_imageManager.Close();
+
+ 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);
+ }
+ }
+
private void CloseCleanup(bool shutdownCircuit)
{
m_scene.RemoveClient(AgentId);
@@ -236,9 +245,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
Thread.Sleep(2000);
// Shut down timers. Thread Context of this method is murky. Lock all timers
- if (m_clientPingTimer.Enabled)
- lock (m_clientPingTimer)
- m_clientPingTimer.Stop();
if (m_avatarTerseUpdateTimer.Enabled)
lock (m_avatarTerseUpdateTimer)
m_avatarTerseUpdateTimer.Stop();
@@ -271,43 +277,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// of the client thread regardless of where Close() is called.
KillEndDone();
}
-
- Terminate();
- }
- ///
- /// Close down the client view. This *must* be the last method called, since the last #
- /// statement of CloseCleanup() aborts the thread.
- ///
- ///
- public void Close(bool shutdownCircuit)
- {
- m_clientPingTimer.Enabled = false;
+ IsActive = false;
- m_log.DebugFormat(
- "[CLIENT]: Close has been called with shutdownCircuit = {0} for {1} attached to scene {2}",
- shutdownCircuit, Name, m_scene.RegionInfo.RegionName);
+ m_avatarTerseUpdateTimer.Close();
+ m_primTerseUpdateTimer.Close();
+ m_primFullUpdateTimer.Close();
- if (m_imageManager != null)
- m_imageManager.Close();
+ //m_udpServer.OnPacketStats -= PopulateStats;
+ m_udpClient.Shutdown();
- if (m_udpServer != null)
- m_udpServer.Flush();
+ // wait for thread stoped
+ // m_clientThread.Join();
- // 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);
- }
+ // delete circuit code
+ //m_networkServer.CloseClient(this);
}
public void Kick(string message)
@@ -329,10 +313,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public void Stop()
{
// Shut down timers. Thread Context is Murky, lock all timers!
- if (m_clientPingTimer.Enabled)
- lock (m_clientPingTimer)
- m_clientPingTimer.Stop();
-
if (m_avatarTerseUpdateTimer.Enabled)
lock (m_avatarTerseUpdateTimer)
m_avatarTerseUpdateTimer.Stop();
@@ -346,25 +326,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_primFullUpdateTimer.Stop();
}
- private void Terminate()
- {
- IsActive = false;
-
- m_clientPingTimer.Close();
- m_avatarTerseUpdateTimer.Close();
- m_primTerseUpdateTimer.Close();
- m_primFullUpdateTimer.Close();
-
- //m_udpServer.OnPacketStats -= PopulateStats;
- m_udpClient.Shutdown();
-
- // wait for thread stoped
- // m_clientThread.Join();
-
- // delete circuit code
- //m_networkServer.CloseClient(this);
- }
-
#endregion Client Methods
#region Packet Handling
@@ -452,7 +413,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return result;
}
- protected void DebugPacket(string direction, Packet packet)
+ /*protected void DebugPacket(string direction, Packet packet)
{
string info;
@@ -478,7 +439,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
Console.WriteLine(m_circuitCode + ":" + direction + ": " + info);
- }
+ }*/
#endregion Packet Handling
@@ -490,8 +451,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
///
protected virtual void InitNewClient()
{
- //this.UploadAssets = new AgentAssetUpload(this, m_assetCache, m_inventoryCache);
-
m_avatarTerseUpdateTimer = new Timer(m_avatarTerseUpdateRate);
m_avatarTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates);
m_avatarTerseUpdateTimer.AutoReset = false;
@@ -511,11 +470,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public virtual void Start()
{
- m_clientThread = new Thread(RunUserSession);
- m_clientThread.Name = "ClientThread";
- m_clientThread.IsBackground = true;
- m_clientThread.Start();
- ThreadTracker.Add(m_clientThread);
+ // This sets up all the timers
+ InitNewClient();
}
///
@@ -523,14 +479,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
///
protected void RunUserSession()
{
- //tell this thread we are using the culture set up for the sim (currently hardcoded to en_US)
- //otherwise it will override this and use the system default
- Culture.SetCurrentCulture();
-
try
{
- // This sets up all the timers
- InitNewClient();
+
}
catch (Exception e)
{
@@ -1373,8 +1324,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public void SendStartPingCheck(byte seq)
{
StartPingCheckPacket pc = (StartPingCheckPacket)PacketPool.Instance.GetPacket(PacketType.StartPingCheck);
- pc.PingID.PingID = seq;
pc.Header.Reliable = false;
+
+ OutgoingPacket oldestPacket = m_udpClient.NeedAcks.GetOldest();
+
+ pc.PingID.PingID = seq;
+ pc.PingID.OldestUnacked = (oldestPacket != null) ? oldestPacket.SequenceNumber : 0;
+
OutPacket(pc, ThrottleOutPacketType.Unknown);
}
@@ -1450,12 +1406,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
descend.ItemData[i].AssetID = item.AssetID;
descend.ItemData[i].CreatorID = item.CreatorIdAsUuid;
descend.ItemData[i].BaseMask = item.BasePermissions;
- descend.ItemData[i].Description = LLUtil.StringToPacketBytes(item.Description);
+ descend.ItemData[i].Description = Util.StringToBytes256(item.Description);
descend.ItemData[i].EveryoneMask = item.EveryOnePermissions;
descend.ItemData[i].OwnerMask = item.CurrentPermissions;
descend.ItemData[i].FolderID = item.Folder;
descend.ItemData[i].InvType = (sbyte)item.InvType;
- descend.ItemData[i].Name = LLUtil.StringToPacketBytes(item.Name);
+ descend.ItemData[i].Name = Util.StringToBytes256(item.Name);
descend.ItemData[i].NextOwnerMask = item.NextPermissions;
descend.ItemData[i].OwnerID = item.Owner;
descend.ItemData[i].Type = (sbyte)item.AssetType;
@@ -1536,7 +1492,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
descend.FolderData[i] = new InventoryDescendentsPacket.FolderDataBlock();
descend.FolderData[i].FolderID = folder.ID;
- descend.FolderData[i].Name = LLUtil.StringToPacketBytes(folder.Name);
+ descend.FolderData[i].Name = Util.StringToBytes256(folder.Name);
descend.FolderData[i].ParentID = folder.ParentID;
descend.FolderData[i].Type = (sbyte)folder.Type;
@@ -1651,11 +1607,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
inventoryReply.InventoryData[0].BaseMask = item.BasePermissions;
inventoryReply.InventoryData[0].CreationDate = item.CreationDate;
- inventoryReply.InventoryData[0].Description = LLUtil.StringToPacketBytes(item.Description);
+ inventoryReply.InventoryData[0].Description = Util.StringToBytes256(item.Description);
inventoryReply.InventoryData[0].EveryoneMask = item.EveryOnePermissions;
inventoryReply.InventoryData[0].FolderID = item.Folder;
inventoryReply.InventoryData[0].InvType = (sbyte)item.InvType;
- inventoryReply.InventoryData[0].Name = LLUtil.StringToPacketBytes(item.Name);
+ inventoryReply.InventoryData[0].Name = Util.StringToBytes256(item.Name);
inventoryReply.InventoryData[0].NextOwnerMask = item.NextPermissions;
inventoryReply.InventoryData[0].OwnerID = item.Owner;
inventoryReply.InventoryData[0].OwnerMask = item.CurrentPermissions;
@@ -1780,7 +1736,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
folderBlock.FolderID = folder.ID;
folderBlock.ParentID = folder.ParentID;
folderBlock.Type = -1;
- folderBlock.Name = LLUtil.StringToPacketBytes(folder.Name);
+ folderBlock.Name = Util.StringToBytes256(folder.Name);
return folderBlock;
}
@@ -1798,11 +1754,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
itemBlock.AssetID = item.AssetID;
itemBlock.CreatorID = item.CreatorIdAsUuid;
itemBlock.BaseMask = item.BasePermissions;
- itemBlock.Description = LLUtil.StringToPacketBytes(item.Description);
+ itemBlock.Description = Util.StringToBytes256(item.Description);
itemBlock.EveryoneMask = item.EveryOnePermissions;
itemBlock.FolderID = item.Folder;
itemBlock.InvType = (sbyte)item.InvType;
- itemBlock.Name = LLUtil.StringToPacketBytes(item.Name);
+ itemBlock.Name = Util.StringToBytes256(item.Name);
itemBlock.NextOwnerMask = item.NextPermissions;
itemBlock.OwnerID = item.Owner;
itemBlock.OwnerMask = item.CurrentPermissions;
@@ -1862,11 +1818,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
bulkUpdate.ItemData[0].CreatorID = item.CreatorIdAsUuid;
bulkUpdate.ItemData[0].BaseMask = item.BasePermissions;
bulkUpdate.ItemData[0].CreationDate = item.CreationDate;
- bulkUpdate.ItemData[0].Description = LLUtil.StringToPacketBytes(item.Description);
+ bulkUpdate.ItemData[0].Description = Util.StringToBytes256(item.Description);
bulkUpdate.ItemData[0].EveryoneMask = item.EveryOnePermissions;
bulkUpdate.ItemData[0].FolderID = item.Folder;
bulkUpdate.ItemData[0].InvType = (sbyte)item.InvType;
- bulkUpdate.ItemData[0].Name = LLUtil.StringToPacketBytes(item.Name);
+ bulkUpdate.ItemData[0].Name = Util.StringToBytes256(item.Name);
bulkUpdate.ItemData[0].NextOwnerMask = item.NextPermissions;
bulkUpdate.ItemData[0].OwnerID = item.Owner;
bulkUpdate.ItemData[0].OwnerMask = item.CurrentPermissions;
@@ -1909,11 +1865,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
InventoryReply.InventoryData[0].AssetID = Item.AssetID;
InventoryReply.InventoryData[0].CreatorID = Item.CreatorIdAsUuid;
InventoryReply.InventoryData[0].BaseMask = Item.BasePermissions;
- InventoryReply.InventoryData[0].Description = LLUtil.StringToPacketBytes(Item.Description);
+ InventoryReply.InventoryData[0].Description = Util.StringToBytes256(Item.Description);
InventoryReply.InventoryData[0].EveryoneMask = Item.EveryOnePermissions;
InventoryReply.InventoryData[0].FolderID = Item.Folder;
InventoryReply.InventoryData[0].InvType = (sbyte)Item.InvType;
- InventoryReply.InventoryData[0].Name = LLUtil.StringToPacketBytes(Item.Name);
+ InventoryReply.InventoryData[0].Name = Util.StringToBytes256(Item.Name);
InventoryReply.InventoryData[0].NextOwnerMask = Item.NextPermissions;
InventoryReply.InventoryData[0].OwnerID = Item.Owner;
InventoryReply.InventoryData[0].OwnerMask = Item.CurrentPermissions;
@@ -2080,7 +2036,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
///
///
///
- protected AgentAlertMessagePacket BuildAgentAlertPacket(string message, bool modal)
+ public AgentAlertMessagePacket BuildAgentAlertPacket(string message, bool modal)
{
AgentAlertMessagePacket alertPack = (AgentAlertMessagePacket)PacketPool.Instance.GetPacket(PacketType.AgentAlertMessage);
alertPack.AgentData.AgentID = AgentId;
@@ -3533,7 +3489,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
objectData.FullID = objectID;
objectData.OwnerID = ownerID;
- objectData.Text = LLUtil.StringToPacketBytes(text);
+ objectData.Text = Util.StringToBytes256(text);
objectData.TextColor[0] = color[0];
objectData.TextColor[1] = color[1];
objectData.TextColor[2] = color[2];
@@ -3911,8 +3867,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
objPropDB.SalePrice = SalePrice;
objPropDB.Category = Category;
objPropDB.LastOwnerID = LastOwnerID;
- objPropDB.Name = LLUtil.StringToPacketBytes(ObjectName);
- objPropDB.Description = LLUtil.StringToPacketBytes(Description);
+ objPropDB.Name = Util.StringToBytes256(ObjectName);
+ objPropDB.Description = Util.StringToBytes256(Description);
objPropFamilyPack.ObjectData = objPropDB;
objPropFamilyPack.Header.Zerocoded = true;
OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task);
@@ -3946,11 +3902,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
proper.ObjectData[0].OwnerID = UUID.Zero;
else
proper.ObjectData[0].OwnerID = OwnerUUID;
- proper.ObjectData[0].TouchName = LLUtil.StringToPacketBytes(TouchTitle);
+ proper.ObjectData[0].TouchName = Util.StringToBytes256(TouchTitle);
proper.ObjectData[0].TextureID = TextureID;
- proper.ObjectData[0].SitName = LLUtil.StringToPacketBytes(SitTitle);
- proper.ObjectData[0].Name = LLUtil.StringToPacketBytes(ItemName);
- proper.ObjectData[0].Description = LLUtil.StringToPacketBytes(ItemDescription);
+ proper.ObjectData[0].SitName = Util.StringToBytes256(SitTitle);
+ proper.ObjectData[0].Name = Util.StringToBytes256(ItemName);
+ proper.ObjectData[0].Description = Util.StringToBytes256(ItemDescription);
proper.ObjectData[0].OwnerMask = OwnerMask;
proper.ObjectData[0].NextOwnerMask = NextOwnerMask;
proper.ObjectData[0].GroupMask = GroupMask;
@@ -4191,11 +4147,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
updatePacket.ParcelData.MediaAutoScale = landData.MediaAutoScale;
updatePacket.ParcelData.MediaID = landData.MediaID;
- updatePacket.ParcelData.MediaURL = LLUtil.StringToPacketBytes(landData.MediaURL);
- updatePacket.ParcelData.MusicURL = LLUtil.StringToPacketBytes(landData.MusicURL);
- updatePacket.ParcelData.Name = Utils.StringToBytes(landData.Name);
+ updatePacket.ParcelData.MediaURL = Util.StringToBytes256(landData.MediaURL);
+ updatePacket.ParcelData.MusicURL = Util.StringToBytes256(landData.MusicURL);
+ updatePacket.ParcelData.Name = Util.StringToBytes256(landData.Name);
updatePacket.ParcelData.OtherCleanTime = landData.OtherCleanTime;
- updatePacket.ParcelData.OtherCount = 0; //unemplemented
+ updatePacket.ParcelData.OtherCount = 0; //TODO: Unimplemented
updatePacket.ParcelData.OtherPrims = landData.OtherPrims;
updatePacket.ParcelData.OwnerID = landData.OwnerID;
updatePacket.ParcelData.OwnerPrims = landData.OwnerPrims;
@@ -4203,22 +4159,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
updatePacket.ParcelData.ParcelPrimBonus = simObjectBonusFactor;
updatePacket.ParcelData.PassHours = landData.PassHours;
updatePacket.ParcelData.PassPrice = landData.PassPrice;
- updatePacket.ParcelData.PublicCount = 0; //unemplemented
+ updatePacket.ParcelData.PublicCount = 0; //TODO: Unimplemented
- updatePacket.ParcelData.RegionDenyAnonymous = ((regionFlags & (uint)RegionFlags.DenyAnonymous) >
- 0);
- updatePacket.ParcelData.RegionDenyIdentified = ((regionFlags & (uint)RegionFlags.DenyIdentified) >
- 0);
- updatePacket.ParcelData.RegionDenyTransacted = ((regionFlags & (uint)RegionFlags.DenyTransacted) >
- 0);
- updatePacket.ParcelData.RegionPushOverride = ((regionFlags & (uint)RegionFlags.RestrictPushObject) >
- 0);
+ updatePacket.ParcelData.RegionDenyAnonymous = (regionFlags & (uint)RegionFlags.DenyAnonymous) > 0;
+ updatePacket.ParcelData.RegionDenyIdentified = (regionFlags & (uint)RegionFlags.DenyIdentified) > 0;
+ updatePacket.ParcelData.RegionDenyTransacted = (regionFlags & (uint)RegionFlags.DenyTransacted) > 0;
+ updatePacket.ParcelData.RegionPushOverride = (regionFlags & (uint)RegionFlags.RestrictPushObject) > 0;
updatePacket.ParcelData.RentPrice = 0;
updatePacket.ParcelData.RequestResult = request_result;
updatePacket.ParcelData.SalePrice = landData.SalePrice;
updatePacket.ParcelData.SelectedPrims = landData.SelectedPrims;
- updatePacket.ParcelData.SelfCount = 0; //unemplemented
+ updatePacket.ParcelData.SelfCount = 0; //TODO: Unimplemented
updatePacket.ParcelData.SequenceID = sequence_id;
if (landData.SimwideArea > 0)
{
@@ -5265,18 +5217,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_udpClient.SetThrottles(throttles);
}
+ ///
+ /// Get the current throttles for this client as a packed byte array
+ ///
+ /// Unused
+ ///
public byte[] GetThrottlesPacked(float multiplier)
{
return m_udpClient.GetThrottlesPacked();
}
- public bool IsThrottleEmpty(ThrottleOutPacketType category)
- {
- return m_udpClient.IsThrottleEmpty(category);
- }
-
///
- /// Unused
+ /// Cruft?
///
public virtual void InPacket(object NewPack)
{
@@ -6231,14 +6183,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
break;
case PacketType.AgentPause:
- m_probesWithNoIngressPackets = 0;
- m_clientBlocked = true;
+ m_udpClient.IsPaused = true;
break;
case PacketType.AgentResume:
- m_probesWithNoIngressPackets = 0;
- m_clientBlocked = false;
- SendStartPingCheck(0);
+ m_udpClient.IsPaused = false;
+ SendStartPingCheck(m_udpClient.CurrentPingSequence++);
break;
@@ -8904,14 +8854,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#region unimplemented handlers
case PacketType.StartPingCheck:
- // Send the client the ping response back
- // Pass the same PingID in the matching packet
- // Handled In the packet processing
- //m_log.Debug("[CLIENT]: possibly unhandled StartPingCheck packet");
+ StartPingCheckPacket pingStart = (StartPingCheckPacket)Pack;
+ CompletePingCheckPacket pingComplete = new CompletePingCheckPacket();
+ pingComplete.PingID.PingID = pingStart.PingID.PingID;
+ m_udpServer.SendPacket(m_udpClient, pingComplete, ThrottleOutPacketType.Unknown, false);
break;
+
case PacketType.CompletePingCheck:
- // TODO: Perhaps this should be processed on the Sim to determine whether or not to drop a dead client
- //m_log.Warn("[CLIENT]: unhandled CompletePingCheck packet");
+ // TODO: Do stats tracking or something with these?
break;
case PacketType.ViewerStats:
@@ -10209,14 +10159,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return info;
}
- public EndPoint GetClientEP()
+ public void SetClientInfo(ClientInfo info)
{
- return m_userEndPoint;
+ m_udpClient.SetClientInfo(info);
}
- public void SetClientInfo(ClientInfo info)
+ public EndPoint GetClientEP()
{
- m_udpClient.SetClientInfo(info);
+ return m_userEndPoint;
}
#region Media Parcel Members
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
deleted file mode 100644
index f30df4d..0000000
--- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
+++ /dev/null
@@ -1,875 +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.Reflection;
-using System.Collections.Generic;
-using System.Net.Sockets;
-using System.Threading;
-using System.Timers;
-using OpenMetaverse;
-using OpenMetaverse.Packets;
-using log4net;
-using OpenSim.Framework;
-using Timer=System.Timers.Timer;
-
-namespace OpenSim.Region.ClientStack.LindenUDP
-{
- public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes);
- public delegate void PacketDrop(Packet pack, Object id);
- public delegate void QueueEmpty(ThrottleOutPacketType queue);
- public delegate bool SynchronizeClientHandler(IScene scene, Packet packet, UUID agentID, ThrottleOutPacketType throttlePacketType);
-
- public class LLPacketHandler
- {
- private static readonly ILog m_log
- = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
- //private int m_resentCount;
-
- // Packet queues
- //
- LLPacketQueue m_PacketQueue;
-
- public LLPacketQueue PacketQueue
- {
- get { return m_PacketQueue; }
- }
-
- // Timer to run stats and acks on
- //
- private Timer m_AckTimer = new Timer(250);
-
- // A list of the packets we haven't acked yet
- //
- private List m_PendingAcks = new List();
- private Dictionary m_PendingAcksMap = new Dictionary();
-
- private Dictionary m_NeedAck =
- new Dictionary();
-
- ///
- /// The number of milliseconds that can pass before a packet that needs an ack is resent.
- ///
- private uint m_ResendTimeout = 4000;
-
- public uint ResendTimeout
- {
- get { return m_ResendTimeout; }
- set { m_ResendTimeout = value; }
- }
-
- private int m_MaxReliableResends = 3;
-
- public int MaxReliableResends
- {
- get { return m_MaxReliableResends; }
- set { m_MaxReliableResends = value; }
- }
-
- // Track duplicated packets. This uses a Dictionary. Both insertion
- // and lookup are common operations and need to take advantage of
- // the hashing. Expiration is less common and can be allowed the
- // time for a linear scan.
- //
- private List m_alreadySeenList = new List();
- private Dictionarym_alreadySeenTracker = new Dictionary();
- private int m_alreadySeenWindow = 30000;
- private int m_lastAlreadySeenCheck = Environment.TickCount & Int32.MaxValue;
-
- // private Dictionary m_DupeTracker =
- // new Dictionary();
- // private uint m_DupeTrackerWindow = 30;
- // private int m_DupeTrackerLastCheck = Environment.TickCount;
-
- // Values for the SimStatsReporter
- //
- private int m_PacketsReceived = 0;
- private int m_PacketsReceivedReported = 0;
- private int m_PacketsSent = 0;
- private int m_PacketsSentReported = 0;
- private int m_UnackedBytes = 0;
-
- private int m_LastResend = 0;
-
- public int PacketsReceived
- {
- get { return m_PacketsReceived; }
- }
-
- public int PacketsReceivedReported
- {
- get { return m_PacketsReceivedReported; }
- }
-
- // The client we are working for
- //
- private IClientAPI m_Client;
-
- // Some events
- //
- public event PacketStats OnPacketStats;
- public event PacketDrop OnPacketDrop;
- public event QueueEmpty OnQueueEmpty;
-
-
- //private SynchronizeClientHandler m_SynchronizeClient = null;
-
- public SynchronizeClientHandler SynchronizeClient
- {
- set { /* m_SynchronizeClient = value; */ }
- }
-
- // Packet sequencing
- //
- private uint m_Sequence = 0;
- private object m_SequenceLock = new object();
- private const int MAX_SEQUENCE = 0xFFFFFF;
-
- // Packet dropping
- //
- List m_ImportantPackets = new List();
- private bool m_ReliableIsImportant = false;
-
- public bool ReliableIsImportant
- {
- get { return m_ReliableIsImportant; }
- set { m_ReliableIsImportant = value; }
- }
-
- private int m_DropSafeTimeout;
-
- LLPacketServer m_PacketServer;
- private byte[] m_ZeroOutBuffer = new byte[4096];
-
- ////////////////////////////////////////////////////////////////////
-
- // Constructors
- //
- public LLPacketHandler(IClientAPI client, LLPacketServer server, ClientStackUserSettings userSettings)
- {
- m_Client = client;
- m_PacketServer = server;
- m_DropSafeTimeout = Environment.TickCount + 15000;
-
- m_PacketQueue = new LLPacketQueue(client.AgentId, userSettings);
-
- m_PacketQueue.OnQueueEmpty += TriggerOnQueueEmpty;
-
- m_AckTimer.Elapsed += AckTimerElapsed;
- m_AckTimer.Start();
- }
-
- public void Dispose()
- {
- m_AckTimer.Stop();
- m_AckTimer.Close();
-
- m_PacketQueue.Enqueue(null);
- m_PacketQueue.Close();
- m_Client = null;
- }
-
- // Send one packet. This actually doesn't send anything, it queues
- // it. Designed to be fire-and-forget, but there is an optional
- // notifier.
- //
- public void OutPacket(
- Packet packet, ThrottleOutPacketType throttlePacketType)
- {
- OutPacket(packet, throttlePacketType, null);
- }
-
- public void OutPacket(
- Packet packet, ThrottleOutPacketType throttlePacketType,
- Object id)
- {
- // Call the load balancer's hook. If this is not active here
- // we defer to the sim server this client is actually connected
- // to. Packet drop notifies will not be triggered in this
- // configuration!
- //
-
- packet.Header.Sequence = 0;
-
- lock (m_NeedAck)
- {
- DropResend(id);
-
- AddAcks(ref packet);
- QueuePacket(packet, throttlePacketType, id);
- }
- }
-
- private void AddAcks(ref Packet packet)
- {
- // These packet types have shown to have issues with
- // acks being appended to the payload, just don't send
- // any with them until libsl is fixed.
- //
- if (packet is ViewerEffectPacket)
- return;
- if (packet is SimStatsPacket)
- return;
-
- // Add acks to outgoing packets
- //
- if (m_PendingAcks.Count > 0)
- {
- int count = m_PendingAcks.Count;
- if (count > 10)
- count = 10;
- packet.Header.AckList = new uint[count];
- packet.Header.AppendedAcks = true;
-
- for (int i = 0; i < count; i++)
- {
- packet.Header.AckList[i] = m_PendingAcks[i];
- m_PendingAcksMap.Remove(m_PendingAcks[i]);
- }
- m_PendingAcks.RemoveRange(0, count);
- }
- }
-
- private void QueuePacket(
- Packet packet, ThrottleOutPacketType throttlePacketType,
- Object id)
- {
- LLQueItem item = new LLQueItem();
- item.Packet = packet;
- item.Incoming = false;
- item.throttleType = throttlePacketType;
- item.TickCount = Environment.TickCount;
- item.Identifier = id;
- item.Resends = 0;
- item.Length = packet.Length;
- item.Sequence = packet.Header.Sequence;
-
- m_PacketQueue.Enqueue(item);
- m_PacketsSent++;
- }
-
- private void ResendUnacked()
- {
- int now = Environment.TickCount;
-
- int intervalMs = 250;
-
- if (m_LastResend != 0)
- intervalMs = now - m_LastResend;
-
- lock (m_NeedAck)
- {
- if (m_DropSafeTimeout > now ||
- intervalMs > 500) // We were frozen!
- {
- foreach (LLQueItem data in m_NeedAck.Values)
- {
- if (m_DropSafeTimeout > now)
- {
- m_NeedAck[data.Packet.Header.Sequence].TickCount = now;
- }
- else
- {
- m_NeedAck[data.Packet.Header.Sequence].TickCount += intervalMs;
- }
- }
- }
-
- m_LastResend = now;
-
- // Unless we have received at least one ack, don't bother resending
- // anything. There may not be a client there, don't clog up the
- // pipes.
-
-
- // Nothing to do
- //
- if (m_NeedAck.Count == 0)
- return;
-
- int resent = 0;
- long dueDate = now - m_ResendTimeout;
-
- List dropped = new List();
- foreach (LLQueItem data in m_NeedAck.Values)
- {
- Packet packet = data.Packet;
-
- // Packets this old get resent
- //
- if (data.TickCount < dueDate && data.Sequence != 0 && !m_PacketQueue.Contains(data.Sequence))
- {
- if (resent < 20) // Was 20 (= Max 117kbit/sec resends)
- {
- m_NeedAck[packet.Header.Sequence].Resends++;
-
- // The client needs to be told that a packet is being resent, otherwise it appears to believe
- // that it should reset its sequence to that packet number.
- packet.Header.Resent = true;
-
- if ((m_NeedAck[packet.Header.Sequence].Resends >= m_MaxReliableResends) &&
- (!m_ReliableIsImportant))
- {
- dropped.Add(data);
- continue;
- }
-
- m_NeedAck[packet.Header.Sequence].TickCount = Environment.TickCount;
- QueuePacket(packet, ThrottleOutPacketType.Resend, data.Identifier);
- resent++;
- }
- else
- {
- m_NeedAck[packet.Header.Sequence].TickCount += intervalMs;
- }
- }
- }
-
- foreach (LLQueItem data in dropped)
- {
- m_NeedAck.Remove(data.Packet.Header.Sequence);
- TriggerOnPacketDrop(data.Packet, data.Identifier);
- m_PacketQueue.Cancel(data.Packet.Header.Sequence);
- PacketPool.Instance.ReturnPacket(data.Packet);
- }
- }
- }
-
- // Send the pending packet acks to the client
- // Will send blocks of acks for up to 250 packets
- //
- private void SendAcks()
- {
- lock (m_NeedAck)
- {
- if (m_PendingAcks.Count == 0)
- return;
-
- PacketAckPacket acks = (PacketAckPacket)PacketPool.Instance.GetPacket(PacketType.PacketAck);
-
- // The case of equality is more common than one might think,
- // because this function will be called unconditionally when
- // the counter reaches 250. So there is a good chance another
- // packet with 250 blocks exists.
- //
- if (acks.Packets == null ||
- acks.Packets.Length != m_PendingAcks.Count)
- acks.Packets = new PacketAckPacket.PacketsBlock[m_PendingAcks.Count];
-
- for (int i = 0; i < m_PendingAcks.Count; i++)
- {
- acks.Packets[i] = new PacketAckPacket.PacketsBlock();
- acks.Packets[i].ID = m_PendingAcks[i];
-
- }
- m_PendingAcks.Clear();
- m_PendingAcksMap.Clear();
-
- acks.Header.Reliable = false;
- OutPacket(acks, ThrottleOutPacketType.Unknown);
- }
- }
-
- // Queue a packet ack. It will be sent either after 250 acks are
- // queued, or when the timer fires.
- //
- private void AckPacket(Packet packet)
- {
- lock (m_NeedAck)
- {
- if (m_PendingAcks.Count < 250)
- {
- if (!m_PendingAcksMap.ContainsKey(packet.Header.Sequence))
- {
- m_PendingAcks.Add(packet.Header.Sequence);
- m_PendingAcksMap.Add(packet.Header.Sequence,
- packet.Header.Sequence);
- }
- return;
- }
- }
-
- SendAcks();
-
- lock (m_NeedAck)
- {
- // If this is still full we have a truly exceptional
- // condition (means, can't happen)
- //
- if (m_PendingAcks.Count < 250)
- {
- if (!m_PendingAcksMap.ContainsKey(packet.Header.Sequence))
- {
- m_PendingAcks.Add(packet.Header.Sequence);
- m_PendingAcksMap.Add(packet.Header.Sequence,
- packet.Header.Sequence);
- }
- return;
- }
- }
- }
-
- // When the timer elapses, send the pending acks, trigger resends
- // and report all the stats.
- //
- private void AckTimerElapsed(object sender, ElapsedEventArgs ea)
- {
- SendAcks();
- ResendUnacked();
- SendPacketStats();
- }
-
- // Push out pachet counts for the sim status reporter
- //
- private void SendPacketStats()
- {
- PacketStats handlerPacketStats = OnPacketStats;
- if (handlerPacketStats != null)
- {
- handlerPacketStats(
- m_PacketsReceived - m_PacketsReceivedReported,
- m_PacketsSent - m_PacketsSentReported,
- m_UnackedBytes);
-
- m_PacketsReceivedReported = m_PacketsReceived;
- m_PacketsSentReported = m_PacketsSent;
- }
- }
-
- // We can't keep an unlimited record of dupes. This will prune the
- // dictionary by age.
- //
- // NOTE: this needs to be called from within lock
- // (m_alreadySeenTracker) context!
- private void ExpireSeenPackets()
- {
- if (m_alreadySeenList.Count < 1024)
- return;
-
- int ticks = 0;
- int tc = Environment.TickCount & Int32.MaxValue;
- if (tc >= m_lastAlreadySeenCheck)
- ticks = tc - m_lastAlreadySeenCheck;
- else
- ticks = Int32.MaxValue - m_lastAlreadySeenCheck + tc;
-
- if (ticks < 2000) return;
- m_lastAlreadySeenCheck = tc;
-
- // we calculate the drop dead tick count here instead of
- // in the loop: any packet with a timestamp before
- // dropDeadTC can be expired
- int dropDeadTC = tc - m_alreadySeenWindow;
- int i = 0;
- while (i < m_alreadySeenList.Count && m_alreadySeenTracker[m_alreadySeenList[i]] < dropDeadTC)
- {
- m_alreadySeenTracker.Remove(m_alreadySeenList[i]);
- i++;
- }
- // if we dropped packet from m_alreadySeenTracker we need
- // to drop them from m_alreadySeenList as well, let's do
- // that in one go: the list is ordered after all.
- if (i > 0)
- {
- m_alreadySeenList.RemoveRange(0, i);
- // m_log.DebugFormat("[CLIENT]: expired {0} packets, {1}:{2} left", i, m_alreadySeenList.Count, m_alreadySeenTracker.Count);
- }
- }
-
- public void InPacket(Packet packet)
- {
- if (packet == null)
- return;
-
- // When too many acks are needed to be sent, the client sends
- // a packet consisting of acks only
- //
- if (packet.Type == PacketType.PacketAck)
- {
- PacketAckPacket ackPacket = (PacketAckPacket)packet;
-
- foreach (PacketAckPacket.PacketsBlock block in ackPacket.Packets)
- {
- ProcessAck(block.ID);
- }
-
- PacketPool.Instance.ReturnPacket(packet);
- return;
- }
-
- // Any packet can have some packet acks in the header.
- // Process them here
- //
- if (packet.Header.AppendedAcks)
- {
- foreach (uint id in packet.Header.AckList)
- {
- ProcessAck(id);
- }
- }
-
- // If this client is on another partial instance, no need
- // to handle packets
- //
- if (!m_Client.IsActive && packet.Type != PacketType.LogoutRequest)
- {
- PacketPool.Instance.ReturnPacket(packet);
- return;
- }
-
- if (packet.Type == PacketType.StartPingCheck)
- {
- StartPingCheckPacket startPing = (StartPingCheckPacket)packet;
- CompletePingCheckPacket endPing
- = (CompletePingCheckPacket)PacketPool.Instance.GetPacket(PacketType.CompletePingCheck);
-
- endPing.PingID.PingID = startPing.PingID.PingID;
- OutPacket(endPing, ThrottleOutPacketType.Task);
- }
- else
- {
- LLQueItem item = new LLQueItem();
- item.Packet = packet;
- item.Incoming = true;
- m_PacketQueue.Enqueue(item);
- }
- }
-
- public void ProcessInPacket(LLQueItem item)
- {
- Packet packet = item.Packet;
-
- // Always ack the packet!
- //
- if (packet.Header.Reliable)
- AckPacket(packet);
-
- if (packet.Type != PacketType.AgentUpdate)
- m_PacketsReceived++;
-
- // Check for duplicate packets.. packets that the client is
- // resending because it didn't receive our ack
- //
- lock (m_alreadySeenTracker)
- {
- ExpireSeenPackets();
-
- if (m_alreadySeenTracker.ContainsKey(packet.Header.Sequence))
- return;
-
- m_alreadySeenTracker.Add(packet.Header.Sequence, Environment.TickCount & Int32.MaxValue);
- m_alreadySeenList.Add(packet.Header.Sequence);
- }
-
- m_Client.ProcessInPacket(packet);
- }
-
- public void Flush()
- {
- m_PacketQueue.Flush();
- m_UnackedBytes = (-1 * m_UnackedBytes);
- SendPacketStats();
- }
-
- public void Clear()
- {
- m_UnackedBytes = (-1 * m_UnackedBytes);
- SendPacketStats();
- lock (m_NeedAck)
- {
- m_NeedAck.Clear();
- m_PendingAcks.Clear();
- m_PendingAcksMap.Clear();
- }
- m_Sequence += 1000000;
- }
-
- private void ProcessAck(uint id)
- {
- LLQueItem data;
-
- lock (m_NeedAck)
- {
- //m_log.DebugFormat("[CLIENT]: In {0} received ack for packet {1}", m_Client.Scene.RegionInfo.ExternalEndPoint.Port, id);
-
- if (!m_NeedAck.TryGetValue(id, out data))
- return;
-
- m_NeedAck.Remove(id);
- m_PacketQueue.Cancel(data.Sequence);
- PacketPool.Instance.ReturnPacket(data.Packet);
- m_UnackedBytes -= data.Length;
- }
- }
-
- // Allocate packet sequence numbers in a threadsave manner
- //
- protected uint NextPacketSequenceNumber()
- {
- // Set the sequence number
- uint seq = 1;
- lock (m_SequenceLock)
- {
- if (m_Sequence >= MAX_SEQUENCE)
- {
- m_Sequence = 1;
- }
- else
- {
- m_Sequence++;
- }
- seq = m_Sequence;
- }
- return seq;
- }
-
- public ClientInfo GetClientInfo()
- {
- ClientInfo info = new ClientInfo();
-
- info.pendingAcks = m_PendingAcksMap;
- info.needAck = new Dictionary();
-
- lock (m_NeedAck)
- {
- foreach (uint key in m_NeedAck.Keys)
- info.needAck.Add(key, m_NeedAck[key].Packet.ToBytes());
- }
-
- LLQueItem[] queitems = m_PacketQueue.GetQueueArray();
-
- for (int i = 0; i < queitems.Length; i++)
- {
- if (queitems[i].Incoming == false)
- info.out_packets.Add(queitems[i].Packet.ToBytes());
- }
-
- info.sequence = m_Sequence;
-
- float multiplier = m_PacketQueue.ThrottleMultiplier;
- info.resendThrottle = (int) (m_PacketQueue.ResendThrottle.Throttle / multiplier);
- info.landThrottle = (int) (m_PacketQueue.LandThrottle.Throttle / multiplier);
- info.windThrottle = (int) (m_PacketQueue.WindThrottle.Throttle / multiplier);
- info.cloudThrottle = (int) (m_PacketQueue.CloudThrottle.Throttle / multiplier);
- info.taskThrottle = (int) (m_PacketQueue.TaskThrottle.Throttle / multiplier);
- info.assetThrottle = (int) (m_PacketQueue.AssetThrottle.Throttle / multiplier);
- info.textureThrottle = (int) (m_PacketQueue.TextureThrottle.Throttle / multiplier);
- info.totalThrottle = (int) (m_PacketQueue.TotalThrottle.Throttle / multiplier);
-
- return info;
- }
-
- public void SetClientInfo(ClientInfo info)
- {
- m_PendingAcksMap = info.pendingAcks;
- m_PendingAcks = new List(m_PendingAcksMap.Keys);
- m_NeedAck = new Dictionary();
-
- Packet packet = null;
- int packetEnd = 0;
- byte[] zero = new byte[3000];
-
- foreach (uint key in info.needAck.Keys)
- {
- byte[] buff = info.needAck[key];
- packetEnd = buff.Length - 1;
-
- try
- {
- packet = PacketPool.Instance.GetPacket(buff, ref packetEnd, zero);
- }
- catch (Exception)
- {
- }
-
- LLQueItem item = new LLQueItem();
- item.Packet = packet;
- item.Incoming = false;
- item.throttleType = 0;
- item.TickCount = Environment.TickCount;
- item.Identifier = 0;
- item.Resends = 0;
- item.Length = packet.Length;
- item.Sequence = packet.Header.Sequence;
- m_NeedAck.Add(key, item);
- }
-
- m_Sequence = info.sequence;
-
- m_PacketQueue.ResendThrottle.Throttle = info.resendThrottle;
- m_PacketQueue.LandThrottle.Throttle = info.landThrottle;
- m_PacketQueue.WindThrottle.Throttle = info.windThrottle;
- m_PacketQueue.CloudThrottle.Throttle = info.cloudThrottle;
- m_PacketQueue.TaskThrottle.Throttle = info.taskThrottle;
- m_PacketQueue.AssetThrottle.Throttle = info.assetThrottle;
- m_PacketQueue.TextureThrottle.Throttle = info.textureThrottle;
- m_PacketQueue.TotalThrottle.Throttle = info.totalThrottle;
- }
-
- public void AddImportantPacket(PacketType type)
- {
- if (m_ImportantPackets.Contains(type))
- return;
-
- m_ImportantPackets.Add(type);
- }
-
- public void RemoveImportantPacket(PacketType type)
- {
- if (!m_ImportantPackets.Contains(type))
- return;
-
- m_ImportantPackets.Remove(type);
- }
-
- private void DropResend(Object id)
- {
- LLQueItem d = null;
-
- foreach (LLQueItem data in m_NeedAck.Values)
- {
- if (data.Identifier != null && data.Identifier == id)
- {
- d = data;
- break;
- }
- }
-
- if (null == d) return;
-
- m_NeedAck.Remove(d.Packet.Header.Sequence);
- m_PacketQueue.Cancel(d.Sequence);
- PacketPool.Instance.ReturnPacket(d.Packet);
- }
-
- private void TriggerOnPacketDrop(Packet packet, Object id)
- {
- PacketDrop handlerPacketDrop = OnPacketDrop;
-
- if (handlerPacketDrop == null)
- return;
-
- handlerPacketDrop(packet, id);
- }
-
- private void TriggerOnQueueEmpty(ThrottleOutPacketType queue)
- {
- QueueEmpty handlerQueueEmpty = OnQueueEmpty;
-
- if (handlerQueueEmpty != null)
- handlerQueueEmpty(queue);
- }
-
- // Convert the packet to bytes and stuff it onto the send queue
- //
- public void ProcessOutPacket(LLQueItem item)
- {
- Packet packet = item.Packet;
-
- // Assign sequence number here to prevent out of order packets
- if (packet.Header.Sequence == 0)
- {
- lock (m_NeedAck)
- {
- packet.Header.Sequence = NextPacketSequenceNumber();
- item.Sequence = packet.Header.Sequence;
- item.TickCount = Environment.TickCount;
-
- // We want to see that packet arrive if it's reliable
- if (packet.Header.Reliable)
- {
- m_UnackedBytes += item.Length;
-
- // Keep track of when this packet was sent out
- item.TickCount = Environment.TickCount;
-
- m_NeedAck[packet.Header.Sequence] = item;
- }
- }
- }
-
- // If we sent a killpacket
- if (packet is KillPacket)
- Abort();
-
- try
- {
- // If this packet has been reused/returned, the ToBytes
- // will blow up in our face.
- // Fail gracefully.
- //
-
- // Actually make the byte array and send it
- byte[] sendbuffer = item.Packet.ToBytes();
-
- if (packet.Header.Zerocoded)
- {
- int packetsize = Helpers.ZeroEncode(sendbuffer,
- sendbuffer.Length, m_ZeroOutBuffer);
- m_PacketServer.SendPacketTo(m_ZeroOutBuffer, packetsize,
- SocketFlags.None, m_Client.CircuitCode);
- }
- else
- {
- // Need some extra space in case we need to add proxy
- // information to the message later
- Buffer.BlockCopy(sendbuffer, 0, m_ZeroOutBuffer, 0,
- sendbuffer.Length);
- m_PacketServer.SendPacketTo(m_ZeroOutBuffer,
- sendbuffer.Length, SocketFlags.None, m_Client.CircuitCode);
- }
- }
- catch (NullReferenceException)
- {
- m_log.Error("[PACKET]: Detected reuse of a returned packet");
- m_PacketQueue.Cancel(item.Sequence);
- return;
- }
-
- // If this is a reliable packet, we are still holding a ref
- // Dont't return in that case
- //
- if (!packet.Header.Reliable)
- {
- m_PacketQueue.Cancel(item.Sequence);
- PacketPool.Instance.ReturnPacket(packet);
- }
- }
-
- private void Abort()
- {
- m_PacketQueue.Close();
- Thread.CurrentThread.Abort();
- }
-
- public int GetQueueCount(ThrottleOutPacketType queue)
- {
- return m_PacketQueue.GetQueueCount(queue);
- }
- }
-}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
deleted file mode 100644
index 3eed2e0..0000000
--- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
+++ /dev/null
@@ -1,742 +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.Reflection;
-using System.Threading;
-using System.Timers;
-using log4net;
-using OpenMetaverse;
-using OpenSim.Framework;
-using OpenSim.Framework.Statistics;
-using OpenSim.Framework.Statistics.Interfaces;
-using Timer=System.Timers.Timer;
-
-namespace OpenSim.Region.ClientStack.LindenUDP
-{
- public class LLPacketQueue : IPullStatsProvider, IDisposable
- {
- private static readonly ILog m_log
- = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
- ///
- /// Is queueing enabled at all?
- ///
- private bool m_enabled = true;
-
- private OpenSim.Framework.BlockingQueue SendQueue;
-
- private Queue IncomingPacketQueue;
- private Queue OutgoingPacketQueue;
- private Queue ResendOutgoingPacketQueue;
- private Queue LandOutgoingPacketQueue;
- private Queue WindOutgoingPacketQueue;
- private Queue CloudOutgoingPacketQueue;
- private Queue TaskOutgoingPacketQueue;
- private Queue TaskLowpriorityPacketQueue;
- private Queue TextureOutgoingPacketQueue;
- private Queue AssetOutgoingPacketQueue;
-
- private List Empty = new List();
- // m_log.Info("[THROTTLE]: Entering Throttle");
- // private Dictionary PendingAcks = new Dictionary();
- // private Dictionary NeedAck = new Dictionary();
-
- // All throttle times and number of bytes are calculated by dividing by this value
- // This value also determines how many times per throttletimems the timer will run
- // If throttleimems is 1000 ms, then the timer will fire every 1000/7 milliseconds
-
- private float throttleMultiplier = 2.0f; // Default value really doesn't matter.
- private int throttleTimeDivisor = 7;
-
- private int throttletimems = 1000;
-
- internal LLPacketThrottle ResendThrottle;
- internal LLPacketThrottle LandThrottle;
- internal LLPacketThrottle WindThrottle;
- internal LLPacketThrottle CloudThrottle;
- internal LLPacketThrottle TaskThrottle;
- internal LLPacketThrottle AssetThrottle;
- internal LLPacketThrottle TextureThrottle;
- internal LLPacketThrottle TotalThrottle;
-
- private Dictionary contents = new Dictionary();
-
- // private long LastThrottle;
- // private long ThrottleInterval;
- private Timer throttleTimer;
-
- private UUID m_agentId;
-
- public event QueueEmpty OnQueueEmpty;
-
- public LLPacketQueue(UUID agentId, ClientStackUserSettings userSettings)
- {
- // While working on this, the BlockingQueue had me fooled for a bit.
- // The Blocking queue causes the thread to stop until there's something
- // in it to process. it's an on-purpose threadlock though because
- // without it, the clientloop will suck up all sim resources.
-
- SendQueue = new OpenSim.Framework.BlockingQueue();
-
- IncomingPacketQueue = new Queue();
- OutgoingPacketQueue = new Queue();
- ResendOutgoingPacketQueue = new Queue();
- LandOutgoingPacketQueue = new Queue();
- WindOutgoingPacketQueue = new Queue();
- CloudOutgoingPacketQueue = new Queue();
- TaskOutgoingPacketQueue = new Queue();
- TaskLowpriorityPacketQueue = new Queue();
- TextureOutgoingPacketQueue = new Queue();
- AssetOutgoingPacketQueue = new Queue();
-
- // Store the throttle multiplier for posterity.
- throttleMultiplier = userSettings.ClientThrottleMultipler;
-
-
- int throttleMaxBPS = 1500000;
- if (userSettings.TotalThrottleSettings != null)
- throttleMaxBPS = userSettings.TotalThrottleSettings.Max;
-
- // Set up the throttle classes (min, max, current) in bits per second
- ResendThrottle = new LLPacketThrottle(5000, throttleMaxBPS / 15, 16000, userSettings.ClientThrottleMultipler);
- LandThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 15, 2000, userSettings.ClientThrottleMultipler);
- WindThrottle = new LLPacketThrottle(0, throttleMaxBPS / 15, 0, userSettings.ClientThrottleMultipler);
- CloudThrottle = new LLPacketThrottle(0, throttleMaxBPS / 15, 0, userSettings.ClientThrottleMultipler);
- TaskThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 3000, userSettings.ClientThrottleMultipler);
- AssetThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 1000, userSettings.ClientThrottleMultipler);
- TextureThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 4000, userSettings.ClientThrottleMultipler);
-
-
- // Total Throttle trumps all - it is the number of bits in total that are allowed to go out per second.
-
-
- ThrottleSettings totalThrottleSettings = userSettings.TotalThrottleSettings;
- if (null == totalThrottleSettings)
- {
- totalThrottleSettings = new ThrottleSettings(0, throttleMaxBPS, 28000);
- }
-
- TotalThrottle
- = new LLPacketThrottle(
- totalThrottleSettings.Min, totalThrottleSettings.Max, totalThrottleSettings.Current,
- userSettings.ClientThrottleMultipler);
-
- throttleTimer = new Timer((int)(throttletimems / throttleTimeDivisor));
- throttleTimer.Elapsed += ThrottleTimerElapsed;
- throttleTimer.Start();
-
- // TIMERS needed for this
- // LastThrottle = DateTime.Now.Ticks;
- // ThrottleInterval = (long)(throttletimems/throttleTimeDivisor);
-
- m_agentId = agentId;
-
- if (StatsManager.SimExtraStats != null)
- {
- StatsManager.SimExtraStats.RegisterPacketQueueStatsProvider(m_agentId, this);
- }
- }
-
- /* STANDARD QUEUE MANIPULATION INTERFACES */
-
- public void Enqueue(LLQueItem item)
- {
- if (!m_enabled)
- {
- return;
- }
- // We could micro lock, but that will tend to actually
- // probably be worse than just synchronizing on SendQueue
-
- if (item == null)
- {
- SendQueue.Enqueue(item);
- return;
- }
-
- if (item.Incoming)
- {
- SendQueue.PriorityEnqueue(item);
- return;
- }
-
- if (item.Sequence != 0)
- lock (contents)
- {
- if (contents.ContainsKey(item.Sequence))
- contents[item.Sequence] += 1;
- else
- contents.Add(item.Sequence, 1);
- }
-
- lock (this)
- {
- switch (item.throttleType & ThrottleOutPacketType.TypeMask)
- {
- case ThrottleOutPacketType.Resend:
- ThrottleCheck(ref ResendThrottle, ref ResendOutgoingPacketQueue, item, ThrottleOutPacketType.Resend);
- break;
- case ThrottleOutPacketType.Texture:
- ThrottleCheck(ref TextureThrottle, ref TextureOutgoingPacketQueue, item, ThrottleOutPacketType.Texture);
- break;
- case ThrottleOutPacketType.Task:
- if ((item.throttleType & ThrottleOutPacketType.LowPriority) != 0)
- ThrottleCheck(ref TaskThrottle, ref TaskLowpriorityPacketQueue, item, ThrottleOutPacketType.Task);
- else
- ThrottleCheck(ref TaskThrottle, ref TaskOutgoingPacketQueue, item, ThrottleOutPacketType.Task);
- break;
- case ThrottleOutPacketType.Land:
- ThrottleCheck(ref LandThrottle, ref LandOutgoingPacketQueue, item, ThrottleOutPacketType.Land);
- break;
- case ThrottleOutPacketType.Asset:
- ThrottleCheck(ref AssetThrottle, ref AssetOutgoingPacketQueue, item, ThrottleOutPacketType.Asset);
- break;
- case ThrottleOutPacketType.Cloud:
- ThrottleCheck(ref CloudThrottle, ref CloudOutgoingPacketQueue, item, ThrottleOutPacketType.Cloud);
- break;
- case ThrottleOutPacketType.Wind:
- ThrottleCheck(ref WindThrottle, ref WindOutgoingPacketQueue, item, ThrottleOutPacketType.Wind);
- break;
-
- default:
- // Acknowledgements and other such stuff should go directly to the blocking Queue
- // Throttling them may and likely 'will' be problematic
- SendQueue.PriorityEnqueue(item);
- break;
- }
- }
- }
-
- public LLQueItem Dequeue()
- {
- while (true)
- {
- LLQueItem item = SendQueue.Dequeue();
- if (item == null)
- return null;
- if (item.Incoming)
- return item;
- item.TickCount = System.Environment.TickCount;
- if (item.Sequence == 0)
- return item;
- lock (contents)
- {
- if (contents.ContainsKey(item.Sequence))
- {
- if (contents[item.Sequence] == 1)
- contents.Remove(item.Sequence);
- else
- contents[item.Sequence] -= 1;
- return item;
- }
- }
- }
- }
-
- public void Cancel(uint sequence)
- {
- lock (contents) contents.Remove(sequence);
- }
-
- public bool Contains(uint sequence)
- {
- lock (contents) return contents.ContainsKey(sequence);
- }
-
- public void Flush()
- {
- lock (this)
- {
- // These categories do not contain transactional packets so we can safely drop any pending data in them
- LandOutgoingPacketQueue.Clear();
- WindOutgoingPacketQueue.Clear();
- CloudOutgoingPacketQueue.Clear();
- TextureOutgoingPacketQueue.Clear();
- AssetOutgoingPacketQueue.Clear();
-
- // Now comes the fun part.. we dump all remaining resend and task packets into the send queue
- while (ResendOutgoingPacketQueue.Count > 0 || TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0)
- {
- if (ResendOutgoingPacketQueue.Count > 0)
- SendQueue.Enqueue(ResendOutgoingPacketQueue.Dequeue());
-
- if (TaskOutgoingPacketQueue.Count > 0)
- SendQueue.PriorityEnqueue(TaskOutgoingPacketQueue.Dequeue());
-
- if (TaskLowpriorityPacketQueue.Count > 0)
- SendQueue.Enqueue(TaskLowpriorityPacketQueue.Dequeue());
- }
- }
- }
-
- public void WipeClean()
- {
- lock (this)
- {
- ResendOutgoingPacketQueue.Clear();
- LandOutgoingPacketQueue.Clear();
- WindOutgoingPacketQueue.Clear();
- CloudOutgoingPacketQueue.Clear();
- TaskOutgoingPacketQueue.Clear();
- TaskLowpriorityPacketQueue.Clear();
- TextureOutgoingPacketQueue.Clear();
- AssetOutgoingPacketQueue.Clear();
- SendQueue.Clear();
- lock (contents) contents.Clear();
- }
- }
-
- public void Close()
- {
- Dispose();
- }
-
- public void Dispose()
- {
- Flush();
- WipeClean(); // I'm sure there's a dirty joke in here somewhere. -AFrisby
-
- m_enabled = false;
- throttleTimer.Stop();
- throttleTimer.Close();
-
- if (StatsManager.SimExtraStats != null)
- {
- StatsManager.SimExtraStats.DeregisterPacketQueueStatsProvider(m_agentId);
- }
- }
-
- private void ResetCounters()
- {
- ResendThrottle.Reset();
- LandThrottle.Reset();
- WindThrottle.Reset();
- CloudThrottle.Reset();
- TaskThrottle.Reset();
- AssetThrottle.Reset();
- TextureThrottle.Reset();
- TotalThrottle.Reset();
- }
-
- private bool PacketsWaiting()
- {
- return (ResendOutgoingPacketQueue.Count > 0 ||
- LandOutgoingPacketQueue.Count > 0 ||
- WindOutgoingPacketQueue.Count > 0 ||
- CloudOutgoingPacketQueue.Count > 0 ||
- TaskOutgoingPacketQueue.Count > 0 ||
- TaskLowpriorityPacketQueue.Count > 0 ||
- AssetOutgoingPacketQueue.Count > 0 ||
- TextureOutgoingPacketQueue.Count > 0);
- }
-
- public void ProcessThrottle()
- {
- // I was considering this.. Will an event fire if the thread it's on is blocked?
-
- // Then I figured out.. it doesn't really matter.. because this thread won't be blocked for long
- // The General overhead of the UDP protocol gets sent to the queue un-throttled by this
- // so This'll pick up about around the right time.
-
- int MaxThrottleLoops = 4550; // 50*7 packets can be dequeued at once.
- int throttleLoops = 0;
- List e;
-
- // We're going to dequeue all of the saved up packets until
- // we've hit the throttle limit or there's no more packets to send
- lock (this)
- {
- // this variable will be true if there was work done in the last execution of the
- // loop, since each pass through the loop checks the queue length, we no longer
- // need the check on entering the loop
- bool qchanged = true;
-
- ResetCounters();
-
- while (TotalThrottle.UnderLimit() && qchanged && throttleLoops <= MaxThrottleLoops)
- {
- qchanged = false; // We will break out of the loop if no work was accomplished
-
- throttleLoops++;
- //Now comes the fun part.. we dump all our elements into m_packetQueue that we've saved up.
- if ((ResendOutgoingPacketQueue.Count > 0) && ResendThrottle.UnderLimit())
- {
- LLQueItem qpack = ResendOutgoingPacketQueue.Dequeue();
-
- SendQueue.Enqueue(qpack);
- TotalThrottle.AddBytes(qpack.Length);
- ResendThrottle.AddBytes(qpack.Length);
-
- qchanged = true;
- }
-
- if ((LandOutgoingPacketQueue.Count > 0) && LandThrottle.UnderLimit())
- {
- LLQueItem qpack = LandOutgoingPacketQueue.Dequeue();
-
- SendQueue.Enqueue(qpack);
- TotalThrottle.AddBytes(qpack.Length);
- LandThrottle.AddBytes(qpack.Length);
- qchanged = true;
-
- if (LandOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Land))
- Empty.Add(ThrottleOutPacketType.Land);
- }
-
- if ((WindOutgoingPacketQueue.Count > 0) && WindThrottle.UnderLimit())
- {
- LLQueItem qpack = WindOutgoingPacketQueue.Dequeue();
-
- SendQueue.Enqueue(qpack);
- TotalThrottle.AddBytes(qpack.Length);
- WindThrottle.AddBytes(qpack.Length);
- qchanged = true;
-
- if (WindOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Wind))
- Empty.Add(ThrottleOutPacketType.Wind);
- }
-
- if ((CloudOutgoingPacketQueue.Count > 0) && CloudThrottle.UnderLimit())
- {
- LLQueItem qpack = CloudOutgoingPacketQueue.Dequeue();
-
- SendQueue.Enqueue(qpack);
- TotalThrottle.AddBytes(qpack.Length);
- CloudThrottle.AddBytes(qpack.Length);
- qchanged = true;
-
- if (CloudOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Cloud))
- Empty.Add(ThrottleOutPacketType.Cloud);
- }
-
- if ((TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0) && TaskThrottle.UnderLimit())
- {
- LLQueItem qpack;
- if (TaskOutgoingPacketQueue.Count > 0)
- {
- qpack = TaskOutgoingPacketQueue.Dequeue();
- SendQueue.PriorityEnqueue(qpack);
- }
- else
- {
- qpack = TaskLowpriorityPacketQueue.Dequeue();
- SendQueue.Enqueue(qpack);
- }
-
- TotalThrottle.AddBytes(qpack.Length);
- TaskThrottle.AddBytes(qpack.Length);
- qchanged = true;
-
- if (TaskOutgoingPacketQueue.Count == 0 && TaskLowpriorityPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Task))
- Empty.Add(ThrottleOutPacketType.Task);
- }
-
- if ((TextureOutgoingPacketQueue.Count > 0) && TextureThrottle.UnderLimit())
- {
- LLQueItem qpack = TextureOutgoingPacketQueue.Dequeue();
-
- SendQueue.Enqueue(qpack);
- TotalThrottle.AddBytes(qpack.Length);
- TextureThrottle.AddBytes(qpack.Length);
- qchanged = true;
-
- if (TextureOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Texture))
- Empty.Add(ThrottleOutPacketType.Texture);
- }
-
- if ((AssetOutgoingPacketQueue.Count > 0) && AssetThrottle.UnderLimit())
- {
- LLQueItem qpack = AssetOutgoingPacketQueue.Dequeue();
-
- SendQueue.Enqueue(qpack);
- TotalThrottle.AddBytes(qpack.Length);
- AssetThrottle.AddBytes(qpack.Length);
- qchanged = true;
-
- if (AssetOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Asset))
- Empty.Add(ThrottleOutPacketType.Asset);
- }
- }
- // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets");
-
- e = new List(Empty);
- Empty.Clear();
- }
-
- foreach (ThrottleOutPacketType t in e)
- {
- if (GetQueueCount(t) == 0)
- TriggerOnQueueEmpty(t);
- }
- }
-
- private void TriggerOnQueueEmpty(ThrottleOutPacketType queue)
- {
- QueueEmpty handlerQueueEmpty = OnQueueEmpty;
-
- if (handlerQueueEmpty != null)
- handlerQueueEmpty(queue);
- }
-
- private void ThrottleTimerElapsed(object sender, ElapsedEventArgs e)
- {
- // just to change the signature, and that ProcessThrottle
- // will be used elsewhere possibly
- ProcessThrottle();
- }
-
- private void ThrottleCheck(ref LLPacketThrottle throttle, ref Queue q, LLQueItem item, ThrottleOutPacketType itemType)
- {
- // The idea.. is if the packet throttle queues are empty
- // and the client is under throttle for the type. Queue
- // it up directly. This basically short cuts having to
- // wait for the timer to fire to put things into the
- // output queue
-
- if ((q.Count == 0) && (throttle.UnderLimit()))
- {
- try
- {
- Monitor.Enter(this);
- throttle.AddBytes(item.Length);
- TotalThrottle.AddBytes(item.Length);
- SendQueue.Enqueue(item);
- lock (this)
- {
- if (!Empty.Contains(itemType))
- Empty.Add(itemType);
- }
- }
- catch (Exception e)
- {
- // Probably a serialization exception
- m_log.WarnFormat("ThrottleCheck: {0}", e.ToString());
- }
- finally
- {
- Monitor.Pulse(this);
- Monitor.Exit(this);
- }
- }
- else
- {
- q.Enqueue(item);
- }
- }
-
- private static int ScaleThrottle(int value, int curmax, int newmax)
- {
- return (int)((value / (float)curmax) * newmax);
- }
-
- public byte[] GetThrottlesPacked(float multiplier)
- {
- int singlefloat = 4;
- float tResend = ResendThrottle.Throttle*multiplier;
- float tLand = LandThrottle.Throttle*multiplier;
- float tWind = WindThrottle.Throttle*multiplier;
- float tCloud = CloudThrottle.Throttle*multiplier;
- float tTask = TaskThrottle.Throttle*multiplier;
- float tTexture = TextureThrottle.Throttle*multiplier;
- float tAsset = AssetThrottle.Throttle*multiplier;
-
- byte[] throttles = new byte[singlefloat*7];
- int i = 0;
- Buffer.BlockCopy(BitConverter.GetBytes(tResend), 0, throttles, singlefloat*i, singlefloat);
- i++;
- Buffer.BlockCopy(BitConverter.GetBytes(tLand), 0, throttles, singlefloat*i, singlefloat);
- i++;
- Buffer.BlockCopy(BitConverter.GetBytes(tWind), 0, throttles, singlefloat*i, singlefloat);
- i++;
- Buffer.BlockCopy(BitConverter.GetBytes(tCloud), 0, throttles, singlefloat*i, singlefloat);
- i++;
- Buffer.BlockCopy(BitConverter.GetBytes(tTask), 0, throttles, singlefloat*i, singlefloat);
- i++;
- Buffer.BlockCopy(BitConverter.GetBytes(tTexture), 0, throttles, singlefloat*i, singlefloat);
- i++;
- Buffer.BlockCopy(BitConverter.GetBytes(tAsset), 0, throttles, singlefloat*i, singlefloat);
-
- return throttles;
- }
-
- public void SetThrottleFromClient(byte[] throttle)
- {
- // From mantis http://opensimulator.org/mantis/view.php?id=1374
- // it appears that sometimes we are receiving empty throttle byte arrays.
- // TODO: Investigate this behaviour
- if (throttle.Length == 0)
- {
- m_log.Warn("[PACKET QUEUE]: SetThrottleFromClient unexpectedly received a throttle byte array containing no elements!");
- return;
- }
-
- int tResend = -1;
- int tLand = -1;
- int tWind = -1;
- int tCloud = -1;
- int tTask = -1;
- int tTexture = -1;
- int tAsset = -1;
- int tall = -1;
- int singlefloat = 4;
-
- //Agent Throttle Block contains 7 single floatingpoint values.
- int j = 0;
-
- // Some Systems may be big endian...
- // it might be smart to do this check more often...
- if (!BitConverter.IsLittleEndian)
- for (int i = 0; i < 7; i++)
- Array.Reverse(throttle, j + i*singlefloat, singlefloat);
-
- // values gotten from OpenMetaverse.org/wiki/Throttle. Thanks MW_
- // bytes
- // Convert to integer, since.. the full fp space isn't used.
- tResend = (int) BitConverter.ToSingle(throttle, j);
- j += singlefloat;
- tLand = (int) BitConverter.ToSingle(throttle, j);
- j += singlefloat;
- tWind = (int) BitConverter.ToSingle(throttle, j);
- j += singlefloat;
- tCloud = (int) BitConverter.ToSingle(throttle, j);
- j += singlefloat;
- tTask = (int) BitConverter.ToSingle(throttle, j);
- j += singlefloat;
- tTexture = (int) BitConverter.ToSingle(throttle, j);
- j += singlefloat;
- tAsset = (int) BitConverter.ToSingle(throttle, j);
-
- tall = tResend + tLand + tWind + tCloud + tTask + tTexture + tAsset;
-
- /*
- m_log.Info("[CLIENT]: Client AgentThrottle - Got throttle:resendbits=" + tResend +
- " landbits=" + tLand +
- " windbits=" + tWind +
- " cloudbits=" + tCloud +
- " taskbits=" + tTask +
- " texturebits=" + tTexture +
- " Assetbits=" + tAsset +
- " Allbits=" + tall);
- */
-
-
- // Total Sanity
- // Make sure that the client sent sane total values.
-
- // If the client didn't send acceptable values....
- // Scale the clients values down until they are acceptable.
-
- if (tall <= TotalThrottle.Max)
- {
- ResendThrottle.Throttle = tResend;
- LandThrottle.Throttle = tLand;
- WindThrottle.Throttle = tWind;
- CloudThrottle.Throttle = tCloud;
- TaskThrottle.Throttle = tTask;
- TextureThrottle.Throttle = tTexture;
- AssetThrottle.Throttle = tAsset;
- TotalThrottle.Throttle = tall;
- }
-// else if (tall < 1)
-// {
-// // client is stupid, penalize him by minning everything
-// ResendThrottle.Throttle = ResendThrottle.Min;
-// LandThrottle.Throttle = LandThrottle.Min;
-// WindThrottle.Throttle = WindThrottle.Min;
-// CloudThrottle.Throttle = CloudThrottle.Min;
-// TaskThrottle.Throttle = TaskThrottle.Min;
-// TextureThrottle.Throttle = TextureThrottle.Min;
-// AssetThrottle.Throttle = AssetThrottle.Min;
-// TotalThrottle.Throttle = TotalThrottle.Min;
-// }
- else
- {
- // we're over so figure out percentages and use those
- ResendThrottle.Throttle = tResend;
-
- LandThrottle.Throttle = ScaleThrottle(tLand, tall, TotalThrottle.Max);
- WindThrottle.Throttle = ScaleThrottle(tWind, tall, TotalThrottle.Max);
- CloudThrottle.Throttle = ScaleThrottle(tCloud, tall, TotalThrottle.Max);
- TaskThrottle.Throttle = ScaleThrottle(tTask, tall, TotalThrottle.Max);
- TextureThrottle.Throttle = ScaleThrottle(tTexture, tall, TotalThrottle.Max);
- AssetThrottle.Throttle = ScaleThrottle(tAsset, tall, TotalThrottle.Max);
- TotalThrottle.Throttle = TotalThrottle.Max;
- }
- // effectively wiggling the slider causes things reset
-// ResetCounters(); // DO NOT reset, better to send less for one period than more
- }
-
- // See IPullStatsProvider
- public string GetStats()
- {
- return string.Format("{0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}",
- SendQueue.Count(),
- IncomingPacketQueue.Count,
- OutgoingPacketQueue.Count,
- ResendOutgoingPacketQueue.Count,
- LandOutgoingPacketQueue.Count,
- WindOutgoingPacketQueue.Count,
- CloudOutgoingPacketQueue.Count,
- TaskOutgoingPacketQueue.Count,
- TextureOutgoingPacketQueue.Count,
- AssetOutgoingPacketQueue.Count);
- }
-
- public LLQueItem[] GetQueueArray()
- {
- return SendQueue.GetQueueArray();
- }
-
- public float ThrottleMultiplier
- {
- get { return throttleMultiplier; }
- }
-
- public int GetQueueCount(ThrottleOutPacketType queue)
- {
- switch (queue)
- {
- case ThrottleOutPacketType.Land:
- return LandOutgoingPacketQueue.Count;
- case ThrottleOutPacketType.Wind:
- return WindOutgoingPacketQueue.Count;
- case ThrottleOutPacketType.Cloud:
- return CloudOutgoingPacketQueue.Count;
- case ThrottleOutPacketType.Task:
- return TaskOutgoingPacketQueue.Count;
- case ThrottleOutPacketType.Texture:
- return TextureOutgoingPacketQueue.Count;
- case ThrottleOutPacketType.Asset:
- return AssetOutgoingPacketQueue.Count;
- }
-
- return 0;
- }
- }
-}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketServer.cs
deleted file mode 100644
index f08b7be..0000000
--- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketServer.cs
+++ /dev/null
@@ -1,206 +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.Net;
-using System.Net.Sockets;
-using OpenMetaverse;
-using OpenMetaverse.Packets;
-using OpenSim.Framework;
-
-namespace OpenSim.Region.ClientStack.LindenUDP
-{
- ///
- /// This class sets up new client stacks. It also handles the immediate distribution of incoming packets to
- /// client stacks
- ///
- public class LLPacketServer
- {
-// private static readonly log4net.ILog m_log
-// = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
-
- protected readonly LLUDPServer m_networkHandler;
- protected IScene m_scene;
-
- ///
- /// Tweakable user settings
- ///
- private ClientStackUserSettings m_userSettings;
-
- public LLPacketServer(LLUDPServer networkHandler, ClientStackUserSettings userSettings)
- {
- m_userSettings = userSettings;
- m_networkHandler = networkHandler;
-
- m_networkHandler.RegisterPacketServer(this);
- }
-
- public IScene LocalScene
- {
- set { m_scene = value; }
- }
-
- ///
- /// Process an incoming packet.
- ///
- ///
- ///
- public virtual void InPacket(uint circuitCode, Packet packet)
- {
- m_scene.ClientManager.InPacket(circuitCode, packet);
- }
-
- ///
- /// Create a new client circuit
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- protected virtual IClientAPI CreateNewCircuit(
- EndPoint remoteEP, IScene scene,
- LLPacketServer packServer, AuthenticateResponse sessionInfo,
- UUID agentId, UUID sessionId, uint circuitCode, EndPoint proxyEP)
- {
- return
- new LLClientView(
- remoteEP, scene, packServer, sessionInfo, agentId, sessionId, circuitCode, proxyEP,
- m_userSettings);
- }
-
- ///
- /// Check whether a given client is authorized to connect.
- ///
- ///
- ///
- ///
- ///
- public virtual bool IsClientAuthorized(
- UseCircuitCodePacket useCircuit, AgentCircuitManager circuitManager, out AuthenticateResponse sessionInfo)
- {
- UUID agentId = useCircuit.CircuitCode.ID;
- UUID sessionId = useCircuit.CircuitCode.SessionID;
- uint circuitCode = useCircuit.CircuitCode.Code;
-
- sessionInfo = circuitManager.AuthenticateSession(sessionId, agentId, circuitCode);
-
- if (!sessionInfo.Authorised)
- return false;
-
- return true;
- }
-
- ///
- /// Add a new client circuit. We assume that is has already passed an authorization check
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- /// true if a new circuit was created, false if a circuit with the given circuit code already existed
- ///
- public virtual bool AddNewClient(
- EndPoint epSender, UseCircuitCodePacket useCircuit,
- AuthenticateResponse sessionInfo, EndPoint proxyEP)
- {
- IClientAPI newuser;
- uint circuitCode = useCircuit.CircuitCode.Code;
-
- if (m_scene.ClientManager.TryGetClient(circuitCode, out newuser))
- {
- // The circuit is already known to the scene. This not actually a problem since this will currently
- // occur if a client is crossing borders (hence upgrading its circuit). However, we shouldn't
- // really by trying to add a new client if this is the case.
- return false;
- }
-
- UUID agentId = useCircuit.CircuitCode.ID;
- UUID sessionId = useCircuit.CircuitCode.SessionID;
-
- newuser
- = CreateNewCircuit(
- epSender, m_scene, this, sessionInfo, agentId, sessionId, circuitCode, proxyEP);
-
- m_scene.ClientManager.Add(circuitCode, newuser);
-
- newuser.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler;
- newuser.OnLogout += LogoutHandler;
- newuser.OnConnectionClosed += CloseClient;
-
- newuser.Start();
-
- return true;
- }
-
- public void LogoutHandler(IClientAPI client)
- {
- client.SendLogoutPacket();
- CloseClient(client);
- }
-
- ///
- /// Send a packet to the given circuit
- ///
- ///
- ///
- ///
- ///
- public virtual void SendPacketTo(byte[] buffer, int size, SocketFlags flags, uint circuitcode)
- {
- m_networkHandler.SendPacketTo(buffer, size, flags, circuitcode);
- }
-
- ///
- /// Close a client circuit only
- ///
- ///
- public virtual void CloseCircuit(uint circuitcode)
- {
- m_networkHandler.RemoveClientCircuit(circuitcode);
- }
-
- ///
- /// Completely close down the given client.
- ///
- ///
- public virtual void CloseClient(IClientAPI client)
- {
- //m_log.Info("PacketServer:CloseClient()");
-
- CloseCircuit(client.CircuitCode);
- m_scene.ClientManager.Remove(client.CircuitCode);
- client.Close(false);
- }
- }
-}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLQueItem.cs b/OpenSim/Region/ClientStack/LindenUDP/LLQueItem.cs
deleted file mode 100644
index 0ed2bc1..0000000
--- a/OpenSim/Region/ClientStack/LindenUDP/LLQueItem.cs
+++ /dev/null
@@ -1,49 +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 OpenMetaverse.Packets;
-using OpenSim.Framework;
-
-namespace OpenSim.Region.ClientStack.LindenUDP
-{
- public class LLQueItem
- {
- public LLQueItem()
- {
- }
-
- public Packet Packet;
- public bool Incoming;
- public ThrottleOutPacketType throttleType;
- public int TickCount;
- public Object Identifier;
- public int Resends;
- public int Length;
- public uint Sequence;
- }
-}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
new file mode 100644
index 0000000..ad01135
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
@@ -0,0 +1,370 @@
+/*
+ * 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;
+
+namespace OpenSim.Region.ClientStack.LindenUDP
+{
+ public delegate void QueueEmpty(ThrottleOutPacketType category);
+
+ public class LLUDPClient
+ {
+ /// 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;
+
+ public event QueueEmpty OnQueueEmpty;
+
+ /// AgentID for this client
+ public readonly UUID AgentID;
+ /// The remote address of the connected client
+ public readonly IPEndPoint RemoteEndPoint;
+ /// Circuit code that this client is connected on
+ public readonly uint CircuitCode;
+ /// Sequence numbers of packets we've received (for duplicate checking)
+ public readonly IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(200);
+ /// 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();
+
+ /// Reference to the IClientAPI for this client
+ public LLClientView ClientAPI;
+ /// Current packet sequence number
+ public int CurrentSequence;
+ /// Current ping sequence number
+ public byte CurrentPingSequence;
+ /// True when this connection is alive, otherwise false
+ public bool IsConnected = true;
+ /// True when this connection is paused, otherwise false
+ public bool IsPaused = true;
+ /// Environment.TickCount when the last packet was received for this client
+ public int TickLastPacketReceived;
+
+ /// Timer granularity. This is set to the measured resolution of Environment.TickCount
+ public readonly float G;
+ /// Smoothed round-trip time. A smoothed average of the round-trip time for sending a
+ /// reliable packet to the client and receiving an ACK
+ public float SRTT;
+ /// Round-trip time variance. Measures the consistency of round-trip times
+ public float RTTVAR;
+ /// Retransmission timeout. Packets that have not been acknowledged in this number of
+ /// milliseconds or longer will be resent
+ /// Calculated from and using the
+ /// guidelines in RFC 2988
+ public int RTO;
+ /// Number of bytes received since the last acknowledgement was sent out. This is used
+ /// to loosely follow the TCP delayed ACK algorithm in RFC 1122 (4.2.3.2)
+ public int BytesSinceLastACK;
+
+ /// Throttle bucket for this agent's connection
+ private readonly TokenBucket throttle;
+ /// Throttle buckets for each packet category
+ private readonly TokenBucket[] throttleCategories;
+ /// Throttle rate defaults and limits
+ private readonly ThrottleRates defaultThrottleRates;
+ /// Outgoing queues for throttled packets
+ private readonly LocklessQueue[] packetOutboxes = new 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];
+ /// A reference to the LLUDPServer that is managing this client
+ private readonly LLUDPServer udpServer;
+
+ public LLUDPClient(LLUDPServer server, ThrottleRates rates, TokenBucket parentThrottle, uint circuitCode, UUID agentID, IPEndPoint remoteEndPoint)
+ {
+ udpServer = server;
+ AgentID = agentID;
+ RemoteEndPoint = remoteEndPoint;
+ CircuitCode = circuitCode;
+ defaultThrottleRates = rates;
+
+ for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
+ packetOutboxes[i] = new LocklessQueue();
+
+ throttle = new TokenBucket(parentThrottle, 0, 0);
+ 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.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);
+
+ // Set the granularity variable used for retransmission calculations to
+ // the measured resolution of Environment.TickCount
+ G = server.TickCountResolution;
+
+ // Default the retransmission timeout to three seconds
+ RTO = 3000;
+ }
+
+ public void Shutdown()
+ {
+ IsConnected = false;
+ }
+
+ public ClientInfo GetClientInfo()
+ {
+ // TODO: This data structure is wrong in so many ways
+ ClientInfo info = new ClientInfo();
+ 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.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 +
+ info.taskThrottle + info.assetThrottle + info.textureThrottle;
+
+ return info;
+ }
+
+ public void SetClientInfo(ClientInfo info)
+ {
+ }
+
+ public string GetStats()
+ {
+ return string.Format("{0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}",
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0);
+ }
+
+ public void SetThrottles(byte[] throttleData)
+ {
+ byte[] adjData;
+ int pos = 0;
+
+ if (!BitConverter.IsLittleEndian)
+ {
+ byte[] newData = new byte[7 * 4];
+ Buffer.BlockCopy(throttleData, 0, newData, 0, 7 * 4);
+
+ for (int i = 0; i < 7; i++)
+ Array.Reverse(newData, i * 4, 4);
+
+ adjData = newData;
+ }
+ else
+ {
+ adjData = throttleData;
+ }
+
+ 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;
+ int cloud = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
+ 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);
+
+ 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);
+ }
+
+ public byte[] GetThrottlesPacked()
+ {
+ 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), 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;
+
+ return data;
+ }
+
+ public void SetThrottle(ThrottleOutPacketType category, int rate)
+ {
+ int i = (int)category;
+ if (i >= 0 && i < throttleCategories.Length)
+ {
+ TokenBucket bucket = throttleCategories[(int)category];
+ bucket.MaxBurst = rate;
+ bucket.DripRate = rate;
+ }
+ }
+
+ public bool EnqueueOutgoing(OutgoingPacket packet)
+ {
+ int category = (int)packet.Category;
+
+ if (category >= 0 && category < packetOutboxes.Length)
+ {
+ LocklessQueue queue = packetOutboxes[category];
+ TokenBucket bucket = throttleCategories[category];
+
+ if (throttleCategories[category].RemoveTokens(packet.Buffer.DataLength))
+ {
+ // Enough tokens were removed from the bucket, the packet will not be queued
+ return false;
+ }
+ else
+ {
+ // Not enough tokens in the bucket, queue this packet
+ queue.Enqueue(packet);
+ return true;
+ }
+ }
+ else
+ {
+ // We don't have a token bucket for this category, so it will not be queued
+ return false;
+ }
+ }
+
+ ///
+ /// Loops through all of the packet queues for this client and tries to send
+ /// any outgoing packets, obeying the throttling bucket limits
+ ///
+ /// This function is only called from a synchronous loop in the
+ /// UDPServer so we don't need to bother making this thread safe
+ /// True if any packets were sent, otherwise false
+ public bool DequeueOutgoing()
+ {
+ OutgoingPacket packet;
+ LocklessQueue queue;
+ TokenBucket bucket;
+ bool packetSent = false;
+
+ for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
+ {
+ bucket = throttleCategories[i];
+
+ if (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
+ if (bucket.RemoveTokens(nextPacketLengths[i]))
+ {
+ // Send the packet
+ udpServer.SendPacketFinal(nextPackets[i]);
+ nextPackets[i] = null;
+ packetSent = true;
+ }
+ }
+ else
+ {
+ // No dequeued packet waiting to be sent, try to pull one off
+ // this queue
+ queue = packetOutboxes[i];
+ if (queue.Dequeue(out packet))
+ {
+ // 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))
+ {
+ // Send the packet
+ udpServer.SendPacketFinal(packet);
+ packetSent = true;
+ }
+ else
+ {
+ // Save the dequeued packet and the length calculation for
+ // the next iteration
+ nextPackets[i] = packet;
+ nextPacketLengths[i] = packet.Buffer.DataLength;
+ }
+ }
+ else
+ {
+ // No packets in this queue. Fire the queue empty callback
+ QueueEmpty callback = OnQueueEmpty;
+ if (callback != null)
+ callback((ThrottleOutPacketType)i);
+ }
+ }
+ }
+
+ return packetSent;
+ }
+
+ public void UpdateRoundTrip(float r)
+ {
+ const float ALPHA = 0.125f;
+ const float BETA = 0.25f;
+ const float K = 4.0f;
+
+ if (RTTVAR == 0.0f)
+ {
+ // First RTT measurement
+ SRTT = r;
+ RTTVAR = r * 0.5f;
+ }
+ else
+ {
+ // Subsequence RTT measurement
+ RTTVAR = (1.0f - BETA) * RTTVAR + BETA * Math.Abs(SRTT - r);
+ SRTT = (1.0f - ALPHA) * SRTT + ALPHA * r;
+ }
+
+ // 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 " +
+ // RTTVAR + " based on new RTT of " + r + "ms");
+ }
+ }
+}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs
new file mode 100644
index 0000000..7d2da68
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs
@@ -0,0 +1,185 @@
+/*
+ * 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 ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim;
+
+namespace OpenSim.Region.ClientStack.LindenUDP
+{
+ public sealed class UDPClientCollection
+ {
+ Dictionary Dictionary1;
+ Dictionary Dictionary2;
+ LLUDPClient[] Array;
+ ReaderWriterLockImpl rwLock = new ReaderWriterLockImpl();
+
+ public UDPClientCollection()
+ {
+ Dictionary1 = new Dictionary();
+ Dictionary2 = new Dictionary();
+ Array = new LLUDPClient[0];
+ }
+
+ public UDPClientCollection(int capacity)
+ {
+ Dictionary1 = new Dictionary(capacity);
+ Dictionary2 = new Dictionary(capacity);
+ Array = new LLUDPClient[0];
+ }
+
+ public void Add(UUID key1, IPEndPoint key2, LLUDPClient value)
+ {
+ 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(); }
+ }
+
+ public bool Remove(UUID key1, IPEndPoint key2)
+ {
+ rwLock.EnterWriteLock();
+
+ try
+ {
+ 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;
+ }
+ }
+ finally { rwLock.ExitWriteLock(); }
+
+ return false;
+ }
+
+ public void Clear()
+ {
+ rwLock.EnterWriteLock();
+
+ try
+ {
+ Dictionary1.Clear();
+ Dictionary2.Clear();
+ Array = new LLUDPClient[0];
+ }
+ finally { rwLock.ExitWriteLock(); }
+ }
+
+ public int Count
+ {
+ get { return Array.Length; }
+ }
+
+ public bool ContainsKey(UUID key)
+ {
+ return Dictionary1.ContainsKey(key);
+ }
+
+ public bool ContainsKey(IPEndPoint key)
+ {
+ return Dictionary2.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;
+ }
+
+ 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;
+ }
+
+ public void ForEach(Action action)
+ {
+ bool doLock = !rwLock.IsUpgradeableReadLockHeld;
+ if (doLock) rwLock.EnterUpgradeableReadLock();
+
+ try { Parallel.ForEach(Array, action); }
+ finally { if (doLock) rwLock.ExitUpgradeableReadLock(); }
+ }
+ }
+}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
index 7964c50..348615e 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
@@ -35,6 +35,7 @@ using log4net;
using Nini.Config;
using OpenMetaverse.Packets;
using OpenSim.Framework;
+using OpenSim.Framework.Statistics;
using OpenSim.Region.Framework.Scenes;
using OpenMetaverse;
@@ -190,31 +191,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
}
- public void RemoveClient(LLUDPClient udpClient)
- {
- IClientAPI client;
-
- if (m_scene.ClientManager.TryGetClient(udpClient.CircuitCode, out client))
- RemoveClient(client);
- else
- m_log.Warn("[LLUDPSERVER]: Failed to lookup IClientAPI for LLUDPClient " + udpClient.AgentID);
- }
-
- public void SetClientPaused(UUID agentID, bool paused)
- {
- LLUDPClient client;
- if (clients.TryGetValue(agentID, out client))
- {
- client.IsPaused = paused;
- }
- else
- {
- m_log.Warn("[LLUDPSERVER]: Attempted to pause/unpause unknown agent " + agentID);
- }
- }
-
public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting)
{
+ // CoarseLocationUpdate packets cannot be split in an automated way
+ if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting)
+ allowSplitting = false;
+
if (allowSplitting && packet.HasVariableBlocks)
{
byte[][] datas = packet.ToBytesMultiple();
@@ -251,6 +233,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public void SendPacket(LLUDPClient client, Packet packet, ThrottleOutPacketType category, bool allowSplitting)
{
+ // CoarseLocationUpdate packets cannot be split in an automated way
+ if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting)
+ allowSplitting = false;
+
if (allowSplitting && packet.HasVariableBlocks)
{
byte[][] datas = packet.ToBytesMultiple();
@@ -339,6 +325,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
}
+ public void SendPing(LLUDPClient client)
+ {
+ IClientAPI api = client.ClientAPI;
+ if (api != null)
+ api.SendStartPingCheck(client.CurrentPingSequence++);
+ }
+
public void ResendUnacked(LLUDPClient client)
{
if (client.NeedAcks.Count > 0)
@@ -387,9 +380,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
//FIXME: Make 60 an .ini setting
if (Environment.TickCount - client.TickLastPacketReceived > 1000 * 60)
{
- m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + client.RemoteEndPoint);
+ m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + client.ClientAPI.Name);
- RemoveClient(client);
+ RemoveClient(client.ClientAPI);
return;
}
}
@@ -590,8 +583,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
LLUDPClient client = new LLUDPClient(this, m_throttleRates, m_throttle, circuitCode, agentID, remoteEndPoint);
clients.Add(agentID, client.RemoteEndPoint, client);
- // Create the IClientAPI
- IClientAPI clientApi = new LLClientView(remoteEndPoint, m_scene, this, client, sessionInfo, agentID, sessionID, circuitCode);
+ // Create the LLClientView
+ LLClientView clientApi = new LLClientView(remoteEndPoint, m_scene, this, client, sessionInfo, agentID, sessionID, circuitCode);
clientApi.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler;
clientApi.OnLogout += LogoutHandler;
clientApi.OnConnectionClosed += RemoveClient;
@@ -618,23 +611,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private void IncomingPacketHandler()
{
- IncomingPacket incomingPacket = new IncomingPacket();
- Packet packet = null;
- LLUDPClient client = null;
+ // Set this culture for the thread that incoming packets are received
+ // on to en-US to avoid number parsing issues
+ Culture.SetCurrentCulture();
+
+ IncomingPacket incomingPacket = default(IncomingPacket);
while (base.IsRunning)
{
- // Reset packet to null for the check below
- packet = null;
-
if (packetInbox.Dequeue(100, ref incomingPacket))
- {
- packet = incomingPacket.Packet;
- client = incomingPacket.Client;
-
- if (packet != null && client != null)
- client.ClientAPI.ProcessInPacket(packet);
- }
+ Util.FireAndForget(ProcessInPacket, incomingPacket);
}
if (packetInbox.Count > 0)
@@ -642,32 +628,98 @@ namespace OpenSim.Region.ClientStack.LindenUDP
packetInbox.Clear();
}
+ private void ProcessInPacket(object state)
+ {
+ IncomingPacket incomingPacket = (IncomingPacket)state;
+ Packet packet = incomingPacket.Packet;
+ LLUDPClient client = incomingPacket.Client;
+
+ if (packet != null && client != null)
+ {
+ try
+ {
+ client.ClientAPI.ProcessInPacket(packet);
+ }
+ catch (ThreadAbortException)
+ {
+ throw;
+ }
+ catch (Exception e)
+ {
+ if (StatsManager.SimExtraStats != null)
+ StatsManager.SimExtraStats.AddAbnormalClientThreadTermination();
+
+ // Don't let a failure in an individual client thread crash the whole sim.
+ m_log.ErrorFormat("[LLUDPSERVER]: Client thread for {0} {1} crashed. Logging them out", client.ClientAPI.Name, client.AgentID);
+ m_log.Error(e.Message, e);
+
+ try
+ {
+ // Make an attempt to alert the user that their session has crashed
+ AgentAlertMessagePacket alert = client.ClientAPI.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);
+
+ SendPacket(client, alert, ThrottleOutPacketType.Unknown, false);
+
+ // TODO: There may be a better way to do this. Perhaps kick? Not sure this propogates notifications to
+ // listeners yet, though.
+ client.ClientAPI.SendLogoutPacket();
+ RemoveClient(client.ClientAPI);
+ }
+ catch (ThreadAbortException)
+ {
+ throw;
+ }
+ catch (Exception e2)
+ {
+ m_log.Error("[LLUDPSERVER]: Further exception thrown on forced session logout for " + client.ClientAPI.Name);
+ m_log.Error(e2.Message, e2);
+ }
+ }
+ }
+ }
+
private void OutgoingPacketHandler()
{
+ // Set this culture for the thread that outgoing packets are sent
+ // on to en-US to avoid number parsing issues
+ Culture.SetCurrentCulture();
+
int now = Environment.TickCount;
int elapsedMS = 0;
int elapsed100MS = 0;
+ int elapsed500MS = 0;
while (base.IsRunning)
{
bool resendUnacked = false;
bool sendAcks = false;
+ bool sendPings = false;
bool packetSent = false;
elapsedMS += Environment.TickCount - now;
- // Check for packets that need to be resent every 100ms
+ // Check for pending outgoing resends every 100ms
if (elapsedMS >= 100)
{
resendUnacked = true;
elapsedMS -= 100;
++elapsed100MS;
}
- // Check for ACKs that need to be sent out every 500ms
+ // Check for pending outgoing ACKs every 500ms
if (elapsed100MS >= 5)
{
sendAcks = true;
elapsed100MS = 0;
+ ++elapsed500MS;
+ }
+ // Send pings to clients every 2000ms
+ if (elapsed500MS >= 4)
+ {
+ sendPings = true;
+ elapsed500MS = 0;
}
clients.ForEach(
@@ -679,6 +731,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
ResendUnacked(client);
if (sendAcks)
SendAcks(client);
+ if (sendPings)
+ SendPing(client);
}
);
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUtil.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUtil.cs
deleted file mode 100644
index 066b19d..0000000
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUtil.cs
+++ /dev/null
@@ -1,56 +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 OpenMetaverse;
-
-using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim;
-
-namespace OpenSim.Region.ClientStack.LindenUDP
-{
- public class LLUtil
- {
- ///
- /// Convert a string to bytes suitable for use in an LL UDP packet.
- ///
- /// Truncated to 254 characters if necessary
- ///
- public static byte[] StringToPacketBytes(string s)
- {
- // Anything more than 254 will cause libsecondlife to barf
- // (libsl 1550) adds an \0 on the Utils.StringToBytes conversion if it isn't present
- if (s.Length > 254)
- {
- s = s.Remove(254);
- }
-
- return Utils.StringToBytes(s);
- }
- }
-}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs b/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs
new file mode 100644
index 0000000..69b0c5f
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs
@@ -0,0 +1,56 @@
+/*
+ * 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 OpenSim.Framework;
+using OpenMetaverse;
+
+namespace OpenSim.Region.ClientStack.LindenUDP
+{
+ public sealed class OutgoingPacket
+ {
+ /// Client this packet is destined for
+ public LLUDPClient Client;
+ /// Packet data to send
+ public UDPPacketBuffer Buffer;
+ /// Sequence number of the wrapped packet
+ public uint SequenceNumber;
+ /// Number of times this packet has been resent
+ public int ResendCount;
+ /// Environment.TickCount when this packet was last sent over the wire
+ public int TickCount;
+ /// Category this packet belongs to
+ public ThrottleOutPacketType Category;
+
+ public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category)
+ {
+ Client = client;
+ Buffer = buffer;
+ Category = category;
+ }
+ }
+}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs
new file mode 100644
index 0000000..ffa20d5
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs
@@ -0,0 +1,76 @@
+/*
+ * 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 Nini.Config;
+
+namespace OpenSim.Region.ClientStack.LindenUDP
+{
+ public sealed class ThrottleRates
+ {
+ public int Resend;
+ public int Land;
+ public int Wind;
+ public int Cloud;
+ public int Task;
+ public int Texture;
+ public int Asset;
+
+ public int ResendLimit;
+ public int LandLimit;
+ public int WindLimit;
+ public int CloudLimit;
+ public int TaskLimit;
+ public int TextureLimit;
+ public int AssetLimit;
+
+ public ThrottleRates(IConfigSource config)
+ {
+ try
+ {
+ 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);
+
+ 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);
+ }
+ catch (Exception) { }
+ }
+ }
+}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
new file mode 100644
index 0000000..16c0035
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
@@ -0,0 +1,114 @@
+/*
+ * 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 OpenMetaverse;
+
+namespace OpenSim.Region.ClientStack.LindenUDP
+{
+ public sealed class UnackedPacketCollection
+ {
+ public object SyncRoot = new object();
+
+ SortedDictionary packets;
+
+ public int Count { get { return packets.Count; } }
+
+ public UnackedPacketCollection()
+ {
+ packets = new SortedDictionary();
+ }
+
+ public bool Add(OutgoingPacket packet)
+ {
+ lock (SyncRoot)
+ {
+ if (!packets.ContainsKey(packet.SequenceNumber))
+ {
+ packets.Add(packet.SequenceNumber, packet);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public bool RemoveUnsafe(uint sequenceNumber)
+ {
+ return packets.Remove(sequenceNumber);
+ }
+
+ public bool RemoveUnsafe(uint sequenceNumber, out OutgoingPacket packet)
+ {
+ if (packets.TryGetValue(sequenceNumber, out packet))
+ {
+ packets.Remove(sequenceNumber);
+ return true;
+ }
+
+ return false;
+ }
+
+ public OutgoingPacket GetOldest()
+ {
+ lock (SyncRoot)
+ {
+ using (SortedDictionary.ValueCollection.Enumerator e = packets.Values.GetEnumerator())
+ return e.Current;
+ }
+ }
+
+ public List GetExpiredPackets(int timeout)
+ {
+ List expiredPackets = null;
+
+ lock (SyncRoot)
+ {
+ int now = Environment.TickCount;
+ foreach (OutgoingPacket packet in packets.Values)
+ {
+ if (packet.TickCount == 0)
+ continue;
+
+ if (now - packet.TickCount >= timeout)
+ {
+ if (expiredPackets == null)
+ expiredPackets = new List();
+ expiredPackets.Add(packet);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ return expiredPackets;
+ }
+ }
+}
--
cgit v1.1