aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/Linden/UDP
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/Linden/UDP')
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/IncomingPacketAsyncHandlingEngine.cs328
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs98
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs66
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs505
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs762
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs18
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/OutgoingQueueRefillEngine.cs4
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs106
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs1
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs79
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/Tests/PacketHandlerTests.cs1
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/Tests/TestLLUDPServer.cs170
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/Tests/ThrottleTests.cs425
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs1
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs224
15 files changed, 1894 insertions, 894 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacketAsyncHandlingEngine.cs b/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacketAsyncHandlingEngine.cs
new file mode 100644
index 0000000..6f40b24
--- /dev/null
+++ b/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacketAsyncHandlingEngine.cs
@@ -0,0 +1,328 @@
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.Concurrent;
30using System.Reflection;
31using System.Threading;
32using log4net;
33using OpenSim.Framework;
34using OpenSim.Framework.Monitoring;
35using OpenSim.Region.Framework.Scenes;
36
37namespace OpenSim.Region.ClientStack.LindenUDP
38{
39 public class Job
40 {
41 public string Name;
42 public WaitCallback Callback;
43 public object O;
44
45 public Job(string name, WaitCallback callback, object o)
46 {
47 Name = name;
48 Callback = callback;
49 O = o;
50 }
51 }
52
53 // TODO: These kinds of classes MUST be generalized with JobEngine, etc.
54 public class IncomingPacketAsyncHandlingEngine
55 {
56 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
57
58 public int LogLevel { get; set; }
59
60 public bool IsRunning { get; private set; }
61
62 /// <summary>
63 /// The timeout in milliseconds to wait for at least one event to be written when the recorder is stopping.
64 /// </summary>
65 public int RequestProcessTimeoutOnStop { get; set; }
66
67 /// <summary>
68 /// Controls whether we need to warn in the log about exceeding the max queue size.
69 /// </summary>
70 /// <remarks>
71 /// This is flipped to false once queue max has been exceeded and back to true when it falls below max, in
72 /// order to avoid spamming the log with lots of warnings.
73 /// </remarks>
74 private bool m_warnOverMaxQueue = true;
75
76 private BlockingCollection<Job> m_requestQueue;
77
78 private CancellationTokenSource m_cancelSource = new CancellationTokenSource();
79
80 private LLUDPServer m_udpServer;
81
82 private Stat m_requestsWaitingStat;
83
84 private Job m_currentJob;
85
86 /// <summary>
87 /// Used to signal that we are ready to complete stop.
88 /// </summary>
89 private ManualResetEvent m_finishedProcessingAfterStop = new ManualResetEvent(false);
90
91 public IncomingPacketAsyncHandlingEngine(LLUDPServer server)
92 {
93 //LogLevel = 1;
94 m_udpServer = server;
95 RequestProcessTimeoutOnStop = 5000;
96
97 // MainConsole.Instance.Commands.AddCommand(
98 // "Debug",
99 // false,
100 // "debug jobengine",
101 // "debug jobengine <start|stop|status>",
102 // "Start, stop or get status of the job engine.",
103 // "If stopped then all jobs are processed immediately.",
104 // HandleControlCommand);
105 }
106
107 public void Start()
108 {
109 lock (this)
110 {
111 if (IsRunning)
112 return;
113
114 IsRunning = true;
115
116 m_finishedProcessingAfterStop.Reset();
117
118 m_requestQueue = new BlockingCollection<Job>(new ConcurrentQueue<Job>(), 5000);
119
120 m_requestsWaitingStat =
121 new Stat(
122 "IncomingPacketAsyncRequestsWaiting",
123 "Number of incoming packets waiting for async processing in engine.",
124 "",
125 "",
126 "clientstack",
127 m_udpServer.Scene.Name,
128 StatType.Pull,
129 MeasuresOfInterest.None,
130 stat => stat.Value = m_requestQueue.Count,
131 StatVerbosity.Debug);
132
133 StatsManager.RegisterStat(m_requestsWaitingStat);
134
135 WorkManager.StartThread(
136 ProcessRequests,
137 string.Format("Incoming Packet Async Handling Engine Thread ({0})", m_udpServer.Scene.Name),
138 ThreadPriority.Normal,
139 false,
140 true,
141 null,
142 int.MaxValue);
143 }
144 }
145
146 public void Stop()
147 {
148 lock (this)
149 {
150 try
151 {
152 if (!IsRunning)
153 return;
154
155 IsRunning = false;
156
157 int requestsLeft = m_requestQueue.Count;
158
159 if (requestsLeft <= 0)
160 {
161 m_cancelSource.Cancel();
162 }
163 else
164 {
165 m_log.InfoFormat("[INCOMING PACKET ASYNC HANDLING ENGINE]: Waiting to write {0} events after stop.", requestsLeft);
166
167 while (requestsLeft > 0)
168 {
169 if (!m_finishedProcessingAfterStop.WaitOne(RequestProcessTimeoutOnStop))
170 {
171 // After timeout no events have been written
172 if (requestsLeft == m_requestQueue.Count)
173 {
174 m_log.WarnFormat(
175 "[INCOMING PACKET ASYNC HANDLING ENGINE]: No requests processed after {0} ms wait. Discarding remaining {1} requests",
176 RequestProcessTimeoutOnStop, requestsLeft);
177
178 break;
179 }
180 }
181
182 requestsLeft = m_requestQueue.Count;
183 }
184 }
185 }
186 finally
187 {
188 m_cancelSource.Dispose();
189 StatsManager.DeregisterStat(m_requestsWaitingStat);
190 m_requestsWaitingStat = null;
191 m_requestQueue = null;
192 }
193 }
194 }
195
196 public bool QueueRequest(string name, WaitCallback req, object o)
197 {
198 if (LogLevel >= 1)
199 m_log.DebugFormat("[INCOMING PACKET ASYNC HANDLING ENGINE]: Queued job {0}", name);
200
201 if (m_requestQueue.Count < m_requestQueue.BoundedCapacity)
202 {
203 // m_log.DebugFormat(
204 // "[OUTGOING QUEUE REFILL ENGINE]: Adding request for categories {0} for {1} in {2}",
205 // categories, client.AgentID, m_udpServer.Scene.Name);
206
207 m_requestQueue.Add(new Job(name, req, o));
208
209 if (!m_warnOverMaxQueue)
210 m_warnOverMaxQueue = true;
211
212 return true;
213 }
214 else
215 {
216 if (m_warnOverMaxQueue)
217 {
218 // m_log.WarnFormat(
219 // "[JOB ENGINE]: Request queue at maximum capacity, not recording request from {0} in {1}",
220 // client.AgentID, m_udpServer.Scene.Name);
221
222 m_log.WarnFormat("[INCOMING PACKET ASYNC HANDLING ENGINE]: Request queue at maximum capacity, not recording job");
223
224 m_warnOverMaxQueue = false;
225 }
226
227 return false;
228 }
229 }
230
231 private void ProcessRequests()
232 {
233 try
234 {
235 while (IsRunning || m_requestQueue.Count > 0)
236 {
237 m_currentJob = m_requestQueue.Take(m_cancelSource.Token);
238
239 // QueueEmpty callback = req.Client.OnQueueEmpty;
240 //
241 // if (callback != null)
242 // {
243 // try
244 // {
245 // callback(req.Categories);
246 // }
247 // catch (Exception e)
248 // {
249 // m_log.Error("[OUTGOING QUEUE REFILL ENGINE]: ProcessRequests(" + req.Categories + ") threw an exception: " + e.Message, e);
250 // }
251 // }
252
253 if (LogLevel >= 1)
254 m_log.DebugFormat("[INCOMING PACKET ASYNC HANDLING ENGINE]: Processing job {0}", m_currentJob.Name);
255
256 try
257 {
258 m_currentJob.Callback.Invoke(m_currentJob.O);
259 }
260 catch (Exception e)
261 {
262 m_log.Error(
263 string.Format(
264 "[INCOMING PACKET ASYNC HANDLING ENGINE]: Job {0} failed, continuing. Exception ", m_currentJob.Name), e);
265 }
266
267 if (LogLevel >= 1)
268 m_log.DebugFormat("[INCOMING PACKET ASYNC HANDLING ENGINE]: Processed job {0}", m_currentJob.Name);
269
270 m_currentJob = null;
271 }
272 }
273 catch (OperationCanceledException)
274 {
275 }
276
277 m_finishedProcessingAfterStop.Set();
278 }
279
280 // private void HandleControlCommand(string module, string[] args)
281 // {
282 // // if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
283 // // return;
284 //
285 // if (args.Length < 3)
286 // {
287 // MainConsole.Instance.Output("Usage: debug jobengine <stop|start|status|loglevel>");
288 // return;
289 // }
290 //
291 // string subCommand = args[2];
292 //
293 // if (subCommand == "stop")
294 // {
295 // Stop();
296 // MainConsole.Instance.OutputFormat("Stopped job engine.");
297 // }
298 // else if (subCommand == "start")
299 // {
300 // Start();
301 // MainConsole.Instance.OutputFormat("Started job engine.");
302 // }
303 // else if (subCommand == "status")
304 // {
305 // MainConsole.Instance.OutputFormat("Job engine running: {0}", IsRunning);
306 // MainConsole.Instance.OutputFormat("Current job {0}", m_currentJob != null ? m_currentJob.Name : "none");
307 // MainConsole.Instance.OutputFormat(
308 // "Jobs waiting: {0}", IsRunning ? m_requestQueue.Count.ToString() : "n/a");
309 // MainConsole.Instance.OutputFormat("Log Level: {0}", LogLevel);
310 // }
311 //
312 // else if (subCommand == "loglevel")
313 // {
314 // // int logLevel;
315 // int logLevel = int.Parse(args[3]);
316 // // if (ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out logLevel))
317 // // {
318 // LogLevel = logLevel;
319 // MainConsole.Instance.OutputFormat("Set log level to {0}", LogLevel);
320 // // }
321 // }
322 // else
323 // {
324 // MainConsole.Instance.OutputFormat("Unrecognized job engine subcommand {0}", subCommand);
325 // }
326 // }
327 }
328}
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
index c839c05..85f9d68 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
@@ -648,12 +648,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP
648 /// <returns>true if the handler was added. This is currently always the case.</returns> 648 /// <returns>true if the handler was added. This is currently always the case.</returns>
649 public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync) 649 public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync)
650 { 650 {
651 return AddLocalPacketHandler(packetType, handler, doAsync, false);
652 }
653
654 /// <summary>
655 /// Add a handler for the given packet type.
656 /// </summary>
657 /// <param name="packetType"></param>
658 /// <param name="handler"></param>
659 /// <param name="doAsync">
660 /// If true, when the packet is received handle it on a different thread. Whether this is given direct to
661 /// a threadpool thread or placed in a queue depends on the inEngine parameter.
662 /// </param>
663 /// <param name="inEngine">
664 /// If async is false then this parameter is ignored.
665 /// If async is true and inEngine is false, then the packet is sent directly to a
666 /// threadpool thread.
667 /// If async is true and inEngine is true, then the packet is sent to the IncomingPacketAsyncHandlingEngine.
668 /// This may result in slower handling but reduces the risk of overloading the simulator when there are many
669 /// simultaneous async requests.
670 /// </param>
671 /// <returns>true if the handler was added. This is currently always the case.</returns>
672 public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync, bool inEngine)
673 {
651 bool result = false; 674 bool result = false;
652 lock (m_packetHandlers) 675 lock (m_packetHandlers)
653 { 676 {
654 if (!m_packetHandlers.ContainsKey(packetType)) 677 if (!m_packetHandlers.ContainsKey(packetType))
655 { 678 {
656 m_packetHandlers.Add(packetType, new PacketProcessor() { method = handler, Async = doAsync }); 679 m_packetHandlers.Add(
680 packetType, new PacketProcessor() { method = handler, Async = doAsync, InEngine = inEngine });
657 result = true; 681 result = true;
658 } 682 }
659 } 683 }
@@ -688,21 +712,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
688 PacketProcessor pprocessor; 712 PacketProcessor pprocessor;
689 if (m_packetHandlers.TryGetValue(packet.Type, out pprocessor)) 713 if (m_packetHandlers.TryGetValue(packet.Type, out pprocessor))
690 { 714 {
715 ClientInfo cinfo = UDPClient.GetClientInfo();
716
691 //there is a local handler for this packet type 717 //there is a local handler for this packet type
692 if (pprocessor.Async) 718 if (pprocessor.Async)
693 { 719 {
694 ClientInfo cinfo = UDPClient.GetClientInfo();
695 if (!cinfo.AsyncRequests.ContainsKey(packet.Type.ToString())) 720 if (!cinfo.AsyncRequests.ContainsKey(packet.Type.ToString()))
696 cinfo.AsyncRequests[packet.Type.ToString()] = 0; 721 cinfo.AsyncRequests[packet.Type.ToString()] = 0;
697 cinfo.AsyncRequests[packet.Type.ToString()]++; 722 cinfo.AsyncRequests[packet.Type.ToString()]++;
698 723
699 object obj = new AsyncPacketProcess(this, pprocessor.method, packet); 724 object obj = new AsyncPacketProcess(this, pprocessor.method, packet);
700 Util.FireAndForget(ProcessSpecificPacketAsync, obj, packet.Type.ToString()); 725
726 if (pprocessor.InEngine)
727 m_udpServer.IpahEngine.QueueRequest(
728 packet.Type.ToString(),
729 ProcessSpecificPacketAsync,
730 obj);
731 else
732 Util.FireAndForget(ProcessSpecificPacketAsync, obj, packet.Type.ToString());
733
701 result = true; 734 result = true;
702 } 735 }
703 else 736 else
704 { 737 {
705 ClientInfo cinfo = UDPClient.GetClientInfo();
706 if (!cinfo.SyncRequests.ContainsKey(packet.Type.ToString())) 738 if (!cinfo.SyncRequests.ContainsKey(packet.Type.ToString()))
707 cinfo.SyncRequests[packet.Type.ToString()] = 0; 739 cinfo.SyncRequests[packet.Type.ToString()] = 0;
708 cinfo.SyncRequests[packet.Type.ToString()]++; 740 cinfo.SyncRequests[packet.Type.ToString()]++;
@@ -1161,7 +1193,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1161 /// <param name="map">heightmap</param> 1193 /// <param name="map">heightmap</param>
1162 public virtual void SendLayerData(float[] map) 1194 public virtual void SendLayerData(float[] map)
1163 { 1195 {
1164 Util.FireAndForget(DoSendLayerData, m_scene.Heightmap.GetTerrainData()); 1196 Util.FireAndForget(DoSendLayerData, m_scene.Heightmap.GetTerrainData(), "LLClientView.DoSendLayerData");
1165 } 1197 }
1166 1198
1167 /// <summary> 1199 /// <summary>
@@ -1373,7 +1405,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1373 /// <param name="windSpeeds">16x16 array of wind speeds</param> 1405 /// <param name="windSpeeds">16x16 array of wind speeds</param>
1374 public virtual void SendWindData(Vector2[] windSpeeds) 1406 public virtual void SendWindData(Vector2[] windSpeeds)
1375 { 1407 {
1376 Util.FireAndForget(DoSendWindData, windSpeeds); 1408 Util.FireAndForget(DoSendWindData, windSpeeds, "LLClientView.SendWindData");
1377 } 1409 }
1378 1410
1379 /// <summary> 1411 /// <summary>
@@ -1382,7 +1414,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1382 /// <param name="windSpeeds">16x16 array of cloud densities</param> 1414 /// <param name="windSpeeds">16x16 array of cloud densities</param>
1383 public virtual void SendCloudData(float[] cloudDensity) 1415 public virtual void SendCloudData(float[] cloudDensity)
1384 { 1416 {
1385 Util.FireAndForget(DoSendCloudData, cloudDensity); 1417 Util.FireAndForget(DoSendCloudData, cloudDensity, "LLClientView.SendCloudData");
1386 } 1418 }
1387 1419
1388 /// <summary> 1420 /// <summary>
@@ -3834,11 +3866,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3834 /// </summary> 3866 /// </summary>
3835 public void SendEntityUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) 3867 public void SendEntityUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags)
3836 { 3868 {
3837 //double priority = m_prioritizer.GetUpdatePriority(this, entity); 3869 if (entity.UUID == m_agentId && !updateFlags.HasFlag(PrimUpdateFlags.FullUpdate))
3838 uint priority = m_prioritizer.GetUpdatePriority(this, entity); 3870 {
3871 ImprovedTerseObjectUpdatePacket packet
3872 = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate);
3839 3873
3840 lock (m_entityUpdates.SyncRoot) 3874 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
3841 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); 3875 packet.RegionData.TimeDilation = Utils.FloatToUInt16(1, 0.0f, 1.0f);
3876 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1];
3877 packet.ObjectData[0] = CreateImprovedTerseBlock(entity, false);
3878 OutPacket(packet, ThrottleOutPacketType.Unknown, true);
3879 }
3880 else
3881 {
3882 //double priority = m_prioritizer.GetUpdatePriority(this, entity);
3883 uint priority = m_prioritizer.GetUpdatePriority(this, entity);
3884
3885 lock (m_entityUpdates.SyncRoot)
3886 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation));
3887 }
3842 } 3888 }
3843 3889
3844 /// <summary> 3890 /// <summary>
@@ -5540,10 +5586,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
5540 AddLocalPacketHandler(PacketType.ParcelBuy, HandleParcelBuyRequest, false); 5586 AddLocalPacketHandler(PacketType.ParcelBuy, HandleParcelBuyRequest, false);
5541 AddLocalPacketHandler(PacketType.UUIDGroupNameRequest, HandleUUIDGroupNameRequest); 5587 AddLocalPacketHandler(PacketType.UUIDGroupNameRequest, HandleUUIDGroupNameRequest);
5542 AddLocalPacketHandler(PacketType.ObjectGroup, HandleObjectGroupRequest); 5588 AddLocalPacketHandler(PacketType.ObjectGroup, HandleObjectGroupRequest);
5543 AddLocalPacketHandler(PacketType.GenericMessage, HandleGenericMessage); 5589 AddLocalPacketHandler(PacketType.GenericMessage, HandleGenericMessage, true, true);
5544 AddLocalPacketHandler(PacketType.AvatarPropertiesRequest, HandleAvatarPropertiesRequest); 5590 AddLocalPacketHandler(PacketType.AvatarPropertiesRequest, HandleAvatarPropertiesRequest, true, true);
5545 AddLocalPacketHandler(PacketType.ChatFromViewer, HandleChatFromViewer); 5591 AddLocalPacketHandler(PacketType.ChatFromViewer, HandleChatFromViewer);
5546 AddLocalPacketHandler(PacketType.AvatarPropertiesUpdate, HandlerAvatarPropertiesUpdate); 5592 AddLocalPacketHandler(PacketType.AvatarPropertiesUpdate, HandlerAvatarPropertiesUpdate, true, true);
5547 AddLocalPacketHandler(PacketType.ScriptDialogReply, HandlerScriptDialogReply); 5593 AddLocalPacketHandler(PacketType.ScriptDialogReply, HandlerScriptDialogReply);
5548 AddLocalPacketHandler(PacketType.ImprovedInstantMessage, HandlerImprovedInstantMessage); 5594 AddLocalPacketHandler(PacketType.ImprovedInstantMessage, HandlerImprovedInstantMessage);
5549 AddLocalPacketHandler(PacketType.AcceptFriendship, HandlerAcceptFriendship); 5595 AddLocalPacketHandler(PacketType.AcceptFriendship, HandlerAcceptFriendship);
@@ -5728,8 +5774,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
5728 AddLocalPacketHandler(PacketType.PickDelete, HandlePickDelete); 5774 AddLocalPacketHandler(PacketType.PickDelete, HandlePickDelete);
5729 AddLocalPacketHandler(PacketType.PickGodDelete, HandlePickGodDelete); 5775 AddLocalPacketHandler(PacketType.PickGodDelete, HandlePickGodDelete);
5730 AddLocalPacketHandler(PacketType.PickInfoUpdate, HandlePickInfoUpdate); 5776 AddLocalPacketHandler(PacketType.PickInfoUpdate, HandlePickInfoUpdate);
5731 AddLocalPacketHandler(PacketType.AvatarNotesUpdate, HandleAvatarNotesUpdate); 5777 AddLocalPacketHandler(PacketType.AvatarNotesUpdate, HandleAvatarNotesUpdate, true, true);
5732 AddLocalPacketHandler(PacketType.AvatarInterestsUpdate, HandleAvatarInterestsUpdate); 5778 AddLocalPacketHandler(PacketType.AvatarInterestsUpdate, HandleAvatarInterestsUpdate, true, true);
5733 AddLocalPacketHandler(PacketType.GrantUserRights, HandleGrantUserRights); 5779 AddLocalPacketHandler(PacketType.GrantUserRights, HandleGrantUserRights);
5734 AddLocalPacketHandler(PacketType.PlacesQuery, HandlePlacesQuery); 5780 AddLocalPacketHandler(PacketType.PlacesQuery, HandlePlacesQuery);
5735 AddLocalPacketHandler(PacketType.UpdateMuteListEntry, HandleUpdateMuteListEntry); 5781 AddLocalPacketHandler(PacketType.UpdateMuteListEntry, HandleUpdateMuteListEntry);
@@ -8079,7 +8125,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
8079 { 8125 {
8080 // This requests the asset if needed 8126 // This requests the asset if needed
8081 HandleSimInventoryTransferRequestWithPermsCheck(sender, transfer); 8127 HandleSimInventoryTransferRequestWithPermsCheck(sender, transfer);
8082 }); 8128 }, null, "LLClientView.HandleTransferRequest");
8129
8083 return true; 8130 return true;
8084 } 8131 }
8085 } 8132 }
@@ -12786,8 +12833,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
12786 12833
12787 public struct PacketProcessor 12834 public struct PacketProcessor
12788 { 12835 {
12789 public PacketMethod method; 12836 /// <summary>
12790 public bool Async; 12837 /// Packet handling method.
12838 /// </summary>
12839 public PacketMethod method { get; set; }
12840
12841 /// <summary>
12842 /// Should this packet be handled asynchronously?
12843 /// </summary>
12844 public bool Async { get; set; }
12845
12846 /// <summary>
12847 /// If async is true, should this packet be handled in the async engine or given directly to a threadpool
12848 /// thread?
12849 /// </summary>
12850 public bool InEngine { get; set; }
12791 } 12851 }
12792 12852
12793 public class AsyncPacketProcess 12853 public class AsyncPacketProcess
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs
index d8ca343..8f14806 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs
@@ -97,7 +97,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
97 { 97 {
98 m_throttleDebugLevel = value; 98 m_throttleDebugLevel = value;
99 m_throttleClient.DebugLevel = m_throttleDebugLevel; 99 m_throttleClient.DebugLevel = m_throttleDebugLevel;
100 m_throttleCategory.DebugLevel = m_throttleDebugLevel;
101 foreach (TokenBucket tb in m_throttleCategories) 100 foreach (TokenBucket tb in m_throttleCategories)
102 tb.DebugLevel = m_throttleDebugLevel; 101 tb.DebugLevel = m_throttleDebugLevel;
103 } 102 }
@@ -172,8 +171,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
172 get { return m_throttleClient; } 171 get { return m_throttleClient; }
173 } 172 }
174 173
175 /// <summary>Throttle bucket for this agent's connection</summary>
176 private readonly TokenBucket m_throttleCategory;
177 /// <summary>Throttle buckets for each packet category</summary> 174 /// <summary>Throttle buckets for each packet category</summary>
178 private readonly TokenBucket[] m_throttleCategories; 175 private readonly TokenBucket[] m_throttleCategories;
179 /// <summary>Outgoing queues for throttled packets</summary> 176 /// <summary>Outgoing queues for throttled packets</summary>
@@ -232,13 +229,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
232 m_throttleClient 229 m_throttleClient
233 = new AdaptiveTokenBucket( 230 = new AdaptiveTokenBucket(
234 string.Format("adaptive throttle for {0} in {1}", AgentID, server.Scene.Name), 231 string.Format("adaptive throttle for {0} in {1}", AgentID, server.Scene.Name),
235 parentThrottle, rates.Total, rates.AdaptiveThrottlesEnabled); 232 parentThrottle, 0, rates.Total, rates.AdaptiveThrottlesEnabled);
236
237 // Create a token bucket throttle for the total category with the client bucket as a throttle
238 m_throttleCategory
239 = new TokenBucket(
240 string.Format("total throttle for {0} in {1}", AgentID, server.Scene.Name),
241 m_throttleClient, 0);
242 233
243 // Create an array of token buckets for this clients different throttle categories 234 // Create an array of token buckets for this clients different throttle categories
244 m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; 235 m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
@@ -256,7 +247,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
256 m_throttleCategories[i] 247 m_throttleCategories[i]
257 = new TokenBucket( 248 = new TokenBucket(
258 string.Format("{0} throttle for {1} in {2}", type, AgentID, server.Scene.Name), 249 string.Format("{0} throttle for {1} in {2}", type, AgentID, server.Scene.Name),
259 m_throttleCategory, rates.GetRate(type)); 250 m_throttleClient, rates.GetRate(type), 0);
260 } 251 }
261 252
262 // Default the retransmission timeout to one second 253 // Default the retransmission timeout to one second
@@ -301,7 +292,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
301 m_info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; 292 m_info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
302 m_info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; 293 m_info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
303 m_info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; 294 m_info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
304 m_info.totalThrottle = (int)m_throttleCategory.DripRate; 295 m_info.totalThrottle = (int)m_throttleClient.DripRate;
296 m_info.targetThrottle = (int)m_throttleClient.TargetDripRate;
305 m_info.maxThrottle = (int)m_throttleClient.MaxDripRate; 297 m_info.maxThrottle = (int)m_throttleClient.MaxDripRate;
306 298
307 return m_info; 299 return m_info;
@@ -320,6 +312,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP
320 } 312 }
321 313
322 /// <summary> 314 /// <summary>
315 /// Get the total number of pakcets queued for this client.
316 /// </summary>
317 /// <returns></returns>
318 public int GetTotalPacketsQueuedCount()
319 {
320 int total = 0;
321
322 for (int i = 0; i <= (int)ThrottleOutPacketType.Asset; i++)
323 total += m_packetOutboxes[i].Count;
324
325 return total;
326 }
327
328 /// <summary>
329 /// Get the number of packets queued for the given throttle type.
330 /// </summary>
331 /// <returns></returns>
332 /// <param name="throttleType"></param>
333 public int GetPacketsQueuedCount(ThrottleOutPacketType throttleType)
334 {
335 if ((int)throttleType > 0)
336 return m_packetOutboxes[(int)throttleType].Count;
337 else
338 return 0;
339 }
340
341 /// <summary>
323 /// Return statistics information about client packet queues. 342 /// Return statistics information about client packet queues.
324 /// </summary> 343 /// </summary>
325 /// <remarks> 344 /// <remarks>
@@ -388,6 +407,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
388 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; 407 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
389 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); 408 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
390 409
410 if (ThrottleDebugLevel > 0)
411 {
412 long total = resend + land + wind + cloud + task + texture + asset;
413 m_log.DebugFormat(
414 "[LLUDPCLIENT]: {0} is setting throttles in {1} to Resend={2}, Land={3}, Wind={4}, Cloud={5}, Task={6}, Texture={7}, Asset={8}, TOTAL = {9}",
415 AgentID, m_udpServer.Scene.Name, resend, land, wind, cloud, task, texture, asset, total);
416 }
417
391 // Make sure none of the throttles are set below our packet MTU, 418 // Make sure none of the throttles are set below our packet MTU,
392 // otherwise a throttle could become permanently clogged 419 // otherwise a throttle could become permanently clogged
393 resend = Math.Max(resend, LLUDPServer.MTU); 420 resend = Math.Max(resend, LLUDPServer.MTU);
@@ -407,11 +434,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
407 //int total = resend + land + wind + cloud + task + texture + asset; 434 //int total = resend + land + wind + cloud + task + texture + asset;
408 435
409 if (ThrottleDebugLevel > 0) 436 if (ThrottleDebugLevel > 0)
437 {
438 long total = resend + land + wind + cloud + task + texture + asset;
410 m_log.DebugFormat( 439 m_log.DebugFormat(
411 "[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}", 440 "[LLUDPCLIENT]: {0} is setting throttles in {1} to Resend={2}, Land={3}, Wind={4}, Cloud={5}, Task={6}, Texture={7}, Asset={8}, TOTAL = {9}",
412 AgentID, m_udpServer.Scene.Name, resend, land, wind, cloud, task, texture, asset); 441 AgentID, m_udpServer.Scene.Name, resend, land, wind, cloud, task, texture, asset, total);
442 }
413 443
414 // Update the token buckets with new throttle values 444 // Update the token buckets with new throttle values
445 if (m_throttleClient.AdaptiveEnabled)
446 {
447 long total = resend + land + wind + cloud + task + texture + asset;
448 m_throttleClient.TargetDripRate = total;
449 }
450
415 TokenBucket bucket; 451 TokenBucket bucket;
416 452
417 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; 453 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
@@ -696,7 +732,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
696 if (!m_udpServer.OqrEngine.IsRunning) 732 if (!m_udpServer.OqrEngine.IsRunning)
697 { 733 {
698 // Asynchronously run the callback 734 // Asynchronously run the callback
699 Util.FireAndForget(FireQueueEmpty, categories); 735 Util.FireAndForget(FireQueueEmpty, categories, "LLUDPClient.BeginFireQueueEmpty");
700 } 736 }
701 else 737 else
702 { 738 {
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
index 9757d35..2f97516 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
@@ -34,7 +34,6 @@ using System.Net.Sockets;
34using System.Reflection; 34using System.Reflection;
35using System.Threading; 35using System.Threading;
36using log4net; 36using log4net;
37using NDesk.Options;
38using Nini.Config; 37using Nini.Config;
39using OpenMetaverse.Packets; 38using OpenMetaverse.Packets;
40using OpenSim.Framework; 39using OpenSim.Framework;
@@ -222,6 +221,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
222 /// </summary> 221 /// </summary>
223 public int DefaultClientPacketDebugLevel { get; set; } 222 public int DefaultClientPacketDebugLevel { get; set; }
224 223
224 /// <summary>
225 /// If set then all inbound agent updates are discarded. For debugging purposes.
226 /// discard agent update.
227 /// </summary>
228 public bool DiscardInboundAgentUpdates { get; set; }
229
225 /// <summary>The measured resolution of Environment.TickCount</summary> 230 /// <summary>The measured resolution of Environment.TickCount</summary>
226 public readonly float TickCountResolution; 231 public readonly float TickCountResolution;
227 232
@@ -238,12 +243,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
238 /// <summary>Incoming packets that are awaiting handling</summary> 243 /// <summary>Incoming packets that are awaiting handling</summary>
239 private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>(); 244 private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>();
240 245
241 /// <summary></summary>
242 //private UDPClientCollection m_clients = new UDPClientCollection();
243 /// <summary>Bandwidth throttle for this UDP server</summary> 246 /// <summary>Bandwidth throttle for this UDP server</summary>
244 protected TokenBucket m_throttle; 247 public TokenBucket Throttle { get; private set; }
245 248
246 /// <summary>Bandwidth throttle rates for this UDP server</summary> 249 /// <summary>Per client throttle rates enforced by this server</summary>
250 /// <remarks>
251 /// If the total rate is non-zero, then this is the maximum total throttle setting that any client can ever have.
252 /// The other rates (resend, asset, etc.) are the defaults for a new client and can be changed (and usually
253 /// do get changed immediately). They do not need to sum to the total.
254 /// </remarks>
247 public ThrottleRates ThrottleRates { get; private set; } 255 public ThrottleRates ThrottleRates { get; private set; }
248 256
249 /// <summary>Manages authentication for agent circuits</summary> 257 /// <summary>Manages authentication for agent circuits</summary>
@@ -356,6 +364,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
356 private IClientAPI m_currentIncomingClient; 364 private IClientAPI m_currentIncomingClient;
357 365
358 /// <summary> 366 /// <summary>
367 /// Queue some low priority but potentially high volume async requests so that they don't overwhelm available
368 /// threadpool threads.
369 /// </summary>
370 public IncomingPacketAsyncHandlingEngine IpahEngine { get; private set; }
371
372 /// <summary>
359 /// Experimental facility to run queue empty processing within a controlled number of threads rather than 373 /// Experimental facility to run queue empty processing within a controlled number of threads rather than
360 /// requiring massive numbers of short-lived threads from the threadpool when there are a high number of 374 /// requiring massive numbers of short-lived threads from the threadpool when there are a high number of
361 /// connections. 375 /// connections.
@@ -439,13 +453,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
439// = new TokenBucket( 453// = new TokenBucket(
440// string.Format("server throttle bucket for {0}", Scene.Name), null, sceneThrottleBps); 454// string.Format("server throttle bucket for {0}", Scene.Name), null, sceneThrottleBps);
441 455
442 m_throttle = new TokenBucket("server throttle bucket", null, sceneThrottleBps); 456 Throttle = new TokenBucket("server throttle bucket", null, 0, sceneThrottleBps);
443 457
444 ThrottleRates = new ThrottleRates(configSource); 458 ThrottleRates = new ThrottleRates(configSource);
445 459
446 if (usePools) 460 if (usePools)
447 EnablePools(); 461 EnablePools();
448 462
463 IpahEngine = new IncomingPacketAsyncHandlingEngine(this);
449 OqrEngine = new OutgoingQueueRefillEngine(this); 464 OqrEngine = new OutgoingQueueRefillEngine(this);
450 } 465 }
451 466
@@ -453,12 +468,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
453 { 468 {
454 StartInbound(); 469 StartInbound();
455 StartOutbound(); 470 StartOutbound();
471 IpahEngine.Start();
456 OqrEngine.Start(); 472 OqrEngine.Start();
457 473
458 m_elapsedMSSinceLastStatReport = Environment.TickCount; 474 m_elapsedMSSinceLastStatReport = Environment.TickCount;
459 } 475 }
460 476
461 private void StartInbound() 477 public void StartInbound()
462 { 478 {
463 m_log.InfoFormat( 479 m_log.InfoFormat(
464 "[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server in {0} mode with UsePools = {1}", 480 "[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server in {0} mode with UsePools = {1}",
@@ -467,7 +483,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
467 base.StartInbound(m_recvBufferSize, m_asyncPacketHandling); 483 base.StartInbound(m_recvBufferSize, m_asyncPacketHandling);
468 484
469 // This thread will process the packets received that are placed on the packetInbox 485 // This thread will process the packets received that are placed on the packetInbox
470 Watchdog.StartThread( 486 WorkManager.StartThread(
471 IncomingPacketHandler, 487 IncomingPacketHandler,
472 string.Format("Incoming Packets ({0})", Scene.Name), 488 string.Format("Incoming Packets ({0})", Scene.Name),
473 ThreadPriority.Normal, 489 ThreadPriority.Normal,
@@ -477,13 +493,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
477 Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS); 493 Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
478 } 494 }
479 495
480 private new void StartOutbound() 496 public override void StartOutbound()
481 { 497 {
482 m_log.Info("[LLUDPSERVER]: Starting outbound packet processing for the LLUDP server"); 498 m_log.Info("[LLUDPSERVER]: Starting outbound packet processing for the LLUDP server");
483 499
484 base.StartOutbound(); 500 base.StartOutbound();
485 501
486 Watchdog.StartThread( 502 WorkManager.StartThread(
487 OutgoingPacketHandler, 503 OutgoingPacketHandler,
488 string.Format("Outgoing Packets ({0})", Scene.Name), 504 string.Format("Outgoing Packets ({0})", Scene.Name),
489 ThreadPriority.Normal, 505 ThreadPriority.Normal,
@@ -498,10 +514,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
498 m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + Scene.Name); 514 m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + Scene.Name);
499 base.StopOutbound(); 515 base.StopOutbound();
500 base.StopInbound(); 516 base.StopInbound();
517 IpahEngine.Stop();
501 OqrEngine.Stop(); 518 OqrEngine.Stop();
502 } 519 }
503 520
504 protected override bool EnablePools() 521 public override bool EnablePools()
505 { 522 {
506 if (!UsePools) 523 if (!UsePools)
507 { 524 {
@@ -515,7 +532,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
515 return false; 532 return false;
516 } 533 }
517 534
518 protected override bool DisablePools() 535 public override bool DisablePools()
519 { 536 {
520 if (UsePools) 537 if (UsePools)
521 { 538 {
@@ -535,7 +552,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
535 /// This is a seperate method so that it can be called once we have an m_scene to distinguish different scene 552 /// This is a seperate method so that it can be called once we have an m_scene to distinguish different scene
536 /// stats. 553 /// stats.
537 /// </summary> 554 /// </summary>
538 private void EnablePoolStats() 555 protected internal void EnablePoolStats()
539 { 556 {
540 m_poolCountStat 557 m_poolCountStat
541 = new Stat( 558 = new Stat(
@@ -569,7 +586,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
569 /// <summary> 586 /// <summary>
570 /// Disables pool stats. 587 /// Disables pool stats.
571 /// </summary> 588 /// </summary>
572 private void DisablePoolStats() 589 protected internal void DisablePoolStats()
573 { 590 {
574 StatsManager.DeregisterStat(m_poolCountStat); 591 StatsManager.DeregisterStat(m_poolCountStat);
575 m_poolCountStat = null; 592 m_poolCountStat = null;
@@ -683,434 +700,45 @@ namespace OpenSim.Region.ClientStack.LindenUDP
683 StatType.Pull, 700 StatType.Pull,
684 stat => stat.Value = PacketPool.Instance.BlocksPooled, 701 stat => stat.Value = PacketPool.Instance.BlocksPooled,
685 StatVerbosity.Debug)); 702 StatVerbosity.Debug));
703
704 StatsManager.RegisterStat(
705 new Stat(
706 "OutgoingPacketsQueuedCount",
707 "Packets queued for outgoing send",
708 "Number of queued outgoing packets across all connections",
709 "",
710 "clientstack",
711 Scene.Name,
712 StatType.Pull,
713 MeasuresOfInterest.AverageChangeOverTime,
714 stat => stat.Value = GetTotalQueuedOutgoingPackets(),
715 StatVerbosity.Info));
686 716
687 // We delay enabling pool stats to AddScene() instead of Initialize() so that we can distinguish pool stats by 717 // We delay enabling pool stats to AddScene() instead of Initialize() so that we can distinguish pool stats by
688 // scene name 718 // scene name
689 if (UsePools) 719 if (UsePools)
690 EnablePoolStats(); 720 EnablePoolStats();
691 721
692 MainConsole.Instance.Commands.AddCommand( 722 LLUDPServerCommands commands = new LLUDPServerCommands(MainConsole.Instance, this);
693 "Debug", false, "debug lludp packet", 723 commands.Register();
694 "debug lludp packet [--default | --all] <level> [<avatar-first-name> <avatar-last-name>]",
695 "Turn on packet debugging. This logs information when the client stack hands a processed packet off to downstream code or when upstream code first requests that a certain packet be sent.",
696 "If level > 255 then all incoming and outgoing packets are logged.\n"
697 + "If level <= 255 then incoming AgentUpdate and outgoing SimStats and SimulatorViewerTimeMessage packets are not logged.\n"
698 + "If level <= 200 then incoming RequestImage and outgoing ImagePacket, ImageData, LayerData and CoarseLocationUpdate packets are not logged.\n"
699 + "If level <= 100 then incoming ViewerEffect and AgentAnimation and outgoing ViewerEffect and AvatarAnimation packets are not logged.\n"
700 + "If level <= 50 then outgoing ImprovedTerseObjectUpdate packets are not logged.\n"
701 + "If level <= 0 then no packets are logged.\n"
702 + "If --default is specified then the level becomes the default logging level for all subsequent agents.\n"
703 + "If --all is specified then the level becomes the default logging level for all current and subsequent agents.\n"
704 + "In these cases, you cannot also specify an avatar name.\n"
705 + "If an avatar name is given then only packets from that avatar are logged.",
706 HandlePacketCommand);
707
708 MainConsole.Instance.Commands.AddCommand(
709 "Debug", false, "debug lludp data out",
710 "debug lludp data out <level> <avatar-first-name> <avatar-last-name>\"",
711 "Turn on debugging for final outgoing data to the given user's client.",
712 "This operates at a much lower level than the packet command and prints out available details when the data is actually sent.\n"
713 + "If level > 0 then information about all outgoing UDP data for this avatar is logged.\n"
714 + "If level <= 0 then no information about outgoing UDP data for this avatar is logged.",
715 HandleDataCommand);
716
717 MainConsole.Instance.Commands.AddCommand(
718 "Debug", false, "debug lludp drop",
719 "debug lludp drop <in|out> <add|remove> <packet-name>",
720 "Drop all in or outbound packets that match the given name",
721 "For test purposes.",
722 HandleDropCommand);
723
724 MainConsole.Instance.Commands.AddCommand(
725 "Debug",
726 false,
727 "debug lludp start",
728 "debug lludp start <in|out|all>",
729 "Control LLUDP packet processing.",
730 "No effect if packet processing has already started.\n"
731 + "in - start inbound processing.\n"
732 + "out - start outbound processing.\n"
733 + "all - start in and outbound processing.\n",
734 HandleStartCommand);
735
736 MainConsole.Instance.Commands.AddCommand(
737 "Debug",
738 false,
739 "debug lludp stop",
740 "debug lludp stop <in|out|all>",
741 "Stop LLUDP packet processing.",
742 "No effect if packet processing has already stopped.\n"
743 + "in - stop inbound processing.\n"
744 + "out - stop outbound processing.\n"
745 + "all - stop in and outbound processing.\n",
746 HandleStopCommand);
747
748 MainConsole.Instance.Commands.AddCommand(
749 "Debug",
750 false,
751 "debug lludp pool",
752 "debug lludp pool <on|off>",
753 "Turn object pooling within the lludp component on or off.",
754 HandlePoolCommand);
755
756 MainConsole.Instance.Commands.AddCommand(
757 "Debug",
758 false,
759 "debug lludp status",
760 "debug lludp status",
761 "Return status of LLUDP packet processing.",
762 HandleStatusCommand);
763
764 MainConsole.Instance.Commands.AddCommand(
765 "Debug",
766 false,
767 "debug lludp throttle log",
768 "debug lludp throttle log <level> <avatar-first-name> <avatar-last-name>",
769 "Change debug logging level for throttles.",
770 "If level >= 0 then throttle debug logging is performed.\n"
771 + "If level <= 0 then no throttle debug logging is performed.",
772 HandleThrottleCommand);
773
774 MainConsole.Instance.Commands.AddCommand(
775 "Debug",
776 false,
777 "debug lludp throttle status",
778 "debug lludp throttle status <avatar-first-name> <avatar-last-name>",
779 "Return status information about throttles.",
780 HandleThrottleStatusCommand);
781
782 MainConsole.Instance.Commands.AddCommand(
783 "Debug",
784 false,
785 "debug lludp toggle agentupdate",
786 "debug lludp toggle agentupdate",
787 "Toggle whether agentupdate packets are processed or simply discarded.",
788 HandleAgentUpdateCommand);
789 }
790
791 private void HandleDataCommand(string module, string[] args)
792 {
793 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
794 return;
795
796 if (args.Length != 7)
797 {
798 MainConsole.Instance.OutputFormat("Usage: debug lludp data out <true|false> <avatar-first-name> <avatar-last-name>");
799 return;
800 }
801
802 int level;
803 if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out level))
804 return;
805
806 string firstName = args[5];
807 string lastName = args[6];
808
809 Scene.ForEachScenePresence(sp =>
810 {
811 if (sp.Firstname == firstName && sp.Lastname == lastName)
812 {
813 MainConsole.Instance.OutputFormat(
814 "Data debug for {0} ({1}) set to {2} in {3}",
815 sp.Name, sp.IsChildAgent ? "child" : "root", level, Scene.Name);
816
817 ((LLClientView)sp.ControllingClient).UDPClient.DebugDataOutLevel = level;
818 }
819 });
820 } 724 }
821 725
822 private void HandleThrottleCommand(string module, string[] args) 726 public bool HandlesRegion(Location x)
823 {
824 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
825 return;
826
827 if (args.Length != 7)
828 {
829 MainConsole.Instance.OutputFormat("Usage: debug lludp throttle log <level> <avatar-first-name> <avatar-last-name>");
830 return;
831 }
832
833 int level;
834 if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out level))
835 return;
836
837 string firstName = args[5];
838 string lastName = args[6];
839
840 Scene.ForEachScenePresence(sp =>
841 {
842 if (sp.Firstname == firstName && sp.Lastname == lastName)
843 {
844 MainConsole.Instance.OutputFormat(
845 "Throttle log level for {0} ({1}) set to {2} in {3}",
846 sp.Name, sp.IsChildAgent ? "child" : "root", level, Scene.Name);
847
848 ((LLClientView)sp.ControllingClient).UDPClient.ThrottleDebugLevel = level;
849 }
850 });
851 }
852
853 private void HandleThrottleStatusCommand(string module, string[] args)
854 {
855 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
856 return;
857
858 if (args.Length != 6)
859 {
860 MainConsole.Instance.OutputFormat("Usage: debug lludp throttle status <avatar-first-name> <avatar-last-name>");
861 return;
862 }
863
864 string firstName = args[4];
865 string lastName = args[5];
866
867 Scene.ForEachScenePresence(sp =>
868 {
869 if (sp.Firstname == firstName && sp.Lastname == lastName)
870 {
871 MainConsole.Instance.OutputFormat(
872 "Status for {0} ({1}) in {2}",
873 sp.Name, sp.IsChildAgent ? "child" : "root", Scene.Name);
874
875 LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
876 MainConsole.Instance.OutputFormat("Adaptive throttle: {0}", udpClient.FlowThrottle.Enabled);
877 }
878 });
879 }
880
881 private void HandlePacketCommand(string module, string[] args)
882 {
883 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
884 return;
885
886 bool setAsDefaultLevel = false;
887 bool setAll = false;
888 OptionSet optionSet = new OptionSet()
889 .Add("default", o => setAsDefaultLevel = (o != null))
890 .Add("all", o => setAll = (o != null));
891 List<string> filteredArgs = optionSet.Parse(args);
892
893 string name = null;
894
895 if (filteredArgs.Count == 6)
896 {
897 if (!(setAsDefaultLevel || setAll))
898 {
899 name = string.Format("{0} {1}", filteredArgs[4], filteredArgs[5]);
900 }
901 else
902 {
903 MainConsole.Instance.OutputFormat("ERROR: Cannot specify a user name when setting default/all logging level");
904 return;
905 }
906 }
907
908 if (filteredArgs.Count > 3)
909 {
910 int newDebug;
911 if (int.TryParse(filteredArgs[3], out newDebug))
912 {
913 if (setAsDefaultLevel || setAll)
914 {
915 DefaultClientPacketDebugLevel = newDebug;
916
917 MainConsole.Instance.OutputFormat(
918 "Packet debug for {0} clients set to {1} in {2}",
919 (setAll ? "all" : "future"), DefaultClientPacketDebugLevel, Scene.Name);
920
921 if (setAll)
922 {
923 Scene.ForEachScenePresence(sp =>
924 {
925 MainConsole.Instance.OutputFormat(
926 "Packet debug for {0} ({1}) set to {2} in {3}",
927 sp.Name, sp.IsChildAgent ? "child" : "root", newDebug, Scene.Name);
928
929 sp.ControllingClient.DebugPacketLevel = newDebug;
930 });
931 }
932 }
933 else
934 {
935 Scene.ForEachScenePresence(sp =>
936 {
937 if (name == null || sp.Name == name)
938 {
939 MainConsole.Instance.OutputFormat(
940 "Packet debug for {0} ({1}) set to {2} in {3}",
941 sp.Name, sp.IsChildAgent ? "child" : "root", newDebug, Scene.Name);
942
943 sp.ControllingClient.DebugPacketLevel = newDebug;
944 }
945 });
946 }
947 }
948 else
949 {
950 MainConsole.Instance.Output("Usage: debug lludp packet [--default | --all] 0..255 [<first-name> <last-name>]");
951 }
952 }
953 }
954
955 private void HandleDropCommand(string module, string[] args)
956 {
957 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
958 return;
959
960 if (args.Length != 6)
961 {
962 MainConsole.Instance.Output("Usage: debug lludp drop <in|out> <add|remove> <packet-name>");
963 return;
964 }
965
966 string direction = args[3];
967 string subCommand = args[4];
968 string packetName = args[5];
969
970 if (subCommand == "add")
971 {
972 MainConsole.Instance.OutputFormat(
973 "Adding packet {0} to {1} drop list for all connections in {2}", direction, packetName, Scene.Name);
974
975 Scene.ForEachScenePresence(
976 sp =>
977 {
978 LLClientView llcv = (LLClientView)sp.ControllingClient;
979
980 if (direction == "in")
981 llcv.AddInPacketToDropSet(packetName);
982 else if (direction == "out")
983 llcv.AddOutPacketToDropSet(packetName);
984 }
985 );
986 }
987 else if (subCommand == "remove")
988 {
989 MainConsole.Instance.OutputFormat(
990 "Removing packet {0} from {1} drop list for all connections in {2}", direction, packetName, Scene.Name);
991
992 Scene.ForEachScenePresence(
993 sp =>
994 {
995 LLClientView llcv = (LLClientView)sp.ControllingClient;
996
997 if (direction == "in")
998 llcv.RemoveInPacketFromDropSet(packetName);
999 else if (direction == "out")
1000 llcv.RemoveOutPacketFromDropSet(packetName);
1001 }
1002 );
1003 }
1004 }
1005
1006 private void HandleStartCommand(string module, string[] args)
1007 {
1008 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
1009 return;
1010
1011 if (args.Length != 4)
1012 {
1013 MainConsole.Instance.Output("Usage: debug lludp start <in|out|all>");
1014 return;
1015 }
1016
1017 string subCommand = args[3];
1018
1019 if (subCommand == "in" || subCommand == "all")
1020 StartInbound();
1021
1022 if (subCommand == "out" || subCommand == "all")
1023 StartOutbound();
1024 }
1025
1026 private void HandleStopCommand(string module, string[] args)
1027 { 727 {
1028 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene) 728 return x == m_location;
1029 return;
1030
1031 if (args.Length != 4)
1032 {
1033 MainConsole.Instance.Output("Usage: debug lludp stop <in|out|all>");
1034 return;
1035 }
1036
1037 string subCommand = args[3];
1038
1039 if (subCommand == "in" || subCommand == "all")
1040 StopInbound();
1041
1042 if (subCommand == "out" || subCommand == "all")
1043 StopOutbound();
1044 } 729 }
1045 730
1046 private void HandlePoolCommand(string module, string[] args) 731 public int GetTotalQueuedOutgoingPackets()
1047 { 732 {
1048 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene) 733 int total = 0;
1049 return;
1050 734
1051 if (args.Length != 4) 735 foreach (ScenePresence sp in Scene.GetScenePresences())
1052 { 736 {
1053 MainConsole.Instance.Output("Usage: debug lludp pool <on|off>"); 737 LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
1054 return; 738 total += udpClient.GetTotalPacketsQueuedCount();
1055 } 739 }
1056 740
1057 string enabled = args[3]; 741 return total;
1058
1059 if (enabled == "on")
1060 {
1061 if (EnablePools())
1062 {
1063 EnablePoolStats();
1064 MainConsole.Instance.OutputFormat("Packet pools enabled on {0}", Scene.Name);
1065 }
1066 }
1067 else if (enabled == "off")
1068 {
1069 if (DisablePools())
1070 {
1071 DisablePoolStats();
1072 MainConsole.Instance.OutputFormat("Packet pools disabled on {0}", Scene.Name);
1073 }
1074 }
1075 else
1076 {
1077 MainConsole.Instance.Output("Usage: debug lludp pool <on|off>");
1078 }
1079 }
1080
1081 bool m_discardAgentUpdates;
1082
1083 private void HandleAgentUpdateCommand(string module, string[] args)
1084 {
1085 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
1086 return;
1087
1088 m_discardAgentUpdates = !m_discardAgentUpdates;
1089
1090 MainConsole.Instance.OutputFormat(
1091 "Discard AgentUpdates now {0} for {1}", m_discardAgentUpdates, Scene.Name);
1092 }
1093
1094 private void HandleStatusCommand(string module, string[] args)
1095 {
1096 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
1097 return;
1098
1099 MainConsole.Instance.OutputFormat(
1100 "IN LLUDP packet processing for {0} is {1}", Scene.Name, IsRunningInbound ? "enabled" : "disabled");
1101
1102 MainConsole.Instance.OutputFormat(
1103 "OUT LLUDP packet processing for {0} is {1}", Scene.Name, IsRunningOutbound ? "enabled" : "disabled");
1104
1105 MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", Scene.Name, UsePools ? "on" : "off");
1106
1107 MainConsole.Instance.OutputFormat(
1108 "Packet debug level for new clients is {0}", DefaultClientPacketDebugLevel);
1109 }
1110
1111 public bool HandlesRegion(Location x)
1112 {
1113 return x == m_location;
1114 } 742 }
1115 743
1116// public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting) 744// public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting)
@@ -1279,6 +907,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1279 // packet so that it isn't sent before a queued update packet. 907 // packet so that it isn't sent before a queued update packet.
1280 bool forceQueue = (type == PacketType.KillObject); 908 bool forceQueue = (type == PacketType.KillObject);
1281 909
910// if (type == PacketType.ImprovedTerseObjectUpdate)
911// {
912// m_log.DebugFormat("Direct send ITOU to {0} in {1}", udpClient.AgentID, Scene.Name);
913// SendPacketFinal(outgoingPacket);
914// return false;
915// }
916// else
917// {
1282 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, forceQueue)) 918 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, forceQueue))
1283 { 919 {
1284 SendPacketFinal(outgoingPacket); 920 SendPacketFinal(outgoingPacket);
@@ -1288,6 +924,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1288 { 924 {
1289 return false; 925 return false;
1290 } 926 }
927// }
1291 928
1292 #endregion Queue or Send 929 #endregion Queue or Send
1293 } 930 }
@@ -1363,7 +1000,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1363 // Fire this out on a different thread so that we don't hold up outgoing packet processing for 1000 // Fire this out on a different thread so that we don't hold up outgoing packet processing for
1364 // everybody else if this is being called due to an ack timeout. 1001 // everybody else if this is being called due to an ack timeout.
1365 // This is the same as processing as the async process of a logout request. 1002 // This is the same as processing as the async process of a logout request.
1366 Util.FireAndForget(o => DeactivateClientDueToTimeout(client, timeoutTicks)); 1003 Util.FireAndForget(
1004 o => DeactivateClientDueToTimeout(client, timeoutTicks), null, "LLUDPServer.DeactivateClientDueToTimeout");
1367 1005
1368 return; 1006 return;
1369 } 1007 }
@@ -1597,7 +1235,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1597 // buffer. 1235 // buffer.
1598 object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet }; 1236 object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
1599 1237
1600 Util.FireAndForget(HandleUseCircuitCode, array); 1238 Util.FireAndForget(HandleUseCircuitCode, array, "LLUDPServer.HandleUseCircuitCode");
1601 1239
1602 return; 1240 return;
1603 } 1241 }
@@ -1610,7 +1248,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1610 // buffer. 1248 // buffer.
1611 object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet }; 1249 object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
1612 1250
1613 Util.FireAndForget(HandleCompleteMovementIntoRegion, array); 1251 Util.FireAndForget(
1252 HandleCompleteMovementIntoRegion, array, "LLUDPServer.HandleCompleteMovementIntoRegion");
1614 1253
1615 return; 1254 return;
1616 } 1255 }
@@ -1732,7 +1371,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1732 1371
1733 if (packet.Type == PacketType.AgentUpdate) 1372 if (packet.Type == PacketType.AgentUpdate)
1734 { 1373 {
1735 if (m_discardAgentUpdates) 1374 if (DiscardInboundAgentUpdates)
1736 return; 1375 return;
1737 1376
1738 ((LLClientView)client).TotalAgentUpdates++; 1377 ((LLClientView)client).TotalAgentUpdates++;
@@ -2140,7 +1779,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2140 { 1779 {
2141 if (!Scene.TryGetClient(agentID, out client)) 1780 if (!Scene.TryGetClient(agentID, out client))
2142 { 1781 {
2143 LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO); 1782 LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, Throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO);
2144 1783
2145 client = new LLClientView(Scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); 1784 client = new LLClientView(Scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode);
2146 client.OnLogout += LogoutHandler; 1785 client.OnLogout += LogoutHandler;
@@ -2183,6 +1822,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2183 1822
2184 private void IncomingPacketHandler() 1823 private void IncomingPacketHandler()
2185 { 1824 {
1825 Thread.CurrentThread.Priority = ThreadPriority.Highest;
1826
2186 // Set this culture for the thread that incoming packets are received 1827 // Set this culture for the thread that incoming packets are received
2187 // on to en-US to avoid number parsing issues 1828 // on to en-US to avoid number parsing issues
2188 Culture.SetCurrentCulture(); 1829 Culture.SetCurrentCulture();
@@ -2228,6 +1869,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2228 1869
2229 private void OutgoingPacketHandler() 1870 private void OutgoingPacketHandler()
2230 { 1871 {
1872 Thread.CurrentThread.Priority = ThreadPriority.Highest;
1873
2231 // Set this culture for the thread that outgoing packets are sent 1874 // Set this culture for the thread that outgoing packets are sent
2232 // on to en-US to avoid number parsing issues 1875 // on to en-US to avoid number parsing issues
2233 Culture.SetCurrentCulture(); 1876 Culture.SetCurrentCulture();
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs
new file mode 100644
index 0000000..e0398d5
--- /dev/null
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs
@@ -0,0 +1,762 @@
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.Text;
31using NDesk.Options;
32using OpenSim.Framework;
33using OpenSim.Framework.Console;
34using OpenSim.Region.Framework.Scenes;
35
36namespace OpenSim.Region.ClientStack.LindenUDP
37{
38 public class LLUDPServerCommands
39 {
40 private ICommandConsole m_console;
41 private LLUDPServer m_udpServer;
42
43 public LLUDPServerCommands(ICommandConsole console, LLUDPServer udpServer)
44 {
45 m_console = console;
46 m_udpServer = udpServer;
47 }
48
49 public void Register()
50 {
51 m_console.Commands.AddCommand(
52 "Comms", false, "show server throttles",
53 "show server throttles",
54 "Show information about server throttles",
55 HandleShowServerThrottlesCommand);
56
57 m_console.Commands.AddCommand(
58 "Debug", false, "debug lludp packet",
59 "debug lludp packet [--default | --all] <level> [<avatar-first-name> <avatar-last-name>]",
60 "Turn on packet debugging. This logs information when the client stack hands a processed packet off to downstream code or when upstream code first requests that a certain packet be sent.",
61 "If level > 255 then all incoming and outgoing packets are logged.\n"
62 + "If level <= 255 then incoming AgentUpdate and outgoing SimStats and SimulatorViewerTimeMessage packets are not logged.\n"
63 + "If level <= 200 then incoming RequestImage and outgoing ImagePacket, ImageData, LayerData and CoarseLocationUpdate packets are not logged.\n"
64 + "If level <= 100 then incoming ViewerEffect and AgentAnimation and outgoing ViewerEffect and AvatarAnimation packets are not logged.\n"
65 + "If level <= 50 then outgoing ImprovedTerseObjectUpdate packets are not logged.\n"
66 + "If level <= 0 then no packets are logged.\n"
67 + "If --default is specified then the level becomes the default logging level for all subsequent agents.\n"
68 + "If --all is specified then the level becomes the default logging level for all current and subsequent agents.\n"
69 + "In these cases, you cannot also specify an avatar name.\n"
70 + "If an avatar name is given then only packets from that avatar are logged.",
71 HandlePacketCommand);
72
73 m_console.Commands.AddCommand(
74 "Debug", false, "debug lludp data out",
75 "debug lludp data out <level> <avatar-first-name> <avatar-last-name>\"",
76 "Turn on debugging for final outgoing data to the given user's client.",
77 "This operates at a much lower level than the packet command and prints out available details when the data is actually sent.\n"
78 + "If level > 0 then information about all outgoing UDP data for this avatar is logged.\n"
79 + "If level <= 0 then no information about outgoing UDP data for this avatar is logged.",
80 HandleDataCommand);
81
82 m_console.Commands.AddCommand(
83 "Debug", false, "debug lludp drop",
84 "debug lludp drop <in|out> <add|remove> <packet-name>",
85 "Drop all in or outbound packets that match the given name",
86 "For test purposes.",
87 HandleDropCommand);
88
89 m_console.Commands.AddCommand(
90 "Debug",
91 false,
92 "debug lludp start",
93 "debug lludp start <in|out|all>",
94 "Control LLUDP packet processing.",
95 "No effect if packet processing has already started.\n"
96 + "in - start inbound processing.\n"
97 + "out - start outbound processing.\n"
98 + "all - start in and outbound processing.\n",
99 HandleStartCommand);
100
101 m_console.Commands.AddCommand(
102 "Debug",
103 false,
104 "debug lludp stop",
105 "debug lludp stop <in|out|all>",
106 "Stop LLUDP packet processing.",
107 "No effect if packet processing has already stopped.\n"
108 + "in - stop inbound processing.\n"
109 + "out - stop outbound processing.\n"
110 + "all - stop in and outbound processing.\n",
111 HandleStopCommand);
112
113 m_console.Commands.AddCommand(
114 "Debug",
115 false,
116 "debug lludp pool",
117 "debug lludp pool <on|off>",
118 "Turn object pooling within the lludp component on or off.",
119 HandlePoolCommand);
120
121 m_console.Commands.AddCommand(
122 "Debug",
123 false,
124 "debug lludp status",
125 "debug lludp status",
126 "Return status of LLUDP packet processing.",
127 HandleStatusCommand);
128
129 m_console.Commands.AddCommand(
130 "Debug",
131 false,
132 "debug lludp throttles log",
133 "debug lludp throttles log <level> [<avatar-first-name> <avatar-last-name>]",
134 "Change debug logging level for throttles.",
135 "If level >= 0 then throttle debug logging is performed.\n"
136 + "If level <= 0 then no throttle debug logging is performed.",
137 HandleThrottleCommand);
138
139 m_console.Commands.AddCommand(
140 "Debug",
141 false,
142 "debug lludp throttles get",
143 "debug lludp throttles get [<avatar-first-name> <avatar-last-name>]",
144 "Return debug settings for throttles.",
145 "adaptive - true/false, controls adaptive throttle setting.\n"
146 + "request - request drip rate in kbps.\n"
147 + "max - the max kbps throttle allowed for the specified existing clients. Use 'debug lludp get new-client-throttle-max' to see the setting for new clients.\n",
148 HandleThrottleGetCommand);
149
150 m_console.Commands.AddCommand(
151 "Debug",
152 false,
153 "debug lludp throttles set",
154 "debug lludp throttles set <param> <value> [<avatar-first-name> <avatar-last-name>]",
155 "Set a throttle parameter for the given client.",
156 "adaptive - true/false, controls adaptive throttle setting.\n"
157 + "current - current drip rate in kbps.\n"
158 + "request - requested drip rate in kbps.\n"
159 + "max - the max kbps throttle allowed for the specified existing clients. Use 'debug lludp set new-client-throttle-max' to change the settings for new clients.\n",
160 HandleThrottleSetCommand);
161
162 m_console.Commands.AddCommand(
163 "Debug",
164 false,
165 "debug lludp get",
166 "debug lludp get",
167 "Get debug parameters for the server.",
168 "max-scene-throttle - the current max cumulative kbps provided for this scene to clients.\n"
169 + "max-new-client-throttle - the max kbps throttle allowed to new clients. Use 'debug lludp throttles get max' to see the settings for existing clients.",
170 HandleGetCommand);
171
172 m_console.Commands.AddCommand(
173 "Debug",
174 false,
175 "debug lludp set",
176 "debug lludp set <param> <value>",
177 "Set a parameter for the server.",
178 "max-scene-throttle - the current max cumulative kbps provided for this scene to clients.\n"
179 + "max-new-client-throttle - the max kbps throttle allowed to each new client. Use 'debug lludp throttles set max' to set for existing clients.",
180 HandleSetCommand);
181
182 m_console.Commands.AddCommand(
183 "Debug",
184 false,
185 "debug lludp toggle agentupdate",
186 "debug lludp toggle agentupdate",
187 "Toggle whether agentupdate packets are processed or simply discarded.",
188 HandleAgentUpdateCommand);
189 }
190
191 private void HandleShowServerThrottlesCommand(string module, string[] args)
192 {
193 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
194 return;
195
196 m_console.OutputFormat("Throttles for {0}", m_udpServer.Scene.Name);
197 ConsoleDisplayList cdl = new ConsoleDisplayList();
198 cdl.AddRow("Adaptive throttles", m_udpServer.ThrottleRates.AdaptiveThrottlesEnabled);
199
200 long maxSceneDripRate = m_udpServer.Throttle.MaxDripRate;
201 cdl.AddRow(
202 "Max scene throttle",
203 maxSceneDripRate != 0 ? string.Format("{0} kbps", maxSceneDripRate * 8 / 1000) : "unset");
204
205 int maxClientDripRate = m_udpServer.ThrottleRates.Total;
206 cdl.AddRow(
207 "Max new client throttle",
208 maxClientDripRate != 0 ? string.Format("{0} kbps", maxClientDripRate * 8 / 1000) : "unset");
209
210 m_console.Output(cdl.ToString());
211
212 m_console.OutputFormat("{0}\n", GetServerThrottlesReport(m_udpServer));
213 }
214
215 private string GetServerThrottlesReport(LLUDPServer udpServer)
216 {
217 StringBuilder report = new StringBuilder();
218
219 report.AppendFormat(
220 "{0,7} {1,8} {2,7} {3,7} {4,7} {5,7} {6,9} {7,7}\n",
221 "Total",
222 "Resend",
223 "Land",
224 "Wind",
225 "Cloud",
226 "Task",
227 "Texture",
228 "Asset");
229
230 report.AppendFormat(
231 "{0,7} {1,8} {2,7} {3,7} {4,7} {5,7} {6,9} {7,7}\n",
232 "kb/s",
233 "kb/s",
234 "kb/s",
235 "kb/s",
236 "kb/s",
237 "kb/s",
238 "kb/s",
239 "kb/s");
240
241 ThrottleRates throttleRates = udpServer.ThrottleRates;
242 report.AppendFormat(
243 "{0,7} {1,8} {2,7} {3,7} {4,7} {5,7} {6,9} {7,7}",
244 (throttleRates.Total * 8) / 1000,
245 (throttleRates.Resend * 8) / 1000,
246 (throttleRates.Land * 8) / 1000,
247 (throttleRates.Wind * 8) / 1000,
248 (throttleRates.Cloud * 8) / 1000,
249 (throttleRates.Task * 8) / 1000,
250 (throttleRates.Texture * 8) / 1000,
251 (throttleRates.Asset * 8) / 1000);
252
253 return report.ToString();
254 }
255
256 protected string GetColumnEntry(string entry, int maxLength, int columnPadding)
257 {
258 return string.Format(
259 "{0,-" + maxLength + "}{1,-" + columnPadding + "}",
260 entry.Length > maxLength ? entry.Substring(0, maxLength) : entry,
261 "");
262 }
263
264 private void HandleDataCommand(string module, string[] args)
265 {
266 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
267 return;
268
269 if (args.Length != 7)
270 {
271 MainConsole.Instance.OutputFormat("Usage: debug lludp data out <true|false> <avatar-first-name> <avatar-last-name>");
272 return;
273 }
274
275 int level;
276 if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out level))
277 return;
278
279 string firstName = args[5];
280 string lastName = args[6];
281
282 m_udpServer.Scene.ForEachScenePresence(sp =>
283 {
284 if (sp.Firstname == firstName && sp.Lastname == lastName)
285 {
286 MainConsole.Instance.OutputFormat(
287 "Data debug for {0} ({1}) set to {2} in {3}",
288 sp.Name, sp.IsChildAgent ? "child" : "root", level, m_udpServer.Scene.Name);
289
290 ((LLClientView)sp.ControllingClient).UDPClient.DebugDataOutLevel = level;
291 }
292 });
293 }
294
295 private void HandleThrottleCommand(string module, string[] args)
296 {
297 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
298 return;
299
300 bool all = args.Length == 5;
301 bool one = args.Length == 7;
302
303 if (!all && !one)
304 {
305 MainConsole.Instance.OutputFormat(
306 "Usage: debug lludp throttles log <level> [<avatar-first-name> <avatar-last-name>]");
307 return;
308 }
309
310 int level;
311 if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out level))
312 return;
313
314 string firstName = null;
315 string lastName = null;
316
317 if (one)
318 {
319 firstName = args[5];
320 lastName = args[6];
321 }
322
323 m_udpServer.Scene.ForEachScenePresence(sp =>
324 {
325 if (all || (sp.Firstname == firstName && sp.Lastname == lastName))
326 {
327 MainConsole.Instance.OutputFormat(
328 "Throttle log level for {0} ({1}) set to {2} in {3}",
329 sp.Name, sp.IsChildAgent ? "child" : "root", level, m_udpServer.Scene.Name);
330
331 ((LLClientView)sp.ControllingClient).UDPClient.ThrottleDebugLevel = level;
332 }
333 });
334 }
335
336 private void HandleThrottleSetCommand(string module, string[] args)
337 {
338 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
339 return;
340
341 bool all = args.Length == 6;
342 bool one = args.Length == 8;
343
344 if (!all && !one)
345 {
346 MainConsole.Instance.OutputFormat(
347 "Usage: debug lludp throttles set <param> <value> [<avatar-first-name> <avatar-last-name>]");
348 return;
349 }
350
351 string param = args[4];
352 string rawValue = args[5];
353
354 string firstName = null;
355 string lastName = null;
356
357 if (one)
358 {
359 firstName = args[6];
360 lastName = args[7];
361 }
362
363 if (param == "adaptive")
364 {
365 bool newValue;
366 if (!ConsoleUtil.TryParseConsoleBool(MainConsole.Instance, rawValue, out newValue))
367 return;
368
369 m_udpServer.Scene.ForEachScenePresence(sp =>
370 {
371 if (all || (sp.Firstname == firstName && sp.Lastname == lastName))
372 {
373 MainConsole.Instance.OutputFormat(
374 "Setting param {0} to {1} for {2} ({3}) in {4}",
375 param, newValue, sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name);
376
377 LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
378 udpClient.FlowThrottle.AdaptiveEnabled = newValue;
379 // udpClient.FlowThrottle.MaxDripRate = 0;
380 // udpClient.FlowThrottle.AdjustedDripRate = 0;
381 }
382 });
383 }
384 else if (param == "request")
385 {
386 int newValue;
387 if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, rawValue, out newValue))
388 return;
389
390 int newCurrentThrottleKbps = newValue * 1000 / 8;
391
392 m_udpServer.Scene.ForEachScenePresence(sp =>
393 {
394 if (all || (sp.Firstname == firstName && sp.Lastname == lastName))
395 {
396 MainConsole.Instance.OutputFormat(
397 "Setting param {0} to {1} for {2} ({3}) in {4}",
398 param, newValue, sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name);
399
400 LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
401 udpClient.FlowThrottle.RequestedDripRate = newCurrentThrottleKbps;
402 }
403 });
404 }
405 else if (param == "max")
406 {
407 int newValue;
408 if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, rawValue, out newValue))
409 return;
410
411 int newThrottleMaxKbps = newValue * 1000 / 8;
412
413 m_udpServer.Scene.ForEachScenePresence(sp =>
414 {
415 if (all || (sp.Firstname == firstName && sp.Lastname == lastName))
416 {
417 MainConsole.Instance.OutputFormat(
418 "Setting param {0} to {1} for {2} ({3}) in {4}",
419 param, newValue, sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name);
420
421 LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
422 udpClient.FlowThrottle.MaxDripRate = newThrottleMaxKbps;
423 }
424 });
425 }
426 }
427
428 private void HandleThrottleGetCommand(string module, string[] args)
429 {
430 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
431 return;
432
433 bool all = args.Length == 4;
434 bool one = args.Length == 6;
435
436 if (!all && !one)
437 {
438 MainConsole.Instance.OutputFormat(
439 "Usage: debug lludp throttles get [<avatar-first-name> <avatar-last-name>]");
440 return;
441 }
442
443 string firstName = null;
444 string lastName = null;
445
446 if (one)
447 {
448 firstName = args[4];
449 lastName = args[5];
450 }
451
452 m_udpServer.Scene.ForEachScenePresence(sp =>
453 {
454 if (all || (sp.Firstname == firstName && sp.Lastname == lastName))
455 {
456 m_console.OutputFormat(
457 "Status for {0} ({1}) in {2}",
458 sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name);
459
460 LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
461
462 ConsoleDisplayList cdl = new ConsoleDisplayList();
463 cdl.AddRow("adaptive", udpClient.FlowThrottle.AdaptiveEnabled);
464 cdl.AddRow("current", string.Format("{0} kbps", udpClient.FlowThrottle.DripRate * 8 / 1000));
465 cdl.AddRow("request", string.Format("{0} kbps", udpClient.FlowThrottle.RequestedDripRate * 8 / 1000));
466 cdl.AddRow("max", string.Format("{0} kbps", udpClient.FlowThrottle.MaxDripRate * 8 / 1000));
467
468 m_console.Output(cdl.ToString());
469 }
470 });
471 }
472
473 private void HandleGetCommand(string module, string[] args)
474 {
475 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
476 return;
477
478 m_console.OutputFormat("Debug settings for {0}", m_udpServer.Scene.Name);
479 ConsoleDisplayList cdl = new ConsoleDisplayList();
480
481 long maxSceneDripRate = m_udpServer.Throttle.MaxDripRate;
482 cdl.AddRow(
483 "max-scene-throttle",
484 maxSceneDripRate != 0 ? string.Format("{0} kbps", maxSceneDripRate * 8 / 1000) : "unset");
485
486 int maxClientDripRate = m_udpServer.ThrottleRates.Total;
487 cdl.AddRow(
488 "max-new-client-throttle",
489 maxClientDripRate != 0 ? string.Format("{0} kbps", maxClientDripRate * 8 / 1000) : "unset");
490
491 m_console.Output(cdl.ToString());
492 }
493
494 private void HandleSetCommand(string module, string[] args)
495 {
496 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
497 return;
498
499 if (args.Length != 5)
500 {
501 MainConsole.Instance.OutputFormat("Usage: debug lludp set <param> <value>");
502 return;
503 }
504
505 string param = args[3];
506 string rawValue = args[4];
507
508 int newValue;
509
510 if (param == "max-scene-throttle")
511 {
512 if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, rawValue, out newValue))
513 return;
514
515 m_udpServer.Throttle.MaxDripRate = newValue * 1000 / 8;
516 }
517 else if (param == "max-new-client-throttle")
518 {
519 if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, rawValue, out newValue))
520 return;
521
522 m_udpServer.ThrottleRates.Total = newValue * 1000 / 8;
523 }
524 else
525 {
526 return;
527 }
528
529 m_console.OutputFormat("{0} set to {1} in {2}", param, rawValue, m_udpServer.Scene.Name);
530 }
531
532 private void HandlePacketCommand(string module, string[] args)
533 {
534 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
535 return;
536
537 bool setAsDefaultLevel = false;
538 bool setAll = false;
539 OptionSet optionSet = new OptionSet()
540 .Add("default", o => setAsDefaultLevel = (o != null))
541 .Add("all", o => setAll = (o != null));
542 List<string> filteredArgs = optionSet.Parse(args);
543
544 string name = null;
545
546 if (filteredArgs.Count == 6)
547 {
548 if (!(setAsDefaultLevel || setAll))
549 {
550 name = string.Format("{0} {1}", filteredArgs[4], filteredArgs[5]);
551 }
552 else
553 {
554 MainConsole.Instance.OutputFormat("ERROR: Cannot specify a user name when setting default/all logging level");
555 return;
556 }
557 }
558
559 if (filteredArgs.Count > 3)
560 {
561 int newDebug;
562 if (int.TryParse(filteredArgs[3], out newDebug))
563 {
564 if (setAsDefaultLevel || setAll)
565 {
566 m_udpServer.DefaultClientPacketDebugLevel = newDebug;
567
568 MainConsole.Instance.OutputFormat(
569 "Packet debug for {0} clients set to {1} in {2}",
570 (setAll ? "all" : "future"), m_udpServer.DefaultClientPacketDebugLevel, m_udpServer.Scene.Name);
571
572 if (setAll)
573 {
574 m_udpServer.Scene.ForEachScenePresence(sp =>
575 {
576 MainConsole.Instance.OutputFormat(
577 "Packet debug for {0} ({1}) set to {2} in {3}",
578 sp.Name, sp.IsChildAgent ? "child" : "root", newDebug, m_udpServer.Scene.Name);
579
580 sp.ControllingClient.DebugPacketLevel = newDebug;
581 });
582 }
583 }
584 else
585 {
586 m_udpServer.Scene.ForEachScenePresence(sp =>
587 {
588 if (name == null || sp.Name == name)
589 {
590 MainConsole.Instance.OutputFormat(
591 "Packet debug for {0} ({1}) set to {2} in {3}",
592 sp.Name, sp.IsChildAgent ? "child" : "root", newDebug, m_udpServer.Scene.Name);
593
594 sp.ControllingClient.DebugPacketLevel = newDebug;
595 }
596 });
597 }
598 }
599 else
600 {
601 MainConsole.Instance.Output("Usage: debug lludp packet [--default | --all] 0..255 [<first-name> <last-name>]");
602 }
603 }
604 }
605
606 private void HandleDropCommand(string module, string[] args)
607 {
608 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
609 return;
610
611 if (args.Length != 6)
612 {
613 MainConsole.Instance.Output("Usage: debug lludp drop <in|out> <add|remove> <packet-name>");
614 return;
615 }
616
617 string direction = args[3];
618 string subCommand = args[4];
619 string packetName = args[5];
620
621 if (subCommand == "add")
622 {
623 MainConsole.Instance.OutputFormat(
624 "Adding packet {0} to {1} drop list for all connections in {2}",
625 direction, packetName, m_udpServer.Scene.Name);
626
627 m_udpServer.Scene.ForEachScenePresence(
628 sp =>
629 {
630 LLClientView llcv = (LLClientView)sp.ControllingClient;
631
632 if (direction == "in")
633 llcv.AddInPacketToDropSet(packetName);
634 else if (direction == "out")
635 llcv.AddOutPacketToDropSet(packetName);
636 }
637 );
638 }
639 else if (subCommand == "remove")
640 {
641 MainConsole.Instance.OutputFormat(
642 "Removing packet {0} from {1} drop list for all connections in {2}",
643 direction, packetName, m_udpServer.Scene.Name);
644
645 m_udpServer.Scene.ForEachScenePresence(
646 sp =>
647 {
648 LLClientView llcv = (LLClientView)sp.ControllingClient;
649
650 if (direction == "in")
651 llcv.RemoveInPacketFromDropSet(packetName);
652 else if (direction == "out")
653 llcv.RemoveOutPacketFromDropSet(packetName);
654 }
655 );
656 }
657 }
658
659 private void HandleStartCommand(string module, string[] args)
660 {
661 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
662 return;
663
664 if (args.Length != 4)
665 {
666 MainConsole.Instance.Output("Usage: debug lludp start <in|out|all>");
667 return;
668 }
669
670 string subCommand = args[3];
671
672 if (subCommand == "in" || subCommand == "all")
673 m_udpServer.StartInbound();
674
675 if (subCommand == "out" || subCommand == "all")
676 m_udpServer.StartOutbound();
677 }
678
679 private void HandleStopCommand(string module, string[] args)
680 {
681 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
682 return;
683
684 if (args.Length != 4)
685 {
686 MainConsole.Instance.Output("Usage: debug lludp stop <in|out|all>");
687 return;
688 }
689
690 string subCommand = args[3];
691
692 if (subCommand == "in" || subCommand == "all")
693 m_udpServer.StopInbound();
694
695 if (subCommand == "out" || subCommand == "all")
696 m_udpServer.StopOutbound();
697 }
698
699 private void HandlePoolCommand(string module, string[] args)
700 {
701 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
702 return;
703
704 if (args.Length != 4)
705 {
706 MainConsole.Instance.Output("Usage: debug lludp pool <on|off>");
707 return;
708 }
709
710 string enabled = args[3];
711
712 if (enabled == "on")
713 {
714 if (m_udpServer.EnablePools())
715 {
716 m_udpServer.EnablePoolStats();
717 MainConsole.Instance.OutputFormat("Packet pools enabled on {0}", m_udpServer.Scene.Name);
718 }
719 }
720 else if (enabled == "off")
721 {
722 if (m_udpServer.DisablePools())
723 {
724 m_udpServer.DisablePoolStats();
725 MainConsole.Instance.OutputFormat("Packet pools disabled on {0}", m_udpServer.Scene.Name);
726 }
727 }
728 else
729 {
730 MainConsole.Instance.Output("Usage: debug lludp pool <on|off>");
731 }
732 }
733
734 private void HandleAgentUpdateCommand(string module, string[] args)
735 {
736 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
737 return;
738
739 m_udpServer.DiscardInboundAgentUpdates = !m_udpServer.DiscardInboundAgentUpdates;
740
741 MainConsole.Instance.OutputFormat(
742 "Discard AgentUpdates now {0} for {1}", m_udpServer.DiscardInboundAgentUpdates, m_udpServer.Scene.Name);
743 }
744
745 private void HandleStatusCommand(string module, string[] args)
746 {
747 if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
748 return;
749
750 MainConsole.Instance.OutputFormat(
751 "IN LLUDP packet processing for {0} is {1}", m_udpServer.Scene.Name, m_udpServer.IsRunningInbound ? "enabled" : "disabled");
752
753 MainConsole.Instance.OutputFormat(
754 "OUT LLUDP packet processing for {0} is {1}", m_udpServer.Scene.Name, m_udpServer.IsRunningOutbound ? "enabled" : "disabled");
755
756 MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", m_udpServer.Scene.Name, m_udpServer.UsePools ? "on" : "off");
757
758 MainConsole.Instance.OutputFormat(
759 "Packet debug level for new clients is {0}", m_udpServer.DefaultClientPacketDebugLevel);
760 }
761 }
762} \ No newline at end of file
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs
index 88494be..94300f8 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs
@@ -135,7 +135,7 @@ namespace OpenMetaverse
135 /// manner (not throwing an exception when the remote side resets the 135 /// manner (not throwing an exception when the remote side resets the
136 /// connection). This call is ignored on Mono where the flag is not 136 /// connection). This call is ignored on Mono where the flag is not
137 /// necessary</remarks> 137 /// necessary</remarks>
138 public void StartInbound(int recvBufferSize, bool asyncPacketHandling) 138 public virtual void StartInbound(int recvBufferSize, bool asyncPacketHandling)
139 { 139 {
140 m_asyncPacketHandling = asyncPacketHandling; 140 m_asyncPacketHandling = asyncPacketHandling;
141 141
@@ -168,6 +168,12 @@ namespace OpenMetaverse
168 m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring"); 168 m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring");
169 } 169 }
170 170
171 // On at least Mono 3.2.8, multiple UDP sockets can bind to the same port by default. At the moment
172 // we never want two regions to listen on the same port as they cannot demultiplex each other's messages,
173 // leading to a confusing bug.
174 // By default, Windows does not allow two sockets to bind to the same port.
175 m_udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, false);
176
171 if (recvBufferSize != 0) 177 if (recvBufferSize != 0)
172 m_udpSocket.ReceiveBufferSize = recvBufferSize; 178 m_udpSocket.ReceiveBufferSize = recvBufferSize;
173 179
@@ -185,14 +191,14 @@ namespace OpenMetaverse
185 /// <summary> 191 /// <summary>
186 /// Start outbound UDP packet handling. 192 /// Start outbound UDP packet handling.
187 /// </summary> 193 /// </summary>
188 public void StartOutbound() 194 public virtual void StartOutbound()
189 { 195 {
190 m_log.DebugFormat("[UDPBASE]: Starting outbound UDP loop"); 196 m_log.DebugFormat("[UDPBASE]: Starting outbound UDP loop");
191 197
192 IsRunningOutbound = true; 198 IsRunningOutbound = true;
193 } 199 }
194 200
195 public void StopInbound() 201 public virtual void StopInbound()
196 { 202 {
197 if (IsRunningInbound) 203 if (IsRunningInbound)
198 { 204 {
@@ -203,14 +209,14 @@ namespace OpenMetaverse
203 } 209 }
204 } 210 }
205 211
206 public void StopOutbound() 212 public virtual void StopOutbound()
207 { 213 {
208 m_log.DebugFormat("[UDPBASE]: Stopping outbound UDP loop"); 214 m_log.DebugFormat("[UDPBASE]: Stopping outbound UDP loop");
209 215
210 IsRunningOutbound = false; 216 IsRunningOutbound = false;
211 } 217 }
212 218
213 protected virtual bool EnablePools() 219 public virtual bool EnablePools()
214 { 220 {
215 if (!UsePools) 221 if (!UsePools)
216 { 222 {
@@ -224,7 +230,7 @@ namespace OpenMetaverse
224 return false; 230 return false;
225 } 231 }
226 232
227 protected virtual bool DisablePools() 233 public virtual bool DisablePools()
228 { 234 {
229 if (UsePools) 235 if (UsePools)
230 { 236 {
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OutgoingQueueRefillEngine.cs b/OpenSim/Region/ClientStack/Linden/UDP/OutgoingQueueRefillEngine.cs
index 069c9c8..1e915c3 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/OutgoingQueueRefillEngine.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/OutgoingQueueRefillEngine.cs
@@ -124,7 +124,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
124 124
125 StatsManager.RegisterStat(m_oqreRequestsWaitingStat); 125 StatsManager.RegisterStat(m_oqreRequestsWaitingStat);
126 126
127 Watchdog.StartThread( 127 WorkManager.StartThread(
128 ProcessRequests, 128 ProcessRequests,
129 String.Format("OutgoingQueueRefillEngineThread ({0})", m_udpServer.Scene.Name), 129 String.Format("OutgoingQueueRefillEngineThread ({0})", m_udpServer.Scene.Name),
130 ThreadPriority.Normal, 130 ThreadPriority.Normal,
@@ -217,6 +217,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
217 217
218 private void ProcessRequests() 218 private void ProcessRequests()
219 { 219 {
220 Thread.CurrentThread.Priority = ThreadPriority.Highest;
221
220 try 222 try
221 { 223 {
222 while (IsRunning || m_requestQueue.Count > 0) 224 while (IsRunning || m_requestQueue.Count > 0)
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs
index e9aeffe..a935dd2 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs
@@ -36,7 +36,6 @@ using OpenSim.Framework;
36using OpenSim.Framework.Monitoring; 36using OpenSim.Framework.Monitoring;
37using OpenSim.Region.Framework.Scenes; 37using OpenSim.Region.Framework.Scenes;
38using OpenSim.Tests.Common; 38using OpenSim.Tests.Common;
39using OpenSim.Tests.Common.Mock;
40 39
41namespace OpenSim.Region.ClientStack.LindenUDP.Tests 40namespace OpenSim.Region.ClientStack.LindenUDP.Tests
42{ 41{
@@ -47,7 +46,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
47 public class BasicCircuitTests : OpenSimTestCase 46 public class BasicCircuitTests : OpenSimTestCase
48 { 47 {
49 private Scene m_scene; 48 private Scene m_scene;
50 private TestLLUDPServer m_udpServer;
51 49
52 [TestFixtureSetUp] 50 [TestFixtureSetUp]
53 public void FixtureInit() 51 public void FixtureInit()
@@ -73,72 +71,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
73 StatsManager.SimExtraStats = new SimExtraStatsCollector(); 71 StatsManager.SimExtraStats = new SimExtraStatsCollector();
74 } 72 }
75 73
76 /// <summary> 74// /// <summary>
77 /// Build an object name packet for test purposes 75// /// Build an object name packet for test purposes
78 /// </summary> 76// /// </summary>
79 /// <param name="objectLocalId"></param> 77// /// <param name="objectLocalId"></param>
80 /// <param name="objectName"></param> 78// /// <param name="objectName"></param>
81 private ObjectNamePacket BuildTestObjectNamePacket(uint objectLocalId, string objectName) 79// private ObjectNamePacket BuildTestObjectNamePacket(uint objectLocalId, string objectName)
82 { 80// {
83 ObjectNamePacket onp = new ObjectNamePacket(); 81// ObjectNamePacket onp = new ObjectNamePacket();
84 ObjectNamePacket.ObjectDataBlock odb = new ObjectNamePacket.ObjectDataBlock(); 82// ObjectNamePacket.ObjectDataBlock odb = new ObjectNamePacket.ObjectDataBlock();
85 odb.LocalID = objectLocalId; 83// odb.LocalID = objectLocalId;
86 odb.Name = Utils.StringToBytes(objectName); 84// odb.Name = Utils.StringToBytes(objectName);
87 onp.ObjectData = new ObjectNamePacket.ObjectDataBlock[] { odb }; 85// onp.ObjectData = new ObjectNamePacket.ObjectDataBlock[] { odb };
88 onp.Header.Zerocoded = false; 86// onp.Header.Zerocoded = false;
89 87//
90 return onp; 88// return onp;
91 } 89// }
92 90//
93 private void AddUdpServer()
94 {
95 AddUdpServer(new IniConfigSource());
96 }
97
98 private void AddUdpServer(IniConfigSource configSource)
99 {
100 uint port = 0;
101 AgentCircuitManager acm = m_scene.AuthenticateHandler;
102
103 m_udpServer = new TestLLUDPServer(IPAddress.Any, ref port, 0, false, configSource, acm);
104 m_udpServer.AddScene(m_scene);
105 }
106
107 /// <summary>
108 /// Used by tests that aren't testing this stage.
109 /// </summary>
110 private ScenePresence AddClient()
111 {
112 UUID myAgentUuid = TestHelpers.ParseTail(0x1);
113 UUID mySessionUuid = TestHelpers.ParseTail(0x2);
114 uint myCircuitCode = 123456;
115 IPEndPoint testEp = new IPEndPoint(IPAddress.Loopback, 999);
116
117 UseCircuitCodePacket uccp = new UseCircuitCodePacket();
118
119 UseCircuitCodePacket.CircuitCodeBlock uccpCcBlock
120 = new UseCircuitCodePacket.CircuitCodeBlock();
121 uccpCcBlock.Code = myCircuitCode;
122 uccpCcBlock.ID = myAgentUuid;
123 uccpCcBlock.SessionID = mySessionUuid;
124 uccp.CircuitCode = uccpCcBlock;
125
126 byte[] uccpBytes = uccp.ToBytes();
127 UDPPacketBuffer upb = new UDPPacketBuffer(testEp, uccpBytes.Length);
128 upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor.
129 Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length);
130
131 AgentCircuitData acd = new AgentCircuitData();
132 acd.AgentID = myAgentUuid;
133 acd.SessionID = mySessionUuid;
134
135 m_scene.AuthenticateHandler.AddNewCircuit(myCircuitCode, acd);
136
137 m_udpServer.PacketReceived(upb);
138
139 return m_scene.GetScenePresence(myAgentUuid);
140 }
141
142 /// <summary> 91 /// <summary>
143 /// Test adding a client to the stack 92 /// Test adding a client to the stack
144 /// </summary> 93 /// </summary>
@@ -148,7 +97,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
148 TestHelpers.InMethod(); 97 TestHelpers.InMethod();
149// TestHelpers.EnableLogging(); 98// TestHelpers.EnableLogging();
150 99
151 AddUdpServer(); 100 TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(m_scene);
152 101
153 UUID myAgentUuid = TestHelpers.ParseTail(0x1); 102 UUID myAgentUuid = TestHelpers.ParseTail(0x1);
154 UUID mySessionUuid = TestHelpers.ParseTail(0x2); 103 UUID mySessionUuid = TestHelpers.ParseTail(0x2);
@@ -169,7 +118,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
169 upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor. 118 upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor.
170 Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length); 119 Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length);
171 120
172 m_udpServer.PacketReceived(upb); 121 udpServer.PacketReceived(upb);
173 122
174 // Presence shouldn't exist since the circuit manager doesn't know about this circuit for authentication yet 123 // Presence shouldn't exist since the circuit manager doesn't know about this circuit for authentication yet
175 Assert.That(m_scene.GetScenePresence(myAgentUuid), Is.Null); 124 Assert.That(m_scene.GetScenePresence(myAgentUuid), Is.Null);
@@ -180,15 +129,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
180 129
181 m_scene.AuthenticateHandler.AddNewCircuit(myCircuitCode, acd); 130 m_scene.AuthenticateHandler.AddNewCircuit(myCircuitCode, acd);
182 131
183 m_udpServer.PacketReceived(upb); 132 udpServer.PacketReceived(upb);
184 133
185 // Should succeed now 134 // Should succeed now
186 ScenePresence sp = m_scene.GetScenePresence(myAgentUuid); 135 ScenePresence sp = m_scene.GetScenePresence(myAgentUuid);
187 Assert.That(sp.UUID, Is.EqualTo(myAgentUuid)); 136 Assert.That(sp.UUID, Is.EqualTo(myAgentUuid));
188 137
189 Assert.That(m_udpServer.PacketsSent.Count, Is.EqualTo(1)); 138 Assert.That(udpServer.PacketsSent.Count, Is.EqualTo(1));
190 139
191 Packet packet = m_udpServer.PacketsSent[0]; 140 Packet packet = udpServer.PacketsSent[0];
192 Assert.That(packet, Is.InstanceOf(typeof(PacketAckPacket))); 141 Assert.That(packet, Is.InstanceOf(typeof(PacketAckPacket)));
193 142
194 PacketAckPacket ackPacket = packet as PacketAckPacket; 143 PacketAckPacket ackPacket = packet as PacketAckPacket;
@@ -200,15 +149,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
200 public void TestLogoutClientDueToAck() 149 public void TestLogoutClientDueToAck()
201 { 150 {
202 TestHelpers.InMethod(); 151 TestHelpers.InMethod();
203 TestHelpers.EnableLogging(); 152// TestHelpers.EnableLogging();
204 153
205 IniConfigSource ics = new IniConfigSource(); 154 IniConfigSource ics = new IniConfigSource();
206 IConfig config = ics.AddConfig("ClientStack.LindenUDP"); 155 IConfig config = ics.AddConfig("ClientStack.LindenUDP");
207 config.Set("AckTimeout", -1); 156 config.Set("AckTimeout", -1);
208 AddUdpServer(ics); 157 TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(m_scene, ics);
158
159 ScenePresence sp
160 = ClientStackHelpers.AddChildClient(
161 m_scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
209 162
210 ScenePresence sp = AddClient(); 163 udpServer.ClientOutgoingPacketHandler(sp.ControllingClient, true, false, false);
211 m_udpServer.ClientOutgoingPacketHandler(sp.ControllingClient, true, false, false);
212 164
213 ScenePresence spAfterAckTimeout = m_scene.GetScenePresence(sp.UUID); 165 ScenePresence spAfterAckTimeout = m_scene.GetScenePresence(sp.UUID);
214 Assert.That(spAfterAckTimeout, Is.Null); 166 Assert.That(spAfterAckTimeout, Is.Null);
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs
index 575e54c..6c57e6d 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs
@@ -39,7 +39,6 @@ using OpenSim.Framework;
39using OpenSim.Region.CoreModules.Agent.TextureSender; 39using OpenSim.Region.CoreModules.Agent.TextureSender;
40using OpenSim.Region.Framework.Scenes; 40using OpenSim.Region.Framework.Scenes;
41using OpenSim.Tests.Common; 41using OpenSim.Tests.Common;
42using OpenSim.Tests.Common.Mock;
43 42
44namespace OpenSim.Region.ClientStack.LindenUDP.Tests 43namespace OpenSim.Region.ClientStack.LindenUDP.Tests
45{ 44{
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs
deleted file mode 100644
index 39d1875..0000000
--- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs
+++ /dev/null
@@ -1,79 +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 OpenMetaverse;
30using OpenSim.Framework;
31using OpenSim.Region.Framework.Scenes;
32using GridRegion = OpenSim.Services.Interfaces.GridRegion;
33
34namespace OpenSim.Region.ClientStack.LindenUDP.Tests
35{
36 /// <summary>
37 /// Mock scene for unit tests
38 /// </summary>
39 public class MockScene : SceneBase
40 {
41 public int ObjectNameCallsReceived
42 {
43 get { return m_objectNameCallsReceived; }
44 }
45 protected int m_objectNameCallsReceived;
46
47 public MockScene() : base(new RegionInfo(1000, 1000, null, null))
48 {
49 m_regStatus = RegionStatus.Up;
50 }
51
52 public override bool Update(int frames) { return true; }
53 public override void LoadWorldMap() {}
54
55 public override ISceneAgent AddNewAgent(IClientAPI client, PresenceType type)
56 {
57 client.OnObjectName += RecordObjectNameCall;
58
59 // FIXME
60 return null;
61 }
62
63 public override bool CloseAgent(UUID agentID, bool force) { return true; }
64
65 public override bool CheckClient(UUID clientId, IPEndPoint endPoint) { return true; }
66
67 public override void OtherRegionUp(GridRegion otherRegion) { }
68
69 public override bool TryGetScenePresence(UUID uuid, out ScenePresence sp) { sp = null; return false; }
70
71 /// <summary>
72 /// Doesn't really matter what the call is - we're using this to test that a packet has actually been received
73 /// </summary>
74 protected void RecordObjectNameCall(IClientAPI remoteClient, uint localID, string message)
75 {
76 m_objectNameCallsReceived++;
77 }
78 }
79}
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/PacketHandlerTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/PacketHandlerTests.cs
index 5f73a94..92f1fc3 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/PacketHandlerTests.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/PacketHandlerTests.cs
@@ -30,7 +30,6 @@ using NUnit.Framework;
30using OpenMetaverse; 30using OpenMetaverse;
31using OpenMetaverse.Packets; 31using OpenMetaverse.Packets;
32using OpenSim.Framework; 32using OpenSim.Framework;
33using OpenSim.Tests.Common.Mock;
34using OpenSim.Tests.Common; 33using OpenSim.Tests.Common;
35 34
36namespace OpenSim.Region.ClientStack.LindenUDP.Tests 35namespace OpenSim.Region.ClientStack.LindenUDP.Tests
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/TestLLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/TestLLUDPServer.cs
deleted file mode 100644
index 27b9e5b..0000000
--- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/TestLLUDPServer.cs
+++ /dev/null
@@ -1,170 +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.Net;
31using System.Net.Sockets;
32using Nini.Config;
33using OpenMetaverse.Packets;
34using OpenSim.Framework;
35
36namespace OpenSim.Region.ClientStack.LindenUDP.Tests
37{
38 /// <summary>
39 /// This class enables regression testing of the LLUDPServer by allowing us to intercept outgoing data.
40 /// </summary>
41 public class TestLLUDPServer : LLUDPServer
42 {
43 public List<Packet> PacketsSent { get; private set; }
44
45 public TestLLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager)
46 : base(listenIP, ref port, proxyPortOffsetParm, allow_alternate_port, configSource, circuitManager)
47 {
48 PacketsSent = new List<Packet>();
49 }
50
51 public override void SendAckImmediate(IPEndPoint remoteEndpoint, PacketAckPacket ack)
52 {
53 PacketsSent.Add(ack);
54 }
55
56 public override void SendPacket(
57 LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting, UnackedPacketMethod method)
58 {
59 PacketsSent.Add(packet);
60 }
61
62 public void ClientOutgoingPacketHandler(IClientAPI client, bool resendUnacked, bool sendAcks, bool sendPing)
63 {
64 m_resendUnacked = resendUnacked;
65 m_sendAcks = sendAcks;
66 m_sendPing = sendPing;
67
68 ClientOutgoingPacketHandler(client);
69 }
70
71//// /// <summary>
72//// /// The chunks of data to pass to the LLUDPServer when it calls EndReceive
73//// /// </summary>
74//// protected Queue<ChunkSenderTuple> m_chunksToLoad = new Queue<ChunkSenderTuple>();
75//
76//// protected override void BeginReceive()
77//// {
78//// if (m_chunksToLoad.Count > 0 && m_chunksToLoad.Peek().BeginReceiveException)
79//// {
80//// ChunkSenderTuple tuple = m_chunksToLoad.Dequeue();
81//// reusedEpSender = tuple.Sender;
82//// throw new SocketException();
83//// }
84//// }
85//
86//// protected override bool EndReceive(out int numBytes, IAsyncResult result, ref EndPoint epSender)
87//// {
88//// numBytes = 0;
89////
90//// //m_log.Debug("Queue size " + m_chunksToLoad.Count);
91////
92//// if (m_chunksToLoad.Count <= 0)
93//// return false;
94////
95//// ChunkSenderTuple tuple = m_chunksToLoad.Dequeue();
96//// RecvBuffer = tuple.Data;
97//// numBytes = tuple.Data.Length;
98//// epSender = tuple.Sender;
99////
100//// return true;
101//// }
102//
103//// public override void SendPacketTo(byte[] buffer, int size, SocketFlags flags, uint circuitcode)
104//// {
105//// // Don't do anything just yet
106//// }
107//
108// /// <summary>
109// /// Signal that this chunk should throw an exception on Socket.BeginReceive()
110// /// </summary>
111// /// <param name="epSender"></param>
112// public void LoadReceiveWithBeginException(EndPoint epSender)
113// {
114// ChunkSenderTuple tuple = new ChunkSenderTuple(epSender);
115// tuple.BeginReceiveException = true;
116// m_chunksToLoad.Enqueue(tuple);
117// }
118//
119// /// <summary>
120// /// Load some data to be received by the LLUDPServer on the next receive call
121// /// </summary>
122// /// <param name="data"></param>
123// /// <param name="epSender"></param>
124// public void LoadReceive(byte[] data, EndPoint epSender)
125// {
126// m_chunksToLoad.Enqueue(new ChunkSenderTuple(data, epSender));
127// }
128//
129// /// <summary>
130// /// Load a packet to be received by the LLUDPServer on the next receive call
131// /// </summary>
132// /// <param name="packet"></param>
133// public void LoadReceive(Packet packet, EndPoint epSender)
134// {
135// LoadReceive(packet.ToBytes(), epSender);
136// }
137//
138// /// <summary>
139// /// Calls the protected asynchronous result method. This fires out all data chunks currently queued for send
140// /// </summary>
141// /// <param name="result"></param>
142// public void ReceiveData(IAsyncResult result)
143// {
144// // Doesn't work the same way anymore
145//// while (m_chunksToLoad.Count > 0)
146//// OnReceivedData(result);
147// }
148 }
149
150 /// <summary>
151 /// Record the data and sender tuple
152 /// </summary>
153 public class ChunkSenderTuple
154 {
155 public byte[] Data;
156 public EndPoint Sender;
157 public bool BeginReceiveException;
158
159 public ChunkSenderTuple(byte[] data, EndPoint sender)
160 {
161 Data = data;
162 Sender = sender;
163 }
164
165 public ChunkSenderTuple(EndPoint sender)
166 {
167 Sender = sender;
168 }
169 }
170}
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/ThrottleTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/ThrottleTests.cs
new file mode 100644
index 0000000..0560b9b
--- /dev/null
+++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/ThrottleTests.cs
@@ -0,0 +1,425 @@
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;
30using NUnit.Framework;
31using OpenMetaverse.Packets;
32using OpenSim.Framework;
33using OpenSim.Region.Framework.Scenes;
34using OpenSim.Tests.Common;
35
36namespace OpenSim.Region.ClientStack.LindenUDP.Tests
37{
38 [TestFixture]
39 public class ThrottleTests : OpenSimTestCase
40 {
41 [TestFixtureSetUp]
42 public void FixtureInit()
43 {
44 // Don't allow tests to be bamboozled by asynchronous events. Execute everything on the same thread.
45 Util.FireAndForgetMethod = FireAndForgetMethod.RegressionTest;
46 }
47
48 [TestFixtureTearDown]
49 public void TearDown()
50 {
51 // We must set this back afterwards, otherwise later tests will fail since they're expecting multiple
52 // threads. Possibly, later tests should be rewritten so none of them require async stuff (which regression
53 // tests really shouldn't).
54 Util.FireAndForgetMethod = Util.DefaultFireAndForgetMethod;
55 }
56
57 [Test]
58 public void TestSetRequestDripRate()
59 {
60 TestHelpers.InMethod();
61
62 TokenBucket tb = new TokenBucket("tb", null, 5000, 0);
63 AssertRates(tb, 5000, 0, 5000, 0);
64
65 tb.RequestedDripRate = 4000;
66 AssertRates(tb, 4000, 0, 4000, 0);
67
68 tb.RequestedDripRate = 6000;
69 AssertRates(tb, 6000, 0, 6000, 0);
70 }
71
72 [Test]
73 public void TestSetRequestDripRateWithMax()
74 {
75 TestHelpers.InMethod();
76
77 TokenBucket tb = new TokenBucket("tb", null, 5000, 10000);
78 AssertRates(tb, 5000, 0, 5000, 10000);
79
80 tb.RequestedDripRate = 4000;
81 AssertRates(tb, 4000, 0, 4000, 10000);
82
83 tb.RequestedDripRate = 6000;
84 AssertRates(tb, 6000, 0, 6000, 10000);
85
86 tb.RequestedDripRate = 12000;
87 AssertRates(tb, 10000, 0, 10000, 10000);
88 }
89
90 [Test]
91 public void TestSetRequestDripRateWithChildren()
92 {
93 TestHelpers.InMethod();
94
95 TokenBucket tbParent = new TokenBucket("tbParent", null, 0, 0);
96 TokenBucket tbChild1 = new TokenBucket("tbChild1", tbParent, 3000, 0);
97 TokenBucket tbChild2 = new TokenBucket("tbChild2", tbParent, 5000, 0);
98
99 AssertRates(tbParent, 8000, 8000, 8000, 0);
100 AssertRates(tbChild1, 3000, 0, 3000, 0);
101 AssertRates(tbChild2, 5000, 0, 5000, 0);
102
103 // Test: Setting a parent request greater than total children requests.
104 tbParent.RequestedDripRate = 10000;
105
106 AssertRates(tbParent, 10000, 8000, 8000, 0);
107 AssertRates(tbChild1, 3000, 0, 3000, 0);
108 AssertRates(tbChild2, 5000, 0, 5000, 0);
109
110 // Test: Setting a parent request lower than total children requests.
111 tbParent.RequestedDripRate = 6000;
112
113 AssertRates(tbParent, 6000, 8000, 6000, 0);
114 AssertRates(tbChild1, 3000, 0, 6000 / 8 * 3, 0);
115 AssertRates(tbChild2, 5000, 0, 6000 / 8 * 5, 0);
116 }
117
118 private void AssertRates(
119 TokenBucket tb, double requestedDripRate, double totalDripRequest, double dripRate, double maxDripRate)
120 {
121 Assert.AreEqual((int)requestedDripRate, tb.RequestedDripRate, "Requested drip rate");
122 Assert.AreEqual((int)totalDripRequest, tb.TotalDripRequest, "Total drip request");
123 Assert.AreEqual((int)dripRate, tb.DripRate, "Drip rate");
124 Assert.AreEqual((int)maxDripRate, tb.MaxDripRate, "Max drip rate");
125 }
126
127 [Test]
128 public void TestClientThrottleSetNoLimit()
129 {
130 TestHelpers.InMethod();
131// TestHelpers.EnableLogging();
132
133 Scene scene = new SceneHelpers().SetupScene();
134 TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene);
135
136 ScenePresence sp
137 = ClientStackHelpers.AddChildClient(
138 scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
139
140 LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
141
142 udpServer.Throttle.DebugLevel = 1;
143 udpClient.ThrottleDebugLevel = 1;
144
145 int resendBytes = 1000;
146 int landBytes = 2000;
147 int windBytes = 3000;
148 int cloudBytes = 4000;
149 int taskBytes = 5000;
150 int textureBytes = 6000;
151 int assetBytes = 7000;
152
153 SetThrottles(
154 udpClient, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
155
156 // We expect this to be lower because of the minimum bound set by MTU
157 int totalBytes = LLUDPServer.MTU + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes;
158
159 AssertThrottles(
160 udpClient,
161 LLUDPServer.MTU, landBytes, windBytes, cloudBytes, taskBytes,
162 textureBytes, assetBytes, totalBytes, 0, 0);
163 }
164
165 [Test]
166 public void TestClientThrottleAdaptiveNoLimit()
167 {
168 TestHelpers.InMethod();
169// TestHelpers.EnableLogging();
170
171 Scene scene = new SceneHelpers().SetupScene();
172
173 IniConfigSource ics = new IniConfigSource();
174 IConfig config = ics.AddConfig("ClientStack.LindenUDP");
175 config.Set("enable_adaptive_throttles", true);
176 TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene, ics);
177
178 ScenePresence sp
179 = ClientStackHelpers.AddChildClient(
180 scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
181
182 LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
183
184 udpServer.Throttle.DebugLevel = 1;
185 udpClient.ThrottleDebugLevel = 1;
186
187 // Total is 280000
188 int resendBytes = 10000;
189 int landBytes = 20000;
190 int windBytes = 30000;
191 int cloudBytes = 40000;
192 int taskBytes = 50000;
193 int textureBytes = 60000;
194 int assetBytes = 70000;
195 int totalBytes = resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes;
196
197 SetThrottles(
198 udpClient, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
199
200 // Ratio of current adaptive drip rate to requested bytes
201 // XXX: Should hard code this as below so we don't rely on values given by tested code to construct
202 // expected values.
203 double commitRatio = (double)udpClient.FlowThrottle.AdjustedDripRate / udpClient.FlowThrottle.TargetDripRate;
204
205 AssertThrottles(
206 udpClient,
207 LLUDPServer.MTU, landBytes * commitRatio, windBytes * commitRatio, cloudBytes * commitRatio, taskBytes * commitRatio,
208 textureBytes * commitRatio, assetBytes * commitRatio, udpClient.FlowThrottle.AdjustedDripRate, totalBytes, 0);
209
210 // Test an increase in target throttle
211 udpClient.FlowThrottle.AcknowledgePackets(35000);
212 commitRatio = 0.2;
213
214 AssertThrottles(
215 udpClient,
216 resendBytes * commitRatio, landBytes * commitRatio, windBytes * commitRatio, cloudBytes * commitRatio, taskBytes * commitRatio,
217 textureBytes * commitRatio, assetBytes * commitRatio, udpClient.FlowThrottle.AdjustedDripRate, totalBytes, 0);
218
219 // Test a decrease in target throttle
220 udpClient.FlowThrottle.ExpirePackets(1);
221 commitRatio = 0.1;
222
223 AssertThrottles(
224 udpClient,
225 LLUDPServer.MTU, landBytes * commitRatio, windBytes * commitRatio, cloudBytes * commitRatio, taskBytes * commitRatio,
226 textureBytes * commitRatio, assetBytes * commitRatio, udpClient.FlowThrottle.AdjustedDripRate, totalBytes, 0);
227 }
228
229 /// <summary>
230 /// Test throttle setttings where max client throttle has been limited server side.
231 /// </summary>
232 [Test]
233 public void TestSingleClientThrottleRegionLimited()
234 {
235 TestHelpers.InMethod();
236 // TestHelpers.EnableLogging();
237
238 int resendBytes = 6000;
239 int landBytes = 8000;
240 int windBytes = 10000;
241 int cloudBytes = 12000;
242 int taskBytes = 14000;
243 int textureBytes = 16000;
244 int assetBytes = 18000;
245 int totalBytes
246 = (int)((resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes) / 2);
247
248 Scene scene = new SceneHelpers().SetupScene();
249 TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene);
250 udpServer.Throttle.RequestedDripRate = totalBytes;
251
252 ScenePresence sp1
253 = ClientStackHelpers.AddChildClient(
254 scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
255
256 LLUDPClient udpClient1 = ((LLClientView)sp1.ControllingClient).UDPClient;
257
258 SetThrottles(
259 udpClient1, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
260
261 AssertThrottles(
262 udpClient1,
263 resendBytes / 2, landBytes / 2, windBytes / 2, cloudBytes / 2, taskBytes / 2,
264 textureBytes / 2, assetBytes / 2, totalBytes, 0, 0);
265
266 // Test: Now add another client
267 ScenePresence sp2
268 = ClientStackHelpers.AddChildClient(
269 scene, udpServer, TestHelpers.ParseTail(0x10), TestHelpers.ParseTail(0x20), 123457);
270
271 LLUDPClient udpClient2 = ((LLClientView)sp2.ControllingClient).UDPClient;
272 // udpClient.ThrottleDebugLevel = 1;
273
274 SetThrottles(
275 udpClient2, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
276
277 AssertThrottles(
278 udpClient1,
279 resendBytes / 4, landBytes / 4, windBytes / 4, cloudBytes / 4, taskBytes / 4,
280 textureBytes / 4, assetBytes / 4, totalBytes / 2, 0, 0);
281
282 AssertThrottles(
283 udpClient2,
284 resendBytes / 4, landBytes / 4, windBytes / 4, cloudBytes / 4, taskBytes / 4,
285 textureBytes / 4, assetBytes / 4, totalBytes / 2, 0, 0);
286 }
287
288 /// <summary>
289 /// Test throttle setttings where max client throttle has been limited server side.
290 /// </summary>
291 [Test]
292 public void TestClientThrottlePerClientLimited()
293 {
294 TestHelpers.InMethod();
295 // TestHelpers.EnableLogging();
296
297 int resendBytes = 4000;
298 int landBytes = 6000;
299 int windBytes = 8000;
300 int cloudBytes = 10000;
301 int taskBytes = 12000;
302 int textureBytes = 14000;
303 int assetBytes = 16000;
304 int totalBytes
305 = (int)((resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes) / 2);
306
307 Scene scene = new SceneHelpers().SetupScene();
308 TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene);
309 udpServer.ThrottleRates.Total = totalBytes;
310
311 ScenePresence sp
312 = ClientStackHelpers.AddChildClient(
313 scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
314
315 LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
316 // udpClient.ThrottleDebugLevel = 1;
317
318 SetThrottles(
319 udpClient, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
320
321 AssertThrottles(
322 udpClient,
323 resendBytes / 2, landBytes / 2, windBytes / 2, cloudBytes / 2, taskBytes / 2,
324 textureBytes / 2, assetBytes / 2, totalBytes, 0, totalBytes);
325 }
326
327 [Test]
328 public void TestClientThrottlePerClientAndRegionLimited()
329 {
330 TestHelpers.InMethod();
331 //TestHelpers.EnableLogging();
332
333 int resendBytes = 4000;
334 int landBytes = 6000;
335 int windBytes = 8000;
336 int cloudBytes = 10000;
337 int taskBytes = 12000;
338 int textureBytes = 14000;
339 int assetBytes = 16000;
340
341 // current total 70000
342 int totalBytes = resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes;
343
344 Scene scene = new SceneHelpers().SetupScene();
345 TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene);
346 udpServer.ThrottleRates.Total = (int)(totalBytes * 1.1);
347 udpServer.Throttle.RequestedDripRate = (int)(totalBytes * 1.5);
348
349 ScenePresence sp1
350 = ClientStackHelpers.AddChildClient(
351 scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
352
353 LLUDPClient udpClient1 = ((LLClientView)sp1.ControllingClient).UDPClient;
354 udpClient1.ThrottleDebugLevel = 1;
355
356 SetThrottles(
357 udpClient1, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
358
359 AssertThrottles(
360 udpClient1,
361 resendBytes, landBytes, windBytes, cloudBytes, taskBytes,
362 textureBytes, assetBytes, totalBytes, 0, totalBytes * 1.1);
363
364 // Now add another client
365 ScenePresence sp2
366 = ClientStackHelpers.AddChildClient(
367 scene, udpServer, TestHelpers.ParseTail(0x10), TestHelpers.ParseTail(0x20), 123457);
368
369 LLUDPClient udpClient2 = ((LLClientView)sp2.ControllingClient).UDPClient;
370 udpClient2.ThrottleDebugLevel = 1;
371
372 SetThrottles(
373 udpClient2, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
374
375 AssertThrottles(
376 udpClient1,
377 resendBytes * 0.75, landBytes * 0.75, windBytes * 0.75, cloudBytes * 0.75, taskBytes * 0.75,
378 textureBytes * 0.75, assetBytes * 0.75, totalBytes * 0.75, 0, totalBytes * 1.1);
379
380 AssertThrottles(
381 udpClient2,
382 resendBytes * 0.75, landBytes * 0.75, windBytes * 0.75, cloudBytes * 0.75, taskBytes * 0.75,
383 textureBytes * 0.75, assetBytes * 0.75, totalBytes * 0.75, 0, totalBytes * 1.1);
384 }
385
386 private void AssertThrottles(
387 LLUDPClient udpClient,
388 double resendBytes, double landBytes, double windBytes, double cloudBytes, double taskBytes, double textureBytes, double assetBytes,
389 double totalBytes, double targetBytes, double maxBytes)
390 {
391 ClientInfo ci = udpClient.GetClientInfo();
392
393// Console.WriteLine(
394// "Resend={0}, Land={1}, Wind={2}, Cloud={3}, Task={4}, Texture={5}, Asset={6}, TOTAL = {7}",
395// ci.resendThrottle, ci.landThrottle, ci.windThrottle, ci.cloudThrottle, ci.taskThrottle, ci.textureThrottle, ci.assetThrottle, ci.totalThrottle);
396
397 Assert.AreEqual((int)resendBytes, ci.resendThrottle, "Resend");
398 Assert.AreEqual((int)landBytes, ci.landThrottle, "Land");
399 Assert.AreEqual((int)windBytes, ci.windThrottle, "Wind");
400 Assert.AreEqual((int)cloudBytes, ci.cloudThrottle, "Cloud");
401 Assert.AreEqual((int)taskBytes, ci.taskThrottle, "Task");
402 Assert.AreEqual((int)textureBytes, ci.textureThrottle, "Texture");
403 Assert.AreEqual((int)assetBytes, ci.assetThrottle, "Asset");
404 Assert.AreEqual((int)totalBytes, ci.totalThrottle, "Total");
405 Assert.AreEqual((int)targetBytes, ci.targetThrottle, "Target");
406 Assert.AreEqual((int)maxBytes, ci.maxThrottle, "Max");
407 }
408
409 private void SetThrottles(
410 LLUDPClient udpClient, int resendBytes, int landBytes, int windBytes, int cloudBytes, int taskBytes, int textureBytes, int assetBytes)
411 {
412 byte[] throttles = new byte[28];
413
414 Array.Copy(BitConverter.GetBytes((float)resendBytes * 8), 0, throttles, 0, 4);
415 Array.Copy(BitConverter.GetBytes((float)landBytes * 8), 0, throttles, 4, 4);
416 Array.Copy(BitConverter.GetBytes((float)windBytes * 8), 0, throttles, 8, 4);
417 Array.Copy(BitConverter.GetBytes((float)cloudBytes * 8), 0, throttles, 12, 4);
418 Array.Copy(BitConverter.GetBytes((float)taskBytes * 8), 0, throttles, 16, 4);
419 Array.Copy(BitConverter.GetBytes((float)textureBytes * 8), 0, throttles, 20, 4);
420 Array.Copy(BitConverter.GetBytes((float)assetBytes * 8), 0, throttles, 24, 4);
421
422 udpClient.SetThrottles(throttles);
423 }
424 }
425} \ No newline at end of file
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs b/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs
index e5bae6e..dd15cc7 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs
@@ -72,6 +72,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
72 { 72 {
73 IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"]; 73 IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"];
74 74
75 // Current default total is 66750
75 Resend = throttleConfig.GetInt("resend_default", 6625); 76 Resend = throttleConfig.GetInt("resend_default", 6625);
76 Land = throttleConfig.GetInt("land_default", 9125); 77 Land = throttleConfig.GetInt("land_default", 9125);
77 Wind = throttleConfig.GetInt("wind_default", 1750); 78 Wind = throttleConfig.GetInt("wind_default", 1750);
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs
index 658d9bb..d215595 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs
@@ -42,9 +42,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
42 public class TokenBucket 42 public class TokenBucket
43 { 43 {
44 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 44 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
45 private static Int32 m_counter = 0;
46
47 private LLUDPClient m_client;
48 45
49 public string Identifier { get; private set; } 46 public string Identifier { get; private set; }
50 47
@@ -79,20 +76,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
79 /// Map of children buckets and their requested maximum burst rate 76 /// Map of children buckets and their requested maximum burst rate
80 /// </summary> 77 /// </summary>
81 protected Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>(); 78 protected Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>();
82
83#region Properties
84 79
85 /// <summary> 80 /// <summary>
86 /// The parent bucket of this bucket, or null if this bucket has no 81 /// The parent bucket of this bucket, or null if this bucket has no
87 /// parent. The parent bucket will limit the aggregate bandwidth of all 82 /// parent. The parent bucket will limit the aggregate bandwidth of all
88 /// of its children buckets 83 /// of its children buckets
89 /// </summary> 84 /// </summary>
90 protected TokenBucket m_parent; 85 public TokenBucket Parent { get; protected set; }
91 public TokenBucket Parent
92 {
93 get { return m_parent; }
94 set { m_parent = value; }
95 }
96 86
97 /// <summary> 87 /// <summary>
98 /// Maximum burst rate in bytes per second. This is the maximum number 88 /// Maximum burst rate in bytes per second. This is the maximum number
@@ -118,53 +108,85 @@ namespace OpenSim.Region.ClientStack.LindenUDP
118 } 108 }
119 109
120 /// <summary> 110 /// <summary>
121 /// The speed limit of this bucket in bytes per second. This is the 111 /// The requested drip rate for this particular bucket.
122 /// number of tokens that are added to the bucket per quantum
123 /// </summary> 112 /// </summary>
124 /// <remarks>Tokens are added to the bucket any time 113 /// <remarks>
114 /// 0 then TotalDripRequest is used instead.
115 /// Can never be above MaxDripRate.
116 /// Tokens are added to the bucket at any time
125 /// <seealso cref="RemoveTokens"/> is called, at the granularity of 117 /// <seealso cref="RemoveTokens"/> is called, at the granularity of
126 /// the system tick interval (typically around 15-22ms)</remarks> 118 /// the system tick interval (typically around 15-22ms)
127 protected Int64 m_dripRate; 119 /// FIXME: It is extremely confusing to be able to set a RequestedDripRate of 0 and then receive a positive
120 /// number on get if TotalDripRequest is sent. This also stops us being able to retrieve the fact that
121 /// RequestedDripRate is set to 0. Really, this should always return m_dripRate and then we can get
122 /// (m_dripRate == 0 ? TotalDripRequest : m_dripRate) on some other properties.
123 /// </remarks>
128 public virtual Int64 RequestedDripRate 124 public virtual Int64 RequestedDripRate
129 { 125 {
130 get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } 126 get { return (m_dripRate == 0 ? TotalDripRequest : m_dripRate); }
131 set { 127 set
132 m_dripRate = (value < 0 ? 0 : value); 128 {
129 if (value <= 0)
130 m_dripRate = 0;
131 else if (MaxDripRate > 0 && value > MaxDripRate)
132 m_dripRate = MaxDripRate;
133 else
134 m_dripRate = value;
135
133 m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); 136 m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
134 m_totalDripRequest = m_dripRate; 137
135 if (m_parent != null) 138 if (Parent != null)
136 m_parent.RegisterRequest(this,m_dripRate); 139 Parent.RegisterRequest(this, m_dripRate);
137 } 140 }
138 } 141 }
139 142
143 /// <summary>
144 /// Gets the drip rate.
145 /// </summary>
146 /// <value>
147 /// DripRate can never be above max drip rate or below min drip rate.
148 /// If we are a child bucket then the drip rate return is modifed by the total load on the capacity of the
149 /// parent bucket.
150 /// </value>
140 public virtual Int64 DripRate 151 public virtual Int64 DripRate
141 { 152 {
142 get { 153 get
143 if (m_parent == null) 154 {
144 return Math.Min(RequestedDripRate,TotalDripRequest); 155 double rate;
145 156
146 double rate = (double)RequestedDripRate * m_parent.DripRateModifier(); 157 // FIXME: This doesn't properly work if we have a parent and children and a requested drip rate set
158 // on ourselves which is not equal to the child drip rates.
159 if (Parent == null)
160 {
161 if (TotalDripRequest > 0)
162 rate = Math.Min(RequestedDripRate, TotalDripRequest);
163 else
164 rate = RequestedDripRate;
165 }
166 else
167 {
168 rate = (double)RequestedDripRate * Parent.DripRateModifier();
169 }
170
147 if (rate < m_minimumDripRate) 171 if (rate < m_minimumDripRate)
148 rate = m_minimumDripRate; 172 rate = m_minimumDripRate;
173 else if (MaxDripRate > 0 && rate > MaxDripRate)
174 rate = MaxDripRate;
149 175
150 return (Int64)rate; 176 return (Int64)rate;
151 } 177 }
152 } 178 }
179 protected Int64 m_dripRate;
180
181 // <summary>
182 // The maximum rate for flow control. Drip rate can never be greater than this.
183 // </summary>
184 public Int64 MaxDripRate { get; set; }
153 185
154 /// <summary> 186 /// <summary>
155 /// The current total of the requested maximum burst rates of 187 /// The current total of the requested maximum burst rates of children buckets.
156 /// this bucket's children buckets.
157 /// </summary> 188 /// </summary>
158 protected Int64 m_totalDripRequest; 189 public Int64 TotalDripRequest { get; protected set; }
159 public Int64 TotalDripRequest
160 {
161 get { return m_totalDripRequest; }
162 set { m_totalDripRequest = value; }
163 }
164
165#endregion Properties
166
167#region Constructor
168 190
169 /// <summary> 191 /// <summary>
170 /// Default constructor 192 /// Default constructor
@@ -172,21 +194,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
172 /// <param name="identifier">Identifier for this token bucket</param> 194 /// <param name="identifier">Identifier for this token bucket</param>
173 /// <param name="parent">Parent bucket if this is a child bucket, or 195 /// <param name="parent">Parent bucket if this is a child bucket, or
174 /// null if this is a root bucket</param> 196 /// null if this is a root bucket</param>
175 /// <param name="dripRate">Rate that the bucket fills, in bytes per 197 /// <param name="requestedDripRate">
176 /// second. If zero, the bucket always remains full</param> 198 /// Requested rate that the bucket fills, in bytes per
177 public TokenBucket(string identifier, TokenBucket parent, Int64 dripRate) 199 /// second. If zero, the bucket always remains full.
200 /// </param>
201 public TokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate)
178 { 202 {
179 Identifier = identifier; 203 Identifier = identifier;
180 204
181 Parent = parent; 205 Parent = parent;
182 RequestedDripRate = dripRate; 206 RequestedDripRate = requestedDripRate;
183 // TotalDripRequest = dripRate; // this will be overwritten when a child node registers 207 MaxDripRate = maxDripRate;
184 // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst);
185 m_lastDrip = Util.EnvironmentTickCount(); 208 m_lastDrip = Util.EnvironmentTickCount();
186 } 209 }
187 210
188#endregion Constructor
189
190 /// <summary> 211 /// <summary>
191 /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning 212 /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning
192 /// no modification if the requested bandwidth is less than the 213 /// no modification if the requested bandwidth is less than the
@@ -197,7 +218,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
197 protected double DripRateModifier() 218 protected double DripRateModifier()
198 { 219 {
199 Int64 driprate = DripRate; 220 Int64 driprate = DripRate;
200 return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; 221 double modifier = driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest;
222
223// if (DebugLevel > 0)
224// m_log.DebugFormat(
225// "[TOKEN BUCKET]: Returning drip modifier {0}/{1} = {2} from {3}",
226// driprate, TotalDripRequest, modifier, Identifier);
227
228 return modifier;
201 } 229 }
202 230
203 /// <summary> 231 /// <summary>
@@ -219,16 +247,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP
219 lock (m_children) 247 lock (m_children)
220 { 248 {
221 m_children[child] = request; 249 m_children[child] = request;
222 // m_totalDripRequest = m_children.Values.Sum();
223 250
224 m_totalDripRequest = 0; 251 TotalDripRequest = 0;
225 foreach (KeyValuePair<TokenBucket, Int64> cref in m_children) 252 foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
226 m_totalDripRequest += cref.Value; 253 TotalDripRequest += cref.Value;
227 } 254 }
228 255
229 // Pass the new values up to the parent 256 // Pass the new values up to the parent
230 if (m_parent != null) 257 if (Parent != null)
231 m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); 258 {
259 Int64 effectiveDripRate;
260
261 if (RequestedDripRate > 0)
262 effectiveDripRate = Math.Min(RequestedDripRate, TotalDripRequest);
263 else
264 effectiveDripRate = TotalDripRequest;
265
266 Parent.RegisterRequest(this, effectiveDripRate);
267 }
232 } 268 }
233 269
234 /// <summary> 270 /// <summary>
@@ -240,17 +276,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
240 lock (m_children) 276 lock (m_children)
241 { 277 {
242 m_children.Remove(child); 278 m_children.Remove(child);
243 // m_totalDripRequest = m_children.Values.Sum();
244 279
245 m_totalDripRequest = 0; 280 TotalDripRequest = 0;
246 foreach (KeyValuePair<TokenBucket, Int64> cref in m_children) 281 foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
247 m_totalDripRequest += cref.Value; 282 TotalDripRequest += cref.Value;
248 } 283 }
249
250 284
251 // Pass the new values up to the parent 285 // Pass the new values up to the parent
252 if (m_parent != null) 286 if (Parent != null)
253 m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); 287 Parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
254 } 288 }
255 289
256 /// <summary> 290 /// <summary>
@@ -323,64 +357,66 @@ namespace OpenSim.Region.ClientStack.LindenUDP
323 357
324 public class AdaptiveTokenBucket : TokenBucket 358 public class AdaptiveTokenBucket : TokenBucket
325 { 359 {
326 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 360 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
361
362 public bool AdaptiveEnabled { get; set; }
327 363
328 /// <summary> 364 /// <summary>
329 /// The minimum rate for flow control. Minimum drip rate is one 365 /// Target drip rate for this bucket.
330 /// packet per second. Open the throttle to 15 packets per second
331 /// or about 160kbps.
332 /// </summary> 366 /// </summary>
333 protected const Int64 m_minimumFlow = m_minimumDripRate * 15; 367 /// <remarks>Usually set by the client. If adaptive is enabled then throttles will increase until we reach this.</remarks>
334 368 public Int64 TargetDripRate
335 // <summary> 369 {
336 // The maximum rate for flow control. Drip rate can never be 370 get { return m_targetDripRate; }
337 // greater than this. 371 set
338 // </summary> 372 {
339 protected Int64 m_maxDripRate = 0; 373 m_targetDripRate = Math.Max(value, m_minimumFlow);
340 public Int64 MaxDripRate 374 }
341 {
342 get { return (m_maxDripRate == 0 ? m_totalDripRequest : m_maxDripRate); }
343 protected set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); }
344 } 375 }
376 protected Int64 m_targetDripRate;
345 377
346 public bool Enabled { get; private set; }
347
348 // <summary> 378 // <summary>
349 // 379 // Adjust drip rate in response to network conditions.
350 // </summary> 380 // </summary>
351 public virtual Int64 AdjustedDripRate 381 public virtual Int64 AdjustedDripRate
352 { 382 {
353 get { return m_dripRate; } 383 get { return m_dripRate; }
354 set { 384 set
355 m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value,m_minimumFlow,MaxDripRate); 385 {
386 m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value, m_minimumFlow, TargetDripRate);
356 m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); 387 m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
357 if (m_parent != null) 388
358 m_parent.RegisterRequest(this,m_dripRate); 389 if (Parent != null)
390 Parent.RegisterRequest(this, m_dripRate);
359 } 391 }
360 } 392 }
393
394 /// <summary>
395 /// The minimum rate for flow control. Minimum drip rate is one
396 /// packet per second. Open the throttle to 15 packets per second
397 /// or about 160kbps.
398 /// </summary>
399 protected const Int64 m_minimumFlow = m_minimumDripRate * 15;
361 400
362 // <summary> 401 public AdaptiveTokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate, bool enabled)
363 // 402 : base(identifier, parent, requestedDripRate, maxDripRate)
364 // </summary>
365 public AdaptiveTokenBucket(string identifier, TokenBucket parent, Int64 maxDripRate, bool enabled)
366 : base(identifier, parent, maxDripRate)
367 { 403 {
368 Enabled = enabled; 404 AdaptiveEnabled = enabled;
369 405
370 if (Enabled) 406 if (AdaptiveEnabled)
371 { 407 {
372// m_log.DebugFormat("[TOKENBUCKET]: Adaptive throttle enabled"); 408// m_log.DebugFormat("[TOKENBUCKET]: Adaptive throttle enabled");
373 MaxDripRate = maxDripRate; 409 TargetDripRate = m_minimumFlow;
374 AdjustedDripRate = m_minimumFlow; 410 AdjustedDripRate = m_minimumFlow;
375 } 411 }
376 } 412 }
377 413
378 // <summary> 414 // <summary>
379 // 415 // Reliable packets sent to the client for which we never received an ack adjust the drip rate down.
380 // </summary> 416 // </summary>
381 public void ExpirePackets(Int32 count) 417 public void ExpirePackets(Int32 count)
382 { 418 {
383 if (Enabled) 419 if (AdaptiveEnabled)
384 { 420 {
385 if (DebugLevel > 0) 421 if (DebugLevel > 0)
386 m_log.WarnFormat( 422 m_log.WarnFormat(
@@ -392,12 +428,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
392 } 428 }
393 429
394 // <summary> 430 // <summary>
395 // 431 // Reliable packets acked by the client adjust the drip rate up.
396 // </summary> 432 // </summary>
397 public void AcknowledgePackets(Int32 count) 433 public void AcknowledgePackets(Int32 count)
398 { 434 {
399 if (Enabled) 435 if (AdaptiveEnabled)
400 AdjustedDripRate = AdjustedDripRate + count; 436 AdjustedDripRate = AdjustedDripRate + count;
401 } 437 }
402 } 438 }
403} 439} \ No newline at end of file