aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorJohn Hurliman2009-10-06 02:38:00 -0700
committerJohn Hurliman2009-10-06 02:38:00 -0700
commite7c877407f2a72a9519eb53debca5aeef20cded9 (patch)
treeed67cb35522f357874f6e2749d66fd48493ede80
parentMerge branch 'master' of ssh://opensimulator.org/var/git/opensim into htb-thr... (diff)
downloadopensim-SC_OLD-e7c877407f2a72a9519eb53debca5aeef20cded9.zip
opensim-SC_OLD-e7c877407f2a72a9519eb53debca5aeef20cded9.tar.gz
opensim-SC_OLD-e7c877407f2a72a9519eb53debca5aeef20cded9.tar.bz2
opensim-SC_OLD-e7c877407f2a72a9519eb53debca5aeef20cded9.tar.xz
* 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
Diffstat (limited to '')
-rw-r--r--OpenSim/Data/MySQL/MySQLAssetData.cs6
-rw-r--r--OpenSim/Framework/Parallel.cs207
-rw-r--r--OpenSim/Framework/ThreadTracker.cs127
-rw-r--r--OpenSim/Framework/Util.cs36
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/IncomingPacket.cs (renamed from OpenSim/Region/ClientStack/LindenUDP/LLQueItem.cs)21
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/IncomingPacketHistoryCollection.cs (renamed from OpenSim/Region/ClientStack/LindenUDP/KillPacket.cs)60
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs33
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs284
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs875
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs742
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLPacketServer.cs206
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs370
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs185
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs138
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs (renamed from OpenSim/Region/ClientStack/LindenUDP/LLUtil.cs)40
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs76
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs114
17 files changed, 1278 insertions, 2242 deletions
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
239 } 239 }
240 catch (Exception e) 240 catch (Exception e)
241 { 241 {
242 m_log.ErrorFormat( 242 m_log.ErrorFormat("[ASSET DB]: MySQL failure creating asset {0} with name \"{1}\". Attempting reconnect. Error: {2}",
243 "[ASSETS DB]: " + 243 asset.FullID, asset.Name, e.Message);
244 "MySql failure creating asset {0} with name {1}" + Environment.NewLine + e.ToString()
245 + Environment.NewLine + "Attempting reconnection", asset.FullID, asset.Name);
246 _dbConnection.Reconnect(); 244 _dbConnection.Reconnect();
247 } 245 }
248 } 246 }
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 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Threading;
31
32namespace OpenSim.Framework
33{
34 /// <summary>
35 /// Provides helper methods for parallelizing loops
36 /// </summary>
37 public static class Parallel
38 {
39 private static readonly int processorCount = System.Environment.ProcessorCount;
40
41 /// <summary>
42 /// Executes a for loop in which iterations may run in parallel
43 /// </summary>
44 /// <param name="fromInclusive">The loop will be started at this index</param>
45 /// <param name="toExclusive">The loop will be terminated before this index is reached</param>
46 /// <param name="body">Method body to run for each iteration of the loop</param>
47 public static void For(int fromInclusive, int toExclusive, Action<int> body)
48 {
49 For(processorCount, fromInclusive, toExclusive, body);
50 }
51
52 /// <summary>
53 /// Executes a for loop in which iterations may run in parallel
54 /// </summary>
55 /// <param name="threadCount">The number of concurrent execution threads to run</param>
56 /// <param name="fromInclusive">The loop will be started at this index</param>
57 /// <param name="toExclusive">The loop will be terminated before this index is reached</param>
58 /// <param name="body">Method body to run for each iteration of the loop</param>
59 public static void For(int threadCount, int fromInclusive, int toExclusive, Action<int> body)
60 {
61 int counter = threadCount;
62 AutoResetEvent threadFinishEvent = new AutoResetEvent(false);
63 Exception exception = null;
64
65 --fromInclusive;
66
67 for (int i = 0; i < threadCount; i++)
68 {
69 ThreadPool.QueueUserWorkItem(
70 delegate(object o)
71 {
72 int threadIndex = (int)o;
73
74 while (exception == null)
75 {
76 int currentIndex = Interlocked.Increment(ref fromInclusive);
77
78 if (currentIndex >= toExclusive)
79 break;
80
81 try { body(currentIndex); }
82 catch (Exception ex) { exception = ex; break; }
83 }
84
85 if (Interlocked.Decrement(ref counter) == 0)
86 threadFinishEvent.Set();
87 }, i
88 );
89 }
90
91 threadFinishEvent.WaitOne();
92
93 if (exception != null)
94 throw new Exception(exception.Message, exception);
95 }
96
97 /// <summary>
98 /// Executes a foreach loop in which iterations may run in parallel
99 /// </summary>
100 /// <typeparam name="T">Object type that the collection wraps</typeparam>
101 /// <param name="enumerable">An enumerable collection to iterate over</param>
102 /// <param name="body">Method body to run for each object in the collection</param>
103 public static void ForEach<T>(IEnumerable<T> enumerable, Action<T> body)
104 {
105 ForEach<T>(processorCount, enumerable, body);
106 }
107
108 /// <summary>
109 /// Executes a foreach loop in which iterations may run in parallel
110 /// </summary>
111 /// <typeparam name="T">Object type that the collection wraps</typeparam>
112 /// <param name="threadCount">The number of concurrent execution threads to run</param>
113 /// <param name="enumerable">An enumerable collection to iterate over</param>
114 /// <param name="body">Method body to run for each object in the collection</param>
115 public static void ForEach<T>(int threadCount, IEnumerable<T> enumerable, Action<T> body)
116 {
117 int counter = threadCount;
118 AutoResetEvent threadFinishEvent = new AutoResetEvent(false);
119 IEnumerator<T> enumerator = enumerable.GetEnumerator();
120 Exception exception = null;
121
122 for (int i = 0; i < threadCount; i++)
123 {
124 ThreadPool.QueueUserWorkItem(
125 delegate(object o)
126 {
127 int threadIndex = (int)o;
128
129 while (exception == null)
130 {
131 T entry;
132
133 lock (enumerator)
134 {
135 if (!enumerator.MoveNext())
136 break;
137 entry = (T)enumerator.Current; // Explicit typecast for Mono's sake
138 }
139
140 try { body(entry); }
141 catch (Exception ex) { exception = ex; break; }
142 }
143
144 if (Interlocked.Decrement(ref counter) == 0)
145 threadFinishEvent.Set();
146 }, i
147 );
148 }
149
150 threadFinishEvent.WaitOne();
151
152 if (exception != null)
153 throw new Exception(exception.Message, exception);
154 }
155
156 /// <summary>
157 /// Executes a series of tasks in parallel
158 /// </summary>
159 /// <param name="actions">A series of method bodies to execute</param>
160 public static void Invoke(params Action[] actions)
161 {
162 Invoke(processorCount, actions);
163 }
164
165 /// <summary>
166 /// Executes a series of tasks in parallel
167 /// </summary>
168 /// <param name="threadCount">The number of concurrent execution threads to run</param>
169 /// <param name="actions">A series of method bodies to execute</param>
170 public static void Invoke(int threadCount, params Action[] actions)
171 {
172 int counter = threadCount;
173 AutoResetEvent threadFinishEvent = new AutoResetEvent(false);
174 int index = -1;
175 Exception exception = null;
176
177 for (int i = 0; i < threadCount; i++)
178 {
179 ThreadPool.QueueUserWorkItem(
180 delegate(object o)
181 {
182 int threadIndex = (int)o;
183
184 while (exception == null)
185 {
186 int currentIndex = Interlocked.Increment(ref index);
187
188 if (currentIndex >= actions.Length)
189 break;
190
191 try { actions[currentIndex](); }
192 catch (Exception ex) { exception = ex; break; }
193 }
194
195 if (Interlocked.Decrement(ref counter) == 0)
196 threadFinishEvent.Set();
197 }, i
198 );
199 }
200
201 threadFinishEvent.WaitOne();
202
203 if (exception != null)
204 throw new Exception(exception.Message, exception);
205 }
206 }
207}
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 @@
26 */ 26 */
27 27
28using System; 28using System;
29using System.Collections;
30using System.Collections.Generic; 29using System.Collections.Generic;
31using System.Reflection; 30using System.Reflection;
32using System.Threading; 31using System.Diagnostics;
33using log4net; 32using log4net;
34 33
35namespace OpenSim.Framework 34namespace OpenSim.Framework
36{ 35{
37 public static class ThreadTracker 36 public static class ThreadTracker
38 { 37 {
39 private static readonly ILog m_log 38 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
40 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
41
42 private static readonly long ThreadTimeout = 30 * 10000000;
43 public static List<ThreadTrackerItem> m_Threads;
44 public static Thread ThreadTrackerThread;
45 39
46 static ThreadTracker() 40 public static ProcessThreadCollection GetThreads()
47 { 41 {
48#if DEBUG 42 Process thisProc = Process.GetCurrentProcess();
49 m_Threads = new List<ThreadTrackerItem>(); 43 return thisProc.Threads;
50 ThreadTrackerThread = new Thread(ThreadTrackerThreadLoop);
51 ThreadTrackerThread.Name = "ThreadTrackerThread";
52 ThreadTrackerThread.IsBackground = true;
53 ThreadTrackerThread.Priority = ThreadPriority.BelowNormal;
54 ThreadTrackerThread.Start();
55 Add(ThreadTrackerThread);
56#endif
57 } 44 }
58
59 private static void ThreadTrackerThreadLoop()
60 {
61 try
62 {
63 while (true)
64 {
65 Thread.Sleep(5000);
66 CleanUp();
67 }
68 }
69 catch (Exception e)
70 {
71 m_log.ErrorFormat(
72 "[THREAD TRACKER]: Thread tracker cleanup thread terminating with exception. Please report this error. Exception is {0}",
73 e);
74 }
75 }
76
77 public static void Add(Thread thread)
78 {
79#if DEBUG
80 if (thread != null)
81 {
82 lock (m_Threads)
83 {
84 ThreadTrackerItem tti = new ThreadTrackerItem();
85 tti.Thread = thread;
86 tti.LastSeenActive = DateTime.Now.Ticks;
87 m_Threads.Add(tti);
88 }
89 }
90#endif
91 }
92
93 public static void Remove(Thread thread)
94 {
95#if DEBUG
96 lock (m_Threads)
97 {
98 foreach (ThreadTrackerItem tti in new ArrayList(m_Threads))
99 {
100 if (tti.Thread == thread)
101 m_Threads.Remove(tti);
102 }
103 }
104#endif
105 }
106
107 public static void CleanUp()
108 {
109 lock (m_Threads)
110 {
111 foreach (ThreadTrackerItem tti in new ArrayList(m_Threads))
112 {
113 try
114 {
115
116
117 if (tti.Thread.IsAlive)
118 {
119 // Its active
120 tti.LastSeenActive = DateTime.Now.Ticks;
121 }
122 else
123 {
124 // Its not active -- if its expired then remove it
125 if (tti.LastSeenActive + ThreadTimeout < DateTime.Now.Ticks)
126 m_Threads.Remove(tti);
127 }
128 }
129 catch (NullReferenceException)
130 {
131 m_Threads.Remove(tti);
132 }
133 }
134 }
135 }
136
137 public static List<Thread> GetThreads()
138 {
139 if (m_Threads == null)
140 return null;
141
142 List<Thread> threads = new List<Thread>();
143 lock (m_Threads)
144 {
145 foreach (ThreadTrackerItem tti in new ArrayList(m_Threads))
146 {
147 threads.Add(tti.Thread);
148 }
149 }
150 return threads;
151 }
152
153 #region Nested type: ThreadTrackerItem
154
155 public class ThreadTrackerItem
156 {
157 public long LastSeenActive;
158 public Thread Thread;
159 }
160
161 #endregion
162 } 45 }
163} 46}
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
1231 return (ipaddr1 != null) ? "http://" + ipaddr1.ToString() + ":" + port1 : uri; 1231 return (ipaddr1 != null) ? "http://" + ipaddr1.ToString() + ":" + port1 : uri;
1232 } 1232 }
1233 1233
1234 public static byte[] StringToBytes256(string str)
1235 {
1236 if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; }
1237 if (str.Length > 254) str = str.Remove(254);
1238 if (!str.EndsWith("\0")) { str += "\0"; }
1239
1240 // Because this is UTF-8 encoding and not ASCII, it's possible we
1241 // might have gotten an oversized array even after the string trim
1242 byte[] data = UTF8.GetBytes(str);
1243 if (data.Length > 256)
1244 {
1245 Array.Resize<byte>(ref data, 256);
1246 data[255] = 0;
1247 }
1248
1249 return data;
1250 }
1251
1252 public static byte[] StringToBytes1024(string str)
1253 {
1254 if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; }
1255 if (str.Length > 1023) str = str.Remove(1023);
1256 if (!str.EndsWith("\0")) { str += "\0"; }
1257
1258 // Because this is UTF-8 encoding and not ASCII, it's possible we
1259 // might have gotten an oversized array even after the string trim
1260 byte[] data = UTF8.GetBytes(str);
1261 if (data.Length > 1024)
1262 {
1263 Array.Resize<byte>(ref data, 1024);
1264 data[1023] = 0;
1265 }
1266
1267 return data;
1268 }
1269
1234 #region FireAndForget Threading Pattern 1270 #region FireAndForget Threading Pattern
1235 1271
1236 public static void FireAndForget(System.Threading.WaitCallback callback) 1272 public static void FireAndForget(System.Threading.WaitCallback callback)
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLQueItem.cs b/OpenSim/Region/ClientStack/LindenUDP/IncomingPacket.cs
index 0ed2bc1..dc0d62a 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLQueItem.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/IncomingPacket.cs
@@ -1,4 +1,4 @@
1/* 1/*
2 * Copyright (c) Contributors, http://opensimulator.org/ 2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders. 3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 * 4 *
@@ -26,24 +26,17 @@
26 */ 26 */
27 27
28using System; 28using System;
29using OpenMetaverse.Packets;
30using OpenSim.Framework; 29using OpenSim.Framework;
30using OpenMetaverse;
31using OpenMetaverse.Packets;
31 32
32namespace OpenSim.Region.ClientStack.LindenUDP 33namespace OpenSim.Region.ClientStack.LindenUDP
33{ 34{
34 public class LLQueItem 35 public struct IncomingPacket
35 { 36 {
36 public LLQueItem() 37 /// <summary>Client this packet came from</summary>
37 { 38 public LLUDPClient Client;
38 } 39 /// <summary>Packet data that has been received</summary>
39
40 public Packet Packet; 40 public Packet Packet;
41 public bool Incoming;
42 public ThrottleOutPacketType throttleType;
43 public int TickCount;
44 public Object Identifier;
45 public int Resends;
46 public int Length;
47 public uint Sequence;
48 } 41 }
49} 42}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/KillPacket.cs b/OpenSim/Region/ClientStack/LindenUDP/IncomingPacketHistoryCollection.cs
index a80c1f0..1f73a1d 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/KillPacket.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/IncomingPacketHistoryCollection.cs
@@ -1,4 +1,4 @@
1/* 1/*
2 * Copyright (c) Contributors, http://opensimulator.org/ 2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders. 3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 * 4 *
@@ -25,47 +25,49 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using OpenMetaverse.Packets; 28using System;
29using System.Collections.Generic;
29 30
30namespace OpenSim.Region.ClientStack.LindenUDP 31namespace OpenSim.Region.ClientStack.LindenUDP
31{ 32{
32 /// <summary> 33 /// <summary>
33 /// When packetqueue dequeues this packet in the outgoing stream, it thread aborts 34 /// A circular buffer and hashset for tracking incoming packet sequence
34 /// Ensures that the thread abort happens from within the client thread 35 /// numbers
35 /// regardless of where the close method is called
36 /// </summary> 36 /// </summary>
37 class KillPacket : Packet 37 public sealed class IncomingPacketHistoryCollection
38 { 38 {
39 public override int Length 39 private readonly uint[] m_items;
40 { 40 private HashSet<uint> m_hashSet;
41 get { return 0; } 41 private int m_first;
42 } 42 private int m_next;
43 private int m_capacity;
43 44
44 public override void FromBytes(Header header, byte[] bytes, ref int i, ref int packetEnd) 45 public IncomingPacketHistoryCollection(int capacity)
45 { 46 {
47 this.m_capacity = capacity;
48 m_items = new uint[capacity];
49 m_hashSet = new HashSet<uint>();
46 } 50 }
47 51
48 public override void FromBytes(byte[] bytes, ref int i, ref int packetEnd, byte[] zeroBuffer) 52 public bool TryEnqueue(uint ack)
49 { 53 {
50 } 54 lock (m_hashSet)
55 {
56 if (m_hashSet.Add(ack))
57 {
58 m_items[m_next] = ack;
59 m_next = (m_next + 1) % m_capacity;
60 if (m_next == m_first)
61 {
62 m_hashSet.Remove(m_items[m_first]);
63 m_first = (m_first + 1) % m_capacity;
64 }
51 65
52 public override byte[] ToBytes() 66 return true;
53 { 67 }
54 return new byte[0]; 68 }
55 }
56 69
57 public override byte[][] ToBytesMultiple() 70 return false;
58 {
59 return new byte[][] { new byte[0] };
60 }
61
62 public KillPacket()
63 {
64 Type = PacketType.UseCircuitCode;
65 Header = new Header();
66 Header.Frequency = OpenMetaverse.PacketFrequency.Low;
67 Header.ID = 65531;
68 Header.Reliable = true;
69 } 71 }
70 } 72 }
71} 73}
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
76 { 76 {
77 if (m_currentPacket <= m_stopPacket) 77 if (m_currentPacket <= m_stopPacket)
78 { 78 {
79 bool SendMore = true; 79 int count = 0;
80 bool sendMore = true;
81
80 if (!m_sentInfo || (m_currentPacket == 0)) 82 if (!m_sentInfo || (m_currentPacket == 0))
81 { 83 {
82 if (SendFirstPacket(client)) 84 sendMore = !SendFirstPacket(client);
83 { 85
84 SendMore = false;
85 }
86 m_sentInfo = true; 86 m_sentInfo = true;
87 m_currentPacket++; 87 ++m_currentPacket;
88 ++count;
88 } 89 }
89 if (m_currentPacket < 2) 90 if (m_currentPacket < 2)
90 { 91 {
91 m_currentPacket = 2; 92 m_currentPacket = 2;
92 } 93 }
93 94
94 int count = 0; 95 while (sendMore && count < maxpack && m_currentPacket <= m_stopPacket)
95 while (SendMore && count < maxpack && m_currentPacket <= m_stopPacket)
96 { 96 {
97 count++; 97 sendMore = SendPacket(client);
98 SendMore = SendPacket(client); 98 ++m_currentPacket;
99 m_currentPacket++; 99 ++count;
100 } 100 }
101 101
102 if (m_currentPacket > m_stopPacket) 102 if (m_currentPacket > m_stopPacket)
@@ -196,15 +196,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
196 196
197 m_currentPacket = StartPacket; 197 m_currentPacket = StartPacket;
198 } 198 }
199
200 if (m_imageManager != null && m_imageManager.Client != null)
201 {
202 if (m_imageManager.Client.IsThrottleEmpty(ThrottleOutPacketType.Texture))
203 {
204 //m_log.Debug("No textures queued, sending one packet to kickstart it");
205 SendPacket(m_imageManager.Client);
206 }
207 }
208 } 199 }
209 } 200 }
210 } 201 }
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
70 private readonly LLUDPServer m_udpServer; 70 private readonly LLUDPServer m_udpServer;
71 private readonly LLUDPClient m_udpClient; 71 private readonly LLUDPClient m_udpClient;
72 private readonly UUID m_sessionId; 72 private readonly UUID m_sessionId;
73 private readonly UUID m_secureSessionId = UUID.Zero; 73 private readonly UUID m_secureSessionId;
74 private readonly UUID m_agentId; 74 private readonly UUID m_agentId;
75 private readonly uint m_circuitCode; 75 private readonly uint m_circuitCode;
76 private readonly byte[] m_channelVersion = Utils.StringToBytes("OpenSimulator Server"); // Dummy value needed by libSL 76 private readonly byte[] m_channelVersion = Utils.EmptyBytes;
77 private readonly Dictionary<string, UUID> m_defaultAnimations = new Dictionary<string, UUID>(); 77 private readonly Dictionary<string, UUID> m_defaultAnimations = new Dictionary<string, UUID>();
78 private readonly IGroupsModule m_GroupsModule; 78 private readonly IGroupsModule m_GroupsModule;
79 79
80 private int m_debugPacketLevel;
81 private int m_cachedTextureSerial; 80 private int m_cachedTextureSerial;
82 private Timer m_clientPingTimer;
83 private Timer m_avatarTerseUpdateTimer; 81 private Timer m_avatarTerseUpdateTimer;
84 private List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> m_avatarTerseUpdates = new List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>(); 82 private List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> m_avatarTerseUpdates = new List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>();
85 private Timer m_primTerseUpdateTimer; 83 private Timer m_primTerseUpdateTimer;
86 private List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> m_primTerseUpdates = new List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>(); 84 private List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> m_primTerseUpdates = new List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>();
87 private Timer m_primFullUpdateTimer; 85 private Timer m_primFullUpdateTimer;
88 private List<ObjectUpdatePacket.ObjectDataBlock> m_primFullUpdates = new List<ObjectUpdatePacket.ObjectDataBlock>(); 86 private List<ObjectUpdatePacket.ObjectDataBlock> m_primFullUpdates = new List<ObjectUpdatePacket.ObjectDataBlock>();
89 private bool m_clientBlocked;
90 private int m_probesWithNoIngressPackets;
91 private int m_moneyBalance; 87 private int m_moneyBalance;
92 private int m_animationSequenceNumber = 1; 88 private int m_animationSequenceNumber = 1;
93 private bool m_SendLogoutPacketWhenClosing = true; 89 private bool m_SendLogoutPacketWhenClosing = true;
94 private int m_inPacketsChecked;
95 private AgentUpdateArgs lastarg; 90 private AgentUpdateArgs lastarg;
96 private bool m_IsActive = true; 91 private bool m_IsActive = true;
97 92
@@ -170,58 +165,72 @@ namespace OpenSim.Region.ClientStack.LindenUDP
170 RegisterInterface<IClientIM>(this); 165 RegisterInterface<IClientIM>(this);
171 RegisterInterface<IClientChat>(this); 166 RegisterInterface<IClientChat>(this);
172 RegisterInterface<IClientIPEndpoint>(this); 167 RegisterInterface<IClientIPEndpoint>(this);
173 m_GroupsModule = scene.RequestModuleInterface<IGroupsModule>(); 168
174
175 m_moneyBalance = 1000;
176
177 m_channelVersion = Utils.StringToBytes(scene.GetSimulatorVersion());
178
179 InitDefaultAnimations(); 169 InitDefaultAnimations();
180 170
181 m_scene = scene; 171 m_scene = scene;
182 //m_assetCache = assetCache;
183
184 m_assetService = m_scene.RequestModuleInterface<IAssetService>(); 172 m_assetService = m_scene.RequestModuleInterface<IAssetService>();
185 173 m_GroupsModule = scene.RequestModuleInterface<IGroupsModule>();
186 m_udpServer = udpServer; 174 m_imageManager = new LLImageManager(this, m_assetService, Scene.RequestModuleInterface<IJ2KDecoder>());
187 m_udpClient = udpClient; 175 m_channelVersion = Utils.StringToBytes(scene.GetSimulatorVersion());
188
189 m_agentId = agentId; 176 m_agentId = agentId;
190 m_sessionId = sessionId; 177 m_sessionId = sessionId;
178 m_secureSessionId = sessionInfo.LoginInfo.SecureSession;
191 m_circuitCode = circuitCode; 179 m_circuitCode = circuitCode;
192
193 m_userEndPoint = remoteEP; 180 m_userEndPoint = remoteEP;
194
195 m_firstName = sessionInfo.LoginInfo.First; 181 m_firstName = sessionInfo.LoginInfo.First;
196 m_lastName = sessionInfo.LoginInfo.Last; 182 m_lastName = sessionInfo.LoginInfo.Last;
197 m_startpos = sessionInfo.LoginInfo.StartPos; 183 m_startpos = sessionInfo.LoginInfo.StartPos;
184 m_moneyBalance = 1000;
198 185
199 if (sessionInfo.LoginInfo.SecureSession != UUID.Zero) 186 m_udpServer = udpServer;
200 { 187 m_udpClient = udpClient;
201 m_secureSessionId = sessionInfo.LoginInfo.SecureSession; 188 m_udpClient.OnQueueEmpty += HandleQueueEmpty;
202 } 189 // FIXME: Implement this
203 190 //m_udpClient.OnPacketStats += PopulateStats;
204 // While working on this, the BlockingQueue had me fooled for a bit.
205 // The Blocking queue causes the thread to stop until there's something
206 // in it to process. It's an on-purpose threadlock though because
207 // without it, the clientloop will suck up all sim resources.
208
209 //m_PacketHandler = new LLPacketHandler(this, m_networkServer, userSettings);
210 //m_PacketHandler.SynchronizeClient = SynchronizeClient;
211 //m_PacketHandler.OnPacketStats += PopulateStats;
212 //m_PacketHandler.OnQueueEmpty += HandleQueueEmpty;
213 191
214 RegisterLocalPacketHandlers(); 192 RegisterLocalPacketHandlers();
215 m_imageManager = new LLImageManager(this, m_assetService, Scene.RequestModuleInterface<IJ2KDecoder>());
216 } 193 }
217 194
218 public void SetDebugPacketLevel(int newDebugPacketLevel) 195 public void SetDebugPacketLevel(int newDebug)
219 { 196 {
220 m_debugPacketLevel = newDebugPacketLevel;
221 } 197 }
222 198
223 #region Client Methods 199 #region Client Methods
224 200
201 /// <summary>
202 /// Close down the client view. This *must* be the last method called, since the last #
203 /// statement of CloseCleanup() aborts the thread.
204 /// </summary>
205 /// <param name="shutdownCircuit"></param>
206 public void Close(bool shutdownCircuit)
207 {
208 m_log.DebugFormat(
209 "[CLIENT]: Close has been called with shutdownCircuit = {0} for {1} attached to scene {2}",
210 shutdownCircuit, Name, m_scene.RegionInfo.RegionName);
211
212 if (m_imageManager != null)
213 m_imageManager.Close();
214
215 if (m_udpServer != null)
216 m_udpServer.Flush();
217
218 // raise an event on the packet server to Shutdown the circuit
219 // Now, if we raise the event then the packet server will call this method itself, so don't try cleanup
220 // here otherwise we'll end up calling it twice.
221 // FIXME: In truth, I might be wrong but this whole business of calling this method twice (with different args) looks
222 // horribly tangly. Hopefully it should be possible to greatly simplify it.
223 if (shutdownCircuit)
224 {
225 if (OnConnectionClosed != null)
226 OnConnectionClosed(this);
227 }
228 else
229 {
230 CloseCleanup(shutdownCircuit);
231 }
232 }
233
225 private void CloseCleanup(bool shutdownCircuit) 234 private void CloseCleanup(bool shutdownCircuit)
226 { 235 {
227 m_scene.RemoveClient(AgentId); 236 m_scene.RemoveClient(AgentId);
@@ -236,9 +245,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
236 Thread.Sleep(2000); 245 Thread.Sleep(2000);
237 246
238 // Shut down timers. Thread Context of this method is murky. Lock all timers 247 // Shut down timers. Thread Context of this method is murky. Lock all timers
239 if (m_clientPingTimer.Enabled)
240 lock (m_clientPingTimer)
241 m_clientPingTimer.Stop();
242 if (m_avatarTerseUpdateTimer.Enabled) 248 if (m_avatarTerseUpdateTimer.Enabled)
243 lock (m_avatarTerseUpdateTimer) 249 lock (m_avatarTerseUpdateTimer)
244 m_avatarTerseUpdateTimer.Stop(); 250 m_avatarTerseUpdateTimer.Stop();
@@ -271,43 +277,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
271 // of the client thread regardless of where Close() is called. 277 // of the client thread regardless of where Close() is called.
272 KillEndDone(); 278 KillEndDone();
273 } 279 }
274
275 Terminate();
276 }
277 280
278 /// <summary> 281 IsActive = false;
279 /// Close down the client view. This *must* be the last method called, since the last #
280 /// statement of CloseCleanup() aborts the thread.
281 /// </summary>
282 /// <param name="shutdownCircuit"></param>
283 public void Close(bool shutdownCircuit)
284 {
285 m_clientPingTimer.Enabled = false;
286 282
287 m_log.DebugFormat( 283 m_avatarTerseUpdateTimer.Close();
288 "[CLIENT]: Close has been called with shutdownCircuit = {0} for {1} attached to scene {2}", 284 m_primTerseUpdateTimer.Close();
289 shutdownCircuit, Name, m_scene.RegionInfo.RegionName); 285 m_primFullUpdateTimer.Close();
290 286
291 if (m_imageManager != null) 287 //m_udpServer.OnPacketStats -= PopulateStats;
292 m_imageManager.Close(); 288 m_udpClient.Shutdown();
293 289
294 if (m_udpServer != null) 290 // wait for thread stoped
295 m_udpServer.Flush(); 291 // m_clientThread.Join();
296 292
297 // raise an event on the packet server to Shutdown the circuit 293 // delete circuit code
298 // Now, if we raise the event then the packet server will call this method itself, so don't try cleanup 294 //m_networkServer.CloseClient(this);
299 // here otherwise we'll end up calling it twice.
300 // FIXME: In truth, I might be wrong but this whole business of calling this method twice (with different args) looks
301 // horribly tangly. Hopefully it should be possible to greatly simplify it.
302 if (shutdownCircuit)
303 {
304 if (OnConnectionClosed != null)
305 OnConnectionClosed(this);
306 }
307 else
308 {
309 CloseCleanup(shutdownCircuit);
310 }
311 } 295 }
312 296
313 public void Kick(string message) 297 public void Kick(string message)
@@ -329,10 +313,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
329 public void Stop() 313 public void Stop()
330 { 314 {
331 // Shut down timers. Thread Context is Murky, lock all timers! 315 // Shut down timers. Thread Context is Murky, lock all timers!
332 if (m_clientPingTimer.Enabled)
333 lock (m_clientPingTimer)
334 m_clientPingTimer.Stop();
335
336 if (m_avatarTerseUpdateTimer.Enabled) 316 if (m_avatarTerseUpdateTimer.Enabled)
337 lock (m_avatarTerseUpdateTimer) 317 lock (m_avatarTerseUpdateTimer)
338 m_avatarTerseUpdateTimer.Stop(); 318 m_avatarTerseUpdateTimer.Stop();
@@ -346,25 +326,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
346 m_primFullUpdateTimer.Stop(); 326 m_primFullUpdateTimer.Stop();
347 } 327 }
348 328
349 private void Terminate()
350 {
351 IsActive = false;
352
353 m_clientPingTimer.Close();
354 m_avatarTerseUpdateTimer.Close();
355 m_primTerseUpdateTimer.Close();
356 m_primFullUpdateTimer.Close();
357
358 //m_udpServer.OnPacketStats -= PopulateStats;
359 m_udpClient.Shutdown();
360
361 // wait for thread stoped
362 // m_clientThread.Join();
363
364 // delete circuit code
365 //m_networkServer.CloseClient(this);
366 }
367
368 #endregion Client Methods 329 #endregion Client Methods
369 330
370 #region Packet Handling 331 #region Packet Handling
@@ -452,7 +413,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
452 return result; 413 return result;
453 } 414 }
454 415
455 protected void DebugPacket(string direction, Packet packet) 416 /*protected void DebugPacket(string direction, Packet packet)
456 { 417 {
457 string info; 418 string info;
458 419
@@ -478,7 +439,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
478 } 439 }
479 440
480 Console.WriteLine(m_circuitCode + ":" + direction + ": " + info); 441 Console.WriteLine(m_circuitCode + ":" + direction + ": " + info);
481 } 442 }*/
482 443
483 #endregion Packet Handling 444 #endregion Packet Handling
484 445
@@ -490,8 +451,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
490 /// </summary> 451 /// </summary>
491 protected virtual void InitNewClient() 452 protected virtual void InitNewClient()
492 { 453 {
493 //this.UploadAssets = new AgentAssetUpload(this, m_assetCache, m_inventoryCache);
494
495 m_avatarTerseUpdateTimer = new Timer(m_avatarTerseUpdateRate); 454 m_avatarTerseUpdateTimer = new Timer(m_avatarTerseUpdateRate);
496 m_avatarTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates); 455 m_avatarTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates);
497 m_avatarTerseUpdateTimer.AutoReset = false; 456 m_avatarTerseUpdateTimer.AutoReset = false;
@@ -511,11 +470,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
511 470
512 public virtual void Start() 471 public virtual void Start()
513 { 472 {
514 m_clientThread = new Thread(RunUserSession); 473 // This sets up all the timers
515 m_clientThread.Name = "ClientThread"; 474 InitNewClient();
516 m_clientThread.IsBackground = true;
517 m_clientThread.Start();
518 ThreadTracker.Add(m_clientThread);
519 } 475 }
520 476
521 /// <summary> 477 /// <summary>
@@ -523,14 +479,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
523 /// </summary> 479 /// </summary>
524 protected void RunUserSession() 480 protected void RunUserSession()
525 { 481 {
526 //tell this thread we are using the culture set up for the sim (currently hardcoded to en_US)
527 //otherwise it will override this and use the system default
528 Culture.SetCurrentCulture();
529
530 try 482 try
531 { 483 {
532 // This sets up all the timers 484
533 InitNewClient();
534 } 485 }
535 catch (Exception e) 486 catch (Exception e)
536 { 487 {
@@ -1373,8 +1324,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1373 public void SendStartPingCheck(byte seq) 1324 public void SendStartPingCheck(byte seq)
1374 { 1325 {
1375 StartPingCheckPacket pc = (StartPingCheckPacket)PacketPool.Instance.GetPacket(PacketType.StartPingCheck); 1326 StartPingCheckPacket pc = (StartPingCheckPacket)PacketPool.Instance.GetPacket(PacketType.StartPingCheck);
1376 pc.PingID.PingID = seq;
1377 pc.Header.Reliable = false; 1327 pc.Header.Reliable = false;
1328
1329 OutgoingPacket oldestPacket = m_udpClient.NeedAcks.GetOldest();
1330
1331 pc.PingID.PingID = seq;
1332 pc.PingID.OldestUnacked = (oldestPacket != null) ? oldestPacket.SequenceNumber : 0;
1333
1378 OutPacket(pc, ThrottleOutPacketType.Unknown); 1334 OutPacket(pc, ThrottleOutPacketType.Unknown);
1379 } 1335 }
1380 1336
@@ -1450,12 +1406,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1450 descend.ItemData[i].AssetID = item.AssetID; 1406 descend.ItemData[i].AssetID = item.AssetID;
1451 descend.ItemData[i].CreatorID = item.CreatorIdAsUuid; 1407 descend.ItemData[i].CreatorID = item.CreatorIdAsUuid;
1452 descend.ItemData[i].BaseMask = item.BasePermissions; 1408 descend.ItemData[i].BaseMask = item.BasePermissions;
1453 descend.ItemData[i].Description = LLUtil.StringToPacketBytes(item.Description); 1409 descend.ItemData[i].Description = Util.StringToBytes256(item.Description);
1454 descend.ItemData[i].EveryoneMask = item.EveryOnePermissions; 1410 descend.ItemData[i].EveryoneMask = item.EveryOnePermissions;
1455 descend.ItemData[i].OwnerMask = item.CurrentPermissions; 1411 descend.ItemData[i].OwnerMask = item.CurrentPermissions;
1456 descend.ItemData[i].FolderID = item.Folder; 1412 descend.ItemData[i].FolderID = item.Folder;
1457 descend.ItemData[i].InvType = (sbyte)item.InvType; 1413 descend.ItemData[i].InvType = (sbyte)item.InvType;
1458 descend.ItemData[i].Name = LLUtil.StringToPacketBytes(item.Name); 1414 descend.ItemData[i].Name = Util.StringToBytes256(item.Name);
1459 descend.ItemData[i].NextOwnerMask = item.NextPermissions; 1415 descend.ItemData[i].NextOwnerMask = item.NextPermissions;
1460 descend.ItemData[i].OwnerID = item.Owner; 1416 descend.ItemData[i].OwnerID = item.Owner;
1461 descend.ItemData[i].Type = (sbyte)item.AssetType; 1417 descend.ItemData[i].Type = (sbyte)item.AssetType;
@@ -1536,7 +1492,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1536 { 1492 {
1537 descend.FolderData[i] = new InventoryDescendentsPacket.FolderDataBlock(); 1493 descend.FolderData[i] = new InventoryDescendentsPacket.FolderDataBlock();
1538 descend.FolderData[i].FolderID = folder.ID; 1494 descend.FolderData[i].FolderID = folder.ID;
1539 descend.FolderData[i].Name = LLUtil.StringToPacketBytes(folder.Name); 1495 descend.FolderData[i].Name = Util.StringToBytes256(folder.Name);
1540 descend.FolderData[i].ParentID = folder.ParentID; 1496 descend.FolderData[i].ParentID = folder.ParentID;
1541 descend.FolderData[i].Type = (sbyte)folder.Type; 1497 descend.FolderData[i].Type = (sbyte)folder.Type;
1542 1498
@@ -1651,11 +1607,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1651 inventoryReply.InventoryData[0].BaseMask = item.BasePermissions; 1607 inventoryReply.InventoryData[0].BaseMask = item.BasePermissions;
1652 inventoryReply.InventoryData[0].CreationDate = item.CreationDate; 1608 inventoryReply.InventoryData[0].CreationDate = item.CreationDate;
1653 1609
1654 inventoryReply.InventoryData[0].Description = LLUtil.StringToPacketBytes(item.Description); 1610 inventoryReply.InventoryData[0].Description = Util.StringToBytes256(item.Description);
1655 inventoryReply.InventoryData[0].EveryoneMask = item.EveryOnePermissions; 1611 inventoryReply.InventoryData[0].EveryoneMask = item.EveryOnePermissions;
1656 inventoryReply.InventoryData[0].FolderID = item.Folder; 1612 inventoryReply.InventoryData[0].FolderID = item.Folder;
1657 inventoryReply.InventoryData[0].InvType = (sbyte)item.InvType; 1613 inventoryReply.InventoryData[0].InvType = (sbyte)item.InvType;
1658 inventoryReply.InventoryData[0].Name = LLUtil.StringToPacketBytes(item.Name); 1614 inventoryReply.InventoryData[0].Name = Util.StringToBytes256(item.Name);
1659 inventoryReply.InventoryData[0].NextOwnerMask = item.NextPermissions; 1615 inventoryReply.InventoryData[0].NextOwnerMask = item.NextPermissions;
1660 inventoryReply.InventoryData[0].OwnerID = item.Owner; 1616 inventoryReply.InventoryData[0].OwnerID = item.Owner;
1661 inventoryReply.InventoryData[0].OwnerMask = item.CurrentPermissions; 1617 inventoryReply.InventoryData[0].OwnerMask = item.CurrentPermissions;
@@ -1780,7 +1736,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1780 folderBlock.FolderID = folder.ID; 1736 folderBlock.FolderID = folder.ID;
1781 folderBlock.ParentID = folder.ParentID; 1737 folderBlock.ParentID = folder.ParentID;
1782 folderBlock.Type = -1; 1738 folderBlock.Type = -1;
1783 folderBlock.Name = LLUtil.StringToPacketBytes(folder.Name); 1739 folderBlock.Name = Util.StringToBytes256(folder.Name);
1784 1740
1785 return folderBlock; 1741 return folderBlock;
1786 } 1742 }
@@ -1798,11 +1754,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1798 itemBlock.AssetID = item.AssetID; 1754 itemBlock.AssetID = item.AssetID;
1799 itemBlock.CreatorID = item.CreatorIdAsUuid; 1755 itemBlock.CreatorID = item.CreatorIdAsUuid;
1800 itemBlock.BaseMask = item.BasePermissions; 1756 itemBlock.BaseMask = item.BasePermissions;
1801 itemBlock.Description = LLUtil.StringToPacketBytes(item.Description); 1757 itemBlock.Description = Util.StringToBytes256(item.Description);
1802 itemBlock.EveryoneMask = item.EveryOnePermissions; 1758 itemBlock.EveryoneMask = item.EveryOnePermissions;
1803 itemBlock.FolderID = item.Folder; 1759 itemBlock.FolderID = item.Folder;
1804 itemBlock.InvType = (sbyte)item.InvType; 1760 itemBlock.InvType = (sbyte)item.InvType;
1805 itemBlock.Name = LLUtil.StringToPacketBytes(item.Name); 1761 itemBlock.Name = Util.StringToBytes256(item.Name);
1806 itemBlock.NextOwnerMask = item.NextPermissions; 1762 itemBlock.NextOwnerMask = item.NextPermissions;
1807 itemBlock.OwnerID = item.Owner; 1763 itemBlock.OwnerID = item.Owner;
1808 itemBlock.OwnerMask = item.CurrentPermissions; 1764 itemBlock.OwnerMask = item.CurrentPermissions;
@@ -1862,11 +1818,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1862 bulkUpdate.ItemData[0].CreatorID = item.CreatorIdAsUuid; 1818 bulkUpdate.ItemData[0].CreatorID = item.CreatorIdAsUuid;
1863 bulkUpdate.ItemData[0].BaseMask = item.BasePermissions; 1819 bulkUpdate.ItemData[0].BaseMask = item.BasePermissions;
1864 bulkUpdate.ItemData[0].CreationDate = item.CreationDate; 1820 bulkUpdate.ItemData[0].CreationDate = item.CreationDate;
1865 bulkUpdate.ItemData[0].Description = LLUtil.StringToPacketBytes(item.Description); 1821 bulkUpdate.ItemData[0].Description = Util.StringToBytes256(item.Description);
1866 bulkUpdate.ItemData[0].EveryoneMask = item.EveryOnePermissions; 1822 bulkUpdate.ItemData[0].EveryoneMask = item.EveryOnePermissions;
1867 bulkUpdate.ItemData[0].FolderID = item.Folder; 1823 bulkUpdate.ItemData[0].FolderID = item.Folder;
1868 bulkUpdate.ItemData[0].InvType = (sbyte)item.InvType; 1824 bulkUpdate.ItemData[0].InvType = (sbyte)item.InvType;
1869 bulkUpdate.ItemData[0].Name = LLUtil.StringToPacketBytes(item.Name); 1825 bulkUpdate.ItemData[0].Name = Util.StringToBytes256(item.Name);
1870 bulkUpdate.ItemData[0].NextOwnerMask = item.NextPermissions; 1826 bulkUpdate.ItemData[0].NextOwnerMask = item.NextPermissions;
1871 bulkUpdate.ItemData[0].OwnerID = item.Owner; 1827 bulkUpdate.ItemData[0].OwnerID = item.Owner;
1872 bulkUpdate.ItemData[0].OwnerMask = item.CurrentPermissions; 1828 bulkUpdate.ItemData[0].OwnerMask = item.CurrentPermissions;
@@ -1909,11 +1865,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1909 InventoryReply.InventoryData[0].AssetID = Item.AssetID; 1865 InventoryReply.InventoryData[0].AssetID = Item.AssetID;
1910 InventoryReply.InventoryData[0].CreatorID = Item.CreatorIdAsUuid; 1866 InventoryReply.InventoryData[0].CreatorID = Item.CreatorIdAsUuid;
1911 InventoryReply.InventoryData[0].BaseMask = Item.BasePermissions; 1867 InventoryReply.InventoryData[0].BaseMask = Item.BasePermissions;
1912 InventoryReply.InventoryData[0].Description = LLUtil.StringToPacketBytes(Item.Description); 1868 InventoryReply.InventoryData[0].Description = Util.StringToBytes256(Item.Description);
1913 InventoryReply.InventoryData[0].EveryoneMask = Item.EveryOnePermissions; 1869 InventoryReply.InventoryData[0].EveryoneMask = Item.EveryOnePermissions;
1914 InventoryReply.InventoryData[0].FolderID = Item.Folder; 1870 InventoryReply.InventoryData[0].FolderID = Item.Folder;
1915 InventoryReply.InventoryData[0].InvType = (sbyte)Item.InvType; 1871 InventoryReply.InventoryData[0].InvType = (sbyte)Item.InvType;
1916 InventoryReply.InventoryData[0].Name = LLUtil.StringToPacketBytes(Item.Name); 1872 InventoryReply.InventoryData[0].Name = Util.StringToBytes256(Item.Name);
1917 InventoryReply.InventoryData[0].NextOwnerMask = Item.NextPermissions; 1873 InventoryReply.InventoryData[0].NextOwnerMask = Item.NextPermissions;
1918 InventoryReply.InventoryData[0].OwnerID = Item.Owner; 1874 InventoryReply.InventoryData[0].OwnerID = Item.Owner;
1919 InventoryReply.InventoryData[0].OwnerMask = Item.CurrentPermissions; 1875 InventoryReply.InventoryData[0].OwnerMask = Item.CurrentPermissions;
@@ -2080,7 +2036,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2080 /// <param name="message"></param> 2036 /// <param name="message"></param>
2081 /// <param name="modal"></param> 2037 /// <param name="modal"></param>
2082 /// <returns></returns> 2038 /// <returns></returns>
2083 protected AgentAlertMessagePacket BuildAgentAlertPacket(string message, bool modal) 2039 public AgentAlertMessagePacket BuildAgentAlertPacket(string message, bool modal)
2084 { 2040 {
2085 AgentAlertMessagePacket alertPack = (AgentAlertMessagePacket)PacketPool.Instance.GetPacket(PacketType.AgentAlertMessage); 2041 AgentAlertMessagePacket alertPack = (AgentAlertMessagePacket)PacketPool.Instance.GetPacket(PacketType.AgentAlertMessage);
2086 alertPack.AgentData.AgentID = AgentId; 2042 alertPack.AgentData.AgentID = AgentId;
@@ -3533,7 +3489,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3533 objectData.FullID = objectID; 3489 objectData.FullID = objectID;
3534 objectData.OwnerID = ownerID; 3490 objectData.OwnerID = ownerID;
3535 3491
3536 objectData.Text = LLUtil.StringToPacketBytes(text); 3492 objectData.Text = Util.StringToBytes256(text);
3537 objectData.TextColor[0] = color[0]; 3493 objectData.TextColor[0] = color[0];
3538 objectData.TextColor[1] = color[1]; 3494 objectData.TextColor[1] = color[1];
3539 objectData.TextColor[2] = color[2]; 3495 objectData.TextColor[2] = color[2];
@@ -3911,8 +3867,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3911 objPropDB.SalePrice = SalePrice; 3867 objPropDB.SalePrice = SalePrice;
3912 objPropDB.Category = Category; 3868 objPropDB.Category = Category;
3913 objPropDB.LastOwnerID = LastOwnerID; 3869 objPropDB.LastOwnerID = LastOwnerID;
3914 objPropDB.Name = LLUtil.StringToPacketBytes(ObjectName); 3870 objPropDB.Name = Util.StringToBytes256(ObjectName);
3915 objPropDB.Description = LLUtil.StringToPacketBytes(Description); 3871 objPropDB.Description = Util.StringToBytes256(Description);
3916 objPropFamilyPack.ObjectData = objPropDB; 3872 objPropFamilyPack.ObjectData = objPropDB;
3917 objPropFamilyPack.Header.Zerocoded = true; 3873 objPropFamilyPack.Header.Zerocoded = true;
3918 OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task); 3874 OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task);
@@ -3946,11 +3902,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3946 proper.ObjectData[0].OwnerID = UUID.Zero; 3902 proper.ObjectData[0].OwnerID = UUID.Zero;
3947 else 3903 else
3948 proper.ObjectData[0].OwnerID = OwnerUUID; 3904 proper.ObjectData[0].OwnerID = OwnerUUID;
3949 proper.ObjectData[0].TouchName = LLUtil.StringToPacketBytes(TouchTitle); 3905 proper.ObjectData[0].TouchName = Util.StringToBytes256(TouchTitle);
3950 proper.ObjectData[0].TextureID = TextureID; 3906 proper.ObjectData[0].TextureID = TextureID;
3951 proper.ObjectData[0].SitName = LLUtil.StringToPacketBytes(SitTitle); 3907 proper.ObjectData[0].SitName = Util.StringToBytes256(SitTitle);
3952 proper.ObjectData[0].Name = LLUtil.StringToPacketBytes(ItemName); 3908 proper.ObjectData[0].Name = Util.StringToBytes256(ItemName);
3953 proper.ObjectData[0].Description = LLUtil.StringToPacketBytes(ItemDescription); 3909 proper.ObjectData[0].Description = Util.StringToBytes256(ItemDescription);
3954 proper.ObjectData[0].OwnerMask = OwnerMask; 3910 proper.ObjectData[0].OwnerMask = OwnerMask;
3955 proper.ObjectData[0].NextOwnerMask = NextOwnerMask; 3911 proper.ObjectData[0].NextOwnerMask = NextOwnerMask;
3956 proper.ObjectData[0].GroupMask = GroupMask; 3912 proper.ObjectData[0].GroupMask = GroupMask;
@@ -4191,11 +4147,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4191 4147
4192 updatePacket.ParcelData.MediaAutoScale = landData.MediaAutoScale; 4148 updatePacket.ParcelData.MediaAutoScale = landData.MediaAutoScale;
4193 updatePacket.ParcelData.MediaID = landData.MediaID; 4149 updatePacket.ParcelData.MediaID = landData.MediaID;
4194 updatePacket.ParcelData.MediaURL = LLUtil.StringToPacketBytes(landData.MediaURL); 4150 updatePacket.ParcelData.MediaURL = Util.StringToBytes256(landData.MediaURL);
4195 updatePacket.ParcelData.MusicURL = LLUtil.StringToPacketBytes(landData.MusicURL); 4151 updatePacket.ParcelData.MusicURL = Util.StringToBytes256(landData.MusicURL);
4196 updatePacket.ParcelData.Name = Utils.StringToBytes(landData.Name); 4152 updatePacket.ParcelData.Name = Util.StringToBytes256(landData.Name);
4197 updatePacket.ParcelData.OtherCleanTime = landData.OtherCleanTime; 4153 updatePacket.ParcelData.OtherCleanTime = landData.OtherCleanTime;
4198 updatePacket.ParcelData.OtherCount = 0; //unemplemented 4154 updatePacket.ParcelData.OtherCount = 0; //TODO: Unimplemented
4199 updatePacket.ParcelData.OtherPrims = landData.OtherPrims; 4155 updatePacket.ParcelData.OtherPrims = landData.OtherPrims;
4200 updatePacket.ParcelData.OwnerID = landData.OwnerID; 4156 updatePacket.ParcelData.OwnerID = landData.OwnerID;
4201 updatePacket.ParcelData.OwnerPrims = landData.OwnerPrims; 4157 updatePacket.ParcelData.OwnerPrims = landData.OwnerPrims;
@@ -4203,22 +4159,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4203 updatePacket.ParcelData.ParcelPrimBonus = simObjectBonusFactor; 4159 updatePacket.ParcelData.ParcelPrimBonus = simObjectBonusFactor;
4204 updatePacket.ParcelData.PassHours = landData.PassHours; 4160 updatePacket.ParcelData.PassHours = landData.PassHours;
4205 updatePacket.ParcelData.PassPrice = landData.PassPrice; 4161 updatePacket.ParcelData.PassPrice = landData.PassPrice;
4206 updatePacket.ParcelData.PublicCount = 0; //unemplemented 4162 updatePacket.ParcelData.PublicCount = 0; //TODO: Unimplemented
4207 4163
4208 updatePacket.ParcelData.RegionDenyAnonymous = ((regionFlags & (uint)RegionFlags.DenyAnonymous) > 4164 updatePacket.ParcelData.RegionDenyAnonymous = (regionFlags & (uint)RegionFlags.DenyAnonymous) > 0;
4209 0); 4165 updatePacket.ParcelData.RegionDenyIdentified = (regionFlags & (uint)RegionFlags.DenyIdentified) > 0;
4210 updatePacket.ParcelData.RegionDenyIdentified = ((regionFlags & (uint)RegionFlags.DenyIdentified) > 4166 updatePacket.ParcelData.RegionDenyTransacted = (regionFlags & (uint)RegionFlags.DenyTransacted) > 0;
4211 0); 4167 updatePacket.ParcelData.RegionPushOverride = (regionFlags & (uint)RegionFlags.RestrictPushObject) > 0;
4212 updatePacket.ParcelData.RegionDenyTransacted = ((regionFlags & (uint)RegionFlags.DenyTransacted) >
4213 0);
4214 updatePacket.ParcelData.RegionPushOverride = ((regionFlags & (uint)RegionFlags.RestrictPushObject) >
4215 0);
4216 4168
4217 updatePacket.ParcelData.RentPrice = 0; 4169 updatePacket.ParcelData.RentPrice = 0;
4218 updatePacket.ParcelData.RequestResult = request_result; 4170 updatePacket.ParcelData.RequestResult = request_result;
4219 updatePacket.ParcelData.SalePrice = landData.SalePrice; 4171 updatePacket.ParcelData.SalePrice = landData.SalePrice;
4220 updatePacket.ParcelData.SelectedPrims = landData.SelectedPrims; 4172 updatePacket.ParcelData.SelectedPrims = landData.SelectedPrims;
4221 updatePacket.ParcelData.SelfCount = 0; //unemplemented 4173 updatePacket.ParcelData.SelfCount = 0; //TODO: Unimplemented
4222 updatePacket.ParcelData.SequenceID = sequence_id; 4174 updatePacket.ParcelData.SequenceID = sequence_id;
4223 if (landData.SimwideArea > 0) 4175 if (landData.SimwideArea > 0)
4224 { 4176 {
@@ -5265,18 +5217,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
5265 m_udpClient.SetThrottles(throttles); 5217 m_udpClient.SetThrottles(throttles);
5266 } 5218 }
5267 5219
5220 /// <summary>
5221 /// Get the current throttles for this client as a packed byte array
5222 /// </summary>
5223 /// <param name="multiplier">Unused</param>
5224 /// <returns></returns>
5268 public byte[] GetThrottlesPacked(float multiplier) 5225 public byte[] GetThrottlesPacked(float multiplier)
5269 { 5226 {
5270 return m_udpClient.GetThrottlesPacked(); 5227 return m_udpClient.GetThrottlesPacked();
5271 } 5228 }
5272 5229
5273 public bool IsThrottleEmpty(ThrottleOutPacketType category)
5274 {
5275 return m_udpClient.IsThrottleEmpty(category);
5276 }
5277
5278 /// <summary> 5230 /// <summary>
5279 /// Unused 5231 /// Cruft?
5280 /// </summary> 5232 /// </summary>
5281 public virtual void InPacket(object NewPack) 5233 public virtual void InPacket(object NewPack)
5282 { 5234 {
@@ -6231,14 +6183,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
6231 break; 6183 break;
6232 6184
6233 case PacketType.AgentPause: 6185 case PacketType.AgentPause:
6234 m_probesWithNoIngressPackets = 0; 6186 m_udpClient.IsPaused = true;
6235 m_clientBlocked = true;
6236 break; 6187 break;
6237 6188
6238 case PacketType.AgentResume: 6189 case PacketType.AgentResume:
6239 m_probesWithNoIngressPackets = 0; 6190 m_udpClient.IsPaused = false;
6240 m_clientBlocked = false; 6191 SendStartPingCheck(m_udpClient.CurrentPingSequence++);
6241 SendStartPingCheck(0);
6242 6192
6243 break; 6193 break;
6244 6194
@@ -8904,14 +8854,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
8904 #region unimplemented handlers 8854 #region unimplemented handlers
8905 8855
8906 case PacketType.StartPingCheck: 8856 case PacketType.StartPingCheck:
8907 // Send the client the ping response back 8857 StartPingCheckPacket pingStart = (StartPingCheckPacket)Pack;
8908 // Pass the same PingID in the matching packet 8858 CompletePingCheckPacket pingComplete = new CompletePingCheckPacket();
8909 // Handled In the packet processing 8859 pingComplete.PingID.PingID = pingStart.PingID.PingID;
8910 //m_log.Debug("[CLIENT]: possibly unhandled StartPingCheck packet"); 8860 m_udpServer.SendPacket(m_udpClient, pingComplete, ThrottleOutPacketType.Unknown, false);
8911 break; 8861 break;
8862
8912 case PacketType.CompletePingCheck: 8863 case PacketType.CompletePingCheck:
8913 // TODO: Perhaps this should be processed on the Sim to determine whether or not to drop a dead client 8864 // TODO: Do stats tracking or something with these?
8914 //m_log.Warn("[CLIENT]: unhandled CompletePingCheck packet");
8915 break; 8865 break;
8916 8866
8917 case PacketType.ViewerStats: 8867 case PacketType.ViewerStats:
@@ -10209,14 +10159,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
10209 return info; 10159 return info;
10210 } 10160 }
10211 10161
10212 public EndPoint GetClientEP() 10162 public void SetClientInfo(ClientInfo info)
10213 { 10163 {
10214 return m_userEndPoint; 10164 m_udpClient.SetClientInfo(info);
10215 } 10165 }
10216 10166
10217 public void SetClientInfo(ClientInfo info) 10167 public EndPoint GetClientEP()
10218 { 10168 {
10219 m_udpClient.SetClientInfo(info); 10169 return m_userEndPoint;
10220 } 10170 }
10221 10171
10222 #region Media Parcel Members 10172 #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 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Reflection;
30using System.Collections.Generic;
31using System.Net.Sockets;
32using System.Threading;
33using System.Timers;
34using OpenMetaverse;
35using OpenMetaverse.Packets;
36using log4net;
37using OpenSim.Framework;
38using Timer=System.Timers.Timer;
39
40namespace OpenSim.Region.ClientStack.LindenUDP
41{
42 public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes);
43 public delegate void PacketDrop(Packet pack, Object id);
44 public delegate void QueueEmpty(ThrottleOutPacketType queue);
45 public delegate bool SynchronizeClientHandler(IScene scene, Packet packet, UUID agentID, ThrottleOutPacketType throttlePacketType);
46
47 public class LLPacketHandler
48 {
49 private static readonly ILog m_log
50 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51
52 //private int m_resentCount;
53
54 // Packet queues
55 //
56 LLPacketQueue m_PacketQueue;
57
58 public LLPacketQueue PacketQueue
59 {
60 get { return m_PacketQueue; }
61 }
62
63 // Timer to run stats and acks on
64 //
65 private Timer m_AckTimer = new Timer(250);
66
67 // A list of the packets we haven't acked yet
68 //
69 private List<uint> m_PendingAcks = new List<uint>();
70 private Dictionary<uint, uint> m_PendingAcksMap = new Dictionary<uint, uint>();
71
72 private Dictionary<uint, LLQueItem> m_NeedAck =
73 new Dictionary<uint, LLQueItem>();
74
75 /// <summary>
76 /// The number of milliseconds that can pass before a packet that needs an ack is resent.
77 /// </param>
78 private uint m_ResendTimeout = 4000;
79
80 public uint ResendTimeout
81 {
82 get { return m_ResendTimeout; }
83 set { m_ResendTimeout = value; }
84 }
85
86 private int m_MaxReliableResends = 3;
87
88 public int MaxReliableResends
89 {
90 get { return m_MaxReliableResends; }
91 set { m_MaxReliableResends = value; }
92 }
93
94 // Track duplicated packets. This uses a Dictionary. Both insertion
95 // and lookup are common operations and need to take advantage of
96 // the hashing. Expiration is less common and can be allowed the
97 // time for a linear scan.
98 //
99 private List<uint> m_alreadySeenList = new List<uint>();
100 private Dictionary<uint, int>m_alreadySeenTracker = new Dictionary<uint, int>();
101 private int m_alreadySeenWindow = 30000;
102 private int m_lastAlreadySeenCheck = Environment.TickCount & Int32.MaxValue;
103
104 // private Dictionary<uint, int> m_DupeTracker =
105 // new Dictionary<uint, int>();
106 // private uint m_DupeTrackerWindow = 30;
107 // private int m_DupeTrackerLastCheck = Environment.TickCount;
108
109 // Values for the SimStatsReporter
110 //
111 private int m_PacketsReceived = 0;
112 private int m_PacketsReceivedReported = 0;
113 private int m_PacketsSent = 0;
114 private int m_PacketsSentReported = 0;
115 private int m_UnackedBytes = 0;
116
117 private int m_LastResend = 0;
118
119 public int PacketsReceived
120 {
121 get { return m_PacketsReceived; }
122 }
123
124 public int PacketsReceivedReported
125 {
126 get { return m_PacketsReceivedReported; }
127 }
128
129 // The client we are working for
130 //
131 private IClientAPI m_Client;
132
133 // Some events
134 //
135 public event PacketStats OnPacketStats;
136 public event PacketDrop OnPacketDrop;
137 public event QueueEmpty OnQueueEmpty;
138
139
140 //private SynchronizeClientHandler m_SynchronizeClient = null;
141
142 public SynchronizeClientHandler SynchronizeClient
143 {
144 set { /* m_SynchronizeClient = value; */ }
145 }
146
147 // Packet sequencing
148 //
149 private uint m_Sequence = 0;
150 private object m_SequenceLock = new object();
151 private const int MAX_SEQUENCE = 0xFFFFFF;
152
153 // Packet dropping
154 //
155 List<PacketType> m_ImportantPackets = new List<PacketType>();
156 private bool m_ReliableIsImportant = false;
157
158 public bool ReliableIsImportant
159 {
160 get { return m_ReliableIsImportant; }
161 set { m_ReliableIsImportant = value; }
162 }
163
164 private int m_DropSafeTimeout;
165
166 LLPacketServer m_PacketServer;
167 private byte[] m_ZeroOutBuffer = new byte[4096];
168
169 ////////////////////////////////////////////////////////////////////
170
171 // Constructors
172 //
173 public LLPacketHandler(IClientAPI client, LLPacketServer server, ClientStackUserSettings userSettings)
174 {
175 m_Client = client;
176 m_PacketServer = server;
177 m_DropSafeTimeout = Environment.TickCount + 15000;
178
179 m_PacketQueue = new LLPacketQueue(client.AgentId, userSettings);
180
181 m_PacketQueue.OnQueueEmpty += TriggerOnQueueEmpty;
182
183 m_AckTimer.Elapsed += AckTimerElapsed;
184 m_AckTimer.Start();
185 }
186
187 public void Dispose()
188 {
189 m_AckTimer.Stop();
190 m_AckTimer.Close();
191
192 m_PacketQueue.Enqueue(null);
193 m_PacketQueue.Close();
194 m_Client = null;
195 }
196
197 // Send one packet. This actually doesn't send anything, it queues
198 // it. Designed to be fire-and-forget, but there is an optional
199 // notifier.
200 //
201 public void OutPacket(
202 Packet packet, ThrottleOutPacketType throttlePacketType)
203 {
204 OutPacket(packet, throttlePacketType, null);
205 }
206
207 public void OutPacket(
208 Packet packet, ThrottleOutPacketType throttlePacketType,
209 Object id)
210 {
211 // Call the load balancer's hook. If this is not active here
212 // we defer to the sim server this client is actually connected
213 // to. Packet drop notifies will not be triggered in this
214 // configuration!
215 //
216
217 packet.Header.Sequence = 0;
218
219 lock (m_NeedAck)
220 {
221 DropResend(id);
222
223 AddAcks(ref packet);
224 QueuePacket(packet, throttlePacketType, id);
225 }
226 }
227
228 private void AddAcks(ref Packet packet)
229 {
230 // These packet types have shown to have issues with
231 // acks being appended to the payload, just don't send
232 // any with them until libsl is fixed.
233 //
234 if (packet is ViewerEffectPacket)
235 return;
236 if (packet is SimStatsPacket)
237 return;
238
239 // Add acks to outgoing packets
240 //
241 if (m_PendingAcks.Count > 0)
242 {
243 int count = m_PendingAcks.Count;
244 if (count > 10)
245 count = 10;
246 packet.Header.AckList = new uint[count];
247 packet.Header.AppendedAcks = true;
248
249 for (int i = 0; i < count; i++)
250 {
251 packet.Header.AckList[i] = m_PendingAcks[i];
252 m_PendingAcksMap.Remove(m_PendingAcks[i]);
253 }
254 m_PendingAcks.RemoveRange(0, count);
255 }
256 }
257
258 private void QueuePacket(
259 Packet packet, ThrottleOutPacketType throttlePacketType,
260 Object id)
261 {
262 LLQueItem item = new LLQueItem();
263 item.Packet = packet;
264 item.Incoming = false;
265 item.throttleType = throttlePacketType;
266 item.TickCount = Environment.TickCount;
267 item.Identifier = id;
268 item.Resends = 0;
269 item.Length = packet.Length;
270 item.Sequence = packet.Header.Sequence;
271
272 m_PacketQueue.Enqueue(item);
273 m_PacketsSent++;
274 }
275
276 private void ResendUnacked()
277 {
278 int now = Environment.TickCount;
279
280 int intervalMs = 250;
281
282 if (m_LastResend != 0)
283 intervalMs = now - m_LastResend;
284
285 lock (m_NeedAck)
286 {
287 if (m_DropSafeTimeout > now ||
288 intervalMs > 500) // We were frozen!
289 {
290 foreach (LLQueItem data in m_NeedAck.Values)
291 {
292 if (m_DropSafeTimeout > now)
293 {
294 m_NeedAck[data.Packet.Header.Sequence].TickCount = now;
295 }
296 else
297 {
298 m_NeedAck[data.Packet.Header.Sequence].TickCount += intervalMs;
299 }
300 }
301 }
302
303 m_LastResend = now;
304
305 // Unless we have received at least one ack, don't bother resending
306 // anything. There may not be a client there, don't clog up the
307 // pipes.
308
309
310 // Nothing to do
311 //
312 if (m_NeedAck.Count == 0)
313 return;
314
315 int resent = 0;
316 long dueDate = now - m_ResendTimeout;
317
318 List<LLQueItem> dropped = new List<LLQueItem>();
319 foreach (LLQueItem data in m_NeedAck.Values)
320 {
321 Packet packet = data.Packet;
322
323 // Packets this old get resent
324 //
325 if (data.TickCount < dueDate && data.Sequence != 0 && !m_PacketQueue.Contains(data.Sequence))
326 {
327 if (resent < 20) // Was 20 (= Max 117kbit/sec resends)
328 {
329 m_NeedAck[packet.Header.Sequence].Resends++;
330
331 // The client needs to be told that a packet is being resent, otherwise it appears to believe
332 // that it should reset its sequence to that packet number.
333 packet.Header.Resent = true;
334
335 if ((m_NeedAck[packet.Header.Sequence].Resends >= m_MaxReliableResends) &&
336 (!m_ReliableIsImportant))
337 {
338 dropped.Add(data);
339 continue;
340 }
341
342 m_NeedAck[packet.Header.Sequence].TickCount = Environment.TickCount;
343 QueuePacket(packet, ThrottleOutPacketType.Resend, data.Identifier);
344 resent++;
345 }
346 else
347 {
348 m_NeedAck[packet.Header.Sequence].TickCount += intervalMs;
349 }
350 }
351 }
352
353 foreach (LLQueItem data in dropped)
354 {
355 m_NeedAck.Remove(data.Packet.Header.Sequence);
356 TriggerOnPacketDrop(data.Packet, data.Identifier);
357 m_PacketQueue.Cancel(data.Packet.Header.Sequence);
358 PacketPool.Instance.ReturnPacket(data.Packet);
359 }
360 }
361 }
362
363 // Send the pending packet acks to the client
364 // Will send blocks of acks for up to 250 packets
365 //
366 private void SendAcks()
367 {
368 lock (m_NeedAck)
369 {
370 if (m_PendingAcks.Count == 0)
371 return;
372
373 PacketAckPacket acks = (PacketAckPacket)PacketPool.Instance.GetPacket(PacketType.PacketAck);
374
375 // The case of equality is more common than one might think,
376 // because this function will be called unconditionally when
377 // the counter reaches 250. So there is a good chance another
378 // packet with 250 blocks exists.
379 //
380 if (acks.Packets == null ||
381 acks.Packets.Length != m_PendingAcks.Count)
382 acks.Packets = new PacketAckPacket.PacketsBlock[m_PendingAcks.Count];
383
384 for (int i = 0; i < m_PendingAcks.Count; i++)
385 {
386 acks.Packets[i] = new PacketAckPacket.PacketsBlock();
387 acks.Packets[i].ID = m_PendingAcks[i];
388
389 }
390 m_PendingAcks.Clear();
391 m_PendingAcksMap.Clear();
392
393 acks.Header.Reliable = false;
394 OutPacket(acks, ThrottleOutPacketType.Unknown);
395 }
396 }
397
398 // Queue a packet ack. It will be sent either after 250 acks are
399 // queued, or when the timer fires.
400 //
401 private void AckPacket(Packet packet)
402 {
403 lock (m_NeedAck)
404 {
405 if (m_PendingAcks.Count < 250)
406 {
407 if (!m_PendingAcksMap.ContainsKey(packet.Header.Sequence))
408 {
409 m_PendingAcks.Add(packet.Header.Sequence);
410 m_PendingAcksMap.Add(packet.Header.Sequence,
411 packet.Header.Sequence);
412 }
413 return;
414 }
415 }
416
417 SendAcks();
418
419 lock (m_NeedAck)
420 {
421 // If this is still full we have a truly exceptional
422 // condition (means, can't happen)
423 //
424 if (m_PendingAcks.Count < 250)
425 {
426 if (!m_PendingAcksMap.ContainsKey(packet.Header.Sequence))
427 {
428 m_PendingAcks.Add(packet.Header.Sequence);
429 m_PendingAcksMap.Add(packet.Header.Sequence,
430 packet.Header.Sequence);
431 }
432 return;
433 }
434 }
435 }
436
437 // When the timer elapses, send the pending acks, trigger resends
438 // and report all the stats.
439 //
440 private void AckTimerElapsed(object sender, ElapsedEventArgs ea)
441 {
442 SendAcks();
443 ResendUnacked();
444 SendPacketStats();
445 }
446
447 // Push out pachet counts for the sim status reporter
448 //
449 private void SendPacketStats()
450 {
451 PacketStats handlerPacketStats = OnPacketStats;
452 if (handlerPacketStats != null)
453 {
454 handlerPacketStats(
455 m_PacketsReceived - m_PacketsReceivedReported,
456 m_PacketsSent - m_PacketsSentReported,
457 m_UnackedBytes);
458
459 m_PacketsReceivedReported = m_PacketsReceived;
460 m_PacketsSentReported = m_PacketsSent;
461 }
462 }
463
464 // We can't keep an unlimited record of dupes. This will prune the
465 // dictionary by age.
466 //
467 // NOTE: this needs to be called from within lock
468 // (m_alreadySeenTracker) context!
469 private void ExpireSeenPackets()
470 {
471 if (m_alreadySeenList.Count < 1024)
472 return;
473
474 int ticks = 0;
475 int tc = Environment.TickCount & Int32.MaxValue;
476 if (tc >= m_lastAlreadySeenCheck)
477 ticks = tc - m_lastAlreadySeenCheck;
478 else
479 ticks = Int32.MaxValue - m_lastAlreadySeenCheck + tc;
480
481 if (ticks < 2000) return;
482 m_lastAlreadySeenCheck = tc;
483
484 // we calculate the drop dead tick count here instead of
485 // in the loop: any packet with a timestamp before
486 // dropDeadTC can be expired
487 int dropDeadTC = tc - m_alreadySeenWindow;
488 int i = 0;
489 while (i < m_alreadySeenList.Count && m_alreadySeenTracker[m_alreadySeenList[i]] < dropDeadTC)
490 {
491 m_alreadySeenTracker.Remove(m_alreadySeenList[i]);
492 i++;
493 }
494 // if we dropped packet from m_alreadySeenTracker we need
495 // to drop them from m_alreadySeenList as well, let's do
496 // that in one go: the list is ordered after all.
497 if (i > 0)
498 {
499 m_alreadySeenList.RemoveRange(0, i);
500 // m_log.DebugFormat("[CLIENT]: expired {0} packets, {1}:{2} left", i, m_alreadySeenList.Count, m_alreadySeenTracker.Count);
501 }
502 }
503
504 public void InPacket(Packet packet)
505 {
506 if (packet == null)
507 return;
508
509 // When too many acks are needed to be sent, the client sends
510 // a packet consisting of acks only
511 //
512 if (packet.Type == PacketType.PacketAck)
513 {
514 PacketAckPacket ackPacket = (PacketAckPacket)packet;
515
516 foreach (PacketAckPacket.PacketsBlock block in ackPacket.Packets)
517 {
518 ProcessAck(block.ID);
519 }
520
521 PacketPool.Instance.ReturnPacket(packet);
522 return;
523 }
524
525 // Any packet can have some packet acks in the header.
526 // Process them here
527 //
528 if (packet.Header.AppendedAcks)
529 {
530 foreach (uint id in packet.Header.AckList)
531 {
532 ProcessAck(id);
533 }
534 }
535
536 // If this client is on another partial instance, no need
537 // to handle packets
538 //
539 if (!m_Client.IsActive && packet.Type != PacketType.LogoutRequest)
540 {
541 PacketPool.Instance.ReturnPacket(packet);
542 return;
543 }
544
545 if (packet.Type == PacketType.StartPingCheck)
546 {
547 StartPingCheckPacket startPing = (StartPingCheckPacket)packet;
548 CompletePingCheckPacket endPing
549 = (CompletePingCheckPacket)PacketPool.Instance.GetPacket(PacketType.CompletePingCheck);
550
551 endPing.PingID.PingID = startPing.PingID.PingID;
552 OutPacket(endPing, ThrottleOutPacketType.Task);
553 }
554 else
555 {
556 LLQueItem item = new LLQueItem();
557 item.Packet = packet;
558 item.Incoming = true;
559 m_PacketQueue.Enqueue(item);
560 }
561 }
562
563 public void ProcessInPacket(LLQueItem item)
564 {
565 Packet packet = item.Packet;
566
567 // Always ack the packet!
568 //
569 if (packet.Header.Reliable)
570 AckPacket(packet);
571
572 if (packet.Type != PacketType.AgentUpdate)
573 m_PacketsReceived++;
574
575 // Check for duplicate packets.. packets that the client is
576 // resending because it didn't receive our ack
577 //
578 lock (m_alreadySeenTracker)
579 {
580 ExpireSeenPackets();
581
582 if (m_alreadySeenTracker.ContainsKey(packet.Header.Sequence))
583 return;
584
585 m_alreadySeenTracker.Add(packet.Header.Sequence, Environment.TickCount & Int32.MaxValue);
586 m_alreadySeenList.Add(packet.Header.Sequence);
587 }
588
589 m_Client.ProcessInPacket(packet);
590 }
591
592 public void Flush()
593 {
594 m_PacketQueue.Flush();
595 m_UnackedBytes = (-1 * m_UnackedBytes);
596 SendPacketStats();
597 }
598
599 public void Clear()
600 {
601 m_UnackedBytes = (-1 * m_UnackedBytes);
602 SendPacketStats();
603 lock (m_NeedAck)
604 {
605 m_NeedAck.Clear();
606 m_PendingAcks.Clear();
607 m_PendingAcksMap.Clear();
608 }
609 m_Sequence += 1000000;
610 }
611
612 private void ProcessAck(uint id)
613 {
614 LLQueItem data;
615
616 lock (m_NeedAck)
617 {
618 //m_log.DebugFormat("[CLIENT]: In {0} received ack for packet {1}", m_Client.Scene.RegionInfo.ExternalEndPoint.Port, id);
619
620 if (!m_NeedAck.TryGetValue(id, out data))
621 return;
622
623 m_NeedAck.Remove(id);
624 m_PacketQueue.Cancel(data.Sequence);
625 PacketPool.Instance.ReturnPacket(data.Packet);
626 m_UnackedBytes -= data.Length;
627 }
628 }
629
630 // Allocate packet sequence numbers in a threadsave manner
631 //
632 protected uint NextPacketSequenceNumber()
633 {
634 // Set the sequence number
635 uint seq = 1;
636 lock (m_SequenceLock)
637 {
638 if (m_Sequence >= MAX_SEQUENCE)
639 {
640 m_Sequence = 1;
641 }
642 else
643 {
644 m_Sequence++;
645 }
646 seq = m_Sequence;
647 }
648 return seq;
649 }
650
651 public ClientInfo GetClientInfo()
652 {
653 ClientInfo info = new ClientInfo();
654
655 info.pendingAcks = m_PendingAcksMap;
656 info.needAck = new Dictionary<uint, byte[]>();
657
658 lock (m_NeedAck)
659 {
660 foreach (uint key in m_NeedAck.Keys)
661 info.needAck.Add(key, m_NeedAck[key].Packet.ToBytes());
662 }
663
664 LLQueItem[] queitems = m_PacketQueue.GetQueueArray();
665
666 for (int i = 0; i < queitems.Length; i++)
667 {
668 if (queitems[i].Incoming == false)
669 info.out_packets.Add(queitems[i].Packet.ToBytes());
670 }
671
672 info.sequence = m_Sequence;
673
674 float multiplier = m_PacketQueue.ThrottleMultiplier;
675 info.resendThrottle = (int) (m_PacketQueue.ResendThrottle.Throttle / multiplier);
676 info.landThrottle = (int) (m_PacketQueue.LandThrottle.Throttle / multiplier);
677 info.windThrottle = (int) (m_PacketQueue.WindThrottle.Throttle / multiplier);
678 info.cloudThrottle = (int) (m_PacketQueue.CloudThrottle.Throttle / multiplier);
679 info.taskThrottle = (int) (m_PacketQueue.TaskThrottle.Throttle / multiplier);
680 info.assetThrottle = (int) (m_PacketQueue.AssetThrottle.Throttle / multiplier);
681 info.textureThrottle = (int) (m_PacketQueue.TextureThrottle.Throttle / multiplier);
682 info.totalThrottle = (int) (m_PacketQueue.TotalThrottle.Throttle / multiplier);
683
684 return info;
685 }
686
687 public void SetClientInfo(ClientInfo info)
688 {
689 m_PendingAcksMap = info.pendingAcks;
690 m_PendingAcks = new List<uint>(m_PendingAcksMap.Keys);
691 m_NeedAck = new Dictionary<uint, LLQueItem>();
692
693 Packet packet = null;
694 int packetEnd = 0;
695 byte[] zero = new byte[3000];
696
697 foreach (uint key in info.needAck.Keys)
698 {
699 byte[] buff = info.needAck[key];
700 packetEnd = buff.Length - 1;
701
702 try
703 {
704 packet = PacketPool.Instance.GetPacket(buff, ref packetEnd, zero);
705 }
706 catch (Exception)
707 {
708 }
709
710 LLQueItem item = new LLQueItem();
711 item.Packet = packet;
712 item.Incoming = false;
713 item.throttleType = 0;
714 item.TickCount = Environment.TickCount;
715 item.Identifier = 0;
716 item.Resends = 0;
717 item.Length = packet.Length;
718 item.Sequence = packet.Header.Sequence;
719 m_NeedAck.Add(key, item);
720 }
721
722 m_Sequence = info.sequence;
723
724 m_PacketQueue.ResendThrottle.Throttle = info.resendThrottle;
725 m_PacketQueue.LandThrottle.Throttle = info.landThrottle;
726 m_PacketQueue.WindThrottle.Throttle = info.windThrottle;
727 m_PacketQueue.CloudThrottle.Throttle = info.cloudThrottle;
728 m_PacketQueue.TaskThrottle.Throttle = info.taskThrottle;
729 m_PacketQueue.AssetThrottle.Throttle = info.assetThrottle;
730 m_PacketQueue.TextureThrottle.Throttle = info.textureThrottle;
731 m_PacketQueue.TotalThrottle.Throttle = info.totalThrottle;
732 }
733
734 public void AddImportantPacket(PacketType type)
735 {
736 if (m_ImportantPackets.Contains(type))
737 return;
738
739 m_ImportantPackets.Add(type);
740 }
741
742 public void RemoveImportantPacket(PacketType type)
743 {
744 if (!m_ImportantPackets.Contains(type))
745 return;
746
747 m_ImportantPackets.Remove(type);
748 }
749
750 private void DropResend(Object id)
751 {
752 LLQueItem d = null;
753
754 foreach (LLQueItem data in m_NeedAck.Values)
755 {
756 if (data.Identifier != null && data.Identifier == id)
757 {
758 d = data;
759 break;
760 }
761 }
762
763 if (null == d) return;
764
765 m_NeedAck.Remove(d.Packet.Header.Sequence);
766 m_PacketQueue.Cancel(d.Sequence);
767 PacketPool.Instance.ReturnPacket(d.Packet);
768 }
769
770 private void TriggerOnPacketDrop(Packet packet, Object id)
771 {
772 PacketDrop handlerPacketDrop = OnPacketDrop;
773
774 if (handlerPacketDrop == null)
775 return;
776
777 handlerPacketDrop(packet, id);
778 }
779
780 private void TriggerOnQueueEmpty(ThrottleOutPacketType queue)
781 {
782 QueueEmpty handlerQueueEmpty = OnQueueEmpty;
783
784 if (handlerQueueEmpty != null)
785 handlerQueueEmpty(queue);
786 }
787
788 // Convert the packet to bytes and stuff it onto the send queue
789 //
790 public void ProcessOutPacket(LLQueItem item)
791 {
792 Packet packet = item.Packet;
793
794 // Assign sequence number here to prevent out of order packets
795 if (packet.Header.Sequence == 0)
796 {
797 lock (m_NeedAck)
798 {
799 packet.Header.Sequence = NextPacketSequenceNumber();
800 item.Sequence = packet.Header.Sequence;
801 item.TickCount = Environment.TickCount;
802
803 // We want to see that packet arrive if it's reliable
804 if (packet.Header.Reliable)
805 {
806 m_UnackedBytes += item.Length;
807
808 // Keep track of when this packet was sent out
809 item.TickCount = Environment.TickCount;
810
811 m_NeedAck[packet.Header.Sequence] = item;
812 }
813 }
814 }
815
816 // If we sent a killpacket
817 if (packet is KillPacket)
818 Abort();
819
820 try
821 {
822 // If this packet has been reused/returned, the ToBytes
823 // will blow up in our face.
824 // Fail gracefully.
825 //
826
827 // Actually make the byte array and send it
828 byte[] sendbuffer = item.Packet.ToBytes();
829
830 if (packet.Header.Zerocoded)
831 {
832 int packetsize = Helpers.ZeroEncode(sendbuffer,
833 sendbuffer.Length, m_ZeroOutBuffer);
834 m_PacketServer.SendPacketTo(m_ZeroOutBuffer, packetsize,
835 SocketFlags.None, m_Client.CircuitCode);
836 }
837 else
838 {
839 // Need some extra space in case we need to add proxy
840 // information to the message later
841 Buffer.BlockCopy(sendbuffer, 0, m_ZeroOutBuffer, 0,
842 sendbuffer.Length);
843 m_PacketServer.SendPacketTo(m_ZeroOutBuffer,
844 sendbuffer.Length, SocketFlags.None, m_Client.CircuitCode);
845 }
846 }
847 catch (NullReferenceException)
848 {
849 m_log.Error("[PACKET]: Detected reuse of a returned packet");
850 m_PacketQueue.Cancel(item.Sequence);
851 return;
852 }
853
854 // If this is a reliable packet, we are still holding a ref
855 // Dont't return in that case
856 //
857 if (!packet.Header.Reliable)
858 {
859 m_PacketQueue.Cancel(item.Sequence);
860 PacketPool.Instance.ReturnPacket(packet);
861 }
862 }
863
864 private void Abort()
865 {
866 m_PacketQueue.Close();
867 Thread.CurrentThread.Abort();
868 }
869
870 public int GetQueueCount(ThrottleOutPacketType queue)
871 {
872 return m_PacketQueue.GetQueueCount(queue);
873 }
874 }
875}
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 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Reflection;
31using System.Threading;
32using System.Timers;
33using log4net;
34using OpenMetaverse;
35using OpenSim.Framework;
36using OpenSim.Framework.Statistics;
37using OpenSim.Framework.Statistics.Interfaces;
38using Timer=System.Timers.Timer;
39
40namespace OpenSim.Region.ClientStack.LindenUDP
41{
42 public class LLPacketQueue : IPullStatsProvider, IDisposable
43 {
44 private static readonly ILog m_log
45 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46
47 /// <summary>
48 /// Is queueing enabled at all?
49 /// </summary>
50 private bool m_enabled = true;
51
52 private OpenSim.Framework.BlockingQueue<LLQueItem> SendQueue;
53
54 private Queue<LLQueItem> IncomingPacketQueue;
55 private Queue<LLQueItem> OutgoingPacketQueue;
56 private Queue<LLQueItem> ResendOutgoingPacketQueue;
57 private Queue<LLQueItem> LandOutgoingPacketQueue;
58 private Queue<LLQueItem> WindOutgoingPacketQueue;
59 private Queue<LLQueItem> CloudOutgoingPacketQueue;
60 private Queue<LLQueItem> TaskOutgoingPacketQueue;
61 private Queue<LLQueItem> TaskLowpriorityPacketQueue;
62 private Queue<LLQueItem> TextureOutgoingPacketQueue;
63 private Queue<LLQueItem> AssetOutgoingPacketQueue;
64
65 private List<ThrottleOutPacketType> Empty = new List<ThrottleOutPacketType>();
66 // m_log.Info("[THROTTLE]: Entering Throttle");
67 // private Dictionary<uint, uint> PendingAcks = new Dictionary<uint, uint>();
68 // private Dictionary<uint, Packet> NeedAck = new Dictionary<uint, Packet>();
69
70 // All throttle times and number of bytes are calculated by dividing by this value
71 // This value also determines how many times per throttletimems the timer will run
72 // If throttleimems is 1000 ms, then the timer will fire every 1000/7 milliseconds
73
74 private float throttleMultiplier = 2.0f; // Default value really doesn't matter.
75 private int throttleTimeDivisor = 7;
76
77 private int throttletimems = 1000;
78
79 internal LLPacketThrottle ResendThrottle;
80 internal LLPacketThrottle LandThrottle;
81 internal LLPacketThrottle WindThrottle;
82 internal LLPacketThrottle CloudThrottle;
83 internal LLPacketThrottle TaskThrottle;
84 internal LLPacketThrottle AssetThrottle;
85 internal LLPacketThrottle TextureThrottle;
86 internal LLPacketThrottle TotalThrottle;
87
88 private Dictionary<uint,int> contents = new Dictionary<uint, int>();
89
90 // private long LastThrottle;
91 // private long ThrottleInterval;
92 private Timer throttleTimer;
93
94 private UUID m_agentId;
95
96 public event QueueEmpty OnQueueEmpty;
97
98 public LLPacketQueue(UUID agentId, ClientStackUserSettings userSettings)
99 {
100 // While working on this, the BlockingQueue had me fooled for a bit.
101 // The Blocking queue causes the thread to stop until there's something
102 // in it to process. it's an on-purpose threadlock though because
103 // without it, the clientloop will suck up all sim resources.
104
105 SendQueue = new OpenSim.Framework.BlockingQueue<LLQueItem>();
106
107 IncomingPacketQueue = new Queue<LLQueItem>();
108 OutgoingPacketQueue = new Queue<LLQueItem>();
109 ResendOutgoingPacketQueue = new Queue<LLQueItem>();
110 LandOutgoingPacketQueue = new Queue<LLQueItem>();
111 WindOutgoingPacketQueue = new Queue<LLQueItem>();
112 CloudOutgoingPacketQueue = new Queue<LLQueItem>();
113 TaskOutgoingPacketQueue = new Queue<LLQueItem>();
114 TaskLowpriorityPacketQueue = new Queue<LLQueItem>();
115 TextureOutgoingPacketQueue = new Queue<LLQueItem>();
116 AssetOutgoingPacketQueue = new Queue<LLQueItem>();
117
118 // Store the throttle multiplier for posterity.
119 throttleMultiplier = userSettings.ClientThrottleMultipler;
120
121
122 int throttleMaxBPS = 1500000;
123 if (userSettings.TotalThrottleSettings != null)
124 throttleMaxBPS = userSettings.TotalThrottleSettings.Max;
125
126 // Set up the throttle classes (min, max, current) in bits per second
127 ResendThrottle = new LLPacketThrottle(5000, throttleMaxBPS / 15, 16000, userSettings.ClientThrottleMultipler);
128 LandThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 15, 2000, userSettings.ClientThrottleMultipler);
129 WindThrottle = new LLPacketThrottle(0, throttleMaxBPS / 15, 0, userSettings.ClientThrottleMultipler);
130 CloudThrottle = new LLPacketThrottle(0, throttleMaxBPS / 15, 0, userSettings.ClientThrottleMultipler);
131 TaskThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 3000, userSettings.ClientThrottleMultipler);
132 AssetThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 1000, userSettings.ClientThrottleMultipler);
133 TextureThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 4000, userSettings.ClientThrottleMultipler);
134
135
136 // Total Throttle trumps all - it is the number of bits in total that are allowed to go out per second.
137
138
139 ThrottleSettings totalThrottleSettings = userSettings.TotalThrottleSettings;
140 if (null == totalThrottleSettings)
141 {
142 totalThrottleSettings = new ThrottleSettings(0, throttleMaxBPS, 28000);
143 }
144
145 TotalThrottle
146 = new LLPacketThrottle(
147 totalThrottleSettings.Min, totalThrottleSettings.Max, totalThrottleSettings.Current,
148 userSettings.ClientThrottleMultipler);
149
150 throttleTimer = new Timer((int)(throttletimems / throttleTimeDivisor));
151 throttleTimer.Elapsed += ThrottleTimerElapsed;
152 throttleTimer.Start();
153
154 // TIMERS needed for this
155 // LastThrottle = DateTime.Now.Ticks;
156 // ThrottleInterval = (long)(throttletimems/throttleTimeDivisor);
157
158 m_agentId = agentId;
159
160 if (StatsManager.SimExtraStats != null)
161 {
162 StatsManager.SimExtraStats.RegisterPacketQueueStatsProvider(m_agentId, this);
163 }
164 }
165
166 /* STANDARD QUEUE MANIPULATION INTERFACES */
167
168 public void Enqueue(LLQueItem item)
169 {
170 if (!m_enabled)
171 {
172 return;
173 }
174 // We could micro lock, but that will tend to actually
175 // probably be worse than just synchronizing on SendQueue
176
177 if (item == null)
178 {
179 SendQueue.Enqueue(item);
180 return;
181 }
182
183 if (item.Incoming)
184 {
185 SendQueue.PriorityEnqueue(item);
186 return;
187 }
188
189 if (item.Sequence != 0)
190 lock (contents)
191 {
192 if (contents.ContainsKey(item.Sequence))
193 contents[item.Sequence] += 1;
194 else
195 contents.Add(item.Sequence, 1);
196 }
197
198 lock (this)
199 {
200 switch (item.throttleType & ThrottleOutPacketType.TypeMask)
201 {
202 case ThrottleOutPacketType.Resend:
203 ThrottleCheck(ref ResendThrottle, ref ResendOutgoingPacketQueue, item, ThrottleOutPacketType.Resend);
204 break;
205 case ThrottleOutPacketType.Texture:
206 ThrottleCheck(ref TextureThrottle, ref TextureOutgoingPacketQueue, item, ThrottleOutPacketType.Texture);
207 break;
208 case ThrottleOutPacketType.Task:
209 if ((item.throttleType & ThrottleOutPacketType.LowPriority) != 0)
210 ThrottleCheck(ref TaskThrottle, ref TaskLowpriorityPacketQueue, item, ThrottleOutPacketType.Task);
211 else
212 ThrottleCheck(ref TaskThrottle, ref TaskOutgoingPacketQueue, item, ThrottleOutPacketType.Task);
213 break;
214 case ThrottleOutPacketType.Land:
215 ThrottleCheck(ref LandThrottle, ref LandOutgoingPacketQueue, item, ThrottleOutPacketType.Land);
216 break;
217 case ThrottleOutPacketType.Asset:
218 ThrottleCheck(ref AssetThrottle, ref AssetOutgoingPacketQueue, item, ThrottleOutPacketType.Asset);
219 break;
220 case ThrottleOutPacketType.Cloud:
221 ThrottleCheck(ref CloudThrottle, ref CloudOutgoingPacketQueue, item, ThrottleOutPacketType.Cloud);
222 break;
223 case ThrottleOutPacketType.Wind:
224 ThrottleCheck(ref WindThrottle, ref WindOutgoingPacketQueue, item, ThrottleOutPacketType.Wind);
225 break;
226
227 default:
228 // Acknowledgements and other such stuff should go directly to the blocking Queue
229 // Throttling them may and likely 'will' be problematic
230 SendQueue.PriorityEnqueue(item);
231 break;
232 }
233 }
234 }
235
236 public LLQueItem Dequeue()
237 {
238 while (true)
239 {
240 LLQueItem item = SendQueue.Dequeue();
241 if (item == null)
242 return null;
243 if (item.Incoming)
244 return item;
245 item.TickCount = System.Environment.TickCount;
246 if (item.Sequence == 0)
247 return item;
248 lock (contents)
249 {
250 if (contents.ContainsKey(item.Sequence))
251 {
252 if (contents[item.Sequence] == 1)
253 contents.Remove(item.Sequence);
254 else
255 contents[item.Sequence] -= 1;
256 return item;
257 }
258 }
259 }
260 }
261
262 public void Cancel(uint sequence)
263 {
264 lock (contents) contents.Remove(sequence);
265 }
266
267 public bool Contains(uint sequence)
268 {
269 lock (contents) return contents.ContainsKey(sequence);
270 }
271
272 public void Flush()
273 {
274 lock (this)
275 {
276 // These categories do not contain transactional packets so we can safely drop any pending data in them
277 LandOutgoingPacketQueue.Clear();
278 WindOutgoingPacketQueue.Clear();
279 CloudOutgoingPacketQueue.Clear();
280 TextureOutgoingPacketQueue.Clear();
281 AssetOutgoingPacketQueue.Clear();
282
283 // Now comes the fun part.. we dump all remaining resend and task packets into the send queue
284 while (ResendOutgoingPacketQueue.Count > 0 || TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0)
285 {
286 if (ResendOutgoingPacketQueue.Count > 0)
287 SendQueue.Enqueue(ResendOutgoingPacketQueue.Dequeue());
288
289 if (TaskOutgoingPacketQueue.Count > 0)
290 SendQueue.PriorityEnqueue(TaskOutgoingPacketQueue.Dequeue());
291
292 if (TaskLowpriorityPacketQueue.Count > 0)
293 SendQueue.Enqueue(TaskLowpriorityPacketQueue.Dequeue());
294 }
295 }
296 }
297
298 public void WipeClean()
299 {
300 lock (this)
301 {
302 ResendOutgoingPacketQueue.Clear();
303 LandOutgoingPacketQueue.Clear();
304 WindOutgoingPacketQueue.Clear();
305 CloudOutgoingPacketQueue.Clear();
306 TaskOutgoingPacketQueue.Clear();
307 TaskLowpriorityPacketQueue.Clear();
308 TextureOutgoingPacketQueue.Clear();
309 AssetOutgoingPacketQueue.Clear();
310 SendQueue.Clear();
311 lock (contents) contents.Clear();
312 }
313 }
314
315 public void Close()
316 {
317 Dispose();
318 }
319
320 public void Dispose()
321 {
322 Flush();
323 WipeClean(); // I'm sure there's a dirty joke in here somewhere. -AFrisby
324
325 m_enabled = false;
326 throttleTimer.Stop();
327 throttleTimer.Close();
328
329 if (StatsManager.SimExtraStats != null)
330 {
331 StatsManager.SimExtraStats.DeregisterPacketQueueStatsProvider(m_agentId);
332 }
333 }
334
335 private void ResetCounters()
336 {
337 ResendThrottle.Reset();
338 LandThrottle.Reset();
339 WindThrottle.Reset();
340 CloudThrottle.Reset();
341 TaskThrottle.Reset();
342 AssetThrottle.Reset();
343 TextureThrottle.Reset();
344 TotalThrottle.Reset();
345 }
346
347 private bool PacketsWaiting()
348 {
349 return (ResendOutgoingPacketQueue.Count > 0 ||
350 LandOutgoingPacketQueue.Count > 0 ||
351 WindOutgoingPacketQueue.Count > 0 ||
352 CloudOutgoingPacketQueue.Count > 0 ||
353 TaskOutgoingPacketQueue.Count > 0 ||
354 TaskLowpriorityPacketQueue.Count > 0 ||
355 AssetOutgoingPacketQueue.Count > 0 ||
356 TextureOutgoingPacketQueue.Count > 0);
357 }
358
359 public void ProcessThrottle()
360 {
361 // I was considering this.. Will an event fire if the thread it's on is blocked?
362
363 // Then I figured out.. it doesn't really matter.. because this thread won't be blocked for long
364 // The General overhead of the UDP protocol gets sent to the queue un-throttled by this
365 // so This'll pick up about around the right time.
366
367 int MaxThrottleLoops = 4550; // 50*7 packets can be dequeued at once.
368 int throttleLoops = 0;
369 List<ThrottleOutPacketType> e;
370
371 // We're going to dequeue all of the saved up packets until
372 // we've hit the throttle limit or there's no more packets to send
373 lock (this)
374 {
375 // this variable will be true if there was work done in the last execution of the
376 // loop, since each pass through the loop checks the queue length, we no longer
377 // need the check on entering the loop
378 bool qchanged = true;
379
380 ResetCounters();
381
382 while (TotalThrottle.UnderLimit() && qchanged && throttleLoops <= MaxThrottleLoops)
383 {
384 qchanged = false; // We will break out of the loop if no work was accomplished
385
386 throttleLoops++;
387 //Now comes the fun part.. we dump all our elements into m_packetQueue that we've saved up.
388 if ((ResendOutgoingPacketQueue.Count > 0) && ResendThrottle.UnderLimit())
389 {
390 LLQueItem qpack = ResendOutgoingPacketQueue.Dequeue();
391
392 SendQueue.Enqueue(qpack);
393 TotalThrottle.AddBytes(qpack.Length);
394 ResendThrottle.AddBytes(qpack.Length);
395
396 qchanged = true;
397 }
398
399 if ((LandOutgoingPacketQueue.Count > 0) && LandThrottle.UnderLimit())
400 {
401 LLQueItem qpack = LandOutgoingPacketQueue.Dequeue();
402
403 SendQueue.Enqueue(qpack);
404 TotalThrottle.AddBytes(qpack.Length);
405 LandThrottle.AddBytes(qpack.Length);
406 qchanged = true;
407
408 if (LandOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Land))
409 Empty.Add(ThrottleOutPacketType.Land);
410 }
411
412 if ((WindOutgoingPacketQueue.Count > 0) && WindThrottle.UnderLimit())
413 {
414 LLQueItem qpack = WindOutgoingPacketQueue.Dequeue();
415
416 SendQueue.Enqueue(qpack);
417 TotalThrottle.AddBytes(qpack.Length);
418 WindThrottle.AddBytes(qpack.Length);
419 qchanged = true;
420
421 if (WindOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Wind))
422 Empty.Add(ThrottleOutPacketType.Wind);
423 }
424
425 if ((CloudOutgoingPacketQueue.Count > 0) && CloudThrottle.UnderLimit())
426 {
427 LLQueItem qpack = CloudOutgoingPacketQueue.Dequeue();
428
429 SendQueue.Enqueue(qpack);
430 TotalThrottle.AddBytes(qpack.Length);
431 CloudThrottle.AddBytes(qpack.Length);
432 qchanged = true;
433
434 if (CloudOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Cloud))
435 Empty.Add(ThrottleOutPacketType.Cloud);
436 }
437
438 if ((TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0) && TaskThrottle.UnderLimit())
439 {
440 LLQueItem qpack;
441 if (TaskOutgoingPacketQueue.Count > 0)
442 {
443 qpack = TaskOutgoingPacketQueue.Dequeue();
444 SendQueue.PriorityEnqueue(qpack);
445 }
446 else
447 {
448 qpack = TaskLowpriorityPacketQueue.Dequeue();
449 SendQueue.Enqueue(qpack);
450 }
451
452 TotalThrottle.AddBytes(qpack.Length);
453 TaskThrottle.AddBytes(qpack.Length);
454 qchanged = true;
455
456 if (TaskOutgoingPacketQueue.Count == 0 && TaskLowpriorityPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Task))
457 Empty.Add(ThrottleOutPacketType.Task);
458 }
459
460 if ((TextureOutgoingPacketQueue.Count > 0) && TextureThrottle.UnderLimit())
461 {
462 LLQueItem qpack = TextureOutgoingPacketQueue.Dequeue();
463
464 SendQueue.Enqueue(qpack);
465 TotalThrottle.AddBytes(qpack.Length);
466 TextureThrottle.AddBytes(qpack.Length);
467 qchanged = true;
468
469 if (TextureOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Texture))
470 Empty.Add(ThrottleOutPacketType.Texture);
471 }
472
473 if ((AssetOutgoingPacketQueue.Count > 0) && AssetThrottle.UnderLimit())
474 {
475 LLQueItem qpack = AssetOutgoingPacketQueue.Dequeue();
476
477 SendQueue.Enqueue(qpack);
478 TotalThrottle.AddBytes(qpack.Length);
479 AssetThrottle.AddBytes(qpack.Length);
480 qchanged = true;
481
482 if (AssetOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Asset))
483 Empty.Add(ThrottleOutPacketType.Asset);
484 }
485 }
486 // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets");
487
488 e = new List<ThrottleOutPacketType>(Empty);
489 Empty.Clear();
490 }
491
492 foreach (ThrottleOutPacketType t in e)
493 {
494 if (GetQueueCount(t) == 0)
495 TriggerOnQueueEmpty(t);
496 }
497 }
498
499 private void TriggerOnQueueEmpty(ThrottleOutPacketType queue)
500 {
501 QueueEmpty handlerQueueEmpty = OnQueueEmpty;
502
503 if (handlerQueueEmpty != null)
504 handlerQueueEmpty(queue);
505 }
506
507 private void ThrottleTimerElapsed(object sender, ElapsedEventArgs e)
508 {
509 // just to change the signature, and that ProcessThrottle
510 // will be used elsewhere possibly
511 ProcessThrottle();
512 }
513
514 private void ThrottleCheck(ref LLPacketThrottle throttle, ref Queue<LLQueItem> q, LLQueItem item, ThrottleOutPacketType itemType)
515 {
516 // The idea.. is if the packet throttle queues are empty
517 // and the client is under throttle for the type. Queue
518 // it up directly. This basically short cuts having to
519 // wait for the timer to fire to put things into the
520 // output queue
521
522 if ((q.Count == 0) && (throttle.UnderLimit()))
523 {
524 try
525 {
526 Monitor.Enter(this);
527 throttle.AddBytes(item.Length);
528 TotalThrottle.AddBytes(item.Length);
529 SendQueue.Enqueue(item);
530 lock (this)
531 {
532 if (!Empty.Contains(itemType))
533 Empty.Add(itemType);
534 }
535 }
536 catch (Exception e)
537 {
538 // Probably a serialization exception
539 m_log.WarnFormat("ThrottleCheck: {0}", e.ToString());
540 }
541 finally
542 {
543 Monitor.Pulse(this);
544 Monitor.Exit(this);
545 }
546 }
547 else
548 {
549 q.Enqueue(item);
550 }
551 }
552
553 private static int ScaleThrottle(int value, int curmax, int newmax)
554 {
555 return (int)((value / (float)curmax) * newmax);
556 }
557
558 public byte[] GetThrottlesPacked(float multiplier)
559 {
560 int singlefloat = 4;
561 float tResend = ResendThrottle.Throttle*multiplier;
562 float tLand = LandThrottle.Throttle*multiplier;
563 float tWind = WindThrottle.Throttle*multiplier;
564 float tCloud = CloudThrottle.Throttle*multiplier;
565 float tTask = TaskThrottle.Throttle*multiplier;
566 float tTexture = TextureThrottle.Throttle*multiplier;
567 float tAsset = AssetThrottle.Throttle*multiplier;
568
569 byte[] throttles = new byte[singlefloat*7];
570 int i = 0;
571 Buffer.BlockCopy(BitConverter.GetBytes(tResend), 0, throttles, singlefloat*i, singlefloat);
572 i++;
573 Buffer.BlockCopy(BitConverter.GetBytes(tLand), 0, throttles, singlefloat*i, singlefloat);
574 i++;
575 Buffer.BlockCopy(BitConverter.GetBytes(tWind), 0, throttles, singlefloat*i, singlefloat);
576 i++;
577 Buffer.BlockCopy(BitConverter.GetBytes(tCloud), 0, throttles, singlefloat*i, singlefloat);
578 i++;
579 Buffer.BlockCopy(BitConverter.GetBytes(tTask), 0, throttles, singlefloat*i, singlefloat);
580 i++;
581 Buffer.BlockCopy(BitConverter.GetBytes(tTexture), 0, throttles, singlefloat*i, singlefloat);
582 i++;
583 Buffer.BlockCopy(BitConverter.GetBytes(tAsset), 0, throttles, singlefloat*i, singlefloat);
584
585 return throttles;
586 }
587
588 public void SetThrottleFromClient(byte[] throttle)
589 {
590 // From mantis http://opensimulator.org/mantis/view.php?id=1374
591 // it appears that sometimes we are receiving empty throttle byte arrays.
592 // TODO: Investigate this behaviour
593 if (throttle.Length == 0)
594 {
595 m_log.Warn("[PACKET QUEUE]: SetThrottleFromClient unexpectedly received a throttle byte array containing no elements!");
596 return;
597 }
598
599 int tResend = -1;
600 int tLand = -1;
601 int tWind = -1;
602 int tCloud = -1;
603 int tTask = -1;
604 int tTexture = -1;
605 int tAsset = -1;
606 int tall = -1;
607 int singlefloat = 4;
608
609 //Agent Throttle Block contains 7 single floatingpoint values.
610 int j = 0;
611
612 // Some Systems may be big endian...
613 // it might be smart to do this check more often...
614 if (!BitConverter.IsLittleEndian)
615 for (int i = 0; i < 7; i++)
616 Array.Reverse(throttle, j + i*singlefloat, singlefloat);
617
618 // values gotten from OpenMetaverse.org/wiki/Throttle. Thanks MW_
619 // bytes
620 // Convert to integer, since.. the full fp space isn't used.
621 tResend = (int) BitConverter.ToSingle(throttle, j);
622 j += singlefloat;
623 tLand = (int) BitConverter.ToSingle(throttle, j);
624 j += singlefloat;
625 tWind = (int) BitConverter.ToSingle(throttle, j);
626 j += singlefloat;
627 tCloud = (int) BitConverter.ToSingle(throttle, j);
628 j += singlefloat;
629 tTask = (int) BitConverter.ToSingle(throttle, j);
630 j += singlefloat;
631 tTexture = (int) BitConverter.ToSingle(throttle, j);
632 j += singlefloat;
633 tAsset = (int) BitConverter.ToSingle(throttle, j);
634
635 tall = tResend + tLand + tWind + tCloud + tTask + tTexture + tAsset;
636
637 /*
638 m_log.Info("[CLIENT]: Client AgentThrottle - Got throttle:resendbits=" + tResend +
639 " landbits=" + tLand +
640 " windbits=" + tWind +
641 " cloudbits=" + tCloud +
642 " taskbits=" + tTask +
643 " texturebits=" + tTexture +
644 " Assetbits=" + tAsset +
645 " Allbits=" + tall);
646 */
647
648
649 // Total Sanity
650 // Make sure that the client sent sane total values.
651
652 // If the client didn't send acceptable values....
653 // Scale the clients values down until they are acceptable.
654
655 if (tall <= TotalThrottle.Max)
656 {
657 ResendThrottle.Throttle = tResend;
658 LandThrottle.Throttle = tLand;
659 WindThrottle.Throttle = tWind;
660 CloudThrottle.Throttle = tCloud;
661 TaskThrottle.Throttle = tTask;
662 TextureThrottle.Throttle = tTexture;
663 AssetThrottle.Throttle = tAsset;
664 TotalThrottle.Throttle = tall;
665 }
666// else if (tall < 1)
667// {
668// // client is stupid, penalize him by minning everything
669// ResendThrottle.Throttle = ResendThrottle.Min;
670// LandThrottle.Throttle = LandThrottle.Min;
671// WindThrottle.Throttle = WindThrottle.Min;
672// CloudThrottle.Throttle = CloudThrottle.Min;
673// TaskThrottle.Throttle = TaskThrottle.Min;
674// TextureThrottle.Throttle = TextureThrottle.Min;
675// AssetThrottle.Throttle = AssetThrottle.Min;
676// TotalThrottle.Throttle = TotalThrottle.Min;
677// }
678 else
679 {
680 // we're over so figure out percentages and use those
681 ResendThrottle.Throttle = tResend;
682
683 LandThrottle.Throttle = ScaleThrottle(tLand, tall, TotalThrottle.Max);
684 WindThrottle.Throttle = ScaleThrottle(tWind, tall, TotalThrottle.Max);
685 CloudThrottle.Throttle = ScaleThrottle(tCloud, tall, TotalThrottle.Max);
686 TaskThrottle.Throttle = ScaleThrottle(tTask, tall, TotalThrottle.Max);
687 TextureThrottle.Throttle = ScaleThrottle(tTexture, tall, TotalThrottle.Max);
688 AssetThrottle.Throttle = ScaleThrottle(tAsset, tall, TotalThrottle.Max);
689 TotalThrottle.Throttle = TotalThrottle.Max;
690 }
691 // effectively wiggling the slider causes things reset
692// ResetCounters(); // DO NOT reset, better to send less for one period than more
693 }
694
695 // See IPullStatsProvider
696 public string GetStats()
697 {
698 return string.Format("{0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}",
699 SendQueue.Count(),
700 IncomingPacketQueue.Count,
701 OutgoingPacketQueue.Count,
702 ResendOutgoingPacketQueue.Count,
703 LandOutgoingPacketQueue.Count,
704 WindOutgoingPacketQueue.Count,
705 CloudOutgoingPacketQueue.Count,
706 TaskOutgoingPacketQueue.Count,
707 TextureOutgoingPacketQueue.Count,
708 AssetOutgoingPacketQueue.Count);
709 }
710
711 public LLQueItem[] GetQueueArray()
712 {
713 return SendQueue.GetQueueArray();
714 }
715
716 public float ThrottleMultiplier
717 {
718 get { return throttleMultiplier; }
719 }
720
721 public int GetQueueCount(ThrottleOutPacketType queue)
722 {
723 switch (queue)
724 {
725 case ThrottleOutPacketType.Land:
726 return LandOutgoingPacketQueue.Count;
727 case ThrottleOutPacketType.Wind:
728 return WindOutgoingPacketQueue.Count;
729 case ThrottleOutPacketType.Cloud:
730 return CloudOutgoingPacketQueue.Count;
731 case ThrottleOutPacketType.Task:
732 return TaskOutgoingPacketQueue.Count;
733 case ThrottleOutPacketType.Texture:
734 return TextureOutgoingPacketQueue.Count;
735 case ThrottleOutPacketType.Asset:
736 return AssetOutgoingPacketQueue.Count;
737 }
738
739 return 0;
740 }
741 }
742}
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 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System.Net;
29using System.Net.Sockets;
30using OpenMetaverse;
31using OpenMetaverse.Packets;
32using OpenSim.Framework;
33
34namespace OpenSim.Region.ClientStack.LindenUDP
35{
36 /// <summary>
37 /// This class sets up new client stacks. It also handles the immediate distribution of incoming packets to
38 /// client stacks
39 /// </summary>
40 public class LLPacketServer
41 {
42// private static readonly log4net.ILog m_log
43// = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
44
45 protected readonly LLUDPServer m_networkHandler;
46 protected IScene m_scene;
47
48 /// <summary>
49 /// Tweakable user settings
50 /// </summary>
51 private ClientStackUserSettings m_userSettings;
52
53 public LLPacketServer(LLUDPServer networkHandler, ClientStackUserSettings userSettings)
54 {
55 m_userSettings = userSettings;
56 m_networkHandler = networkHandler;
57
58 m_networkHandler.RegisterPacketServer(this);
59 }
60
61 public IScene LocalScene
62 {
63 set { m_scene = value; }
64 }
65
66 /// <summary>
67 /// Process an incoming packet.
68 /// </summary>
69 /// <param name="circuitCode"></param>
70 /// <param name="packet"></param>
71 public virtual void InPacket(uint circuitCode, Packet packet)
72 {
73 m_scene.ClientManager.InPacket(circuitCode, packet);
74 }
75
76 /// <summary>
77 /// Create a new client circuit
78 /// </summary>
79 /// <param name="remoteEP"></param>
80 /// <param name="scene"></param>
81 /// <param name="assetCache"></param>
82 /// <param name="packServer"></param>
83 /// <param name="sessionInfo"></param>
84 /// <param name="agentId"></param>
85 /// <param name="sessionId"></param>
86 /// <param name="circuitCode"></param>
87 /// <param name="proxyEP"></param>
88 /// <returns></returns>
89 protected virtual IClientAPI CreateNewCircuit(
90 EndPoint remoteEP, IScene scene,
91 LLPacketServer packServer, AuthenticateResponse sessionInfo,
92 UUID agentId, UUID sessionId, uint circuitCode, EndPoint proxyEP)
93 {
94 return
95 new LLClientView(
96 remoteEP, scene, packServer, sessionInfo, agentId, sessionId, circuitCode, proxyEP,
97 m_userSettings);
98 }
99
100 /// <summary>
101 /// Check whether a given client is authorized to connect.
102 /// </summary>
103 /// <param name="useCircuit"></param>
104 /// <param name="circuitManager"></param>
105 /// <param name="sessionInfo"></param>
106 /// <returns></returns>
107 public virtual bool IsClientAuthorized(
108 UseCircuitCodePacket useCircuit, AgentCircuitManager circuitManager, out AuthenticateResponse sessionInfo)
109 {
110 UUID agentId = useCircuit.CircuitCode.ID;
111 UUID sessionId = useCircuit.CircuitCode.SessionID;
112 uint circuitCode = useCircuit.CircuitCode.Code;
113
114 sessionInfo = circuitManager.AuthenticateSession(sessionId, agentId, circuitCode);
115
116 if (!sessionInfo.Authorised)
117 return false;
118
119 return true;
120 }
121
122 /// <summary>
123 /// Add a new client circuit. We assume that is has already passed an authorization check
124 /// </summary>
125 /// <param name="epSender"></param>
126 /// <param name="useCircuit"></param>
127 /// <param name="assetCache"></param>
128 /// <param name="sessionInfo"></param>
129 /// <param name="proxyEP"></param>
130 /// <returns>
131 /// true if a new circuit was created, false if a circuit with the given circuit code already existed
132 /// </returns>
133 public virtual bool AddNewClient(
134 EndPoint epSender, UseCircuitCodePacket useCircuit,
135 AuthenticateResponse sessionInfo, EndPoint proxyEP)
136 {
137 IClientAPI newuser;
138 uint circuitCode = useCircuit.CircuitCode.Code;
139
140 if (m_scene.ClientManager.TryGetClient(circuitCode, out newuser))
141 {
142 // The circuit is already known to the scene. This not actually a problem since this will currently
143 // occur if a client is crossing borders (hence upgrading its circuit). However, we shouldn't
144 // really by trying to add a new client if this is the case.
145 return false;
146 }
147
148 UUID agentId = useCircuit.CircuitCode.ID;
149 UUID sessionId = useCircuit.CircuitCode.SessionID;
150
151 newuser
152 = CreateNewCircuit(
153 epSender, m_scene, this, sessionInfo, agentId, sessionId, circuitCode, proxyEP);
154
155 m_scene.ClientManager.Add(circuitCode, newuser);
156
157 newuser.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler;
158 newuser.OnLogout += LogoutHandler;
159 newuser.OnConnectionClosed += CloseClient;
160
161 newuser.Start();
162
163 return true;
164 }
165
166 public void LogoutHandler(IClientAPI client)
167 {
168 client.SendLogoutPacket();
169 CloseClient(client);
170 }
171
172 /// <summary>
173 /// Send a packet to the given circuit
174 /// </summary>
175 /// <param name="buffer"></param>
176 /// <param name="size"></param>
177 /// <param name="flags"></param>
178 /// <param name="circuitcode"></param>
179 public virtual void SendPacketTo(byte[] buffer, int size, SocketFlags flags, uint circuitcode)
180 {
181 m_networkHandler.SendPacketTo(buffer, size, flags, circuitcode);
182 }
183
184 /// <summary>
185 /// Close a client circuit only
186 /// </summary>
187 /// <param name="circuitcode"></param>
188 public virtual void CloseCircuit(uint circuitcode)
189 {
190 m_networkHandler.RemoveClientCircuit(circuitcode);
191 }
192
193 /// <summary>
194 /// Completely close down the given client.
195 /// </summary>
196 /// <param name="client"></param>
197 public virtual void CloseClient(IClientAPI client)
198 {
199 //m_log.Info("PacketServer:CloseClient()");
200
201 CloseCircuit(client.CircuitCode);
202 m_scene.ClientManager.Remove(client.CircuitCode);
203 client.Close(false);
204 }
205 }
206}
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 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Net;
31using OpenSim.Framework;
32using OpenMetaverse;
33
34namespace OpenSim.Region.ClientStack.LindenUDP
35{
36 public delegate void QueueEmpty(ThrottleOutPacketType category);
37
38 public class LLUDPClient
39 {
40 /// <summary>The number of packet categories to throttle on. If a throttle category is added
41 /// or removed, this number must also change</summary>
42 const int THROTTLE_CATEGORY_COUNT = 7;
43
44 public event QueueEmpty OnQueueEmpty;
45
46 /// <summary>AgentID for this client</summary>
47 public readonly UUID AgentID;
48 /// <summary>The remote address of the connected client</summary>
49 public readonly IPEndPoint RemoteEndPoint;
50 /// <summary>Circuit code that this client is connected on</summary>
51 public readonly uint CircuitCode;
52 /// <summary>Sequence numbers of packets we've received (for duplicate checking)</summary>
53 public readonly IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(200);
54 /// <summary>Packets we have sent that need to be ACKed by the client</summary>
55 public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection();
56 /// <summary>ACKs that are queued up, waiting to be sent to the client</summary>
57 public readonly LocklessQueue<uint> PendingAcks = new LocklessQueue<uint>();
58
59 /// <summary>Reference to the IClientAPI for this client</summary>
60 public LLClientView ClientAPI;
61 /// <summary>Current packet sequence number</summary>
62 public int CurrentSequence;
63 /// <summary>Current ping sequence number</summary>
64 public byte CurrentPingSequence;
65 /// <summary>True when this connection is alive, otherwise false</summary>
66 public bool IsConnected = true;
67 /// <summary>True when this connection is paused, otherwise false</summary>
68 public bool IsPaused = true;
69 /// <summary>Environment.TickCount when the last packet was received for this client</summary>
70 public int TickLastPacketReceived;
71
72 /// <summary>Timer granularity. This is set to the measured resolution of Environment.TickCount</summary>
73 public readonly float G;
74 /// <summary>Smoothed round-trip time. A smoothed average of the round-trip time for sending a
75 /// reliable packet to the client and receiving an ACK</summary>
76 public float SRTT;
77 /// <summary>Round-trip time variance. Measures the consistency of round-trip times</summary>
78 public float RTTVAR;
79 /// <summary>Retransmission timeout. Packets that have not been acknowledged in this number of
80 /// milliseconds or longer will be resent</summary>
81 /// <remarks>Calculated from <seealso cref="SRTT"/> and <seealso cref="RTTVAR"/> using the
82 /// guidelines in RFC 2988</remarks>
83 public int RTO;
84 /// <summary>Number of bytes received since the last acknowledgement was sent out. This is used
85 /// to loosely follow the TCP delayed ACK algorithm in RFC 1122 (4.2.3.2)</summary>
86 public int BytesSinceLastACK;
87
88 /// <summary>Throttle bucket for this agent's connection</summary>
89 private readonly TokenBucket throttle;
90 /// <summary>Throttle buckets for each packet category</summary>
91 private readonly TokenBucket[] throttleCategories;
92 /// <summary>Throttle rate defaults and limits</summary>
93 private readonly ThrottleRates defaultThrottleRates;
94 /// <summary>Outgoing queues for throttled packets</summary>
95 private readonly LocklessQueue<OutgoingPacket>[] packetOutboxes = new LocklessQueue<OutgoingPacket>[THROTTLE_CATEGORY_COUNT];
96 /// <summary>A container that can hold one packet for each outbox, used to store
97 /// dequeued packets that are being held for throttling</summary>
98 private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
99 /// <summary>An optimization to store the length of dequeued packets being held
100 /// for throttling. This avoids expensive calls to Packet.Length</summary>
101 private readonly int[] nextPacketLengths = new int[THROTTLE_CATEGORY_COUNT];
102 /// <summary>A reference to the LLUDPServer that is managing this client</summary>
103 private readonly LLUDPServer udpServer;
104
105 public LLUDPClient(LLUDPServer server, ThrottleRates rates, TokenBucket parentThrottle, uint circuitCode, UUID agentID, IPEndPoint remoteEndPoint)
106 {
107 udpServer = server;
108 AgentID = agentID;
109 RemoteEndPoint = remoteEndPoint;
110 CircuitCode = circuitCode;
111 defaultThrottleRates = rates;
112
113 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
114 packetOutboxes[i] = new LocklessQueue<OutgoingPacket>();
115
116 throttle = new TokenBucket(parentThrottle, 0, 0);
117 throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
118 throttleCategories[(int)ThrottleOutPacketType.Resend] = new TokenBucket(throttle, rates.ResendLimit, rates.Resend);
119 throttleCategories[(int)ThrottleOutPacketType.Land] = new TokenBucket(throttle, rates.LandLimit, rates.Land);
120 throttleCategories[(int)ThrottleOutPacketType.Wind] = new TokenBucket(throttle, rates.WindLimit, rates.Wind);
121 throttleCategories[(int)ThrottleOutPacketType.Cloud] = new TokenBucket(throttle, rates.CloudLimit, rates.Cloud);
122 throttleCategories[(int)ThrottleOutPacketType.Task] = new TokenBucket(throttle, rates.TaskLimit, rates.Task);
123 throttleCategories[(int)ThrottleOutPacketType.Texture] = new TokenBucket(throttle, rates.TextureLimit, rates.Texture);
124 throttleCategories[(int)ThrottleOutPacketType.Asset] = new TokenBucket(throttle, rates.AssetLimit, rates.Asset);
125
126 // Set the granularity variable used for retransmission calculations to
127 // the measured resolution of Environment.TickCount
128 G = server.TickCountResolution;
129
130 // Default the retransmission timeout to three seconds
131 RTO = 3000;
132 }
133
134 public void Shutdown()
135 {
136 IsConnected = false;
137 }
138
139 public ClientInfo GetClientInfo()
140 {
141 // TODO: This data structure is wrong in so many ways
142 ClientInfo info = new ClientInfo();
143 info.pendingAcks = new Dictionary<uint, uint>();
144 info.needAck = new Dictionary<uint, byte[]>();
145
146 info.resendThrottle = throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate;
147 info.landThrottle = throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
148 info.windThrottle = throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
149 info.cloudThrottle = throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
150 info.taskThrottle = throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
151 info.assetThrottle = throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
152 info.textureThrottle = throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
153 info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle +
154 info.taskThrottle + info.assetThrottle + info.textureThrottle;
155
156 return info;
157 }
158
159 public void SetClientInfo(ClientInfo info)
160 {
161 }
162
163 public string GetStats()
164 {
165 return string.Format("{0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}",
166 0,
167 0,
168 0,
169 0,
170 0,
171 0,
172 0,
173 0,
174 0,
175 0);
176 }
177
178 public void SetThrottles(byte[] throttleData)
179 {
180 byte[] adjData;
181 int pos = 0;
182
183 if (!BitConverter.IsLittleEndian)
184 {
185 byte[] newData = new byte[7 * 4];
186 Buffer.BlockCopy(throttleData, 0, newData, 0, 7 * 4);
187
188 for (int i = 0; i < 7; i++)
189 Array.Reverse(newData, i * 4, 4);
190
191 adjData = newData;
192 }
193 else
194 {
195 adjData = throttleData;
196 }
197
198 int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
199 int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
200 int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
201 int cloud = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
202 int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
203 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
204 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
205
206 resend = (resend <= defaultThrottleRates.ResendLimit) ? resend : defaultThrottleRates.ResendLimit;
207 land = (land <= defaultThrottleRates.LandLimit) ? land : defaultThrottleRates.LandLimit;
208 wind = (wind <= defaultThrottleRates.WindLimit) ? wind : defaultThrottleRates.WindLimit;
209 cloud = (cloud <= defaultThrottleRates.CloudLimit) ? cloud : defaultThrottleRates.CloudLimit;
210 task = (task <= defaultThrottleRates.TaskLimit) ? task : defaultThrottleRates.TaskLimit;
211 texture = (texture <= defaultThrottleRates.TextureLimit) ? texture : defaultThrottleRates.TextureLimit;
212 asset = (asset <= defaultThrottleRates.AssetLimit) ? asset : defaultThrottleRates.AssetLimit;
213
214 SetThrottle(ThrottleOutPacketType.Resend, resend);
215 SetThrottle(ThrottleOutPacketType.Land, land);
216 SetThrottle(ThrottleOutPacketType.Wind, wind);
217 SetThrottle(ThrottleOutPacketType.Cloud, cloud);
218 SetThrottle(ThrottleOutPacketType.Task, task);
219 SetThrottle(ThrottleOutPacketType.Texture, texture);
220 SetThrottle(ThrottleOutPacketType.Asset, asset);
221 }
222
223 public byte[] GetThrottlesPacked()
224 {
225 byte[] data = new byte[7 * 4];
226 int i = 0;
227
228 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4;
229 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4;
230 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4;
231 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4;
232 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Task].DripRate), 0, data, i, 4); i += 4;
233 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4;
234 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4;
235
236 return data;
237 }
238
239 public void SetThrottle(ThrottleOutPacketType category, int rate)
240 {
241 int i = (int)category;
242 if (i >= 0 && i < throttleCategories.Length)
243 {
244 TokenBucket bucket = throttleCategories[(int)category];
245 bucket.MaxBurst = rate;
246 bucket.DripRate = rate;
247 }
248 }
249
250 public bool EnqueueOutgoing(OutgoingPacket packet)
251 {
252 int category = (int)packet.Category;
253
254 if (category >= 0 && category < packetOutboxes.Length)
255 {
256 LocklessQueue<OutgoingPacket> queue = packetOutboxes[category];
257 TokenBucket bucket = throttleCategories[category];
258
259 if (throttleCategories[category].RemoveTokens(packet.Buffer.DataLength))
260 {
261 // Enough tokens were removed from the bucket, the packet will not be queued
262 return false;
263 }
264 else
265 {
266 // Not enough tokens in the bucket, queue this packet
267 queue.Enqueue(packet);
268 return true;
269 }
270 }
271 else
272 {
273 // We don't have a token bucket for this category, so it will not be queued
274 return false;
275 }
276 }
277
278 /// <summary>
279 /// Loops through all of the packet queues for this client and tries to send
280 /// any outgoing packets, obeying the throttling bucket limits
281 /// </summary>
282 /// <remarks>This function is only called from a synchronous loop in the
283 /// UDPServer so we don't need to bother making this thread safe</remarks>
284 /// <returns>True if any packets were sent, otherwise false</returns>
285 public bool DequeueOutgoing()
286 {
287 OutgoingPacket packet;
288 LocklessQueue<OutgoingPacket> queue;
289 TokenBucket bucket;
290 bool packetSent = false;
291
292 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
293 {
294 bucket = throttleCategories[i];
295
296 if (nextPackets[i] != null)
297 {
298 // This bucket was empty the last time we tried to send a packet,
299 // leaving a dequeued packet still waiting to be sent out. Try to
300 // send it again
301 if (bucket.RemoveTokens(nextPacketLengths[i]))
302 {
303 // Send the packet
304 udpServer.SendPacketFinal(nextPackets[i]);
305 nextPackets[i] = null;
306 packetSent = true;
307 }
308 }
309 else
310 {
311 // No dequeued packet waiting to be sent, try to pull one off
312 // this queue
313 queue = packetOutboxes[i];
314 if (queue.Dequeue(out packet))
315 {
316 // A packet was pulled off the queue. See if we have
317 // enough tokens in the bucket to send it out
318 if (bucket.RemoveTokens(packet.Buffer.DataLength))
319 {
320 // Send the packet
321 udpServer.SendPacketFinal(packet);
322 packetSent = true;
323 }
324 else
325 {
326 // Save the dequeued packet and the length calculation for
327 // the next iteration
328 nextPackets[i] = packet;
329 nextPacketLengths[i] = packet.Buffer.DataLength;
330 }
331 }
332 else
333 {
334 // No packets in this queue. Fire the queue empty callback
335 QueueEmpty callback = OnQueueEmpty;
336 if (callback != null)
337 callback((ThrottleOutPacketType)i);
338 }
339 }
340 }
341
342 return packetSent;
343 }
344
345 public void UpdateRoundTrip(float r)
346 {
347 const float ALPHA = 0.125f;
348 const float BETA = 0.25f;
349 const float K = 4.0f;
350
351 if (RTTVAR == 0.0f)
352 {
353 // First RTT measurement
354 SRTT = r;
355 RTTVAR = r * 0.5f;
356 }
357 else
358 {
359 // Subsequence RTT measurement
360 RTTVAR = (1.0f - BETA) * RTTVAR + BETA * Math.Abs(SRTT - r);
361 SRTT = (1.0f - ALPHA) * SRTT + ALPHA * r;
362 }
363
364 // Always round retransmission timeout up to two seconds
365 RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR)));
366 //Logger.Debug("Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " +
367 // RTTVAR + " based on new RTT of " + r + "ms");
368 }
369 }
370}
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 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Net;
31using OpenSim.Framework;
32using OpenMetaverse;
33
34using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim;
35
36namespace OpenSim.Region.ClientStack.LindenUDP
37{
38 public sealed class UDPClientCollection
39 {
40 Dictionary<UUID, LLUDPClient> Dictionary1;
41 Dictionary<IPEndPoint, LLUDPClient> Dictionary2;
42 LLUDPClient[] Array;
43 ReaderWriterLockImpl rwLock = new ReaderWriterLockImpl();
44
45 public UDPClientCollection()
46 {
47 Dictionary1 = new Dictionary<UUID, LLUDPClient>();
48 Dictionary2 = new Dictionary<IPEndPoint, LLUDPClient>();
49 Array = new LLUDPClient[0];
50 }
51
52 public UDPClientCollection(int capacity)
53 {
54 Dictionary1 = new Dictionary<UUID, LLUDPClient>(capacity);
55 Dictionary2 = new Dictionary<IPEndPoint, LLUDPClient>(capacity);
56 Array = new LLUDPClient[0];
57 }
58
59 public void Add(UUID key1, IPEndPoint key2, LLUDPClient value)
60 {
61 rwLock.EnterWriteLock();
62
63 try
64 {
65 if (Dictionary1.ContainsKey(key1))
66 {
67 if (!Dictionary2.ContainsKey(key2))
68 throw new ArgumentException("key1 exists in the dictionary but not key2");
69 }
70 else if (Dictionary2.ContainsKey(key2))
71 {
72 if (!Dictionary1.ContainsKey(key1))
73 throw new ArgumentException("key2 exists in the dictionary but not key1");
74 }
75
76 Dictionary1[key1] = value;
77 Dictionary2[key2] = value;
78
79 LLUDPClient[] oldArray = Array;
80 int oldLength = oldArray.Length;
81
82 LLUDPClient[] newArray = new LLUDPClient[oldLength + 1];
83 for (int i = 0; i < oldLength; i++)
84 newArray[i] = oldArray[i];
85 newArray[oldLength] = value;
86
87 Array = newArray;
88 }
89 finally { rwLock.ExitWriteLock(); }
90 }
91
92 public bool Remove(UUID key1, IPEndPoint key2)
93 {
94 rwLock.EnterWriteLock();
95
96 try
97 {
98 LLUDPClient value;
99 if (Dictionary1.TryGetValue(key1, out value))
100 {
101 Dictionary1.Remove(key1);
102 Dictionary2.Remove(key2);
103
104 LLUDPClient[] oldArray = Array;
105 int oldLength = oldArray.Length;
106
107 LLUDPClient[] newArray = new LLUDPClient[oldLength - 1];
108 int j = 0;
109 for (int i = 0; i < oldLength; i++)
110 {
111 if (oldArray[i] != value)
112 newArray[j++] = oldArray[i];
113 }
114
115 Array = newArray;
116 return true;
117 }
118 }
119 finally { rwLock.ExitWriteLock(); }
120
121 return false;
122 }
123
124 public void Clear()
125 {
126 rwLock.EnterWriteLock();
127
128 try
129 {
130 Dictionary1.Clear();
131 Dictionary2.Clear();
132 Array = new LLUDPClient[0];
133 }
134 finally { rwLock.ExitWriteLock(); }
135 }
136
137 public int Count
138 {
139 get { return Array.Length; }
140 }
141
142 public bool ContainsKey(UUID key)
143 {
144 return Dictionary1.ContainsKey(key);
145 }
146
147 public bool ContainsKey(IPEndPoint key)
148 {
149 return Dictionary2.ContainsKey(key);
150 }
151
152 public bool TryGetValue(UUID key, out LLUDPClient value)
153 {
154 bool success;
155 bool doLock = !rwLock.IsUpgradeableReadLockHeld;
156 if (doLock) rwLock.EnterReadLock();
157
158 try { success = Dictionary1.TryGetValue(key, out value); }
159 finally { if (doLock) rwLock.ExitReadLock(); }
160
161 return success;
162 }
163
164 public bool TryGetValue(IPEndPoint key, out LLUDPClient value)
165 {
166 bool success;
167 bool doLock = !rwLock.IsUpgradeableReadLockHeld;
168 if (doLock) rwLock.EnterReadLock();
169
170 try { success = Dictionary2.TryGetValue(key, out value); }
171 finally { if (doLock) rwLock.ExitReadLock(); }
172
173 return success;
174 }
175
176 public void ForEach(Action<LLUDPClient> action)
177 {
178 bool doLock = !rwLock.IsUpgradeableReadLockHeld;
179 if (doLock) rwLock.EnterUpgradeableReadLock();
180
181 try { Parallel.ForEach<LLUDPClient>(Array, action); }
182 finally { if (doLock) rwLock.ExitUpgradeableReadLock(); }
183 }
184 }
185}
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;
35using Nini.Config; 35using Nini.Config;
36using OpenMetaverse.Packets; 36using OpenMetaverse.Packets;
37using OpenSim.Framework; 37using OpenSim.Framework;
38using OpenSim.Framework.Statistics;
38using OpenSim.Region.Framework.Scenes; 39using OpenSim.Region.Framework.Scenes;
39using OpenMetaverse; 40using OpenMetaverse;
40 41
@@ -190,31 +191,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
190 } 191 }
191 } 192 }
192 193
193 public void RemoveClient(LLUDPClient udpClient)
194 {
195 IClientAPI client;
196
197 if (m_scene.ClientManager.TryGetClient(udpClient.CircuitCode, out client))
198 RemoveClient(client);
199 else
200 m_log.Warn("[LLUDPSERVER]: Failed to lookup IClientAPI for LLUDPClient " + udpClient.AgentID);
201 }
202
203 public void SetClientPaused(UUID agentID, bool paused)
204 {
205 LLUDPClient client;
206 if (clients.TryGetValue(agentID, out client))
207 {
208 client.IsPaused = paused;
209 }
210 else
211 {
212 m_log.Warn("[LLUDPSERVER]: Attempted to pause/unpause unknown agent " + agentID);
213 }
214 }
215
216 public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting) 194 public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting)
217 { 195 {
196 // CoarseLocationUpdate packets cannot be split in an automated way
197 if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting)
198 allowSplitting = false;
199
218 if (allowSplitting && packet.HasVariableBlocks) 200 if (allowSplitting && packet.HasVariableBlocks)
219 { 201 {
220 byte[][] datas = packet.ToBytesMultiple(); 202 byte[][] datas = packet.ToBytesMultiple();
@@ -251,6 +233,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
251 233
252 public void SendPacket(LLUDPClient client, Packet packet, ThrottleOutPacketType category, bool allowSplitting) 234 public void SendPacket(LLUDPClient client, Packet packet, ThrottleOutPacketType category, bool allowSplitting)
253 { 235 {
236 // CoarseLocationUpdate packets cannot be split in an automated way
237 if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting)
238 allowSplitting = false;
239
254 if (allowSplitting && packet.HasVariableBlocks) 240 if (allowSplitting && packet.HasVariableBlocks)
255 { 241 {
256 byte[][] datas = packet.ToBytesMultiple(); 242 byte[][] datas = packet.ToBytesMultiple();
@@ -339,6 +325,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
339 } 325 }
340 } 326 }
341 327
328 public void SendPing(LLUDPClient client)
329 {
330 IClientAPI api = client.ClientAPI;
331 if (api != null)
332 api.SendStartPingCheck(client.CurrentPingSequence++);
333 }
334
342 public void ResendUnacked(LLUDPClient client) 335 public void ResendUnacked(LLUDPClient client)
343 { 336 {
344 if (client.NeedAcks.Count > 0) 337 if (client.NeedAcks.Count > 0)
@@ -387,9 +380,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
387 //FIXME: Make 60 an .ini setting 380 //FIXME: Make 60 an .ini setting
388 if (Environment.TickCount - client.TickLastPacketReceived > 1000 * 60) 381 if (Environment.TickCount - client.TickLastPacketReceived > 1000 * 60)
389 { 382 {
390 m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + client.RemoteEndPoint); 383 m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + client.ClientAPI.Name);
391 384
392 RemoveClient(client); 385 RemoveClient(client.ClientAPI);
393 return; 386 return;
394 } 387 }
395 } 388 }
@@ -590,8 +583,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
590 LLUDPClient client = new LLUDPClient(this, m_throttleRates, m_throttle, circuitCode, agentID, remoteEndPoint); 583 LLUDPClient client = new LLUDPClient(this, m_throttleRates, m_throttle, circuitCode, agentID, remoteEndPoint);
591 clients.Add(agentID, client.RemoteEndPoint, client); 584 clients.Add(agentID, client.RemoteEndPoint, client);
592 585
593 // Create the IClientAPI 586 // Create the LLClientView
594 IClientAPI clientApi = new LLClientView(remoteEndPoint, m_scene, this, client, sessionInfo, agentID, sessionID, circuitCode); 587 LLClientView clientApi = new LLClientView(remoteEndPoint, m_scene, this, client, sessionInfo, agentID, sessionID, circuitCode);
595 clientApi.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler; 588 clientApi.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler;
596 clientApi.OnLogout += LogoutHandler; 589 clientApi.OnLogout += LogoutHandler;
597 clientApi.OnConnectionClosed += RemoveClient; 590 clientApi.OnConnectionClosed += RemoveClient;
@@ -618,23 +611,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
618 611
619 private void IncomingPacketHandler() 612 private void IncomingPacketHandler()
620 { 613 {
621 IncomingPacket incomingPacket = new IncomingPacket(); 614 // Set this culture for the thread that incoming packets are received
622 Packet packet = null; 615 // on to en-US to avoid number parsing issues
623 LLUDPClient client = null; 616 Culture.SetCurrentCulture();
617
618 IncomingPacket incomingPacket = default(IncomingPacket);
624 619
625 while (base.IsRunning) 620 while (base.IsRunning)
626 { 621 {
627 // Reset packet to null for the check below
628 packet = null;
629
630 if (packetInbox.Dequeue(100, ref incomingPacket)) 622 if (packetInbox.Dequeue(100, ref incomingPacket))
631 { 623 Util.FireAndForget(ProcessInPacket, incomingPacket);
632 packet = incomingPacket.Packet;
633 client = incomingPacket.Client;
634
635 if (packet != null && client != null)
636 client.ClientAPI.ProcessInPacket(packet);
637 }
638 } 624 }
639 625
640 if (packetInbox.Count > 0) 626 if (packetInbox.Count > 0)
@@ -642,32 +628,98 @@ namespace OpenSim.Region.ClientStack.LindenUDP
642 packetInbox.Clear(); 628 packetInbox.Clear();
643 } 629 }
644 630
631 private void ProcessInPacket(object state)
632 {
633 IncomingPacket incomingPacket = (IncomingPacket)state;
634 Packet packet = incomingPacket.Packet;
635 LLUDPClient client = incomingPacket.Client;
636
637 if (packet != null && client != null)
638 {
639 try
640 {
641 client.ClientAPI.ProcessInPacket(packet);
642 }
643 catch (ThreadAbortException)
644 {
645 throw;
646 }
647 catch (Exception e)
648 {
649 if (StatsManager.SimExtraStats != null)
650 StatsManager.SimExtraStats.AddAbnormalClientThreadTermination();
651
652 // Don't let a failure in an individual client thread crash the whole sim.
653 m_log.ErrorFormat("[LLUDPSERVER]: Client thread for {0} {1} crashed. Logging them out", client.ClientAPI.Name, client.AgentID);
654 m_log.Error(e.Message, e);
655
656 try
657 {
658 // Make an attempt to alert the user that their session has crashed
659 AgentAlertMessagePacket alert = client.ClientAPI.BuildAgentAlertPacket(
660 "Unfortunately the session for this client on the server has crashed.\n" +
661 "Any further actions taken will not be processed.\n" +
662 "Please relog", true);
663
664 SendPacket(client, alert, ThrottleOutPacketType.Unknown, false);
665
666 // TODO: There may be a better way to do this. Perhaps kick? Not sure this propogates notifications to
667 // listeners yet, though.
668 client.ClientAPI.SendLogoutPacket();
669 RemoveClient(client.ClientAPI);
670 }
671 catch (ThreadAbortException)
672 {
673 throw;
674 }
675 catch (Exception e2)
676 {
677 m_log.Error("[LLUDPSERVER]: Further exception thrown on forced session logout for " + client.ClientAPI.Name);
678 m_log.Error(e2.Message, e2);
679 }
680 }
681 }
682 }
683
645 private void OutgoingPacketHandler() 684 private void OutgoingPacketHandler()
646 { 685 {
686 // Set this culture for the thread that outgoing packets are sent
687 // on to en-US to avoid number parsing issues
688 Culture.SetCurrentCulture();
689
647 int now = Environment.TickCount; 690 int now = Environment.TickCount;
648 int elapsedMS = 0; 691 int elapsedMS = 0;
649 int elapsed100MS = 0; 692 int elapsed100MS = 0;
693 int elapsed500MS = 0;
650 694
651 while (base.IsRunning) 695 while (base.IsRunning)
652 { 696 {
653 bool resendUnacked = false; 697 bool resendUnacked = false;
654 bool sendAcks = false; 698 bool sendAcks = false;
699 bool sendPings = false;
655 bool packetSent = false; 700 bool packetSent = false;
656 701
657 elapsedMS += Environment.TickCount - now; 702 elapsedMS += Environment.TickCount - now;
658 703
659 // Check for packets that need to be resent every 100ms 704 // Check for pending outgoing resends every 100ms
660 if (elapsedMS >= 100) 705 if (elapsedMS >= 100)
661 { 706 {
662 resendUnacked = true; 707 resendUnacked = true;
663 elapsedMS -= 100; 708 elapsedMS -= 100;
664 ++elapsed100MS; 709 ++elapsed100MS;
665 } 710 }
666 // Check for ACKs that need to be sent out every 500ms 711 // Check for pending outgoing ACKs every 500ms
667 if (elapsed100MS >= 5) 712 if (elapsed100MS >= 5)
668 { 713 {
669 sendAcks = true; 714 sendAcks = true;
670 elapsed100MS = 0; 715 elapsed100MS = 0;
716 ++elapsed500MS;
717 }
718 // Send pings to clients every 2000ms
719 if (elapsed500MS >= 4)
720 {
721 sendPings = true;
722 elapsed500MS = 0;
671 } 723 }
672 724
673 clients.ForEach( 725 clients.ForEach(
@@ -679,6 +731,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
679 ResendUnacked(client); 731 ResendUnacked(client);
680 if (sendAcks) 732 if (sendAcks)
681 SendAcks(client); 733 SendAcks(client);
734 if (sendPings)
735 SendPing(client);
682 } 736 }
683 ); 737 );
684 738
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUtil.cs b/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs
index 066b19d..69b0c5f 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUtil.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs
@@ -1,4 +1,4 @@
1/* 1/*
2 * Copyright (c) Contributors, http://opensimulator.org/ 2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders. 3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 * 4 *
@@ -26,31 +26,31 @@
26 */ 26 */
27 27
28using System; 28using System;
29using System.Collections.Generic; 29using OpenSim.Framework;
30using System.Net;
31using OpenMetaverse; 30using OpenMetaverse;
32 31
33using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim;
34
35namespace OpenSim.Region.ClientStack.LindenUDP 32namespace OpenSim.Region.ClientStack.LindenUDP
36{ 33{
37 public class LLUtil 34 public sealed class OutgoingPacket
38 { 35 {
39 /// <summary> 36 /// <summary>Client this packet is destined for</summary>
40 /// Convert a string to bytes suitable for use in an LL UDP packet. 37 public LLUDPClient Client;
41 /// </summary> 38 /// <summary>Packet data to send</summary>
42 /// <param name="s">Truncated to 254 characters if necessary</param> 39 public UDPPacketBuffer Buffer;
43 /// <returns></returns> 40 /// <summary>Sequence number of the wrapped packet</summary>
44 public static byte[] StringToPacketBytes(string s) 41 public uint SequenceNumber;
42 /// <summary>Number of times this packet has been resent</summary>
43 public int ResendCount;
44 /// <summary>Environment.TickCount when this packet was last sent over the wire</summary>
45 public int TickCount;
46 /// <summary>Category this packet belongs to</summary>
47 public ThrottleOutPacketType Category;
48
49 public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category)
45 { 50 {
46 // Anything more than 254 will cause libsecondlife to barf 51 Client = client;
47 // (libsl 1550) adds an \0 on the Utils.StringToBytes conversion if it isn't present 52 Buffer = buffer;
48 if (s.Length > 254) 53 Category = category;
49 {
50 s = s.Remove(254);
51 }
52
53 return Utils.StringToBytes(s);
54 } 54 }
55 } 55 }
56} 56}
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 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using Nini.Config;
30
31namespace OpenSim.Region.ClientStack.LindenUDP
32{
33 public sealed class ThrottleRates
34 {
35 public int Resend;
36 public int Land;
37 public int Wind;
38 public int Cloud;
39 public int Task;
40 public int Texture;
41 public int Asset;
42
43 public int ResendLimit;
44 public int LandLimit;
45 public int WindLimit;
46 public int CloudLimit;
47 public int TaskLimit;
48 public int TextureLimit;
49 public int AssetLimit;
50
51 public ThrottleRates(IConfigSource config)
52 {
53 try
54 {
55 IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"];
56
57 Resend = throttleConfig.GetInt("ResendDefault", 12500);
58 Land = throttleConfig.GetInt("LandDefault", 500);
59 Wind = throttleConfig.GetInt("WindDefault", 500);
60 Cloud = throttleConfig.GetInt("CloudDefault", 500);
61 Task = throttleConfig.GetInt("TaskDefault", 500);
62 Texture = throttleConfig.GetInt("TextureDefault", 500);
63 Asset = throttleConfig.GetInt("AssetDefault", 500);
64
65 ResendLimit = throttleConfig.GetInt("ResendLimit", 18750);
66 LandLimit = throttleConfig.GetInt("LandLimit", 29750);
67 WindLimit = throttleConfig.GetInt("WindLimit", 18750);
68 CloudLimit = throttleConfig.GetInt("CloudLimit", 18750);
69 TaskLimit = throttleConfig.GetInt("TaskLimit", 55750);
70 TextureLimit = throttleConfig.GetInt("TextureLimit", 55750);
71 AssetLimit = throttleConfig.GetInt("AssetLimit", 27500);
72 }
73 catch (Exception) { }
74 }
75 }
76}
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 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Net;
31using OpenMetaverse;
32
33namespace OpenSim.Region.ClientStack.LindenUDP
34{
35 public sealed class UnackedPacketCollection
36 {
37 public object SyncRoot = new object();
38
39 SortedDictionary<uint, OutgoingPacket> packets;
40
41 public int Count { get { return packets.Count; } }
42
43 public UnackedPacketCollection()
44 {
45 packets = new SortedDictionary<uint, OutgoingPacket>();
46 }
47
48 public bool Add(OutgoingPacket packet)
49 {
50 lock (SyncRoot)
51 {
52 if (!packets.ContainsKey(packet.SequenceNumber))
53 {
54 packets.Add(packet.SequenceNumber, packet);
55 return true;
56 }
57 return false;
58 }
59 }
60
61 public bool RemoveUnsafe(uint sequenceNumber)
62 {
63 return packets.Remove(sequenceNumber);
64 }
65
66 public bool RemoveUnsafe(uint sequenceNumber, out OutgoingPacket packet)
67 {
68 if (packets.TryGetValue(sequenceNumber, out packet))
69 {
70 packets.Remove(sequenceNumber);
71 return true;
72 }
73
74 return false;
75 }
76
77 public OutgoingPacket GetOldest()
78 {
79 lock (SyncRoot)
80 {
81 using (SortedDictionary<uint, OutgoingPacket>.ValueCollection.Enumerator e = packets.Values.GetEnumerator())
82 return e.Current;
83 }
84 }
85
86 public List<OutgoingPacket> GetExpiredPackets(int timeout)
87 {
88 List<OutgoingPacket> expiredPackets = null;
89
90 lock (SyncRoot)
91 {
92 int now = Environment.TickCount;
93 foreach (OutgoingPacket packet in packets.Values)
94 {
95 if (packet.TickCount == 0)
96 continue;
97
98 if (now - packet.TickCount >= timeout)
99 {
100 if (expiredPackets == null)
101 expiredPackets = new List<OutgoingPacket>();
102 expiredPackets.Add(packet);
103 }
104 else
105 {
106 break;
107 }
108 }
109 }
110
111 return expiredPackets;
112 }
113 }
114}