Merge branch 'master' into vehicles

0.6.8-post-fixes
Melanie 2009-10-08 10:32:15 +01:00
commit f6b8bac0fa
56 changed files with 3494 additions and 4972 deletions

View File

@ -89,7 +89,6 @@ namespace OpenSim.Client.MXP.PacketHandler
m_clientThread.Name = "MXPThread"; m_clientThread.Name = "MXPThread";
m_clientThread.IsBackground = true; m_clientThread.IsBackground = true;
m_clientThread.Start(); m_clientThread.Start();
ThreadTracker.Add(m_clientThread);
} }
public void StartListener() public void StartListener()

View File

@ -239,10 +239,8 @@ namespace OpenSim.Data.MySQL
} }
catch (Exception e) catch (Exception e)
{ {
m_log.ErrorFormat( m_log.ErrorFormat("[ASSET DB]: MySQL failure creating asset {0} with name \"{1}\". Attempting reconnect. Error: {2}",
"[ASSETS DB]: " + asset.FullID, asset.Name, e.Message);
"MySql failure creating asset {0} with name {1}" + Environment.NewLine + e.ToString()
+ Environment.NewLine + "Attempting reconnection", asset.FullID, asset.Name);
_dbConnection.Reconnect(); _dbConnection.Reconnect();
} }
} }

View File

@ -124,9 +124,11 @@ namespace OpenSim.Framework.Communications.Clients
// Let's wait for the response // Let's wait for the response
//m_log.Info("[REST COMMS]: Waiting for a reply after DoCreateChildAgentCall"); //m_log.Info("[REST COMMS]: Waiting for a reply after DoCreateChildAgentCall");
WebResponse webResponse = null;
StreamReader sr = null;
try try
{ {
WebResponse webResponse = AgentCreateRequest.GetResponse(); webResponse = AgentCreateRequest.GetResponse();
if (webResponse == null) if (webResponse == null)
{ {
m_log.Info("[REST COMMS]: Null reply on DoCreateChildAgentCall post"); m_log.Info("[REST COMMS]: Null reply on DoCreateChildAgentCall post");
@ -134,9 +136,8 @@ namespace OpenSim.Framework.Communications.Clients
else else
{ {
StreamReader sr = new StreamReader(webResponse.GetResponseStream()); sr = new StreamReader(webResponse.GetResponseStream());
string response = sr.ReadToEnd().Trim(); string response = sr.ReadToEnd().Trim();
sr.Close();
m_log.InfoFormat("[REST COMMS]: DoCreateChildAgentCall reply was {0} ", response); m_log.InfoFormat("[REST COMMS]: DoCreateChildAgentCall reply was {0} ", response);
if (!String.IsNullOrEmpty(response)) if (!String.IsNullOrEmpty(response))
@ -167,6 +168,11 @@ namespace OpenSim.Framework.Communications.Clients
m_log.InfoFormat("[REST COMMS]: exception on reply of DoCreateChildAgentCall {0}", ex.Message); m_log.InfoFormat("[REST COMMS]: exception on reply of DoCreateChildAgentCall {0}", ex.Message);
// ignore, really // ignore, really
} }
finally
{
if (sr != null)
sr.Close();
}
return true; return true;
@ -246,15 +252,17 @@ namespace OpenSim.Framework.Communications.Clients
// Let's wait for the response // Let's wait for the response
//m_log.Info("[REST COMMS]: Waiting for a reply after ChildAgentUpdate"); //m_log.Info("[REST COMMS]: Waiting for a reply after ChildAgentUpdate");
WebResponse webResponse = null;
StreamReader sr = null;
try try
{ {
WebResponse webResponse = ChildUpdateRequest.GetResponse(); webResponse = ChildUpdateRequest.GetResponse();
if (webResponse == null) if (webResponse == null)
{ {
m_log.Info("[REST COMMS]: Null reply on ChilAgentUpdate post"); m_log.Info("[REST COMMS]: Null reply on ChilAgentUpdate post");
} }
StreamReader sr = new StreamReader(webResponse.GetResponseStream()); sr = new StreamReader(webResponse.GetResponseStream());
//reply = sr.ReadToEnd().Trim(); //reply = sr.ReadToEnd().Trim();
sr.ReadToEnd().Trim(); sr.ReadToEnd().Trim();
sr.Close(); sr.Close();
@ -266,6 +274,11 @@ namespace OpenSim.Framework.Communications.Clients
m_log.InfoFormat("[REST COMMS]: exception on reply of ChilAgentUpdate {0}", ex.Message); m_log.InfoFormat("[REST COMMS]: exception on reply of ChilAgentUpdate {0}", ex.Message);
// ignore, really // ignore, really
} }
finally
{
if (sr != null)
sr.Close();
}
return true; return true;
} }
@ -284,6 +297,7 @@ namespace OpenSim.Framework.Communications.Clients
HttpWebResponse webResponse = null; HttpWebResponse webResponse = null;
string reply = string.Empty; string reply = string.Empty;
StreamReader sr = null;
try try
{ {
webResponse = (HttpWebResponse)request.GetResponse(); webResponse = (HttpWebResponse)request.GetResponse();
@ -292,9 +306,8 @@ namespace OpenSim.Framework.Communications.Clients
m_log.Info("[REST COMMS]: Null reply on agent get "); m_log.Info("[REST COMMS]: Null reply on agent get ");
} }
StreamReader sr = new StreamReader(webResponse.GetResponseStream()); sr = new StreamReader(webResponse.GetResponseStream());
reply = sr.ReadToEnd().Trim(); reply = sr.ReadToEnd().Trim();
sr.Close();
//Console.WriteLine("[REST COMMS]: ChilAgentUpdate reply was " + reply); //Console.WriteLine("[REST COMMS]: ChilAgentUpdate reply was " + reply);
@ -305,6 +318,11 @@ namespace OpenSim.Framework.Communications.Clients
// ignore, really // ignore, really
return false; return false;
} }
finally
{
if (sr != null)
sr.Close();
}
if (webResponse.StatusCode == HttpStatusCode.OK) if (webResponse.StatusCode == HttpStatusCode.OK)
{ {
@ -333,6 +351,7 @@ namespace OpenSim.Framework.Communications.Clients
request.Method = "DELETE"; request.Method = "DELETE";
request.Timeout = 10000; request.Timeout = 10000;
StreamReader sr = null;
try try
{ {
WebResponse webResponse = request.GetResponse(); WebResponse webResponse = request.GetResponse();
@ -341,7 +360,7 @@ namespace OpenSim.Framework.Communications.Clients
m_log.Info("[REST COMMS]: Null reply on agent delete "); m_log.Info("[REST COMMS]: Null reply on agent delete ");
} }
StreamReader sr = new StreamReader(webResponse.GetResponseStream()); sr = new StreamReader(webResponse.GetResponseStream());
//reply = sr.ReadToEnd().Trim(); //reply = sr.ReadToEnd().Trim();
sr.ReadToEnd().Trim(); sr.ReadToEnd().Trim();
sr.Close(); sr.Close();
@ -353,6 +372,11 @@ namespace OpenSim.Framework.Communications.Clients
m_log.InfoFormat("[REST COMMS]: exception on reply of agent delete {0}", ex.Message); m_log.InfoFormat("[REST COMMS]: exception on reply of agent delete {0}", ex.Message);
// ignore, really // ignore, really
} }
finally
{
if (sr != null)
sr.Close();
}
return true; return true;
} }
@ -377,6 +401,7 @@ namespace OpenSim.Framework.Communications.Clients
request.Method = "DELETE"; request.Method = "DELETE";
request.Timeout = 10000; request.Timeout = 10000;
StreamReader sr = null;
try try
{ {
WebResponse webResponse = request.GetResponse(); WebResponse webResponse = request.GetResponse();
@ -385,7 +410,7 @@ namespace OpenSim.Framework.Communications.Clients
m_log.Info("[REST COMMS]: Null reply on agent delete "); m_log.Info("[REST COMMS]: Null reply on agent delete ");
} }
StreamReader sr = new StreamReader(webResponse.GetResponseStream()); sr = new StreamReader(webResponse.GetResponseStream());
//reply = sr.ReadToEnd().Trim(); //reply = sr.ReadToEnd().Trim();
sr.ReadToEnd().Trim(); sr.ReadToEnd().Trim();
sr.Close(); sr.Close();
@ -397,6 +422,11 @@ namespace OpenSim.Framework.Communications.Clients
m_log.InfoFormat("[REST COMMS]: exception on reply of agent delete {0}", ex.Message); m_log.InfoFormat("[REST COMMS]: exception on reply of agent delete {0}", ex.Message);
// ignore, really // ignore, really
} }
finally
{
if (sr != null)
sr.Close();
}
return true; return true;
} }
@ -463,6 +493,7 @@ namespace OpenSim.Framework.Communications.Clients
// Let's wait for the response // Let's wait for the response
//m_log.Info("[REST COMMS]: Waiting for a reply after DoCreateChildAgentCall"); //m_log.Info("[REST COMMS]: Waiting for a reply after DoCreateChildAgentCall");
StreamReader sr = null;
try try
{ {
WebResponse webResponse = ObjectCreateRequest.GetResponse(); WebResponse webResponse = ObjectCreateRequest.GetResponse();
@ -471,10 +502,9 @@ namespace OpenSim.Framework.Communications.Clients
m_log.Info("[REST COMMS]: Null reply on DoCreateObjectCall post"); m_log.Info("[REST COMMS]: Null reply on DoCreateObjectCall post");
} }
StreamReader sr = new StreamReader(webResponse.GetResponseStream()); sr = new StreamReader(webResponse.GetResponseStream());
//reply = sr.ReadToEnd().Trim(); //reply = sr.ReadToEnd().Trim();
sr.ReadToEnd().Trim(); sr.ReadToEnd().Trim();
sr.Close();
//m_log.InfoFormat("[REST COMMS]: DoCreateChildAgentCall reply was {0} ", reply); //m_log.InfoFormat("[REST COMMS]: DoCreateChildAgentCall reply was {0} ", reply);
} }
@ -483,6 +513,11 @@ namespace OpenSim.Framework.Communications.Clients
m_log.InfoFormat("[REST COMMS]: exception on reply of DoCreateObjectCall {0}", ex.Message); m_log.InfoFormat("[REST COMMS]: exception on reply of DoCreateObjectCall {0}", ex.Message);
// ignore, really // ignore, really
} }
finally
{
if (sr != null)
sr.Close();
}
return true; return true;
@ -542,6 +577,7 @@ namespace OpenSim.Framework.Communications.Clients
// Let's wait for the response // Let's wait for the response
//m_log.Info("[REST COMMS]: Waiting for a reply after DoCreateChildAgentCall"); //m_log.Info("[REST COMMS]: Waiting for a reply after DoCreateChildAgentCall");
StreamReader sr = null;
try try
{ {
WebResponse webResponse = ObjectCreateRequest.GetResponse(); WebResponse webResponse = ObjectCreateRequest.GetResponse();
@ -550,10 +586,9 @@ namespace OpenSim.Framework.Communications.Clients
m_log.Info("[REST COMMS]: Null reply on DoCreateObjectCall post"); m_log.Info("[REST COMMS]: Null reply on DoCreateObjectCall post");
} }
StreamReader sr = new StreamReader(webResponse.GetResponseStream()); sr = new StreamReader(webResponse.GetResponseStream());
sr.ReadToEnd().Trim(); sr.ReadToEnd().Trim();
sr.ReadToEnd().Trim(); sr.ReadToEnd().Trim();
sr.Close();
//m_log.InfoFormat("[REST COMMS]: DoCreateChildAgentCall reply was {0} ", reply); //m_log.InfoFormat("[REST COMMS]: DoCreateChildAgentCall reply was {0} ", reply);
@ -563,6 +598,11 @@ namespace OpenSim.Framework.Communications.Clients
m_log.InfoFormat("[REST COMMS]: exception on reply of DoCreateObjectCall {0}", ex.Message); m_log.InfoFormat("[REST COMMS]: exception on reply of DoCreateObjectCall {0}", ex.Message);
// ignore, really // ignore, really
} }
finally
{
if (sr != null)
sr.Close();
}
return true; return true;
@ -630,6 +670,7 @@ namespace OpenSim.Framework.Communications.Clients
// Let's wait for the response // Let's wait for the response
//m_log.Info("[REST COMMS]: Waiting for a reply after DoHelloNeighbourCall"); //m_log.Info("[REST COMMS]: Waiting for a reply after DoHelloNeighbourCall");
StreamReader sr = null;
try try
{ {
WebResponse webResponse = HelloNeighbourRequest.GetResponse(); WebResponse webResponse = HelloNeighbourRequest.GetResponse();
@ -638,10 +679,9 @@ namespace OpenSim.Framework.Communications.Clients
m_log.Info("[REST COMMS]: Null reply on DoHelloNeighbourCall post"); m_log.Info("[REST COMMS]: Null reply on DoHelloNeighbourCall post");
} }
StreamReader sr = new StreamReader(webResponse.GetResponseStream()); sr = new StreamReader(webResponse.GetResponseStream());
//reply = sr.ReadToEnd().Trim(); //reply = sr.ReadToEnd().Trim();
sr.ReadToEnd().Trim(); sr.ReadToEnd().Trim();
sr.Close();
//m_log.InfoFormat("[REST COMMS]: DoHelloNeighbourCall reply was {0} ", reply); //m_log.InfoFormat("[REST COMMS]: DoHelloNeighbourCall reply was {0} ", reply);
} }
@ -650,6 +690,11 @@ namespace OpenSim.Framework.Communications.Clients
m_log.InfoFormat("[REST COMMS]: exception on reply of DoHelloNeighbourCall {0}", ex.Message); m_log.InfoFormat("[REST COMMS]: exception on reply of DoHelloNeighbourCall {0}", ex.Message);
// ignore, really // ignore, really
} }
finally
{
if (sr != null)
sr.Close();
}
return true; return true;

View File

@ -0,0 +1,207 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Threading;
namespace OpenSim.Framework
{
/// <summary>
/// Provides helper methods for parallelizing loops
/// </summary>
public static class Parallel
{
private static readonly int processorCount = System.Environment.ProcessorCount;
/// <summary>
/// Executes a for loop in which iterations may run in parallel
/// </summary>
/// <param name="fromInclusive">The loop will be started at this index</param>
/// <param name="toExclusive">The loop will be terminated before this index is reached</param>
/// <param name="body">Method body to run for each iteration of the loop</param>
public static void For(int fromInclusive, int toExclusive, Action<int> body)
{
For(processorCount, fromInclusive, toExclusive, body);
}
/// <summary>
/// Executes a for loop in which iterations may run in parallel
/// </summary>
/// <param name="threadCount">The number of concurrent execution threads to run</param>
/// <param name="fromInclusive">The loop will be started at this index</param>
/// <param name="toExclusive">The loop will be terminated before this index is reached</param>
/// <param name="body">Method body to run for each iteration of the loop</param>
public static void For(int threadCount, int fromInclusive, int toExclusive, Action<int> body)
{
int counter = threadCount;
AutoResetEvent threadFinishEvent = new AutoResetEvent(false);
Exception exception = null;
--fromInclusive;
for (int i = 0; i < threadCount; i++)
{
ThreadPool.QueueUserWorkItem(
delegate(object o)
{
int threadIndex = (int)o;
while (exception == null)
{
int currentIndex = Interlocked.Increment(ref fromInclusive);
if (currentIndex >= toExclusive)
break;
try { body(currentIndex); }
catch (Exception ex) { exception = ex; break; }
}
if (Interlocked.Decrement(ref counter) == 0)
threadFinishEvent.Set();
}, i
);
}
threadFinishEvent.WaitOne();
if (exception != null)
throw new Exception(exception.Message, exception);
}
/// <summary>
/// Executes a foreach loop in which iterations may run in parallel
/// </summary>
/// <typeparam name="T">Object type that the collection wraps</typeparam>
/// <param name="enumerable">An enumerable collection to iterate over</param>
/// <param name="body">Method body to run for each object in the collection</param>
public static void ForEach<T>(IEnumerable<T> enumerable, Action<T> body)
{
ForEach<T>(processorCount, enumerable, body);
}
/// <summary>
/// Executes a foreach loop in which iterations may run in parallel
/// </summary>
/// <typeparam name="T">Object type that the collection wraps</typeparam>
/// <param name="threadCount">The number of concurrent execution threads to run</param>
/// <param name="enumerable">An enumerable collection to iterate over</param>
/// <param name="body">Method body to run for each object in the collection</param>
public static void ForEach<T>(int threadCount, IEnumerable<T> enumerable, Action<T> body)
{
int counter = threadCount;
AutoResetEvent threadFinishEvent = new AutoResetEvent(false);
IEnumerator<T> enumerator = enumerable.GetEnumerator();
Exception exception = null;
for (int i = 0; i < threadCount; i++)
{
ThreadPool.QueueUserWorkItem(
delegate(object o)
{
int threadIndex = (int)o;
while (exception == null)
{
T entry;
lock (enumerator)
{
if (!enumerator.MoveNext())
break;
entry = (T)enumerator.Current; // Explicit typecast for Mono's sake
}
try { body(entry); }
catch (Exception ex) { exception = ex; break; }
}
if (Interlocked.Decrement(ref counter) == 0)
threadFinishEvent.Set();
}, i
);
}
threadFinishEvent.WaitOne();
if (exception != null)
throw new Exception(exception.Message, exception);
}
/// <summary>
/// Executes a series of tasks in parallel
/// </summary>
/// <param name="actions">A series of method bodies to execute</param>
public static void Invoke(params Action[] actions)
{
Invoke(processorCount, actions);
}
/// <summary>
/// Executes a series of tasks in parallel
/// </summary>
/// <param name="threadCount">The number of concurrent execution threads to run</param>
/// <param name="actions">A series of method bodies to execute</param>
public static void Invoke(int threadCount, params Action[] actions)
{
int counter = threadCount;
AutoResetEvent threadFinishEvent = new AutoResetEvent(false);
int index = -1;
Exception exception = null;
for (int i = 0; i < threadCount; i++)
{
ThreadPool.QueueUserWorkItem(
delegate(object o)
{
int threadIndex = (int)o;
while (exception == null)
{
int currentIndex = Interlocked.Increment(ref index);
if (currentIndex >= actions.Length)
break;
try { actions[currentIndex](); }
catch (Exception ex) { exception = ex; break; }
}
if (Interlocked.Decrement(ref counter) == 0)
threadFinishEvent.Set();
}, i
);
}
threadFinishEvent.WaitOne();
if (exception != null)
throw new Exception(exception.Message, exception);
}
}
}

View File

@ -27,6 +27,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
@ -109,9 +110,8 @@ namespace OpenSim.Framework.Servers
m_periodicDiagnosticsTimer.Elapsed += new ElapsedEventHandler(LogDiagnostics); m_periodicDiagnosticsTimer.Elapsed += new ElapsedEventHandler(LogDiagnostics);
m_periodicDiagnosticsTimer.Enabled = true; m_periodicDiagnosticsTimer.Enabled = true;
// Add ourselves to thread monitoring. This thread will go on to become the console listening thread // This thread will go on to become the console listening thread
Thread.CurrentThread.Name = "ConsoleThread"; Thread.CurrentThread.Name = "ConsoleThread";
ThreadTracker.Add(Thread.CurrentThread);
ILoggerRepository repository = LogManager.GetRepository(); ILoggerRepository repository = LogManager.GetRepository();
IAppender[] appenders = repository.GetAppenders(); IAppender[] appenders = repository.GetAppenders();
@ -235,7 +235,7 @@ namespace OpenSim.Framework.Servers
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
List<Thread> threads = ThreadTracker.GetThreads(); ProcessThreadCollection threads = ThreadTracker.GetThreads();
if (threads == null) if (threads == null)
{ {
sb.Append("OpenSim thread tracking is only enabled in DEBUG mode."); sb.Append("OpenSim thread tracking is only enabled in DEBUG mode.");
@ -243,25 +243,15 @@ namespace OpenSim.Framework.Servers
else else
{ {
sb.Append(threads.Count + " threads are being tracked:" + Environment.NewLine); sb.Append(threads.Count + " threads are being tracked:" + Environment.NewLine);
foreach (Thread t in threads) foreach (ProcessThread t in threads)
{ {
if (t.IsAlive) sb.Append("ID: " + t.Id + ", TotalProcessorTime: " + t.TotalProcessorTime + ", TimeRunning: " +
{ (DateTime.Now - t.StartTime) + ", Pri: " + t.CurrentPriority + ", State: " + t.ThreadState );
sb.Append( if (t.ThreadState == System.Diagnostics.ThreadState.Wait)
"ID: " + t.ManagedThreadId + ", Name: " + t.Name + ", Alive: " + t.IsAlive sb.Append(", Reason: " + t.WaitReason + Environment.NewLine);
+ ", Pri: " + t.Priority + ", State: " + t.ThreadState + Environment.NewLine);
}
else else
{ sb.Append(Environment.NewLine);
try
{
sb.Append("ID: " + t.ManagedThreadId + ", Name: " + t.Name + ", DEAD" + Environment.NewLine);
}
catch
{
sb.Append("THREAD ERROR" + Environment.NewLine);
}
}
} }
} }
int workers = 0, ports = 0, maxWorkers = 0, maxPorts = 0; int workers = 0, ports = 0, maxWorkers = 0, maxPorts = 0;

View File

@ -1,192 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using NUnit.Framework;
using System.Threading;
using System.Collections.Generic;
namespace OpenSim.Framework.Tests
{
[TestFixture]
public class ThreadTrackerTests
{
private bool running = true;
private bool running2 = true;
[Test]
public void DefaultThreadTrackerTest()
{
List<Thread> lThread = ThreadTracker.GetThreads();
/*
foreach (Thread t in lThread)
{
System.Console.WriteLine(t.Name);
}
*/
Assert.That(lThread.Count == 1);
Assert.That(lThread[0].Name == "ThreadTrackerThread");
}
/// <summary>
/// Validate that adding a thread to the thread tracker works
/// Validate that removing a thread from the thread tracker also works.
/// </summary>
[Test]
public void AddThreadToThreadTrackerTestAndRemoveTest()
{
Thread t = new Thread(run);
t.Name = "TestThread";
t.Priority = ThreadPriority.BelowNormal;
t.IsBackground = true;
t.SetApartmentState(ApartmentState.MTA);
t.Start();
ThreadTracker.Add(t);
List<Thread> lThread = ThreadTracker.GetThreads();
Assert.That(lThread.Count == 2);
foreach (Thread tr in lThread)
{
Assert.That((tr.Name == "ThreadTrackerThread" || tr.Name == "TestThread"));
}
running = false;
ThreadTracker.Remove(t);
lThread = ThreadTracker.GetThreads();
Assert.That(lThread.Count == 1);
foreach (Thread tr in lThread)
{
Assert.That((tr.Name == "ThreadTrackerThread"));
}
}
/// <summary>
/// Test a dead thread removal by aborting it and setting it's last seen active date to 50 seconds
/// </summary>
[Test]
public void DeadThreadTest()
{
Thread t = new Thread(run2);
t.Name = "TestThread";
t.Priority = ThreadPriority.BelowNormal;
t.IsBackground = true;
t.SetApartmentState(ApartmentState.MTA);
t.Start();
ThreadTracker.Add(t);
t.Abort();
Thread.Sleep(5000);
ThreadTracker.m_Threads[1].LastSeenActive = DateTime.Now.Ticks - (50*10000000);
ThreadTracker.CleanUp();
List<Thread> lThread = ThreadTracker.GetThreads();
Assert.That(lThread.Count == 1);
foreach (Thread tr in lThread)
{
Assert.That((tr.Name == "ThreadTrackerThread"));
}
}
[Test]
public void UnstartedThreadTest()
{
Thread t = new Thread(run2);
t.Name = "TestThread";
t.Priority = ThreadPriority.BelowNormal;
t.IsBackground = true;
t.SetApartmentState(ApartmentState.MTA);
ThreadTracker.Add(t);
ThreadTracker.m_Threads[1].LastSeenActive = DateTime.Now.Ticks - (50 * 10000000);
ThreadTracker.CleanUp();
List<Thread> lThread = ThreadTracker.GetThreads();
Assert.That(lThread.Count == 1);
foreach (Thread tr in lThread)
{
Assert.That((tr.Name == "ThreadTrackerThread"));
}
}
[Test]
public void NullThreadTest()
{
Thread t = null;
ThreadTracker.Add(t);
List<Thread> lThread = ThreadTracker.GetThreads();
Assert.That(lThread.Count == 1);
foreach (Thread tr in lThread)
{
Assert.That((tr.Name == "ThreadTrackerThread"));
}
}
/// <summary>
/// Worker thread 0
/// </summary>
/// <param name="o"></param>
public void run(object o)
{
while (running)
{
Thread.Sleep(5000);
}
}
/// <summary>
/// Worker thread 1
/// </summary>
/// <param name="o"></param>
public void run2(object o)
{
try
{
while (running2)
{
Thread.Sleep(5000);
}
}
catch (ThreadAbortException)
{
}
}
}
}

View File

@ -26,138 +26,21 @@
*/ */
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Diagnostics;
using log4net; using log4net;
namespace OpenSim.Framework namespace OpenSim.Framework
{ {
public static class ThreadTracker public static class ThreadTracker
{ {
private static readonly ILog m_log private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
= LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly long ThreadTimeout = 30 * 10000000; public static ProcessThreadCollection GetThreads()
public static List<ThreadTrackerItem> m_Threads;
public static Thread ThreadTrackerThread;
static ThreadTracker()
{ {
#if DEBUG Process thisProc = Process.GetCurrentProcess();
m_Threads = new List<ThreadTrackerItem>(); return thisProc.Threads;
ThreadTrackerThread = new Thread(ThreadTrackerThreadLoop);
ThreadTrackerThread.Name = "ThreadTrackerThread";
ThreadTrackerThread.IsBackground = true;
ThreadTrackerThread.Priority = ThreadPriority.BelowNormal;
ThreadTrackerThread.Start();
Add(ThreadTrackerThread);
#endif
} }
private static void ThreadTrackerThreadLoop()
{
try
{
while (true)
{
Thread.Sleep(5000);
CleanUp();
}
}
catch (Exception e)
{
m_log.ErrorFormat(
"[THREAD TRACKER]: Thread tracker cleanup thread terminating with exception. Please report this error. Exception is {0}",
e);
}
}
public static void Add(Thread thread)
{
#if DEBUG
if (thread != null)
{
lock (m_Threads)
{
ThreadTrackerItem tti = new ThreadTrackerItem();
tti.Thread = thread;
tti.LastSeenActive = DateTime.Now.Ticks;
m_Threads.Add(tti);
}
}
#endif
}
public static void Remove(Thread thread)
{
#if DEBUG
lock (m_Threads)
{
foreach (ThreadTrackerItem tti in new ArrayList(m_Threads))
{
if (tti.Thread == thread)
m_Threads.Remove(tti);
}
}
#endif
}
public static void CleanUp()
{
lock (m_Threads)
{
foreach (ThreadTrackerItem tti in new ArrayList(m_Threads))
{
try
{
if (tti.Thread.IsAlive)
{
// Its active
tti.LastSeenActive = DateTime.Now.Ticks;
}
else
{
// Its not active -- if its expired then remove it
if (tti.LastSeenActive + ThreadTimeout < DateTime.Now.Ticks)
m_Threads.Remove(tti);
}
}
catch (NullReferenceException)
{
m_Threads.Remove(tti);
}
}
}
}
public static List<Thread> GetThreads()
{
if (m_Threads == null)
return null;
List<Thread> threads = new List<Thread>();
lock (m_Threads)
{
foreach (ThreadTrackerItem tti in new ArrayList(m_Threads))
{
threads.Add(tti.Thread);
}
}
return threads;
}
#region Nested type: ThreadTrackerItem
public class ThreadTrackerItem
{
public long LastSeenActive;
public Thread Thread;
}
#endregion
} }
} }

View File

@ -29,9 +29,9 @@ using System;
namespace OpenSim.Framework namespace OpenSim.Framework
{ {
[Flags]
public enum ThrottleOutPacketType : int public enum ThrottleOutPacketType : int
{ {
Unknown = -1, // Also doubles as 'do not throttle'
Resend = 0, Resend = 0,
Land = 1, Land = 1,
Wind = 2, Wind = 2,
@ -39,11 +39,5 @@ namespace OpenSim.Framework
Task = 4, Task = 4,
Texture = 5, Texture = 5,
Asset = 6, Asset = 6,
Unknown = 7, // Also doubles as 'do not throttle'
Back = 8,
TypeMask = 15, // The mask to mask off the flags
LowPriority = 128 // Additional flags
} }
} }

View File

@ -1231,6 +1231,42 @@ namespace OpenSim.Framework
return (ipaddr1 != null) ? "http://" + ipaddr1.ToString() + ":" + port1 : uri; return (ipaddr1 != null) ? "http://" + ipaddr1.ToString() + ":" + port1 : uri;
} }
public static byte[] StringToBytes256(string str)
{
if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; }
if (str.Length > 254) str = str.Remove(254);
if (!str.EndsWith("\0")) { str += "\0"; }
// Because this is UTF-8 encoding and not ASCII, it's possible we
// might have gotten an oversized array even after the string trim
byte[] data = UTF8.GetBytes(str);
if (data.Length > 256)
{
Array.Resize<byte>(ref data, 256);
data[255] = 0;
}
return data;
}
public static byte[] StringToBytes1024(string str)
{
if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; }
if (str.Length > 1023) str = str.Remove(1023);
if (!str.EndsWith("\0")) { str += "\0"; }
// Because this is UTF-8 encoding and not ASCII, it's possible we
// might have gotten an oversized array even after the string trim
byte[] data = UTF8.GetBytes(str);
if (data.Length > 1024)
{
Array.Resize<byte>(ref data, 1024);
data[1023] = 0;
}
return data;
}
#region FireAndForget Threading Pattern #region FireAndForget Threading Pattern
public static void FireAndForget(System.Threading.WaitCallback callback) public static void FireAndForget(System.Threading.WaitCallback callback)
@ -1247,7 +1283,9 @@ namespace OpenSim.Framework
{ {
System.Threading.WaitCallback callback = (System.Threading.WaitCallback)ar.AsyncState; System.Threading.WaitCallback callback = (System.Threading.WaitCallback)ar.AsyncState;
callback.EndInvoke(ar); try { callback.EndInvoke(ar); }
catch (Exception ex) { m_log.Error("[UTIL]: Asynchronous method threw an exception: " + ex.Message, ex); }
ar.AsyncWaitHandle.Close(); ar.AsyncWaitHandle.Close();
} }

View File

@ -91,6 +91,18 @@ namespace OpenSim
m_log.Info("[OPENSIM MAIN]: configured log4net using default OpenSim.exe.config"); m_log.Info("[OPENSIM MAIN]: configured log4net using default OpenSim.exe.config");
} }
// Increase the number of IOCP threads available. Mono defaults to a tragically low number
int workerThreads, iocpThreads;
System.Threading.ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads);
m_log.InfoFormat("[OPENSIM MAIN]: Runtime gave us {0} worker threads and {1} IOCP threads", workerThreads, iocpThreads);
if (workerThreads < 500 || iocpThreads < 1000)
{
workerThreads = 500;
iocpThreads = 1000;
m_log.Info("[OPENSIM MAIN]: Bumping up to 500 worker threads and 1000 IOCP threads");
System.Threading.ThreadPool.SetMaxThreads(workerThreads, iocpThreads);
}
// Check if the system is compatible with OpenSimulator. // Check if the system is compatible with OpenSimulator.
// Ensures that the minimum system requirements are met // Ensures that the minimum system requirements are met
m_log.Info("Performing compatibility checks... "); m_log.Info("Performing compatibility checks... ");

View File

@ -675,7 +675,7 @@ namespace OpenSim
if (foundClientServer) if (foundClientServer)
{ {
m_clientServers[clientServerElement].Server.Close(); m_clientServers[clientServerElement].NetworkStop();
m_clientServers.RemoveAt(clientServerElement); m_clientServers.RemoveAt(clientServerElement);
} }
IScene scene; IScene scene;

View File

@ -38,7 +38,7 @@ namespace OpenSim.Region.ClientStack
IPAddress _listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, IPAddress _listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource,
AgentCircuitManager authenticateClass); AgentCircuitManager authenticateClass);
Socket Server { get; } void NetworkStop();
bool HandlesRegion(Location x); bool HandlesRegion(Location x);
void AddScene(IScene x); void AddScene(IScene x);

View File

@ -1,38 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.Net.Sockets;
namespace OpenSim.Region.ClientStack.LindenUDP
{
public interface ILLClientStackNetworkHandler
{
void SendPacketTo(byte[] buffer, int size, SocketFlags flags, uint circuitcode); // EndPoint packetSender);
void RemoveClientCircuit(uint circuitcode);
void RegisterPacketServer(LLPacketServer server);
}
}

View File

@ -1,83 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using OpenMetaverse;
using OpenMetaverse.Packets;
using OpenSim.Framework;
namespace OpenSim.Region.ClientStack.LindenUDP
{
public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes);
public delegate void PacketDrop(Packet pack, Object id);
public delegate void QueueEmpty(ThrottleOutPacketType queue);
public delegate bool SynchronizeClientHandler(IScene scene, Packet packet, UUID agentID, ThrottleOutPacketType throttlePacketType);
/// <summary>
/// Interface to a class that handles all the activity involved with maintaining the client circuit (handling acks,
/// resends, pings, etc.)
/// </summary>
public interface ILLPacketHandler : IDisposable
{
event PacketStats OnPacketStats;
event PacketDrop OnPacketDrop;
event QueueEmpty OnQueueEmpty;
SynchronizeClientHandler SynchronizeClient { set; }
int PacketsReceived { get; }
int PacketsReceivedReported { get; }
uint ResendTimeout { get; set; }
bool ReliableIsImportant { get; set; }
int MaxReliableResends { get; set; }
/// <summary>
/// Initial handling of a received packet. It will be processed later in ProcessInPacket()
/// </summary>
/// <param name="packet"></param>
void InPacket(Packet packet);
/// <summary>
/// Take action depending on the type and contents of an received packet.
/// </summary>
/// <param name="item"></param>
void ProcessInPacket(LLQueItem item);
void ProcessOutPacket(LLQueItem item);
void OutPacket(Packet NewPack,
ThrottleOutPacketType throttlePacketType);
void OutPacket(Packet NewPack,
ThrottleOutPacketType throttlePacketType, Object id);
LLPacketQueue PacketQueue { get; }
void Flush();
void Clear();
ClientInfo GetClientInfo();
void SetClientInfo(ClientInfo info);
void AddImportantPacket(PacketType type);
void RemoveImportantPacket(PacketType type);
int GetQueueCount(ThrottleOutPacketType queue);
}
}

View File

@ -1,4 +1,4 @@
/* /*
* Copyright (c) Contributors, http://opensimulator.org/ * Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders. * See CONTRIBUTORS.TXT for a full list of copyright holders.
* *
@ -26,24 +26,32 @@
*/ */
using System; using System;
using OpenMetaverse.Packets;
using OpenSim.Framework; using OpenSim.Framework;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace OpenSim.Region.ClientStack.LindenUDP namespace OpenSim.Region.ClientStack.LindenUDP
{ {
public class LLQueItem /// <summary>
/// Holds a reference to a <seealso cref="LLUDPClient"/> and a <seealso cref="Packet"/>
/// for incoming packets
/// </summary>
public sealed class IncomingPacket
{ {
public LLQueItem() /// <summary>Client this packet came from</summary>
{ public LLUDPClient Client;
} /// <summary>Packet data that has been received</summary>
public Packet Packet; public Packet Packet;
public bool Incoming;
public ThrottleOutPacketType throttleType; /// <summary>
public int TickCount; /// Default constructor
public Object Identifier; /// </summary>
public int Resends; /// <param name="client">Reference to the client this packet came from</param>
public int Length; /// <param name="packet">Packet data</param>
public uint Sequence; public IncomingPacket(LLUDPClient client, Packet packet)
{
Client = client;
Packet = packet;
}
} }
} }

View File

@ -1,4 +1,4 @@
/* /*
* Copyright (c) Contributors, http://opensimulator.org/ * Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders. * See CONTRIBUTORS.TXT for a full list of copyright holders.
* *
@ -25,47 +25,49 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
using OpenMetaverse.Packets; using System;
using System.Collections.Generic;
namespace OpenSim.Region.ClientStack.LindenUDP namespace OpenSim.Region.ClientStack.LindenUDP
{ {
/// <summary> /// <summary>
/// When packetqueue dequeues this packet in the outgoing stream, it thread aborts /// A circular buffer and hashset for tracking incoming packet sequence
/// Ensures that the thread abort happens from within the client thread /// numbers
/// regardless of where the close method is called
/// </summary> /// </summary>
class KillPacket : Packet public sealed class IncomingPacketHistoryCollection
{ {
public override int Length private readonly uint[] m_items;
private HashSet<uint> m_hashSet;
private int m_first;
private int m_next;
private int m_capacity;
public IncomingPacketHistoryCollection(int capacity)
{ {
get { return 0; } this.m_capacity = capacity;
m_items = new uint[capacity];
m_hashSet = new HashSet<uint>();
} }
public override void FromBytes(Header header, byte[] bytes, ref int i, ref int packetEnd) public bool TryEnqueue(uint ack)
{ {
lock (m_hashSet)
{
if (m_hashSet.Add(ack))
{
m_items[m_next] = ack;
m_next = (m_next + 1) % m_capacity;
if (m_next == m_first)
{
m_hashSet.Remove(m_items[m_first]);
m_first = (m_first + 1) % m_capacity;
} }
public override void FromBytes(byte[] bytes, ref int i, ref int packetEnd, byte[] zeroBuffer) return true;
{ }
} }
public override byte[] ToBytes() return false;
{
return new byte[0];
}
public override byte[][] ToBytesMultiple()
{
return new byte[][] { new byte[0] };
}
public KillPacket()
{
Type = PacketType.UseCircuitCode;
Header = new Header();
Header.Frequency = OpenMetaverse.PacketFrequency.Low;
Header.ID = 65531;
Header.Reliable = true;
} }
} }
} }

View File

@ -76,27 +76,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{ {
if (m_currentPacket <= m_stopPacket) if (m_currentPacket <= m_stopPacket)
{ {
bool SendMore = true; int count = 0;
bool sendMore = true;
if (!m_sentInfo || (m_currentPacket == 0)) if (!m_sentInfo || (m_currentPacket == 0))
{ {
if (SendFirstPacket(client)) sendMore = !SendFirstPacket(client);
{
SendMore = false;
}
m_sentInfo = true; m_sentInfo = true;
m_currentPacket++; ++m_currentPacket;
++count;
} }
if (m_currentPacket < 2) if (m_currentPacket < 2)
{ {
m_currentPacket = 2; m_currentPacket = 2;
} }
int count = 0; while (sendMore && count < maxpack && m_currentPacket <= m_stopPacket)
while (SendMore && count < maxpack && m_currentPacket <= m_stopPacket)
{ {
count++; sendMore = SendPacket(client);
SendMore = SendPacket(client); ++m_currentPacket;
m_currentPacket++; ++count;
} }
if (m_currentPacket > m_stopPacket) if (m_currentPacket > m_stopPacket)
@ -196,13 +196,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_currentPacket = StartPacket; m_currentPacket = StartPacket;
} }
if ((m_imageManager != null) && (m_imageManager.Client != null) && (m_imageManager.Client.PacketHandler != null))
if (m_imageManager.Client.PacketHandler.GetQueueCount(ThrottleOutPacketType.Texture) == 0)
{
//m_log.Debug("No textures queued, sending one packet to kickstart it");
SendPacket(m_imageManager.Client);
}
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -172,7 +172,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_lastloopprocessed = DateTime.Now.Ticks; m_lastloopprocessed = DateTime.Now.Ticks;
// This can happen during Close() // This can happen during Close()
if (m_client == null || m_client.PacketHandler == null || m_client.PacketHandler.PacketQueue == null) if (m_client == null)
return false; return false;
while ((imagereq = GetHighestPriorityImage()) != null) while ((imagereq = GetHighestPriorityImage()) != null)

View File

@ -1,870 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Reflection;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading;
using System.Timers;
using OpenMetaverse;
using OpenMetaverse.Packets;
using log4net;
using OpenSim.Framework;
using Timer=System.Timers.Timer;
namespace OpenSim.Region.ClientStack.LindenUDP
{
public class LLPacketHandler : ILLPacketHandler
{
private static readonly ILog m_log
= LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
//private int m_resentCount;
// Packet queues
//
LLPacketQueue m_PacketQueue;
public LLPacketQueue PacketQueue
{
get { return m_PacketQueue; }
}
// Timer to run stats and acks on
//
private Timer m_AckTimer = new Timer(250);
// A list of the packets we haven't acked yet
//
private List<uint> m_PendingAcks = new List<uint>();
private Dictionary<uint, uint> m_PendingAcksMap = new Dictionary<uint, uint>();
private Dictionary<uint, LLQueItem> m_NeedAck =
new Dictionary<uint, LLQueItem>();
/// <summary>
/// The number of milliseconds that can pass before a packet that needs an ack is resent.
/// </param>
private uint m_ResendTimeout = 4000;
public uint ResendTimeout
{
get { return m_ResendTimeout; }
set { m_ResendTimeout = value; }
}
private int m_MaxReliableResends = 3;
public int MaxReliableResends
{
get { return m_MaxReliableResends; }
set { m_MaxReliableResends = value; }
}
// Track duplicated packets. This uses a Dictionary. Both insertion
// and lookup are common operations and need to take advantage of
// the hashing. Expiration is less common and can be allowed the
// time for a linear scan.
//
private List<uint> m_alreadySeenList = new List<uint>();
private Dictionary<uint, int>m_alreadySeenTracker = new Dictionary<uint, int>();
private int m_alreadySeenWindow = 30000;
private int m_lastAlreadySeenCheck = Environment.TickCount & Int32.MaxValue;
// private Dictionary<uint, int> m_DupeTracker =
// new Dictionary<uint, int>();
// private uint m_DupeTrackerWindow = 30;
// private int m_DupeTrackerLastCheck = Environment.TickCount;
// Values for the SimStatsReporter
//
private int m_PacketsReceived = 0;
private int m_PacketsReceivedReported = 0;
private int m_PacketsSent = 0;
private int m_PacketsSentReported = 0;
private int m_UnackedBytes = 0;
private int m_LastResend = 0;
public int PacketsReceived
{
get { return m_PacketsReceived; }
}
public int PacketsReceivedReported
{
get { return m_PacketsReceivedReported; }
}
// The client we are working for
//
private IClientAPI m_Client;
// Some events
//
public event PacketStats OnPacketStats;
public event PacketDrop OnPacketDrop;
public event QueueEmpty OnQueueEmpty;
//private SynchronizeClientHandler m_SynchronizeClient = null;
public SynchronizeClientHandler SynchronizeClient
{
set { /* m_SynchronizeClient = value; */ }
}
// Packet sequencing
//
private uint m_Sequence = 0;
private object m_SequenceLock = new object();
private const int MAX_SEQUENCE = 0xFFFFFF;
// Packet dropping
//
List<PacketType> m_ImportantPackets = new List<PacketType>();
private bool m_ReliableIsImportant = false;
public bool ReliableIsImportant
{
get { return m_ReliableIsImportant; }
set { m_ReliableIsImportant = value; }
}
private int m_DropSafeTimeout;
LLPacketServer m_PacketServer;
private byte[] m_ZeroOutBuffer = new byte[4096];
////////////////////////////////////////////////////////////////////
// Constructors
//
public LLPacketHandler(IClientAPI client, LLPacketServer server, ClientStackUserSettings userSettings)
{
m_Client = client;
m_PacketServer = server;
m_DropSafeTimeout = Environment.TickCount + 15000;
m_PacketQueue = new LLPacketQueue(client.AgentId, userSettings);
m_PacketQueue.OnQueueEmpty += TriggerOnQueueEmpty;
m_AckTimer.Elapsed += AckTimerElapsed;
m_AckTimer.Start();
}
public void Dispose()
{
m_AckTimer.Stop();
m_AckTimer.Close();
m_PacketQueue.Enqueue(null);
m_PacketQueue.Close();
m_Client = null;
}
// Send one packet. This actually doesn't send anything, it queues
// it. Designed to be fire-and-forget, but there is an optional
// notifier.
//
public void OutPacket(
Packet packet, ThrottleOutPacketType throttlePacketType)
{
OutPacket(packet, throttlePacketType, null);
}
public void OutPacket(
Packet packet, ThrottleOutPacketType throttlePacketType,
Object id)
{
// Call the load balancer's hook. If this is not active here
// we defer to the sim server this client is actually connected
// to. Packet drop notifies will not be triggered in this
// configuration!
//
packet.Header.Sequence = 0;
lock (m_NeedAck)
{
DropResend(id);
AddAcks(ref packet);
QueuePacket(packet, throttlePacketType, id);
}
}
private void AddAcks(ref Packet packet)
{
// These packet types have shown to have issues with
// acks being appended to the payload, just don't send
// any with them until libsl is fixed.
//
if (packet is ViewerEffectPacket)
return;
if (packet is SimStatsPacket)
return;
// Add acks to outgoing packets
//
if (m_PendingAcks.Count > 0)
{
int count = m_PendingAcks.Count;
if (count > 10)
count = 10;
packet.Header.AckList = new uint[count];
packet.Header.AppendedAcks = true;
for (int i = 0; i < count; i++)
{
packet.Header.AckList[i] = m_PendingAcks[i];
m_PendingAcksMap.Remove(m_PendingAcks[i]);
}
m_PendingAcks.RemoveRange(0, count);
}
}
private void QueuePacket(
Packet packet, ThrottleOutPacketType throttlePacketType,
Object id)
{
LLQueItem item = new LLQueItem();
item.Packet = packet;
item.Incoming = false;
item.throttleType = throttlePacketType;
item.TickCount = Environment.TickCount;
item.Identifier = id;
item.Resends = 0;
item.Length = packet.Length;
item.Sequence = packet.Header.Sequence;
m_PacketQueue.Enqueue(item);
m_PacketsSent++;
}
private void ResendUnacked()
{
int now = Environment.TickCount;
int intervalMs = 250;
if (m_LastResend != 0)
intervalMs = now - m_LastResend;
lock (m_NeedAck)
{
if (m_DropSafeTimeout > now ||
intervalMs > 500) // We were frozen!
{
foreach (LLQueItem data in m_NeedAck.Values)
{
if (m_DropSafeTimeout > now)
{
m_NeedAck[data.Packet.Header.Sequence].TickCount = now;
}
else
{
m_NeedAck[data.Packet.Header.Sequence].TickCount += intervalMs;
}
}
}
m_LastResend = now;
// Unless we have received at least one ack, don't bother resending
// anything. There may not be a client there, don't clog up the
// pipes.
// Nothing to do
//
if (m_NeedAck.Count == 0)
return;
int resent = 0;
long dueDate = now - m_ResendTimeout;
List<LLQueItem> dropped = new List<LLQueItem>();
foreach (LLQueItem data in m_NeedAck.Values)
{
Packet packet = data.Packet;
// Packets this old get resent
//
if (data.TickCount < dueDate && data.Sequence != 0 && !m_PacketQueue.Contains(data.Sequence))
{
if (resent < 20) // Was 20 (= Max 117kbit/sec resends)
{
m_NeedAck[packet.Header.Sequence].Resends++;
// The client needs to be told that a packet is being resent, otherwise it appears to believe
// that it should reset its sequence to that packet number.
packet.Header.Resent = true;
if ((m_NeedAck[packet.Header.Sequence].Resends >= m_MaxReliableResends) &&
(!m_ReliableIsImportant))
{
dropped.Add(data);
continue;
}
m_NeedAck[packet.Header.Sequence].TickCount = Environment.TickCount;
QueuePacket(packet, ThrottleOutPacketType.Resend, data.Identifier);
resent++;
}
else
{
m_NeedAck[packet.Header.Sequence].TickCount += intervalMs;
}
}
}
foreach (LLQueItem data in dropped)
{
m_NeedAck.Remove(data.Packet.Header.Sequence);
TriggerOnPacketDrop(data.Packet, data.Identifier);
m_PacketQueue.Cancel(data.Packet.Header.Sequence);
PacketPool.Instance.ReturnPacket(data.Packet);
}
}
}
// Send the pending packet acks to the client
// Will send blocks of acks for up to 250 packets
//
private void SendAcks()
{
lock (m_NeedAck)
{
if (m_PendingAcks.Count == 0)
return;
PacketAckPacket acks = (PacketAckPacket)PacketPool.Instance.GetPacket(PacketType.PacketAck);
// The case of equality is more common than one might think,
// because this function will be called unconditionally when
// the counter reaches 250. So there is a good chance another
// packet with 250 blocks exists.
//
if (acks.Packets == null ||
acks.Packets.Length != m_PendingAcks.Count)
acks.Packets = new PacketAckPacket.PacketsBlock[m_PendingAcks.Count];
for (int i = 0; i < m_PendingAcks.Count; i++)
{
acks.Packets[i] = new PacketAckPacket.PacketsBlock();
acks.Packets[i].ID = m_PendingAcks[i];
}
m_PendingAcks.Clear();
m_PendingAcksMap.Clear();
acks.Header.Reliable = false;
OutPacket(acks, ThrottleOutPacketType.Unknown);
}
}
// Queue a packet ack. It will be sent either after 250 acks are
// queued, or when the timer fires.
//
private void AckPacket(Packet packet)
{
lock (m_NeedAck)
{
if (m_PendingAcks.Count < 250)
{
if (!m_PendingAcksMap.ContainsKey(packet.Header.Sequence))
{
m_PendingAcks.Add(packet.Header.Sequence);
m_PendingAcksMap.Add(packet.Header.Sequence,
packet.Header.Sequence);
}
return;
}
}
SendAcks();
lock (m_NeedAck)
{
// If this is still full we have a truly exceptional
// condition (means, can't happen)
//
if (m_PendingAcks.Count < 250)
{
if (!m_PendingAcksMap.ContainsKey(packet.Header.Sequence))
{
m_PendingAcks.Add(packet.Header.Sequence);
m_PendingAcksMap.Add(packet.Header.Sequence,
packet.Header.Sequence);
}
return;
}
}
}
// When the timer elapses, send the pending acks, trigger resends
// and report all the stats.
//
private void AckTimerElapsed(object sender, ElapsedEventArgs ea)
{
SendAcks();
ResendUnacked();
SendPacketStats();
}
// Push out pachet counts for the sim status reporter
//
private void SendPacketStats()
{
PacketStats handlerPacketStats = OnPacketStats;
if (handlerPacketStats != null)
{
handlerPacketStats(
m_PacketsReceived - m_PacketsReceivedReported,
m_PacketsSent - m_PacketsSentReported,
m_UnackedBytes);
m_PacketsReceivedReported = m_PacketsReceived;
m_PacketsSentReported = m_PacketsSent;
}
}
// We can't keep an unlimited record of dupes. This will prune the
// dictionary by age.
//
// NOTE: this needs to be called from within lock
// (m_alreadySeenTracker) context!
private void ExpireSeenPackets()
{
if (m_alreadySeenList.Count < 1024)
return;
int ticks = 0;
int tc = Environment.TickCount & Int32.MaxValue;
if (tc >= m_lastAlreadySeenCheck)
ticks = tc - m_lastAlreadySeenCheck;
else
ticks = Int32.MaxValue - m_lastAlreadySeenCheck + tc;
if (ticks < 2000) return;
m_lastAlreadySeenCheck = tc;
// we calculate the drop dead tick count here instead of
// in the loop: any packet with a timestamp before
// dropDeadTC can be expired
int dropDeadTC = tc - m_alreadySeenWindow;
int i = 0;
while (i < m_alreadySeenList.Count && m_alreadySeenTracker[m_alreadySeenList[i]] < dropDeadTC)
{
m_alreadySeenTracker.Remove(m_alreadySeenList[i]);
i++;
}
// if we dropped packet from m_alreadySeenTracker we need
// to drop them from m_alreadySeenList as well, let's do
// that in one go: the list is ordered after all.
if (i > 0)
{
m_alreadySeenList.RemoveRange(0, i);
// m_log.DebugFormat("[CLIENT]: expired {0} packets, {1}:{2} left", i, m_alreadySeenList.Count, m_alreadySeenTracker.Count);
}
}
public void InPacket(Packet packet)
{
if (packet == null)
return;
// When too many acks are needed to be sent, the client sends
// a packet consisting of acks only
//
if (packet.Type == PacketType.PacketAck)
{
PacketAckPacket ackPacket = (PacketAckPacket)packet;
foreach (PacketAckPacket.PacketsBlock block in ackPacket.Packets)
{
ProcessAck(block.ID);
}
PacketPool.Instance.ReturnPacket(packet);
return;
}
// Any packet can have some packet acks in the header.
// Process them here
//
if (packet.Header.AppendedAcks)
{
foreach (uint id in packet.Header.AckList)
{
ProcessAck(id);
}
}
// If this client is on another partial instance, no need
// to handle packets
//
if (!m_Client.IsActive && packet.Type != PacketType.LogoutRequest)
{
PacketPool.Instance.ReturnPacket(packet);
return;
}
if (packet.Type == PacketType.StartPingCheck)
{
StartPingCheckPacket startPing = (StartPingCheckPacket)packet;
CompletePingCheckPacket endPing
= (CompletePingCheckPacket)PacketPool.Instance.GetPacket(PacketType.CompletePingCheck);
endPing.PingID.PingID = startPing.PingID.PingID;
OutPacket(endPing, ThrottleOutPacketType.Task);
}
else
{
LLQueItem item = new LLQueItem();
item.Packet = packet;
item.Incoming = true;
m_PacketQueue.Enqueue(item);
}
}
public void ProcessInPacket(LLQueItem item)
{
Packet packet = item.Packet;
// Always ack the packet!
//
if (packet.Header.Reliable)
AckPacket(packet);
if (packet.Type != PacketType.AgentUpdate)
m_PacketsReceived++;
// Check for duplicate packets.. packets that the client is
// resending because it didn't receive our ack
//
lock (m_alreadySeenTracker)
{
ExpireSeenPackets();
if (m_alreadySeenTracker.ContainsKey(packet.Header.Sequence))
return;
m_alreadySeenTracker.Add(packet.Header.Sequence, Environment.TickCount & Int32.MaxValue);
m_alreadySeenList.Add(packet.Header.Sequence);
}
m_Client.ProcessInPacket(packet);
}
public void Flush()
{
m_PacketQueue.Flush();
m_UnackedBytes = (-1 * m_UnackedBytes);
SendPacketStats();
}
public void Clear()
{
m_UnackedBytes = (-1 * m_UnackedBytes);
SendPacketStats();
lock (m_NeedAck)
{
m_NeedAck.Clear();
m_PendingAcks.Clear();
m_PendingAcksMap.Clear();
}
m_Sequence += 1000000;
}
private void ProcessAck(uint id)
{
LLQueItem data;
lock (m_NeedAck)
{
//m_log.DebugFormat("[CLIENT]: In {0} received ack for packet {1}", m_Client.Scene.RegionInfo.ExternalEndPoint.Port, id);
if (!m_NeedAck.TryGetValue(id, out data))
return;
m_NeedAck.Remove(id);
m_PacketQueue.Cancel(data.Sequence);
PacketPool.Instance.ReturnPacket(data.Packet);
m_UnackedBytes -= data.Length;
}
}
// Allocate packet sequence numbers in a threadsave manner
//
protected uint NextPacketSequenceNumber()
{
// Set the sequence number
uint seq = 1;
lock (m_SequenceLock)
{
if (m_Sequence >= MAX_SEQUENCE)
{
m_Sequence = 1;
}
else
{
m_Sequence++;
}
seq = m_Sequence;
}
return seq;
}
public ClientInfo GetClientInfo()
{
ClientInfo info = new ClientInfo();
info.pendingAcks = m_PendingAcksMap;
info.needAck = new Dictionary<uint, byte[]>();
lock (m_NeedAck)
{
foreach (uint key in m_NeedAck.Keys)
info.needAck.Add(key, m_NeedAck[key].Packet.ToBytes());
}
LLQueItem[] queitems = m_PacketQueue.GetQueueArray();
for (int i = 0; i < queitems.Length; i++)
{
if (queitems[i].Incoming == false)
info.out_packets.Add(queitems[i].Packet.ToBytes());
}
info.sequence = m_Sequence;
float multiplier = m_PacketQueue.ThrottleMultiplier;
info.resendThrottle = (int) (m_PacketQueue.ResendThrottle.Throttle / multiplier);
info.landThrottle = (int) (m_PacketQueue.LandThrottle.Throttle / multiplier);
info.windThrottle = (int) (m_PacketQueue.WindThrottle.Throttle / multiplier);
info.cloudThrottle = (int) (m_PacketQueue.CloudThrottle.Throttle / multiplier);
info.taskThrottle = (int) (m_PacketQueue.TaskThrottle.Throttle / multiplier);
info.assetThrottle = (int) (m_PacketQueue.AssetThrottle.Throttle / multiplier);
info.textureThrottle = (int) (m_PacketQueue.TextureThrottle.Throttle / multiplier);
info.totalThrottle = (int) (m_PacketQueue.TotalThrottle.Throttle / multiplier);
return info;
}
public void SetClientInfo(ClientInfo info)
{
m_PendingAcksMap = info.pendingAcks;
m_PendingAcks = new List<uint>(m_PendingAcksMap.Keys);
m_NeedAck = new Dictionary<uint, LLQueItem>();
Packet packet = null;
int packetEnd = 0;
byte[] zero = new byte[3000];
foreach (uint key in info.needAck.Keys)
{
byte[] buff = info.needAck[key];
packetEnd = buff.Length - 1;
try
{
packet = PacketPool.Instance.GetPacket(buff, ref packetEnd, zero);
}
catch (Exception)
{
}
LLQueItem item = new LLQueItem();
item.Packet = packet;
item.Incoming = false;
item.throttleType = 0;
item.TickCount = Environment.TickCount;
item.Identifier = 0;
item.Resends = 0;
item.Length = packet.Length;
item.Sequence = packet.Header.Sequence;
m_NeedAck.Add(key, item);
}
m_Sequence = info.sequence;
m_PacketQueue.ResendThrottle.Throttle = info.resendThrottle;
m_PacketQueue.LandThrottle.Throttle = info.landThrottle;
m_PacketQueue.WindThrottle.Throttle = info.windThrottle;
m_PacketQueue.CloudThrottle.Throttle = info.cloudThrottle;
m_PacketQueue.TaskThrottle.Throttle = info.taskThrottle;
m_PacketQueue.AssetThrottle.Throttle = info.assetThrottle;
m_PacketQueue.TextureThrottle.Throttle = info.textureThrottle;
m_PacketQueue.TotalThrottle.Throttle = info.totalThrottle;
}
public void AddImportantPacket(PacketType type)
{
if (m_ImportantPackets.Contains(type))
return;
m_ImportantPackets.Add(type);
}
public void RemoveImportantPacket(PacketType type)
{
if (!m_ImportantPackets.Contains(type))
return;
m_ImportantPackets.Remove(type);
}
private void DropResend(Object id)
{
LLQueItem d = null;
foreach (LLQueItem data in m_NeedAck.Values)
{
if (data.Identifier != null && data.Identifier == id)
{
d = data;
break;
}
}
if (null == d) return;
m_NeedAck.Remove(d.Packet.Header.Sequence);
m_PacketQueue.Cancel(d.Sequence);
PacketPool.Instance.ReturnPacket(d.Packet);
}
private void TriggerOnPacketDrop(Packet packet, Object id)
{
PacketDrop handlerPacketDrop = OnPacketDrop;
if (handlerPacketDrop == null)
return;
handlerPacketDrop(packet, id);
}
private void TriggerOnQueueEmpty(ThrottleOutPacketType queue)
{
QueueEmpty handlerQueueEmpty = OnQueueEmpty;
if (handlerQueueEmpty != null)
handlerQueueEmpty(queue);
}
// Convert the packet to bytes and stuff it onto the send queue
//
public void ProcessOutPacket(LLQueItem item)
{
Packet packet = item.Packet;
// Assign sequence number here to prevent out of order packets
if (packet.Header.Sequence == 0)
{
lock (m_NeedAck)
{
packet.Header.Sequence = NextPacketSequenceNumber();
item.Sequence = packet.Header.Sequence;
item.TickCount = Environment.TickCount;
// We want to see that packet arrive if it's reliable
if (packet.Header.Reliable)
{
m_UnackedBytes += item.Length;
// Keep track of when this packet was sent out
item.TickCount = Environment.TickCount;
m_NeedAck[packet.Header.Sequence] = item;
}
}
}
// If we sent a killpacket
if (packet is KillPacket)
Abort();
try
{
// If this packet has been reused/returned, the ToBytes
// will blow up in our face.
// Fail gracefully.
//
// Actually make the byte array and send it
byte[] sendbuffer = item.Packet.ToBytes();
if (packet.Header.Zerocoded)
{
int packetsize = Helpers.ZeroEncode(sendbuffer,
sendbuffer.Length, m_ZeroOutBuffer);
m_PacketServer.SendPacketTo(m_ZeroOutBuffer, packetsize,
SocketFlags.None, m_Client.CircuitCode);
}
else
{
// Need some extra space in case we need to add proxy
// information to the message later
Buffer.BlockCopy(sendbuffer, 0, m_ZeroOutBuffer, 0,
sendbuffer.Length);
m_PacketServer.SendPacketTo(m_ZeroOutBuffer,
sendbuffer.Length, SocketFlags.None, m_Client.CircuitCode);
}
}
catch (NullReferenceException)
{
m_log.Error("[PACKET]: Detected reuse of a returned packet");
m_PacketQueue.Cancel(item.Sequence);
return;
}
// If this is a reliable packet, we are still holding a ref
// Dont't return in that case
//
if (!packet.Header.Reliable)
{
m_PacketQueue.Cancel(item.Sequence);
PacketPool.Instance.ReturnPacket(packet);
}
}
private void Abort()
{
m_PacketQueue.Close();
Thread.CurrentThread.Abort();
}
public int GetQueueCount(ThrottleOutPacketType queue)
{
return m_PacketQueue.GetQueueCount(queue);
}
}
}

View File

@ -1,742 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using System.Timers;
using log4net;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Framework.Statistics;
using OpenSim.Framework.Statistics.Interfaces;
using Timer=System.Timers.Timer;
namespace OpenSim.Region.ClientStack.LindenUDP
{
public class LLPacketQueue : IPullStatsProvider, IDisposable
{
private static readonly ILog m_log
= LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// Is queueing enabled at all?
/// </summary>
private bool m_enabled = true;
private OpenSim.Framework.BlockingQueue<LLQueItem> SendQueue;
private Queue<LLQueItem> IncomingPacketQueue;
private Queue<LLQueItem> OutgoingPacketQueue;
private Queue<LLQueItem> ResendOutgoingPacketQueue;
private Queue<LLQueItem> LandOutgoingPacketQueue;
private Queue<LLQueItem> WindOutgoingPacketQueue;
private Queue<LLQueItem> CloudOutgoingPacketQueue;
private Queue<LLQueItem> TaskOutgoingPacketQueue;
private Queue<LLQueItem> TaskLowpriorityPacketQueue;
private Queue<LLQueItem> TextureOutgoingPacketQueue;
private Queue<LLQueItem> AssetOutgoingPacketQueue;
private List<ThrottleOutPacketType> Empty = new List<ThrottleOutPacketType>();
// m_log.Info("[THROTTLE]: Entering Throttle");
// private Dictionary<uint, uint> PendingAcks = new Dictionary<uint, uint>();
// private Dictionary<uint, Packet> NeedAck = new Dictionary<uint, Packet>();
// All throttle times and number of bytes are calculated by dividing by this value
// This value also determines how many times per throttletimems the timer will run
// If throttleimems is 1000 ms, then the timer will fire every 1000/7 milliseconds
private float throttleMultiplier = 2.0f; // Default value really doesn't matter.
private int throttleTimeDivisor = 7;
private int throttletimems = 1000;
internal LLPacketThrottle ResendThrottle;
internal LLPacketThrottle LandThrottle;
internal LLPacketThrottle WindThrottle;
internal LLPacketThrottle CloudThrottle;
internal LLPacketThrottle TaskThrottle;
internal LLPacketThrottle AssetThrottle;
internal LLPacketThrottle TextureThrottle;
internal LLPacketThrottle TotalThrottle;
private Dictionary<uint,int> contents = new Dictionary<uint, int>();
// private long LastThrottle;
// private long ThrottleInterval;
private Timer throttleTimer;
private UUID m_agentId;
public event QueueEmpty OnQueueEmpty;
public LLPacketQueue(UUID agentId, ClientStackUserSettings userSettings)
{
// While working on this, the BlockingQueue had me fooled for a bit.
// The Blocking queue causes the thread to stop until there's something
// in it to process. it's an on-purpose threadlock though because
// without it, the clientloop will suck up all sim resources.
SendQueue = new OpenSim.Framework.BlockingQueue<LLQueItem>();
IncomingPacketQueue = new Queue<LLQueItem>();
OutgoingPacketQueue = new Queue<LLQueItem>();
ResendOutgoingPacketQueue = new Queue<LLQueItem>();
LandOutgoingPacketQueue = new Queue<LLQueItem>();
WindOutgoingPacketQueue = new Queue<LLQueItem>();
CloudOutgoingPacketQueue = new Queue<LLQueItem>();
TaskOutgoingPacketQueue = new Queue<LLQueItem>();
TaskLowpriorityPacketQueue = new Queue<LLQueItem>();
TextureOutgoingPacketQueue = new Queue<LLQueItem>();
AssetOutgoingPacketQueue = new Queue<LLQueItem>();
// Store the throttle multiplier for posterity.
throttleMultiplier = userSettings.ClientThrottleMultipler;
int throttleMaxBPS = 1500000;
if (userSettings.TotalThrottleSettings != null)
throttleMaxBPS = userSettings.TotalThrottleSettings.Max;
// Set up the throttle classes (min, max, current) in bits per second
ResendThrottle = new LLPacketThrottle(5000, throttleMaxBPS / 15, 16000, userSettings.ClientThrottleMultipler);
LandThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 15, 2000, userSettings.ClientThrottleMultipler);
WindThrottle = new LLPacketThrottle(0, throttleMaxBPS / 15, 0, userSettings.ClientThrottleMultipler);
CloudThrottle = new LLPacketThrottle(0, throttleMaxBPS / 15, 0, userSettings.ClientThrottleMultipler);
TaskThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 3000, userSettings.ClientThrottleMultipler);
AssetThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 1000, userSettings.ClientThrottleMultipler);
TextureThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 4000, userSettings.ClientThrottleMultipler);
// Total Throttle trumps all - it is the number of bits in total that are allowed to go out per second.
ThrottleSettings totalThrottleSettings = userSettings.TotalThrottleSettings;
if (null == totalThrottleSettings)
{
totalThrottleSettings = new ThrottleSettings(0, throttleMaxBPS, 28000);
}
TotalThrottle
= new LLPacketThrottle(
totalThrottleSettings.Min, totalThrottleSettings.Max, totalThrottleSettings.Current,
userSettings.ClientThrottleMultipler);
throttleTimer = new Timer((int)(throttletimems / throttleTimeDivisor));
throttleTimer.Elapsed += ThrottleTimerElapsed;
throttleTimer.Start();
// TIMERS needed for this
// LastThrottle = DateTime.Now.Ticks;
// ThrottleInterval = (long)(throttletimems/throttleTimeDivisor);
m_agentId = agentId;
if (StatsManager.SimExtraStats != null)
{
StatsManager.SimExtraStats.RegisterPacketQueueStatsProvider(m_agentId, this);
}
}
/* STANDARD QUEUE MANIPULATION INTERFACES */
public void Enqueue(LLQueItem item)
{
if (!m_enabled)
{
return;
}
// We could micro lock, but that will tend to actually
// probably be worse than just synchronizing on SendQueue
if (item == null)
{
SendQueue.Enqueue(item);
return;
}
if (item.Incoming)
{
SendQueue.PriorityEnqueue(item);
return;
}
if (item.Sequence != 0)
lock (contents)
{
if (contents.ContainsKey(item.Sequence))
contents[item.Sequence] += 1;
else
contents.Add(item.Sequence, 1);
}
lock (this)
{
switch (item.throttleType & ThrottleOutPacketType.TypeMask)
{
case ThrottleOutPacketType.Resend:
ThrottleCheck(ref ResendThrottle, ref ResendOutgoingPacketQueue, item, ThrottleOutPacketType.Resend);
break;
case ThrottleOutPacketType.Texture:
ThrottleCheck(ref TextureThrottle, ref TextureOutgoingPacketQueue, item, ThrottleOutPacketType.Texture);
break;
case ThrottleOutPacketType.Task:
if ((item.throttleType & ThrottleOutPacketType.LowPriority) != 0)
ThrottleCheck(ref TaskThrottle, ref TaskLowpriorityPacketQueue, item, ThrottleOutPacketType.Task);
else
ThrottleCheck(ref TaskThrottle, ref TaskOutgoingPacketQueue, item, ThrottleOutPacketType.Task);
break;
case ThrottleOutPacketType.Land:
ThrottleCheck(ref LandThrottle, ref LandOutgoingPacketQueue, item, ThrottleOutPacketType.Land);
break;
case ThrottleOutPacketType.Asset:
ThrottleCheck(ref AssetThrottle, ref AssetOutgoingPacketQueue, item, ThrottleOutPacketType.Asset);
break;
case ThrottleOutPacketType.Cloud:
ThrottleCheck(ref CloudThrottle, ref CloudOutgoingPacketQueue, item, ThrottleOutPacketType.Cloud);
break;
case ThrottleOutPacketType.Wind:
ThrottleCheck(ref WindThrottle, ref WindOutgoingPacketQueue, item, ThrottleOutPacketType.Wind);
break;
default:
// Acknowledgements and other such stuff should go directly to the blocking Queue
// Throttling them may and likely 'will' be problematic
SendQueue.PriorityEnqueue(item);
break;
}
}
}
public LLQueItem Dequeue()
{
while (true)
{
LLQueItem item = SendQueue.Dequeue();
if (item == null)
return null;
if (item.Incoming)
return item;
item.TickCount = System.Environment.TickCount;
if (item.Sequence == 0)
return item;
lock (contents)
{
if (contents.ContainsKey(item.Sequence))
{
if (contents[item.Sequence] == 1)
contents.Remove(item.Sequence);
else
contents[item.Sequence] -= 1;
return item;
}
}
}
}
public void Cancel(uint sequence)
{
lock (contents) contents.Remove(sequence);
}
public bool Contains(uint sequence)
{
lock (contents) return contents.ContainsKey(sequence);
}
public void Flush()
{
lock (this)
{
// These categories do not contain transactional packets so we can safely drop any pending data in them
LandOutgoingPacketQueue.Clear();
WindOutgoingPacketQueue.Clear();
CloudOutgoingPacketQueue.Clear();
TextureOutgoingPacketQueue.Clear();
AssetOutgoingPacketQueue.Clear();
// Now comes the fun part.. we dump all remaining resend and task packets into the send queue
while (ResendOutgoingPacketQueue.Count > 0 || TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0)
{
if (ResendOutgoingPacketQueue.Count > 0)
SendQueue.Enqueue(ResendOutgoingPacketQueue.Dequeue());
if (TaskOutgoingPacketQueue.Count > 0)
SendQueue.PriorityEnqueue(TaskOutgoingPacketQueue.Dequeue());
if (TaskLowpriorityPacketQueue.Count > 0)
SendQueue.Enqueue(TaskLowpriorityPacketQueue.Dequeue());
}
}
}
public void WipeClean()
{
lock (this)
{
ResendOutgoingPacketQueue.Clear();
LandOutgoingPacketQueue.Clear();
WindOutgoingPacketQueue.Clear();
CloudOutgoingPacketQueue.Clear();
TaskOutgoingPacketQueue.Clear();
TaskLowpriorityPacketQueue.Clear();
TextureOutgoingPacketQueue.Clear();
AssetOutgoingPacketQueue.Clear();
SendQueue.Clear();
lock (contents) contents.Clear();
}
}
public void Close()
{
Dispose();
}
public void Dispose()
{
Flush();
WipeClean(); // I'm sure there's a dirty joke in here somewhere. -AFrisby
m_enabled = false;
throttleTimer.Stop();
throttleTimer.Close();
if (StatsManager.SimExtraStats != null)
{
StatsManager.SimExtraStats.DeregisterPacketQueueStatsProvider(m_agentId);
}
}
private void ResetCounters()
{
ResendThrottle.Reset();
LandThrottle.Reset();
WindThrottle.Reset();
CloudThrottle.Reset();
TaskThrottle.Reset();
AssetThrottle.Reset();
TextureThrottle.Reset();
TotalThrottle.Reset();
}
private bool PacketsWaiting()
{
return (ResendOutgoingPacketQueue.Count > 0 ||
LandOutgoingPacketQueue.Count > 0 ||
WindOutgoingPacketQueue.Count > 0 ||
CloudOutgoingPacketQueue.Count > 0 ||
TaskOutgoingPacketQueue.Count > 0 ||
TaskLowpriorityPacketQueue.Count > 0 ||
AssetOutgoingPacketQueue.Count > 0 ||
TextureOutgoingPacketQueue.Count > 0);
}
public void ProcessThrottle()
{
// I was considering this.. Will an event fire if the thread it's on is blocked?
// Then I figured out.. it doesn't really matter.. because this thread won't be blocked for long
// The General overhead of the UDP protocol gets sent to the queue un-throttled by this
// so This'll pick up about around the right time.
int MaxThrottleLoops = 4550; // 50*7 packets can be dequeued at once.
int throttleLoops = 0;
List<ThrottleOutPacketType> e;
// We're going to dequeue all of the saved up packets until
// we've hit the throttle limit or there's no more packets to send
lock (this)
{
// this variable will be true if there was work done in the last execution of the
// loop, since each pass through the loop checks the queue length, we no longer
// need the check on entering the loop
bool qchanged = true;
ResetCounters();
while (TotalThrottle.UnderLimit() && qchanged && throttleLoops <= MaxThrottleLoops)
{
qchanged = false; // We will break out of the loop if no work was accomplished
throttleLoops++;
//Now comes the fun part.. we dump all our elements into m_packetQueue that we've saved up.
if ((ResendOutgoingPacketQueue.Count > 0) && ResendThrottle.UnderLimit())
{
LLQueItem qpack = ResendOutgoingPacketQueue.Dequeue();
SendQueue.Enqueue(qpack);
TotalThrottle.AddBytes(qpack.Length);
ResendThrottle.AddBytes(qpack.Length);
qchanged = true;
}
if ((LandOutgoingPacketQueue.Count > 0) && LandThrottle.UnderLimit())
{
LLQueItem qpack = LandOutgoingPacketQueue.Dequeue();
SendQueue.Enqueue(qpack);
TotalThrottle.AddBytes(qpack.Length);
LandThrottle.AddBytes(qpack.Length);
qchanged = true;
if (LandOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Land))
Empty.Add(ThrottleOutPacketType.Land);
}
if ((WindOutgoingPacketQueue.Count > 0) && WindThrottle.UnderLimit())
{
LLQueItem qpack = WindOutgoingPacketQueue.Dequeue();
SendQueue.Enqueue(qpack);
TotalThrottle.AddBytes(qpack.Length);
WindThrottle.AddBytes(qpack.Length);
qchanged = true;
if (WindOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Wind))
Empty.Add(ThrottleOutPacketType.Wind);
}
if ((CloudOutgoingPacketQueue.Count > 0) && CloudThrottle.UnderLimit())
{
LLQueItem qpack = CloudOutgoingPacketQueue.Dequeue();
SendQueue.Enqueue(qpack);
TotalThrottle.AddBytes(qpack.Length);
CloudThrottle.AddBytes(qpack.Length);
qchanged = true;
if (CloudOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Cloud))
Empty.Add(ThrottleOutPacketType.Cloud);
}
if ((TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0) && TaskThrottle.UnderLimit())
{
LLQueItem qpack;
if (TaskOutgoingPacketQueue.Count > 0)
{
qpack = TaskOutgoingPacketQueue.Dequeue();
SendQueue.PriorityEnqueue(qpack);
}
else
{
qpack = TaskLowpriorityPacketQueue.Dequeue();
SendQueue.Enqueue(qpack);
}
TotalThrottle.AddBytes(qpack.Length);
TaskThrottle.AddBytes(qpack.Length);
qchanged = true;
if (TaskOutgoingPacketQueue.Count == 0 && TaskLowpriorityPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Task))
Empty.Add(ThrottleOutPacketType.Task);
}
if ((TextureOutgoingPacketQueue.Count > 0) && TextureThrottle.UnderLimit())
{
LLQueItem qpack = TextureOutgoingPacketQueue.Dequeue();
SendQueue.Enqueue(qpack);
TotalThrottle.AddBytes(qpack.Length);
TextureThrottle.AddBytes(qpack.Length);
qchanged = true;
if (TextureOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Texture))
Empty.Add(ThrottleOutPacketType.Texture);
}
if ((AssetOutgoingPacketQueue.Count > 0) && AssetThrottle.UnderLimit())
{
LLQueItem qpack = AssetOutgoingPacketQueue.Dequeue();
SendQueue.Enqueue(qpack);
TotalThrottle.AddBytes(qpack.Length);
AssetThrottle.AddBytes(qpack.Length);
qchanged = true;
if (AssetOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Asset))
Empty.Add(ThrottleOutPacketType.Asset);
}
}
// m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets");
e = new List<ThrottleOutPacketType>(Empty);
Empty.Clear();
}
foreach (ThrottleOutPacketType t in e)
{
if (GetQueueCount(t) == 0)
TriggerOnQueueEmpty(t);
}
}
private void TriggerOnQueueEmpty(ThrottleOutPacketType queue)
{
QueueEmpty handlerQueueEmpty = OnQueueEmpty;
if (handlerQueueEmpty != null)
handlerQueueEmpty(queue);
}
private void ThrottleTimerElapsed(object sender, ElapsedEventArgs e)
{
// just to change the signature, and that ProcessThrottle
// will be used elsewhere possibly
ProcessThrottle();
}
private void ThrottleCheck(ref LLPacketThrottle throttle, ref Queue<LLQueItem> q, LLQueItem item, ThrottleOutPacketType itemType)
{
// The idea.. is if the packet throttle queues are empty
// and the client is under throttle for the type. Queue
// it up directly. This basically short cuts having to
// wait for the timer to fire to put things into the
// output queue
if ((q.Count == 0) && (throttle.UnderLimit()))
{
try
{
Monitor.Enter(this);
throttle.AddBytes(item.Length);
TotalThrottle.AddBytes(item.Length);
SendQueue.Enqueue(item);
lock (this)
{
if (!Empty.Contains(itemType))
Empty.Add(itemType);
}
}
catch (Exception e)
{
// Probably a serialization exception
m_log.WarnFormat("ThrottleCheck: {0}", e.ToString());
}
finally
{
Monitor.Pulse(this);
Monitor.Exit(this);
}
}
else
{
q.Enqueue(item);
}
}
private static int ScaleThrottle(int value, int curmax, int newmax)
{
return (int)((value / (float)curmax) * newmax);
}
public byte[] GetThrottlesPacked(float multiplier)
{
int singlefloat = 4;
float tResend = ResendThrottle.Throttle*multiplier;
float tLand = LandThrottle.Throttle*multiplier;
float tWind = WindThrottle.Throttle*multiplier;
float tCloud = CloudThrottle.Throttle*multiplier;
float tTask = TaskThrottle.Throttle*multiplier;
float tTexture = TextureThrottle.Throttle*multiplier;
float tAsset = AssetThrottle.Throttle*multiplier;
byte[] throttles = new byte[singlefloat*7];
int i = 0;
Buffer.BlockCopy(BitConverter.GetBytes(tResend), 0, throttles, singlefloat*i, singlefloat);
i++;
Buffer.BlockCopy(BitConverter.GetBytes(tLand), 0, throttles, singlefloat*i, singlefloat);
i++;
Buffer.BlockCopy(BitConverter.GetBytes(tWind), 0, throttles, singlefloat*i, singlefloat);
i++;
Buffer.BlockCopy(BitConverter.GetBytes(tCloud), 0, throttles, singlefloat*i, singlefloat);
i++;
Buffer.BlockCopy(BitConverter.GetBytes(tTask), 0, throttles, singlefloat*i, singlefloat);
i++;
Buffer.BlockCopy(BitConverter.GetBytes(tTexture), 0, throttles, singlefloat*i, singlefloat);
i++;
Buffer.BlockCopy(BitConverter.GetBytes(tAsset), 0, throttles, singlefloat*i, singlefloat);
return throttles;
}
public void SetThrottleFromClient(byte[] throttle)
{
// From mantis http://opensimulator.org/mantis/view.php?id=1374
// it appears that sometimes we are receiving empty throttle byte arrays.
// TODO: Investigate this behaviour
if (throttle.Length == 0)
{
m_log.Warn("[PACKET QUEUE]: SetThrottleFromClient unexpectedly received a throttle byte array containing no elements!");
return;
}
int tResend = -1;
int tLand = -1;
int tWind = -1;
int tCloud = -1;
int tTask = -1;
int tTexture = -1;
int tAsset = -1;
int tall = -1;
int singlefloat = 4;
//Agent Throttle Block contains 7 single floatingpoint values.
int j = 0;
// Some Systems may be big endian...
// it might be smart to do this check more often...
if (!BitConverter.IsLittleEndian)
for (int i = 0; i < 7; i++)
Array.Reverse(throttle, j + i*singlefloat, singlefloat);
// values gotten from OpenMetaverse.org/wiki/Throttle. Thanks MW_
// bytes
// Convert to integer, since.. the full fp space isn't used.
tResend = (int) BitConverter.ToSingle(throttle, j);
j += singlefloat;
tLand = (int) BitConverter.ToSingle(throttle, j);
j += singlefloat;
tWind = (int) BitConverter.ToSingle(throttle, j);
j += singlefloat;
tCloud = (int) BitConverter.ToSingle(throttle, j);
j += singlefloat;
tTask = (int) BitConverter.ToSingle(throttle, j);
j += singlefloat;
tTexture = (int) BitConverter.ToSingle(throttle, j);
j += singlefloat;
tAsset = (int) BitConverter.ToSingle(throttle, j);
tall = tResend + tLand + tWind + tCloud + tTask + tTexture + tAsset;
/*
m_log.Info("[CLIENT]: Client AgentThrottle - Got throttle:resendbits=" + tResend +
" landbits=" + tLand +
" windbits=" + tWind +
" cloudbits=" + tCloud +
" taskbits=" + tTask +
" texturebits=" + tTexture +
" Assetbits=" + tAsset +
" Allbits=" + tall);
*/
// Total Sanity
// Make sure that the client sent sane total values.
// If the client didn't send acceptable values....
// Scale the clients values down until they are acceptable.
if (tall <= TotalThrottle.Max)
{
ResendThrottle.Throttle = tResend;
LandThrottle.Throttle = tLand;
WindThrottle.Throttle = tWind;
CloudThrottle.Throttle = tCloud;
TaskThrottle.Throttle = tTask;
TextureThrottle.Throttle = tTexture;
AssetThrottle.Throttle = tAsset;
TotalThrottle.Throttle = tall;
}
// else if (tall < 1)
// {
// // client is stupid, penalize him by minning everything
// ResendThrottle.Throttle = ResendThrottle.Min;
// LandThrottle.Throttle = LandThrottle.Min;
// WindThrottle.Throttle = WindThrottle.Min;
// CloudThrottle.Throttle = CloudThrottle.Min;
// TaskThrottle.Throttle = TaskThrottle.Min;
// TextureThrottle.Throttle = TextureThrottle.Min;
// AssetThrottle.Throttle = AssetThrottle.Min;
// TotalThrottle.Throttle = TotalThrottle.Min;
// }
else
{
// we're over so figure out percentages and use those
ResendThrottle.Throttle = tResend;
LandThrottle.Throttle = ScaleThrottle(tLand, tall, TotalThrottle.Max);
WindThrottle.Throttle = ScaleThrottle(tWind, tall, TotalThrottle.Max);
CloudThrottle.Throttle = ScaleThrottle(tCloud, tall, TotalThrottle.Max);
TaskThrottle.Throttle = ScaleThrottle(tTask, tall, TotalThrottle.Max);
TextureThrottle.Throttle = ScaleThrottle(tTexture, tall, TotalThrottle.Max);
AssetThrottle.Throttle = ScaleThrottle(tAsset, tall, TotalThrottle.Max);
TotalThrottle.Throttle = TotalThrottle.Max;
}
// effectively wiggling the slider causes things reset
// ResetCounters(); // DO NOT reset, better to send less for one period than more
}
// See IPullStatsProvider
public string GetStats()
{
return string.Format("{0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}",
SendQueue.Count(),
IncomingPacketQueue.Count,
OutgoingPacketQueue.Count,
ResendOutgoingPacketQueue.Count,
LandOutgoingPacketQueue.Count,
WindOutgoingPacketQueue.Count,
CloudOutgoingPacketQueue.Count,
TaskOutgoingPacketQueue.Count,
TextureOutgoingPacketQueue.Count,
AssetOutgoingPacketQueue.Count);
}
public LLQueItem[] GetQueueArray()
{
return SendQueue.GetQueueArray();
}
public float ThrottleMultiplier
{
get { return throttleMultiplier; }
}
public int GetQueueCount(ThrottleOutPacketType queue)
{
switch (queue)
{
case ThrottleOutPacketType.Land:
return LandOutgoingPacketQueue.Count;
case ThrottleOutPacketType.Wind:
return WindOutgoingPacketQueue.Count;
case ThrottleOutPacketType.Cloud:
return CloudOutgoingPacketQueue.Count;
case ThrottleOutPacketType.Task:
return TaskOutgoingPacketQueue.Count;
case ThrottleOutPacketType.Texture:
return TextureOutgoingPacketQueue.Count;
case ThrottleOutPacketType.Asset:
return AssetOutgoingPacketQueue.Count;
}
return 0;
}
}
}

View File

@ -1,206 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.Net;
using System.Net.Sockets;
using OpenMetaverse;
using OpenMetaverse.Packets;
using OpenSim.Framework;
namespace OpenSim.Region.ClientStack.LindenUDP
{
/// <summary>
/// This class sets up new client stacks. It also handles the immediate distribution of incoming packets to
/// client stacks
/// </summary>
public class LLPacketServer
{
// private static readonly log4net.ILog m_log
// = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
protected readonly ILLClientStackNetworkHandler m_networkHandler;
protected IScene m_scene;
/// <summary>
/// Tweakable user settings
/// </summary>
private ClientStackUserSettings m_userSettings;
public LLPacketServer(ILLClientStackNetworkHandler networkHandler, ClientStackUserSettings userSettings)
{
m_userSettings = userSettings;
m_networkHandler = networkHandler;
m_networkHandler.RegisterPacketServer(this);
}
public IScene LocalScene
{
set { m_scene = value; }
}
/// <summary>
/// Process an incoming packet.
/// </summary>
/// <param name="circuitCode"></param>
/// <param name="packet"></param>
public virtual void InPacket(uint circuitCode, Packet packet)
{
m_scene.ClientManager.InPacket(circuitCode, packet);
}
/// <summary>
/// Create a new client circuit
/// </summary>
/// <param name="remoteEP"></param>
/// <param name="scene"></param>
/// <param name="assetCache"></param>
/// <param name="packServer"></param>
/// <param name="sessionInfo"></param>
/// <param name="agentId"></param>
/// <param name="sessionId"></param>
/// <param name="circuitCode"></param>
/// <param name="proxyEP"></param>
/// <returns></returns>
protected virtual IClientAPI CreateNewCircuit(
EndPoint remoteEP, IScene scene,
LLPacketServer packServer, AuthenticateResponse sessionInfo,
UUID agentId, UUID sessionId, uint circuitCode, EndPoint proxyEP)
{
return
new LLClientView(
remoteEP, scene, packServer, sessionInfo, agentId, sessionId, circuitCode, proxyEP,
m_userSettings);
}
/// <summary>
/// Check whether a given client is authorized to connect.
/// </summary>
/// <param name="useCircuit"></param>
/// <param name="circuitManager"></param>
/// <param name="sessionInfo"></param>
/// <returns></returns>
public virtual bool IsClientAuthorized(
UseCircuitCodePacket useCircuit, AgentCircuitManager circuitManager, out AuthenticateResponse sessionInfo)
{
UUID agentId = useCircuit.CircuitCode.ID;
UUID sessionId = useCircuit.CircuitCode.SessionID;
uint circuitCode = useCircuit.CircuitCode.Code;
sessionInfo = circuitManager.AuthenticateSession(sessionId, agentId, circuitCode);
if (!sessionInfo.Authorised)
return false;
return true;
}
/// <summary>
/// Add a new client circuit. We assume that is has already passed an authorization check
/// </summary>
/// <param name="epSender"></param>
/// <param name="useCircuit"></param>
/// <param name="assetCache"></param>
/// <param name="sessionInfo"></param>
/// <param name="proxyEP"></param>
/// <returns>
/// true if a new circuit was created, false if a circuit with the given circuit code already existed
/// </returns>
public virtual bool AddNewClient(
EndPoint epSender, UseCircuitCodePacket useCircuit,
AuthenticateResponse sessionInfo, EndPoint proxyEP)
{
IClientAPI newuser;
uint circuitCode = useCircuit.CircuitCode.Code;
if (m_scene.ClientManager.TryGetClient(circuitCode, out newuser))
{
// The circuit is already known to the scene. This not actually a problem since this will currently
// occur if a client is crossing borders (hence upgrading its circuit). However, we shouldn't
// really by trying to add a new client if this is the case.
return false;
}
UUID agentId = useCircuit.CircuitCode.ID;
UUID sessionId = useCircuit.CircuitCode.SessionID;
newuser
= CreateNewCircuit(
epSender, m_scene, this, sessionInfo, agentId, sessionId, circuitCode, proxyEP);
m_scene.ClientManager.Add(circuitCode, newuser);
newuser.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler;
newuser.OnLogout += LogoutHandler;
newuser.OnConnectionClosed += CloseClient;
newuser.Start();
return true;
}
public void LogoutHandler(IClientAPI client)
{
client.SendLogoutPacket();
CloseClient(client);
}
/// <summary>
/// Send a packet to the given circuit
/// </summary>
/// <param name="buffer"></param>
/// <param name="size"></param>
/// <param name="flags"></param>
/// <param name="circuitcode"></param>
public virtual void SendPacketTo(byte[] buffer, int size, SocketFlags flags, uint circuitcode)
{
m_networkHandler.SendPacketTo(buffer, size, flags, circuitcode);
}
/// <summary>
/// Close a client circuit only
/// </summary>
/// <param name="circuitcode"></param>
public virtual void CloseCircuit(uint circuitcode)
{
m_networkHandler.RemoveClientCircuit(circuitcode);
}
/// <summary>
/// Completely close down the given client.
/// </summary>
/// <param name="client"></param>
public virtual void CloseClient(IClientAPI client)
{
//m_log.Info("PacketServer:CloseClient()");
CloseCircuit(client.CircuitCode);
m_scene.ClientManager.Remove(client.CircuitCode);
client.Close(false);
}
}
}

View File

@ -1,128 +0,0 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace OpenSim.Region.ClientStack.LindenUDP
{
public class LLPacketThrottle
{
private readonly int m_maxAllowableThrottle;
private readonly int m_minAllowableThrottle;
private int m_currentThrottle;
private const int m_throttleTimeDivisor = 7;
private int m_currentBitsSent;
private int m_throttleBits;
/// <value>
/// Value with which to multiply all the throttle fields
/// </value>
private float m_throttleMultiplier;
public int Max
{
get { return m_maxAllowableThrottle; }
}
public int Min
{
get { return m_minAllowableThrottle; }
}
public int Current
{
get { return m_currentThrottle; }
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
/// <param name="throttle"></param>
/// <param name="throttleMultiplier">
/// A parameter that's ends up multiplying all throttle settings. An alternative solution would have been
/// to multiply all the parameters by this before giving them to the constructor. But doing it this way
/// represents the fact that the multiplier is a hack that pumps data to clients much faster than the actual
/// settings that we are given.
/// </param>
public LLPacketThrottle(int min, int max, int throttle, float throttleMultiplier)
{
m_throttleMultiplier = throttleMultiplier;
m_maxAllowableThrottle = max;
m_minAllowableThrottle = min;
m_currentThrottle = throttle;
m_currentBitsSent = 0;
CalcBits();
}
/// <summary>
/// Calculate the actual throttle required.
/// </summary>
private void CalcBits()
{
m_throttleBits = (int)((float)m_currentThrottle * m_throttleMultiplier / (float)m_throttleTimeDivisor);
}
public void Reset()
{
m_currentBitsSent = 0;
}
public bool UnderLimit()
{
return m_currentBitsSent < m_throttleBits;
}
public int AddBytes(int bytes)
{
m_currentBitsSent += bytes * 8;
return m_currentBitsSent;
}
public int Throttle
{
get { return m_currentThrottle; }
set
{
if (value < m_minAllowableThrottle)
{
m_currentThrottle = m_minAllowableThrottle;
}
else if (value > m_maxAllowableThrottle)
{
m_currentThrottle = m_maxAllowableThrottle;
}
else
{
m_currentThrottle = value;
}
CalcBits();
}
}
}
}

View File

@ -0,0 +1,442 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Net;
using OpenSim.Framework;
using OpenMetaverse;
namespace OpenSim.Region.ClientStack.LindenUDP
{
#region Delegates
/// <summary>
/// Fired when updated networking stats are produced for this client
/// </summary>
/// <param name="inPackets">Number of incoming packets received since this
/// event was last fired</param>
/// <param name="outPackets">Number of outgoing packets sent since this
/// event was last fired</param>
/// <param name="unAckedBytes">Current total number of bytes in packets we
/// are waiting on ACKs for</param>
public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes);
/// <summary>
/// Fired when the queue for a packet category is empty. This event can be
/// hooked to put more data on the empty queue
/// </summary>
/// <param name="category">Category of the packet queue that is empty</param>
public delegate void QueueEmpty(ThrottleOutPacketType category);
#endregion Delegates
/// <summary>
/// Tracks state for a client UDP connection and provides client-specific methods
/// </summary>
public sealed class LLUDPClient
{
/// <summary>The number of packet categories to throttle on. If a throttle category is added
/// or removed, this number must also change</summary>
const int THROTTLE_CATEGORY_COUNT = 7;
/// <summary>Fired when updated networking stats are produced for this client</summary>
public event PacketStats OnPacketStats;
/// <summary>Fired when the queue for a packet category is empty. This event can be
/// hooked to put more data on the empty queue</summary>
public event QueueEmpty OnQueueEmpty;
/// <summary>AgentID for this client</summary>
public readonly UUID AgentID;
/// <summary>The remote address of the connected client</summary>
public readonly IPEndPoint RemoteEndPoint;
/// <summary>Circuit code that this client is connected on</summary>
public readonly uint CircuitCode;
/// <summary>Sequence numbers of packets we've received (for duplicate checking)</summary>
public readonly IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(200);
/// <summary>Packets we have sent that need to be ACKed by the client</summary>
public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection();
/// <summary>ACKs that are queued up, waiting to be sent to the client</summary>
public readonly LocklessQueue<uint> PendingAcks = new LocklessQueue<uint>();
/// <summary>Reference to the IClientAPI for this client</summary>
public LLClientView ClientAPI;
/// <summary>Current packet sequence number</summary>
public int CurrentSequence;
/// <summary>Current ping sequence number</summary>
public byte CurrentPingSequence;
/// <summary>True when this connection is alive, otherwise false</summary>
public bool IsConnected = true;
/// <summary>True when this connection is paused, otherwise false</summary>
public bool IsPaused = true;
/// <summary>Environment.TickCount when the last packet was received for this client</summary>
public int TickLastPacketReceived;
/// <summary>Timer granularity. This is set to the measured resolution of Environment.TickCount</summary>
public readonly float G;
/// <summary>Smoothed round-trip time. A smoothed average of the round-trip time for sending a
/// reliable packet to the client and receiving an ACK</summary>
public float SRTT;
/// <summary>Round-trip time variance. Measures the consistency of round-trip times</summary>
public float RTTVAR;
/// <summary>Retransmission timeout. Packets that have not been acknowledged in this number of
/// milliseconds or longer will be resent</summary>
/// <remarks>Calculated from <seealso cref="SRTT"/> and <seealso cref="RTTVAR"/> using the
/// guidelines in RFC 2988</remarks>
public int RTO;
/// <summary>Number of bytes received since the last acknowledgement was sent out. This is used
/// to loosely follow the TCP delayed ACK algorithm in RFC 1122 (4.2.3.2)</summary>
public int BytesSinceLastACK;
/// <summary>Number of packets received from this client</summary>
public int PacketsReceived;
/// <summary>Number of packets sent to this client</summary>
public int PacketsSent;
/// <summary>Total byte count of unacked packets sent to this client</summary>
public int UnackedBytes;
/// <summary>Total number of received packets that we have reported to the OnPacketStats event(s)</summary>
private int m_packetsReceivedReported;
/// <summary>Total number of sent packets that we have reported to the OnPacketStats event(s)</summary>
private int m_packetsSentReported;
/// <summary>Throttle bucket for this agent's connection</summary>
private readonly TokenBucket throttle;
/// <summary>Throttle buckets for each packet category</summary>
private readonly TokenBucket[] throttleCategories;
/// <summary>Throttle rate defaults and limits</summary>
private readonly ThrottleRates defaultThrottleRates;
/// <summary>Outgoing queues for throttled packets</summary>
private readonly LocklessQueue<OutgoingPacket>[] packetOutboxes = new LocklessQueue<OutgoingPacket>[THROTTLE_CATEGORY_COUNT];
/// <summary>A container that can hold one packet for each outbox, used to store
/// dequeued packets that are being held for throttling</summary>
private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
/// <summary>An optimization to store the length of dequeued packets being held
/// for throttling. This avoids expensive calls to Packet.Length</summary>
private readonly int[] nextPacketLengths = new int[THROTTLE_CATEGORY_COUNT];
/// <summary>A reference to the LLUDPServer that is managing this client</summary>
private readonly LLUDPServer udpServer;
/// <summary>
/// Default constructor
/// </summary>
/// <param name="server">Reference to the UDP server this client is connected to</param>
/// <param name="rates">Default throttling rates and maximum throttle limits</param>
/// <param name="parentThrottle">Parent HTB (hierarchical token bucket)
/// that the child throttles will be governed by</param>
/// <param name="circuitCode">Circuit code for this connection</param>
/// <param name="agentID">AgentID for the connected agent</param>
/// <param name="remoteEndPoint">Remote endpoint for this connection</param>
public LLUDPClient(LLUDPServer server, ThrottleRates rates, TokenBucket parentThrottle, uint circuitCode, UUID agentID, IPEndPoint remoteEndPoint)
{
udpServer = server;
AgentID = agentID;
RemoteEndPoint = remoteEndPoint;
CircuitCode = circuitCode;
defaultThrottleRates = rates;
for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
packetOutboxes[i] = new LocklessQueue<OutgoingPacket>();
throttle = new TokenBucket(parentThrottle, 0, 0);
throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
throttleCategories[(int)ThrottleOutPacketType.Resend] = new TokenBucket(throttle, rates.ResendLimit, rates.Resend);
throttleCategories[(int)ThrottleOutPacketType.Land] = new TokenBucket(throttle, rates.LandLimit, rates.Land);
throttleCategories[(int)ThrottleOutPacketType.Wind] = new TokenBucket(throttle, rates.WindLimit, rates.Wind);
throttleCategories[(int)ThrottleOutPacketType.Cloud] = new TokenBucket(throttle, rates.CloudLimit, rates.Cloud);
throttleCategories[(int)ThrottleOutPacketType.Task] = new TokenBucket(throttle, rates.TaskLimit, rates.Task);
throttleCategories[(int)ThrottleOutPacketType.Texture] = new TokenBucket(throttle, rates.TextureLimit, rates.Texture);
throttleCategories[(int)ThrottleOutPacketType.Asset] = new TokenBucket(throttle, rates.AssetLimit, rates.Asset);
// Set the granularity variable used for retransmission calculations to
// the measured resolution of Environment.TickCount
G = server.TickCountResolution;
// Default the retransmission timeout to three seconds
RTO = 3000;
}
/// <summary>
/// Shuts down this client connection
/// </summary>
public void Shutdown()
{
// TODO: Do we need to invalidate the circuit?
IsConnected = false;
}
/// <summary>
/// Gets information about this client connection
/// </summary>
/// <returns>Information about the client connection</returns>
public ClientInfo GetClientInfo()
{
// TODO: This data structure is wrong in so many ways. Locking and copying the entire lists
// of pending and needed ACKs for every client every time some method wants information about
// this connection is a recipe for poor performance
ClientInfo info = new ClientInfo();
info.pendingAcks = new Dictionary<uint, uint>();
info.needAck = new Dictionary<uint, byte[]>();
info.resendThrottle = throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate;
info.landThrottle = throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
info.windThrottle = throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
info.cloudThrottle = throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
info.taskThrottle = throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
info.assetThrottle = throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
info.textureThrottle = throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle +
info.taskThrottle + info.assetThrottle + info.textureThrottle;
return info;
}
/// <summary>
/// Modifies the UDP throttles
/// </summary>
/// <param name="info">New throttling values</param>
public void SetClientInfo(ClientInfo info)
{
// TODO: Allowing throttles to be manually set from this function seems like a reasonable
// idea. On the other hand, letting external code manipulate our ACK accounting is not
// going to happen
throw new NotImplementedException();
}
public string GetStats()
{
// TODO: ???
return string.Format("{0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}",
0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
public void SendPacketStats()
{
PacketStats callback = OnPacketStats;
if (callback != null)
{
int newPacketsReceived = PacketsReceived - m_packetsReceivedReported;
int newPacketsSent = PacketsSent - m_packetsSentReported;
callback(newPacketsReceived, newPacketsSent, UnackedBytes);
m_packetsReceivedReported += newPacketsReceived;
m_packetsSentReported += newPacketsSent;
}
}
public void SetThrottles(byte[] throttleData)
{
byte[] adjData;
int pos = 0;
if (!BitConverter.IsLittleEndian)
{
byte[] newData = new byte[7 * 4];
Buffer.BlockCopy(throttleData, 0, newData, 0, 7 * 4);
for (int i = 0; i < 7; i++)
Array.Reverse(newData, i * 4, 4);
adjData = newData;
}
else
{
adjData = throttleData;
}
int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
int cloud = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
resend = (resend <= defaultThrottleRates.ResendLimit) ? resend : defaultThrottleRates.ResendLimit;
land = (land <= defaultThrottleRates.LandLimit) ? land : defaultThrottleRates.LandLimit;
wind = (wind <= defaultThrottleRates.WindLimit) ? wind : defaultThrottleRates.WindLimit;
cloud = (cloud <= defaultThrottleRates.CloudLimit) ? cloud : defaultThrottleRates.CloudLimit;
task = (task <= defaultThrottleRates.TaskLimit) ? task : defaultThrottleRates.TaskLimit;
texture = (texture <= defaultThrottleRates.TextureLimit) ? texture : defaultThrottleRates.TextureLimit;
asset = (asset <= defaultThrottleRates.AssetLimit) ? asset : defaultThrottleRates.AssetLimit;
SetThrottle(ThrottleOutPacketType.Resend, resend);
SetThrottle(ThrottleOutPacketType.Land, land);
SetThrottle(ThrottleOutPacketType.Wind, wind);
SetThrottle(ThrottleOutPacketType.Cloud, cloud);
SetThrottle(ThrottleOutPacketType.Task, task);
SetThrottle(ThrottleOutPacketType.Texture, texture);
SetThrottle(ThrottleOutPacketType.Asset, asset);
}
public byte[] GetThrottlesPacked()
{
byte[] data = new byte[7 * 4];
int i = 0;
Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Task].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4;
Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4;
return data;
}
public void SetThrottle(ThrottleOutPacketType category, int rate)
{
int i = (int)category;
if (i >= 0 && i < throttleCategories.Length)
{
TokenBucket bucket = throttleCategories[(int)category];
bucket.MaxBurst = rate;
bucket.DripRate = rate;
}
}
public bool EnqueueOutgoing(OutgoingPacket packet)
{
int category = (int)packet.Category;
if (category >= 0 && category < packetOutboxes.Length)
{
LocklessQueue<OutgoingPacket> queue = packetOutboxes[category];
TokenBucket bucket = throttleCategories[category];
if (throttleCategories[category].RemoveTokens(packet.Buffer.DataLength))
{
// Enough tokens were removed from the bucket, the packet will not be queued
return false;
}
else
{
// Not enough tokens in the bucket, queue this packet
queue.Enqueue(packet);
return true;
}
}
else
{
// We don't have a token bucket for this category, so it will not be queued
return false;
}
}
/// <summary>
/// Loops through all of the packet queues for this client and tries to send
/// any outgoing packets, obeying the throttling bucket limits
/// </summary>
/// <remarks>This function is only called from a synchronous loop in the
/// UDPServer so we don't need to bother making this thread safe</remarks>
/// <returns>True if any packets were sent, otherwise false</returns>
public bool DequeueOutgoing()
{
OutgoingPacket packet;
LocklessQueue<OutgoingPacket> queue;
TokenBucket bucket;
bool packetSent = false;
for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
{
bucket = throttleCategories[i];
if (nextPackets[i] != null)
{
// This bucket was empty the last time we tried to send a packet,
// leaving a dequeued packet still waiting to be sent out. Try to
// send it again
if (bucket.RemoveTokens(nextPacketLengths[i]))
{
// Send the packet
udpServer.SendPacketFinal(nextPackets[i]);
nextPackets[i] = null;
packetSent = true;
}
}
else
{
// No dequeued packet waiting to be sent, try to pull one off
// this queue
queue = packetOutboxes[i];
if (queue.Dequeue(out packet))
{
// A packet was pulled off the queue. See if we have
// enough tokens in the bucket to send it out
if (bucket.RemoveTokens(packet.Buffer.DataLength))
{
// Send the packet
udpServer.SendPacketFinal(packet);
packetSent = true;
}
else
{
// Save the dequeued packet and the length calculation for
// the next iteration
nextPackets[i] = packet;
nextPacketLengths[i] = packet.Buffer.DataLength;
}
}
else
{
// No packets in this queue. Fire the queue empty callback
QueueEmpty callback = OnQueueEmpty;
if (callback != null)
callback((ThrottleOutPacketType)i);
}
}
}
return packetSent;
}
public void UpdateRoundTrip(float r)
{
const float ALPHA = 0.125f;
const float BETA = 0.25f;
const float K = 4.0f;
if (RTTVAR == 0.0f)
{
// First RTT measurement
SRTT = r;
RTTVAR = r * 0.5f;
}
else
{
// Subsequence RTT measurement
RTTVAR = (1.0f - BETA) * RTTVAR + BETA * Math.Abs(SRTT - r);
SRTT = (1.0f - ALPHA) * SRTT + ALPHA * r;
}
// Always round retransmission timeout up to two seconds
RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR)));
//Logger.Debug("Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " +
// RTTVAR + " based on new RTT of " + r + "ms");
}
}
}

View File

@ -0,0 +1,282 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Net;
using OpenSim.Framework;
using OpenMetaverse;
using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim;
namespace OpenSim.Region.ClientStack.LindenUDP
{
public sealed class UDPClientCollection
{
Dictionary<UUID, LLUDPClient> Dictionary1;
Dictionary<IPEndPoint, LLUDPClient> Dictionary2;
LLUDPClient[] Array;
ReaderWriterLockImpl rwLock = new ReaderWriterLockImpl();
object m_sync = new object();
public UDPClientCollection()
{
Dictionary1 = new Dictionary<UUID, LLUDPClient>();
Dictionary2 = new Dictionary<IPEndPoint, LLUDPClient>();
Array = new LLUDPClient[0];
}
public UDPClientCollection(int capacity)
{
Dictionary1 = new Dictionary<UUID, LLUDPClient>(capacity);
Dictionary2 = new Dictionary<IPEndPoint, LLUDPClient>(capacity);
Array = new LLUDPClient[0];
}
public void Add(UUID key1, IPEndPoint key2, LLUDPClient value)
{
//rwLock.EnterWriteLock();
//try
//{
// if (Dictionary1.ContainsKey(key1))
// {
// if (!Dictionary2.ContainsKey(key2))
// throw new ArgumentException("key1 exists in the dictionary but not key2");
// }
// else if (Dictionary2.ContainsKey(key2))
// {
// if (!Dictionary1.ContainsKey(key1))
// throw new ArgumentException("key2 exists in the dictionary but not key1");
// }
// Dictionary1[key1] = value;
// Dictionary2[key2] = value;
// LLUDPClient[] oldArray = Array;
// int oldLength = oldArray.Length;
// LLUDPClient[] newArray = new LLUDPClient[oldLength + 1];
// for (int i = 0; i < oldLength; i++)
// newArray[i] = oldArray[i];
// newArray[oldLength] = value;
// Array = newArray;
//}
//finally { rwLock.ExitWriteLock(); }
lock (m_sync)
{
if (Dictionary1.ContainsKey(key1))
{
if (!Dictionary2.ContainsKey(key2))
throw new ArgumentException("key1 exists in the dictionary but not key2");
}
else if (Dictionary2.ContainsKey(key2))
{
if (!Dictionary1.ContainsKey(key1))
throw new ArgumentException("key2 exists in the dictionary but not key1");
}
Dictionary1[key1] = value;
Dictionary2[key2] = value;
LLUDPClient[] oldArray = Array;
int oldLength = oldArray.Length;
LLUDPClient[] newArray = new LLUDPClient[oldLength + 1];
for (int i = 0; i < oldLength; i++)
newArray[i] = oldArray[i];
newArray[oldLength] = value;
Array = newArray;
}
}
public bool Remove(UUID key1, IPEndPoint key2)
{
//rwLock.EnterWriteLock();
//try
//{
// LLUDPClient value;
// if (Dictionary1.TryGetValue(key1, out value))
// {
// Dictionary1.Remove(key1);
// Dictionary2.Remove(key2);
// LLUDPClient[] oldArray = Array;
// int oldLength = oldArray.Length;
// LLUDPClient[] newArray = new LLUDPClient[oldLength - 1];
// int j = 0;
// for (int i = 0; i < oldLength; i++)
// {
// if (oldArray[i] != value)
// newArray[j++] = oldArray[i];
// }
// Array = newArray;
// return true;
// }
//}
//finally { rwLock.ExitWriteLock(); }
//return false;
lock (m_sync)
{
LLUDPClient value;
if (Dictionary1.TryGetValue(key1, out value))
{
Dictionary1.Remove(key1);
Dictionary2.Remove(key2);
LLUDPClient[] oldArray = Array;
int oldLength = oldArray.Length;
LLUDPClient[] newArray = new LLUDPClient[oldLength - 1];
int j = 0;
for (int i = 0; i < oldLength; i++)
{
if (oldArray[i] != value)
newArray[j++] = oldArray[i];
}
Array = newArray;
return true;
}
}
return false;
}
public void Clear()
{
//rwLock.EnterWriteLock();
//try
//{
// Dictionary1.Clear();
// Dictionary2.Clear();
// Array = new LLUDPClient[0];
//}
//finally { rwLock.ExitWriteLock(); }
lock (m_sync)
{
Dictionary1.Clear();
Dictionary2.Clear();
Array = new LLUDPClient[0];
}
}
public int Count
{
get { return Array.Length; }
}
public bool ContainsKey(UUID key)
{
return Dictionary1.ContainsKey(key);
}
public bool ContainsKey(IPEndPoint key)
{
return Dictionary2.ContainsKey(key);
}
public bool TryGetValue(UUID key, out LLUDPClient value)
{
////bool success;
////bool doLock = !rwLock.IsUpgradeableReadLockHeld;
////if (doLock) rwLock.EnterReadLock();
////try { success = Dictionary1.TryGetValue(key, out value); }
////finally { if (doLock) rwLock.ExitReadLock(); }
////return success;
//lock (m_sync)
// return Dictionary1.TryGetValue(key, out value);
try
{
return Dictionary1.TryGetValue(key, out value);
}
catch { }
value = null;
return false;
}
public bool TryGetValue(IPEndPoint key, out LLUDPClient value)
{
////bool success;
////bool doLock = !rwLock.IsUpgradeableReadLockHeld;
////if (doLock) rwLock.EnterReadLock();
////try { success = Dictionary2.TryGetValue(key, out value); }
////finally { if (doLock) rwLock.ExitReadLock(); }
////return success;
lock (m_sync)
return Dictionary2.TryGetValue(key, out value);
//try
//{
// return Dictionary2.TryGetValue(key, out value);
//}
//catch { }
//value = null;
//return false;
}
public void ForEach(Action<LLUDPClient> action)
{
//bool doLock = !rwLock.IsUpgradeableReadLockHeld;
//if (doLock) rwLock.EnterUpgradeableReadLock();
//try { Parallel.ForEach<LLUDPClient>(Array, action); }
//finally { if (doLock) rwLock.ExitUpgradeableReadLock(); }
LLUDPClient[] localArray = null;
lock (m_sync)
{
localArray = new LLUDPClient[Array.Length];
Array.CopyTo(localArray, 0);
}
Parallel.ForEach<LLUDPClient>(localArray, action);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* /*
* Copyright (c) Contributors, http://opensimulator.org/ * Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders. * See CONTRIBUTORS.TXT for a full list of copyright holders.
* *
@ -25,27 +25,46 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
using System;
using OpenSim.Framework;
using OpenMetaverse; using OpenMetaverse;
namespace OpenSim.Region.ClientStack.LindenUDP namespace OpenSim.Region.ClientStack.LindenUDP
{ {
public class LLUtil
{
/// <summary> /// <summary>
/// Convert a string to bytes suitable for use in an LL UDP packet. /// Holds a reference to the <seealso cref="LLUDPClient"/> this packet is
/// destined for, along with the serialized packet data, sequence number
/// (if this is a resend), number of times this packet has been resent,
/// the time of the last resend, and the throttling category for this
/// packet
/// </summary> /// </summary>
/// <param name="s">Truncated to 254 characters if necessary</param> public sealed class OutgoingPacket
/// <returns></returns>
public static byte[] StringToPacketBytes(string s)
{ {
// Anything more than 254 will cause libsecondlife to barf /// <summary>Client this packet is destined for</summary>
// (libsl 1550) adds an \0 on the Utils.StringToBytes conversion if it isn't present public LLUDPClient Client;
if (s.Length > 254) /// <summary>Packet data to send</summary>
{ public UDPPacketBuffer Buffer;
s = s.Remove(254); /// <summary>Sequence number of the wrapped packet</summary>
} public uint SequenceNumber;
/// <summary>Number of times this packet has been resent</summary>
public int ResendCount;
/// <summary>Environment.TickCount when this packet was last sent over the wire</summary>
public int TickCount;
/// <summary>Category this packet belongs to</summary>
public ThrottleOutPacketType Category;
return Utils.StringToBytes(s); /// <summary>
/// Default constructor
/// </summary>
/// <param name="client">Reference to the client this packet is destined for</param>
/// <param name="buffer">Serialized packet data. If the flags or sequence number
/// need to be updated, they will be injected directly into this binary buffer</param>
/// <param name="category">Throttling category for this packet</param>
public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category)
{
Client = client;
Buffer = buffer;
Category = category;
} }
} }
} }

View File

@ -70,7 +70,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
TestClient testClient = new TestClient(agent, scene); TestClient testClient = new TestClient(agent, scene);
ILLPacketHandler packetHandler LLPacketHandler packetHandler
= new LLPacketHandler(testClient, testLLPacketServer, new ClientStackUserSettings()); = new LLPacketHandler(testClient, testLLPacketServer, new ClientStackUserSettings());
packetHandler.InPacket(new AgentAnimationPacket()); packetHandler.InPacket(new AgentAnimationPacket());

View File

@ -37,7 +37,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
/// </summary> /// </summary>
protected Dictionary<PacketType, int> m_packetsReceived = new Dictionary<PacketType, int>(); protected Dictionary<PacketType, int> m_packetsReceived = new Dictionary<PacketType, int>();
public TestLLPacketServer(ILLClientStackNetworkHandler networkHandler, ClientStackUserSettings userSettings) public TestLLPacketServer(LLUDPServer networkHandler, ClientStackUserSettings userSettings)
: base(networkHandler, userSettings) : base(networkHandler, userSettings)
{} {}

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using Nini.Config;
namespace OpenSim.Region.ClientStack.LindenUDP
{
/// <summary>
/// Holds drip rates and maximum burst rates for throttling with hierarchical
/// token buckets. The maximum burst rates set here are hard limits and can
/// not be overridden by client requests
/// </summary>
public sealed class ThrottleRates
{
/// <summary>Drip rate for resent packets</summary>
public int Resend;
/// <summary>Drip rate for terrain packets</summary>
public int Land;
/// <summary>Drip rate for wind packets</summary>
public int Wind;
/// <summary>Drip rate for cloud packets</summary>
public int Cloud;
/// <summary>Drip rate for task (state and transaction) packets</summary>
public int Task;
/// <summary>Drip rate for texture packets</summary>
public int Texture;
/// <summary>Drip rate for asset packets</summary>
public int Asset;
/// <summary>Maximum burst rate for resent packets</summary>
public int ResendLimit;
/// <summary>Maximum burst rate for land packets</summary>
public int LandLimit;
/// <summary>Maximum burst rate for wind packets</summary>
public int WindLimit;
/// <summary>Maximum burst rate for cloud packets</summary>
public int CloudLimit;
/// <summary>Maximum burst rate for task (state and transaction) packets</summary>
public int TaskLimit;
/// <summary>Maximum burst rate for texture packets</summary>
public int TextureLimit;
/// <summary>Maximum burst rate for asset packets</summary>
public int AssetLimit;
/// <summary>
/// Default constructor
/// </summary>
/// <param name="config">Config source to load defaults from</param>
public ThrottleRates(IConfigSource config)
{
try
{
IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"];
Resend = throttleConfig.GetInt("ResendDefault", 12500);
Land = throttleConfig.GetInt("LandDefault", 500);
Wind = throttleConfig.GetInt("WindDefault", 500);
Cloud = throttleConfig.GetInt("CloudDefault", 500);
Task = throttleConfig.GetInt("TaskDefault", 500);
Texture = throttleConfig.GetInt("TextureDefault", 500);
Asset = throttleConfig.GetInt("AssetDefault", 500);
ResendLimit = throttleConfig.GetInt("ResendLimit", 18750);
LandLimit = throttleConfig.GetInt("LandLimit", 29750);
WindLimit = throttleConfig.GetInt("WindLimit", 18750);
CloudLimit = throttleConfig.GetInt("CloudLimit", 18750);
TaskLimit = throttleConfig.GetInt("TaskLimit", 55750);
TextureLimit = throttleConfig.GetInt("TextureLimit", 55750);
AssetLimit = throttleConfig.GetInt("AssetLimit", 27500);
}
catch (Exception) { }
}
}
}

View File

@ -0,0 +1,160 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Net;
using OpenMetaverse;
namespace OpenSim.Region.ClientStack.LindenUDP
{
/// <summary>
/// Special collection that is optimized for tracking unacknowledged packets
/// </summary>
public sealed class UnackedPacketCollection
{
/// <summary>Synchronization primitive. A lock must be acquired on this
/// object before calling any of the unsafe methods</summary>
public object SyncRoot = new object();
/// <summary>Holds the actual unacked packet data, sorted by sequence number</summary>
private SortedDictionary<uint, OutgoingPacket> packets = new SortedDictionary<uint, OutgoingPacket>();
/// <summary>Gets the total number of unacked packets</summary>
public int Count { get { return packets.Count; } }
/// <summary>
/// Default constructor
/// </summary>
public UnackedPacketCollection()
{
}
/// <summary>
/// Add an unacked packet to the collection
/// </summary>
/// <param name="packet">Packet that is awaiting acknowledgement</param>
/// <returns>True if the packet was successfully added, false if the
/// packet already existed in the collection</returns>
public bool Add(OutgoingPacket packet)
{
lock (SyncRoot)
{
if (!packets.ContainsKey(packet.SequenceNumber))
{
packets.Add(packet.SequenceNumber, packet);
return true;
}
return false;
}
}
/// <summary>
/// Removes a packet from the collection without attempting to obtain a
/// lock first
/// </summary>
/// <param name="sequenceNumber">Sequence number of the packet to remove</param>
/// <returns>True if the packet was found and removed, otherwise false</returns>
public bool RemoveUnsafe(uint sequenceNumber)
{
return packets.Remove(sequenceNumber);
}
/// <summary>
/// Removes a packet from the collection without attempting to obtain a
/// lock first
/// </summary>
/// <param name="sequenceNumber">Sequence number of the packet to remove</param>
/// <param name="packet">Returns the removed packet</param>
/// <returns>True if the packet was found and removed, otherwise false</returns>
public bool RemoveUnsafe(uint sequenceNumber, out OutgoingPacket packet)
{
if (packets.TryGetValue(sequenceNumber, out packet))
{
packets.Remove(sequenceNumber);
return true;
}
return false;
}
/// <summary>
/// Gets the packet with the lowest sequence number
/// </summary>
/// <returns>The packet with the lowest sequence number, or null if the
/// collection is empty</returns>
public OutgoingPacket GetOldest()
{
lock (SyncRoot)
{
using (SortedDictionary<uint, OutgoingPacket>.ValueCollection.Enumerator e = packets.Values.GetEnumerator())
{
if (e.MoveNext())
return e.Current;
else
return null;
}
}
}
/// <summary>
/// Returns a list of all of the packets with a TickCount older than
/// the specified timeout
/// </summary>
/// <param name="timeoutMS">Number of ticks (milliseconds) before a
/// packet is considered expired</param>
/// <returns>A list of all expired packets according to the given
/// expiration timeout</returns>
public List<OutgoingPacket> GetExpiredPackets(int timeoutMS)
{
List<OutgoingPacket> expiredPackets = null;
lock (SyncRoot)
{
int now = Environment.TickCount;
foreach (OutgoingPacket packet in packets.Values)
{
if (packet.TickCount == 0)
continue;
if (now - packet.TickCount >= timeoutMS)
{
if (expiredPackets == null)
expiredPackets = new List<OutgoingPacket>();
expiredPackets.Add(packet);
}
else
{
break;
}
}
}
return expiredPackets;
}
}
}

View File

@ -323,7 +323,6 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
httpThread.IsBackground = true; httpThread.IsBackground = true;
_finished = false; _finished = false;
httpThread.Start(); httpThread.Start();
ThreadTracker.Add(httpThread);
} }
/* /*

View File

@ -639,7 +639,6 @@ namespace OpenSim.Region.CoreModules.Scripting.XMLRPC
httpThread.IsBackground = true; httpThread.IsBackground = true;
_finished = false; _finished = false;
httpThread.Start(); httpThread.Start();
ThreadTracker.Add(httpThread);
} }
/* /*

View File

@ -98,7 +98,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsIn.Asset
m_log.Info("[RegionAssetService]: Starting..."); m_log.Info("[RegionAssetService]: Starting...");
Object[] args = new Object[] { m_Config, MainServer.Instance }; Object[] args = new Object[] { m_Config, MainServer.Instance, string.Empty };
ServerUtils.LoadPlugin<IServiceConnector>("OpenSim.Server.Handlers.dll:AssetServiceConnector", args); ServerUtils.LoadPlugin<IServiceConnector>("OpenSim.Server.Handlers.dll:AssetServiceConnector", args);
} }

View File

@ -98,7 +98,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsIn.Inventory
m_log.Info("[RegionInventoryService]: Starting..."); m_log.Info("[RegionInventoryService]: Starting...");
Object[] args = new Object[] { m_Config, MainServer.Instance }; Object[] args = new Object[] { m_Config, MainServer.Instance, String.Empty };
ServerUtils.LoadPlugin<IServiceConnector>("OpenSim.Server.Handlers.dll:InventoryServiceInConnector", args); ServerUtils.LoadPlugin<IServiceConnector>("OpenSim.Server.Handlers.dll:InventoryServiceInConnector", args);
} }

View File

@ -61,6 +61,10 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
private void OnRegionUp(GridRegion otherRegion) private void OnRegionUp(GridRegion otherRegion)
{ {
// This shouldn't happen
if (otherRegion == null)
return;
m_log.DebugFormat("[REGION CACHE]: (on region {0}) Region {1} is up @ {2}-{3}", m_log.DebugFormat("[REGION CACHE]: (on region {0}) Region {1} is up @ {2}-{3}",
m_scene.RegionInfo.RegionName, otherRegion.RegionName, otherRegion.RegionLocX, otherRegion.RegionLocY); m_scene.RegionInfo.RegionName, otherRegion.RegionName, otherRegion.RegionLocX, otherRegion.RegionLocY);

View File

@ -345,7 +345,6 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
mapItemReqThread.Priority = ThreadPriority.BelowNormal; mapItemReqThread.Priority = ThreadPriority.BelowNormal;
mapItemReqThread.SetApartmentState(ApartmentState.MTA); mapItemReqThread.SetApartmentState(ApartmentState.MTA);
mapItemReqThread.Start(); mapItemReqThread.Start();
ThreadTracker.Add(mapItemReqThread);
} }
/// <summary> /// <summary>
@ -447,7 +446,6 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
// end gracefully // end gracefully
if (st.agentID == UUID.Zero) if (st.agentID == UUID.Zero)
{ {
ThreadTracker.Remove(mapItemReqThread);
break; break;
} }

View File

@ -1168,6 +1168,7 @@ namespace OpenSim.Region.Framework.Scenes
private void SendInventoryUpdate(IClientAPI client, InventoryFolderBase folder, bool fetchFolders, bool fetchItems) private void SendInventoryUpdate(IClientAPI client, InventoryFolderBase folder, bool fetchFolders, bool fetchItems)
{ {
m_log.DebugFormat("[AGENT INVENTORY]: Send Inventory Folder {0} Update to {1} {2}", folder.Name, client.FirstName, client.LastName);
InventoryCollection contents = InventoryService.GetFolderContent(client.AgentId, folder.ID); InventoryCollection contents = InventoryService.GetFolderContent(client.AgentId, folder.ID);
client.SendInventoryFolderDetails(client.AgentId, folder.ID, contents.Items, contents.Folders, fetchFolders, fetchItems); client.SendInventoryFolderDetails(client.AgentId, folder.ID, contents.Items, contents.Folders, fetchFolders, fetchItems);
} }

View File

@ -477,9 +477,9 @@ namespace OpenSim.Region.Framework.Scenes
public InventoryCollection HandleFetchInventoryDescendentsCAPS(UUID agentID, UUID folderID, UUID ownerID, public InventoryCollection HandleFetchInventoryDescendentsCAPS(UUID agentID, UUID folderID, UUID ownerID,
bool fetchFolders, bool fetchItems, int sortOrder, out int version) bool fetchFolders, bool fetchItems, int sortOrder, out int version)
{ {
// m_log.DebugFormat( m_log.DebugFormat(
// "[INVENTORY CACHE]: Fetching folders ({0}), items ({1}) from {2} for agent {3}", "[INVENTORY CACHE]: Fetching folders ({0}), items ({1}) from {2} for agent {3}",
// fetchFolders, fetchItems, folderID, agentID); fetchFolders, fetchItems, folderID, agentID);
// FIXME MAYBE: We're not handling sortOrder! // FIXME MAYBE: We're not handling sortOrder!
@ -497,10 +497,11 @@ namespace OpenSim.Region.Framework.Scenes
return ret; return ret;
} }
InventoryCollection contents = InventoryService.GetFolderContent(agentID, folderID); InventoryCollection contents = new InventoryCollection();
if (folderID != UUID.Zero) if (folderID != UUID.Zero)
{ {
contents = InventoryService.GetFolderContent(agentID, folderID);
InventoryFolderBase containingFolder = new InventoryFolderBase(); InventoryFolderBase containingFolder = new InventoryFolderBase();
containingFolder.ID = folderID; containingFolder.ID = folderID;
containingFolder.Owner = agentID; containingFolder.Owner = agentID;

View File

@ -903,7 +903,6 @@ namespace OpenSim.Region.Framework.Scenes
//m_heartbeatTimer.Elapsed += new ElapsedEventHandler(Heartbeat); //m_heartbeatTimer.Elapsed += new ElapsedEventHandler(Heartbeat);
if (HeartbeatThread != null) if (HeartbeatThread != null)
{ {
ThreadTracker.Remove(HeartbeatThread);
HeartbeatThread.Abort(); HeartbeatThread.Abort();
HeartbeatThread = null; HeartbeatThread = null;
} }
@ -912,7 +911,6 @@ namespace OpenSim.Region.Framework.Scenes
HeartbeatThread.SetApartmentState(ApartmentState.MTA); HeartbeatThread.SetApartmentState(ApartmentState.MTA);
HeartbeatThread.Name = string.Format("Heartbeat for region {0}", RegionInfo.RegionName); HeartbeatThread.Name = string.Format("Heartbeat for region {0}", RegionInfo.RegionName);
HeartbeatThread.Priority = ThreadPriority.AboveNormal; HeartbeatThread.Priority = ThreadPriority.AboveNormal;
ThreadTracker.Add(HeartbeatThread);
HeartbeatThread.Start(); HeartbeatThread.Start();
} }
@ -1448,6 +1446,9 @@ namespace OpenSim.Region.Framework.Scenes
m_log.Info("[SCENE]: Loading objects from datastore"); m_log.Info("[SCENE]: Loading objects from datastore");
List<SceneObjectGroup> PrimsFromDB = m_storageManager.DataStore.LoadObjects(regionID); List<SceneObjectGroup> PrimsFromDB = m_storageManager.DataStore.LoadObjects(regionID);
m_log.Info("[SCENE]: Loaded " + PrimsFromDB.Count + " objects from the datastore");
foreach (SceneObjectGroup group in PrimsFromDB) foreach (SceneObjectGroup group in PrimsFromDB)
{ {
if (group.RootPart == null) if (group.RootPart == null)

View File

@ -360,7 +360,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
m_listener.Name = "IRCConnectorListenerThread"; m_listener.Name = "IRCConnectorListenerThread";
m_listener.IsBackground = true; m_listener.IsBackground = true;
m_listener.Start(); m_listener.Start();
ThreadTracker.Add(m_listener);
// This is the message order recommended by RFC 2812 // This is the message order recommended by RFC 2812
if (m_password != null) if (m_password != null)

View File

@ -152,7 +152,6 @@ namespace OpenSim.Region.OptionalModules.ContentManagement
m_thread.Name = "Content Management"; m_thread.Name = "Content Management";
m_thread.IsBackground = true; m_thread.IsBackground = true;
m_thread.Start(); m_thread.Start();
ThreadTracker.Add(m_thread);
m_state = State.NONE; m_state = State.NONE;
} }
} }

View File

@ -138,7 +138,6 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
EventQueueThread.Priority = MyThreadPriority; EventQueueThread.Priority = MyThreadPriority;
EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount; EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount;
EventQueueThread.Start(); EventQueueThread.Start();
ThreadTracker.Add(EventQueueThread);
// Look at this... Don't you wish everyone did that solid // Look at this... Don't you wish everyone did that solid
// coding everywhere? :P // coding everywhere? :P

View File

@ -97,7 +97,6 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
MaintenanceThreadThread.Name = "ScriptMaintenanceThread"; MaintenanceThreadThread.Name = "ScriptMaintenanceThread";
MaintenanceThreadThread.IsBackground = true; MaintenanceThreadThread.IsBackground = true;
MaintenanceThreadThread.Start(); MaintenanceThreadThread.Start();
ThreadTracker.Add(MaintenanceThreadThread);
} }
} }

View File

@ -129,10 +129,6 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
public void AddRegion(Scene Sceneworld) public void AddRegion(Scene Sceneworld)
{ {
m_log.Info("[" + ScriptEngineName + "]: ScriptEngine initializing");
m_Scene = Sceneworld;
// Make sure we have config // Make sure we have config
if (ConfigSource.Configs[ScriptEngineName] == null) if (ConfigSource.Configs[ScriptEngineName] == null)
ConfigSource.AddConfig(ScriptEngineName); ConfigSource.AddConfig(ScriptEngineName);
@ -143,6 +139,10 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
if (!m_enabled) if (!m_enabled)
return; return;
m_log.Info("[" + ScriptEngineName + "]: ScriptEngine initializing");
m_Scene = Sceneworld;
// Create all objects we'll be using // Create all objects we'll be using
m_EventQueueManager = new EventQueueManager(this); m_EventQueueManager = new EventQueueManager(this);
m_EventManager = new EventManager(this, true); m_EventManager = new EventManager(this, true);

View File

@ -142,7 +142,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
cmdHandlerThread.Priority = ThreadPriority.BelowNormal; cmdHandlerThread.Priority = ThreadPriority.BelowNormal;
cmdHandlerThread.IsBackground = true; cmdHandlerThread.IsBackground = true;
cmdHandlerThread.Start(); cmdHandlerThread.Start();
ThreadTracker.Add(cmdHandlerThread);
} }
} }

View File

@ -260,7 +260,7 @@ namespace OpenSim.Server.Base
public static Dictionary<string, object> ParseXmlResponse(string data) public static Dictionary<string, object> ParseXmlResponse(string data)
{ {
m_log.DebugFormat("[XXX]: received xml string: {0}", data); //m_log.DebugFormat("[XXX]: received xml string: {0}", data);
Dictionary<string, object> ret = new Dictionary<string, object>(); Dictionary<string, object> ret = new Dictionary<string, object>();

View File

@ -77,6 +77,7 @@ namespace OpenSim.Server.Handlers.Inventory
m_doLookup = serverConfig.GetBoolean("SessionAuthentication", false); m_doLookup = serverConfig.GetBoolean("SessionAuthentication", false);
AddHttpHandlers(server); AddHttpHandlers(server);
m_log.Debug("[INVENTORY HANDLER]: handlers initialized");
} }
protected virtual void AddHttpHandlers(IHttpServer m_httpServer) protected virtual void AddHttpHandlers(IHttpServer m_httpServer)

View File

@ -99,7 +99,7 @@ namespace OpenSim.Services.Connectors
sendData["METHOD"] = "register"; sendData["METHOD"] = "register";
string reqString = ServerUtils.BuildQueryString(sendData); string reqString = ServerUtils.BuildQueryString(sendData);
//m_log.DebugFormat("[GRID CONNECTOR]: queryString = {0}", reqString); // m_log.DebugFormat("[GRID CONNECTOR]: queryString = {0}", reqString);
try try
{ {
string reply = SynchronousRestFormsRequester.MakeRequest("POST", string reply = SynchronousRestFormsRequester.MakeRequest("POST",

View File

@ -416,13 +416,6 @@ namespace OpenSim.Services.Connectors
e.Source, e.Message); e.Source, e.Message);
} }
foreach (InventoryItemBase item in items)
{
InventoryItemBase itm = this.QueryItem(userID, item, sessionID);
itm.Name = item.Name;
itm.Folder = item.Folder;
this.UpdateItem(userID, itm, sessionID);
}
} }
private void MoveItemsCompleted(IAsyncResult iar) private void MoveItemsCompleted(IAsyncResult iar)

View File

@ -76,10 +76,12 @@ namespace OpenSim.Services.Connectors
// Don't remote-call this instance; that's a startup hickup // Don't remote-call this instance; that's a startup hickup
!((regInfo.ExternalHostName == thisRegion.ExternalHostName) && (regInfo.HttpPort == thisRegion.HttpPort))) !((regInfo.ExternalHostName == thisRegion.ExternalHostName) && (regInfo.HttpPort == thisRegion.HttpPort)))
{ {
DoHelloNeighbourCall(regInfo, thisRegion); if (!DoHelloNeighbourCall(regInfo, thisRegion))
return null;
} }
//else else
// m_log.Warn("[REST COMMS]: Region not found " + regionHandle); return null;
return regInfo; return regInfo;
} }
@ -102,6 +104,7 @@ namespace OpenSim.Services.Connectors
catch (Exception e) catch (Exception e)
{ {
m_log.Debug("[REST COMMS]: PackRegionInfoData failed with exception: " + e.Message); m_log.Debug("[REST COMMS]: PackRegionInfoData failed with exception: " + e.Message);
return false;
} }
// Add the regionhandle of the destination region // Add the regionhandle of the destination region
args["destination_handle"] = OSD.FromString(region.RegionHandle.ToString()); args["destination_handle"] = OSD.FromString(region.RegionHandle.ToString());
@ -118,7 +121,7 @@ namespace OpenSim.Services.Connectors
catch (Exception e) catch (Exception e)
{ {
m_log.WarnFormat("[REST COMMS]: Exception thrown on serialization of HelloNeighbour: {0}", e.Message); m_log.WarnFormat("[REST COMMS]: Exception thrown on serialization of HelloNeighbour: {0}", e.Message);
// ignore. buffer will be empty, caller should check. return false;
} }
Stream os = null; Stream os = null;
@ -127,20 +130,23 @@ namespace OpenSim.Services.Connectors
HelloNeighbourRequest.ContentLength = buffer.Length; //Count bytes to send HelloNeighbourRequest.ContentLength = buffer.Length; //Count bytes to send
os = HelloNeighbourRequest.GetRequestStream(); os = HelloNeighbourRequest.GetRequestStream();
os.Write(buffer, 0, strBuffer.Length); //Send it os.Write(buffer, 0, strBuffer.Length); //Send it
os.Close();
//m_log.InfoFormat("[REST COMMS]: Posted HelloNeighbour request to remote sim {0}", uri); //m_log.InfoFormat("[REST COMMS]: Posted HelloNeighbour request to remote sim {0}", uri);
} }
//catch (WebException ex) catch (Exception ex)
catch
{ {
//m_log.InfoFormat("[REST COMMS]: Bad send on HelloNeighbour {0}", ex.Message); m_log.InfoFormat("[REST COMMS]: Unable to send HelloNeighbour to {0}: {1}", region.RegionName, ex.Message);
return false; return false;
} }
finally
{
if (os != null)
os.Close();
}
// Let's wait for the response // Let's wait for the response
//m_log.Info("[REST COMMS]: Waiting for a reply after DoHelloNeighbourCall"); //m_log.Info("[REST COMMS]: Waiting for a reply after DoHelloNeighbourCall");
StreamReader sr = null;
try try
{ {
WebResponse webResponse = HelloNeighbourRequest.GetResponse(); WebResponse webResponse = HelloNeighbourRequest.GetResponse();
@ -149,17 +155,21 @@ namespace OpenSim.Services.Connectors
m_log.Info("[REST COMMS]: Null reply on DoHelloNeighbourCall post"); m_log.Info("[REST COMMS]: Null reply on DoHelloNeighbourCall post");
} }
StreamReader sr = new StreamReader(webResponse.GetResponseStream()); sr = new StreamReader(webResponse.GetResponseStream());
//reply = sr.ReadToEnd().Trim(); //reply = sr.ReadToEnd().Trim();
sr.ReadToEnd().Trim(); sr.ReadToEnd().Trim();
sr.Close();
//m_log.InfoFormat("[REST COMMS]: DoHelloNeighbourCall reply was {0} ", reply); //m_log.InfoFormat("[REST COMMS]: DoHelloNeighbourCall reply was {0} ", reply);
} }
catch (WebException ex) catch (Exception ex)
{ {
m_log.InfoFormat("[REST COMMS]: exception on reply of DoHelloNeighbourCall {0}", ex.Message); m_log.InfoFormat("[REST COMMS]: exception on reply of DoHelloNeighbourCall {0}", ex.Message);
// ignore, really return false;
}
finally
{
if (sr != null)
sr.Close();
} }
return true; return true;

View File

@ -200,6 +200,7 @@ namespace OpenSim.Services.GridService
rdata.RegionName = rinfo.RegionName; rdata.RegionName = rinfo.RegionName;
rdata.Data = rinfo.ToKeyValuePairs(); rdata.Data = rinfo.ToKeyValuePairs();
rdata.Data["regionHandle"] = Utils.UIntsToLong((uint)rdata.posX, (uint)rdata.posY); rdata.Data["regionHandle"] = Utils.UIntsToLong((uint)rdata.posX, (uint)rdata.posY);
rdata.Data["owner_uuid"] = rinfo.EstateOwner.ToString();
return rdata; return rdata;
} }

View File

@ -140,6 +140,14 @@ namespace OpenSim.Services.Interfaces
} }
protected int m_regionLocY; protected int m_regionLocY;
protected UUID m_estateOwner;
public UUID EstateOwner
{
get { return m_estateOwner; }
set { m_estateOwner = value; }
}
public UUID RegionID = UUID.Zero; public UUID RegionID = UUID.Zero;
public UUID ScopeID = UUID.Zero; public UUID ScopeID = UUID.Zero;
@ -191,6 +199,7 @@ namespace OpenSim.Services.Interfaces
Access = ConvertFrom.AccessLevel; Access = ConvertFrom.AccessLevel;
Maturity = ConvertFrom.RegionSettings.Maturity; Maturity = ConvertFrom.RegionSettings.Maturity;
RegionSecret = ConvertFrom.regionSecret; RegionSecret = ConvertFrom.regionSecret;
EstateOwner = ConvertFrom.EstateSettings.EstateOwner;
} }
public GridRegion(GridRegion ConvertFrom) public GridRegion(GridRegion ConvertFrom)
@ -207,6 +216,7 @@ namespace OpenSim.Services.Interfaces
Access = ConvertFrom.Access; Access = ConvertFrom.Access;
Maturity = ConvertFrom.Maturity; Maturity = ConvertFrom.Maturity;
RegionSecret = ConvertFrom.RegionSecret; RegionSecret = ConvertFrom.RegionSecret;
EstateOwner = ConvertFrom.EstateOwner;
} }
/// <value> /// <value>
@ -291,6 +301,7 @@ namespace OpenSim.Services.Interfaces
kvp["regionMapTexture"] = TerrainImage.ToString(); kvp["regionMapTexture"] = TerrainImage.ToString();
kvp["access"] = Access.ToString(); kvp["access"] = Access.ToString();
kvp["regionSecret"] = RegionSecret; kvp["regionSecret"] = RegionSecret;
kvp["owner_uuid"] = EstateOwner.ToString();
// Maturity doesn't seem to exist in the DB // Maturity doesn't seem to exist in the DB
return kvp; return kvp;
} }
@ -345,6 +356,9 @@ namespace OpenSim.Services.Interfaces
if (kvp.ContainsKey("regionSecret")) if (kvp.ContainsKey("regionSecret"))
RegionSecret =(string)kvp["regionSecret"]; RegionSecret =(string)kvp["regionSecret"];
if (kvp.ContainsKey("owner_uuid"))
EstateOwner = new UUID(kvp["owner_uuid"].ToString());
} }
} }

View File

@ -115,7 +115,6 @@ namespace OpenSim.TestSuite
m_td[pos].IsBackground = true; m_td[pos].IsBackground = true;
m_td[pos].Start(); m_td[pos].Start();
m_lBot.Add(pb); m_lBot.Add(pb);
ThreadTracker.Add(m_td[pos]);
} }
/// <summary> /// <summary>

View File

@ -149,7 +149,6 @@ namespace pCampBot
m_td[pos].IsBackground = true; m_td[pos].IsBackground = true;
m_td[pos].Start(); m_td[pos].Start();
m_lBot.Add(pb); m_lBot.Add(pb);
ThreadTracker.Add(m_td[pos]);
} }
/// <summary> /// <summary>

View File

@ -131,6 +131,7 @@
<ReferencePath>../../bin/</ReferencePath> <ReferencePath>../../bin/</ReferencePath>
<Reference name="System"/> <Reference name="System"/>
<Reference name="System.Core"/>
<Reference name="System.Xml"/> <Reference name="System.Xml"/>
<Reference name="System.Data"/> <Reference name="System.Data"/>
<Reference name="System.Drawing"/> <Reference name="System.Drawing"/>
@ -724,7 +725,6 @@
<Reference name="System"/> <Reference name="System"/>
<Reference name="System.Xml"/> <Reference name="System.Xml"/>
<Reference name="System.Drawing"/> <Reference name="System.Drawing"/>
<Reference name="System.Runtime.Remoting"/>
<Reference name="System.Web"/> <Reference name="System.Web"/>
<Reference name="OpenMetaverseTypes.dll"/> <Reference name="OpenMetaverseTypes.dll"/>
<Reference name="OpenMetaverse.StructuredData.dll"/> <Reference name="OpenMetaverse.StructuredData.dll"/>
@ -805,7 +805,6 @@
<Reference name="System"/> <Reference name="System"/>
<Reference name="System.Xml"/> <Reference name="System.Xml"/>
<Reference name="System.Data"/> <Reference name="System.Data"/>
<Reference name="System.Runtime.Remoting"/>
<Reference name="OpenSim.Framework"/> <Reference name="OpenSim.Framework"/>
<Reference name="OpenSim.Data" /> <Reference name="OpenSim.Data" />
<Reference name="OpenSim.Framework.Communications" /> <Reference name="OpenSim.Framework.Communications" />
@ -1539,7 +1538,6 @@
<Reference name="System"/> <Reference name="System"/>
<Reference name="System.Xml"/> <Reference name="System.Xml"/>
<Reference name="System.Drawing"/> <Reference name="System.Drawing"/>
<Reference name="System.Runtime.Remoting"/>
<Reference name="System.Web"/> <Reference name="System.Web"/>
<Reference name="OpenMetaverseTypes.dll"/> <Reference name="OpenMetaverseTypes.dll"/>
<Reference name="OpenMetaverse.StructuredData.dll"/> <Reference name="OpenMetaverse.StructuredData.dll"/>
@ -1624,7 +1622,6 @@
<Reference name="System"/> <Reference name="System"/>
<Reference name="System.Xml"/> <Reference name="System.Xml"/>
<Reference name="System.Drawing"/> <Reference name="System.Drawing"/>
<Reference name="System.Runtime.Remoting"/>
<Reference name="System.Web"/> <Reference name="System.Web"/>
<Reference name="OpenMetaverseTypes.dll"/> <Reference name="OpenMetaverseTypes.dll"/>
<Reference name="OpenMetaverse.StructuredData.dll"/> <Reference name="OpenMetaverse.StructuredData.dll"/>
@ -1682,7 +1679,6 @@
<ReferencePath>../../../../bin/</ReferencePath> <ReferencePath>../../../../bin/</ReferencePath>
<Reference name="System"/> <Reference name="System"/>
<Reference name="System.Drawing"/> <Reference name="System.Drawing"/>
<Reference name="System.Runtime.Remoting"/>
<Reference name="System.Xml"/> <Reference name="System.Xml"/>
<Reference name="OpenSim.Data"/> <Reference name="OpenSim.Data"/>
<Reference name="OpenSim.Framework"/> <Reference name="OpenSim.Framework"/>
@ -1755,6 +1751,7 @@
<ReferencePath>../../../../bin/</ReferencePath> <ReferencePath>../../../../bin/</ReferencePath>
<Reference name="System"/> <Reference name="System"/>
<Reference name="System.Core"/>
<Reference name="System.Xml"/> <Reference name="System.Xml"/>
<Reference name="OpenMetaverseTypes.dll"/> <Reference name="OpenMetaverseTypes.dll"/>
<Reference name="OpenMetaverse.StructuredData.dll"/> <Reference name="OpenMetaverse.StructuredData.dll"/>
@ -3590,7 +3587,6 @@
<Reference name="System"/> <Reference name="System"/>
<Reference name="System.Xml"/> <Reference name="System.Xml"/>
<Reference name="System.Drawing"/> <Reference name="System.Drawing"/>
<Reference name="System.Runtime.Remoting"/>
<Reference name="OpenMetaverseTypes.dll"/> <Reference name="OpenMetaverseTypes.dll"/>
<Reference name="OpenMetaverse.dll"/> <Reference name="OpenMetaverse.dll"/>
<Reference name="OpenSim.Data"/> <Reference name="OpenSim.Data"/>
@ -3695,6 +3691,7 @@
</Files> </Files>
</Project> </Project>
<!-- Commented for now until new unit tests are written for the new LLUDP implementation
<Project frameworkVersion="v3_5" name="OpenSim.Region.ClientStack.LindenUDP.Tests" path="OpenSim/Region/ClientStack/LindenUDP/Tests" type="Library"> <Project frameworkVersion="v3_5" name="OpenSim.Region.ClientStack.LindenUDP.Tests" path="OpenSim/Region/ClientStack/LindenUDP/Tests" type="Library">
<Configuration name="Debug"> <Configuration name="Debug">
<Options> <Options>
@ -3728,6 +3725,7 @@
<Match pattern="*.cs" recurse="false"/> <Match pattern="*.cs" recurse="false"/>
</Files> </Files>
</Project> </Project>
-->
<Project frameworkVersion="v3_5" name="OpenSim.Region.ScriptEngine.Tests" path="OpenSim/Region/ScriptEngine" type="Library"> <Project frameworkVersion="v3_5" name="OpenSim.Region.ScriptEngine.Tests" path="OpenSim/Region/ScriptEngine" type="Library">
<Configuration name="Debug"> <Configuration name="Debug">