Compare commits
131 Commits
Author | SHA1 | Date |
---|---|---|
Justin Clark-Casey (justincc) | 8b267b5bb5 | |
Justin Clark-Casey (justincc) | c18cecbeb7 | |
Justin Clark-Casey (justincc) | 11ed60645a | |
Justin Clark-Casey (justincc) | 84d557a7bd | |
Justin Clark-Casey (justincc) | c96806a3f5 | |
Justin Clark-Casey (justincc) | 377b79bafc | |
Justin Clark-Casey (justincc) | df75f14bca | |
Justin Clark-Casey (justincc) | bf5e220450 | |
Justin Clark-Casey (justincc) | 1d56029848 | |
Justin Clark-Casey (justincc) | 6affa906ee | |
Justin Clark-Casey (justincc) | 9d78d16cf3 | |
Justin Clark-Casey (justincc) | 6fa2e685a9 | |
Justin Clark-Casey (justincc) | 297b27e473 | |
Justin Clark-Casey (justincc) | 9c8d5f54d8 | |
Justin Clark-Casey (justincc) | 69abadedae | |
Justin Clark-Casey (justincc) | 3372210c46 | |
Justin Clark-Casey (justincc) | d3a550bc85 | |
Justin Clark-Casey (justincc) | 965c53ebd7 | |
Justin Clark-Casey (justincc) | 31b92f0217 | |
Justin Clark-Casey (justincc) | 12f50a2798 | |
Justin Clark-Casey (justincc) | fdb902d762 | |
justincc | a58ae73694 | |
Justin Clark-Casey (justincc) | 43ac867512 | |
Justin Clark-Casey (justincc) | 064897be85 | |
Justin Clark-Casey (justincc) | 4275342515 | |
Justin Clark-Casey (justincc) | cc23d4bac5 | |
Justin Clark-Casey (justincc) | 1c12930c03 | |
Justin Clark-Casey (justincc) | 096aecc097 | |
Justin Clark-Casey (justincc) | bda36ad2e0 | |
Justin Clark-Casey (justincc) | 67007c7d21 | |
Justin Clark-Casey (justincc) | cdc3301a33 | |
Justin Clark-Casey (justincc) | 27efecc82f | |
Justin Clark-Casey (justincc) | a17bbeca9d | |
Justin Clark-Casey (justincc) | 6fa8ddf0fc | |
Justin Clark-Casey (justincc) | 7d2aad3873 | |
Justin Clark-Casey (justincc) | c1a2c4a0bd | |
Justin Clark-Casey (justincc) | eaa048eb89 | |
Justin Clark-Casey (justincc) | bb3e68c069 | |
Justin Clark-Casey (justincc) | 5dc0730f06 | |
Justin Clark-Casey (justincc) | 3e25e6a170 | |
Melanie Thielker | c6f39a0bfd | |
Justin Clark-Casey (justincc) | 830735a42f | |
Justin Clark-Casey (justincc) | dede17b0d2 | |
Justin Clark-Casey (justincc) | a54c784b81 | |
Justin Clark-Casey (justincc) | 493a86dfed | |
Justin Clark-Casey (justincc) | 22020927c6 | |
Justin Clark-Casey (justincc) | b9b2f5686f | |
Justin Clark-Casey (justincc) | 275613ad83 | |
Justin Clark-Casey (justincc) | 2935e79ca3 | |
Justin Clark-Casey (justincc) | 0437d44785 | |
Justin Clark-Casey (justincc) | 00e31de872 | |
Justin Clark-Casey (justincc) | 458540400a | |
Justin Clark-Casey (justincc) | 5c1a14587d | |
Justin Clark-Casey (justincc) | b28b31b3ee | |
Justin Clark-Casey (justincc) | 16eb9253b0 | |
Justin Clark-Casey (justincc) | ba0707ca1e | |
Justin Clark-Casey (justincc) | 3e4550adf7 | |
Justin Clark-Casey (justincc) | e902824da7 | |
Justin Clark-Casey (justincc) | 235eb92c0e | |
Justin Clark-Casey (justincc) | d69ab1e037 | |
Justin Clark-Casey (justincc) | d69e3760e1 | |
Justin Clark-Casey (justincc) | a83f5f2153 | |
Justin Clark-Casey (justincc) | 3802f2da3b | |
Justin Clark-Casey (justincc) | 51043746f3 | |
Justin Clark-Casey (justincc) | b6eaef79dd | |
Justin Clark-Casey (justincc) | c993067204 | |
Justin Clark-Casey (justincc) | ef39fcf465 | |
Justin Clark-Casey (justincc) | e8337d6a51 | |
Justin Clark-Casey (justincc) | 90b31a2f54 | |
Justin Clark-Casey (justincc) | b40220885e | |
Justin Clark-Casey (justincc) | 649891a0d8 | |
Justin Clark-Casey (justincc) | a529bc8f2a | |
Justin Clark-Casey (justincc) | 16aca6ddbd | |
Justin Clark-Casey (justincc) | 38458d4be8 | |
Justin Clark-Casey (justincc) | 9ee171f441 | |
Justin Clark-Casey (justincc) | 7ca4e2cb6f | |
Justin Clark-Casey (justincc) | 523f0b8938 | |
Justin Clark-Casey (justincc) | 4f04c0b560 | |
Justin Clark-Casey (justincc) | b7f78bf0f7 | |
Justin Clark-Casey (justincc) | 5cb3b87b21 | |
Justin Clark-Casey (justincc) | 97e25a0f45 | |
Justin Clark-Casey (justincc) | 026df644b5 | |
Justin Clark-Casey (justincc) | c3b1c72c33 | |
Justin Clark-Casey (justincc) | 277dbb9acd | |
Justin Clark-Casey (justincc) | 6cfebd66ec | |
Justin Clark-Casey (justincc) | c9d7eb30db | |
Justin Clark-Casey (justincc) | db5de62394 | |
Justin Clark-Casey (justincc) | 5c3f33bb48 | |
Justin Clark-Casey (justincc) | d50f85dff6 | |
BlueWall | b7abcd28e1 | |
AliciaRaven | d53703362e | |
Justin Clark-Casey (justincc) | 4e1f2ba1f4 | |
Diva Canto | 51951682e7 | |
Justin Clark-Casey (justincc) | c5c387e838 | |
Justin Clark-Casey (justincc) | f7fef5bc3b | |
Justin Clark-Casey (justincc) | 8e5a62c8e7 | |
Justin Clark-Casey (justincc) | ad15e06611 | |
Justin Clark-Casey (justincc) | 319c51b8a8 | |
Justin Clark-Casey (justincc) | 11830c4363 | |
Justin Clark-Casey (justincc) | 2eece5b009 | |
Roger Kirkman | 528a7f3352 | |
Justin Clark-Casey (justincc) | c02b33d592 | |
Justin Clark-Casey (justincc) | 07c6630c1c | |
Justin Clark-Casey (justincc) | cfc95afc3d | |
Justin Clark-Casey (justincc) | 85e04198fe | |
Justin Clark-Casey (justincc) | 1256d643be | |
Justin Clark-Casey (justincc) | f2715a5a2f | |
Justin Clark-Casey (justincc) | d2b8281df9 | |
Justin Clark-Casey (justincc) | 6b05cfce25 | |
Justin Clark-Casey (justincc) | 0448935b1b | |
Justin Clark-Casey (justincc) | bc01c27c8a | |
Justin Clark-Casey (justincc) | 95aade7fdb | |
Justin Clark-Casey (justincc) | 7ff27e32bc | |
Justin Clark-Casey (justincc) | 89c153ca7f | |
Justin Clark-Casey (justincc) | a6c79fe956 | |
Justin Clark-Casey (justincc) | 1e5c3f26f0 | |
Justin Clark-Casey (justincc) | e6df61fbe9 | |
Justin Clark-Casey (justincc) | 5991a98d80 | |
Justin Clark-Casey (justincc) | c4ed67aeee | |
Justin Clark-Casey (justincc) | 258de1f17f | |
Justin Clark-Casey (justincc) | f8fa76c09f | |
Justin Clark-Casey (justincc) | 0b7736b861 | |
Justin Clark-Casey (justincc) | a086adf427 | |
Justin Clark-Casey (justincc) | e76cc35409 | |
Justin Clark-Casey (justincc) | a129e9e351 | |
Justin Clark-Casey (justincc) | 3c7eacf39b | |
Justin Clark-Casey (justincc) | cd6f73392a | |
Justin Clark-Casey (justincc) | 1559a3a099 | |
Justin Clark-Casey (justincc) | 6520065625 | |
Justin Clark-Casey (justincc) | 693bfdc0fb | |
Justin Clark-Casey (justincc) | f3eaa6d81e |
|
@ -72,10 +72,12 @@ what it is today.
|
|||
* Allen Kerensky
|
||||
* BigFootAg
|
||||
* BlueWall Slade
|
||||
* bobshaffer2
|
||||
* brianw/Sir_Ahzz
|
||||
* CharlieO
|
||||
* ChrisDown
|
||||
* Chris Yeoh (IBM)
|
||||
* cinderblocks
|
||||
* controlbreak
|
||||
* coyled
|
||||
* ctrlaltdavid (David Rowe)
|
||||
|
|
|
@ -150,7 +150,8 @@ namespace OpenSim.Groups
|
|||
data.Data["ShowInList"] = showInList ? "1" : "0";
|
||||
data.Data["AllowPublish"] = allowPublish ? "1" : "0";
|
||||
data.Data["MaturePublish"] = maturePublish ? "1" : "0";
|
||||
data.Data["OwnerRoleID"] = UUID.Random().ToString();
|
||||
UUID roleID = UUID.Random();
|
||||
data.Data["OwnerRoleID"] = roleID.ToString();
|
||||
|
||||
if (!m_Database.StoreGroup(data))
|
||||
return UUID.Zero;
|
||||
|
@ -159,7 +160,6 @@ namespace OpenSim.Groups
|
|||
_AddOrUpdateGroupRole(RequestingAgentID, data.GroupID, UUID.Zero, "Everyone", "Everyone in the group", "Member of " + name, (ulong)DefaultEveryonePowers, true);
|
||||
|
||||
// Create Owner role
|
||||
UUID roleID = UUID.Random();
|
||||
_AddOrUpdateGroupRole(RequestingAgentID, data.GroupID, roleID, "Owners", "Owners of the group", "Owner of " + name, (ulong)OwnerPowers, true);
|
||||
|
||||
// Add founder to group
|
||||
|
@ -247,6 +247,9 @@ namespace OpenSim.Groups
|
|||
if (group == null)
|
||||
return members;
|
||||
|
||||
// Unfortunately this doesn't quite work on legacy group data because of a bug
|
||||
// that's also being fixed here on CreateGroup. The OwnerRoleID sent to the DB was wrong.
|
||||
// See how to find the ownerRoleID a few lines below.
|
||||
UUID ownerRoleID = new UUID(group.Data["OwnerRoleID"]);
|
||||
|
||||
RoleData[] roles = m_Database.RetrieveRoles(GroupID);
|
||||
|
@ -255,6 +258,11 @@ namespace OpenSim.Groups
|
|||
return members;
|
||||
List<RoleData> rolesList = new List<RoleData>(roles);
|
||||
|
||||
// Let's find the "real" ownerRoleID
|
||||
RoleData ownerRole = rolesList.Find(r => r.Data["Powers"] == ((long)OwnerPowers).ToString());
|
||||
if (ownerRole != null)
|
||||
ownerRoleID = ownerRole.RoleID;
|
||||
|
||||
// Check visibility?
|
||||
// When we don't want to check visibility, we pass it "all" as the requestingAgentID
|
||||
bool checkVisibility = !RequestingAgentID.Equals(UUID.Zero.ToString());
|
||||
|
@ -291,17 +299,17 @@ namespace OpenSim.Groups
|
|||
{
|
||||
m.Title = selected.Data["Title"];
|
||||
m.AgentPowers = UInt64.Parse(selected.Data["Powers"]);
|
||||
|
||||
m.AgentID = d.PrincipalID;
|
||||
m.AcceptNotices = d.Data["AcceptNotices"] == "1" ? true : false;
|
||||
m.Contribution = Int32.Parse(d.Data["Contribution"]);
|
||||
m.ListInProfile = d.Data["ListInProfile"] == "1" ? true : false;
|
||||
|
||||
// Is this person an owner of the group?
|
||||
m.IsOwner = (rolemembershipsList.Find(r => r.RoleID == ownerRoleID) != null) ? true : false;
|
||||
|
||||
members.Add(m);
|
||||
}
|
||||
|
||||
m.AgentID = d.PrincipalID;
|
||||
m.AcceptNotices = d.Data["AcceptNotices"] == "1" ? true : false;
|
||||
m.Contribution = Int32.Parse(d.Data["Contribution"]);
|
||||
m.ListInProfile = d.Data["ListInProfile"] == "1" ? true : false;
|
||||
|
||||
// Is this person an owner of the group?
|
||||
m.IsOwner = (rolemembershipsList.Find(r => r.RoleID == ownerRoleID) != null) ? true : false;
|
||||
|
||||
members.Add(m);
|
||||
}
|
||||
|
||||
return members;
|
||||
|
|
|
@ -30,6 +30,7 @@ using System.Collections;
|
|||
using System.Collections.Specialized;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using log4net;
|
||||
using Nini.Config;
|
||||
|
@ -43,41 +44,37 @@ using Caps = OpenSim.Framework.Capabilities.Caps;
|
|||
|
||||
namespace OpenSim.Capabilities.Handlers
|
||||
{
|
||||
public class GetMeshHandler
|
||||
public class GetMeshHandler : BaseStreamHandler
|
||||
{
|
||||
// private static readonly ILog m_log =
|
||||
// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private IAssetService m_assetService;
|
||||
|
||||
public GetMeshHandler(IAssetService assService)
|
||||
public GetMeshHandler(string path, IAssetService assService, string name, string description)
|
||||
: base("GET", path, name, description)
|
||||
{
|
||||
m_assetService = assService;
|
||||
}
|
||||
|
||||
public Hashtable ProcessGetMesh(Hashtable request, UUID AgentId, Caps cap)
|
||||
protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
|
||||
{
|
||||
Hashtable responsedata = new Hashtable();
|
||||
responsedata["int_response_code"] = 400; //501; //410; //404;
|
||||
responsedata["content_type"] = "text/plain";
|
||||
responsedata["keepalive"] = false;
|
||||
responsedata["str_response_string"] = "Request wasn't what was expected";
|
||||
// Try to parse the texture ID from the request URL
|
||||
NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query);
|
||||
string meshStr = query.GetOne("mesh_id");
|
||||
|
||||
string meshStr = string.Empty;
|
||||
|
||||
if (request.ContainsKey("mesh_id"))
|
||||
meshStr = request["mesh_id"].ToString();
|
||||
// m_log.DebugFormat("Fetching mesh {0}", meshStr);
|
||||
|
||||
UUID meshID = UUID.Zero;
|
||||
if (!String.IsNullOrEmpty(meshStr) && UUID.TryParse(meshStr, out meshID))
|
||||
{
|
||||
if (m_assetService == null)
|
||||
{
|
||||
responsedata["int_response_code"] = 404; //501; //410; //404;
|
||||
responsedata["content_type"] = "text/plain";
|
||||
responsedata["keepalive"] = false;
|
||||
responsedata["str_response_string"] = "The asset service is unavailable. So is your mesh.";
|
||||
return responsedata;
|
||||
httpResponse.StatusCode = 404;
|
||||
httpResponse.ContentType = "text/plain";
|
||||
byte[] data = Encoding.UTF8.GetBytes("The asset service is unavailable. So is your mesh.");
|
||||
httpResponse.Body.Write(data, 0, data.Length);
|
||||
return null;
|
||||
}
|
||||
|
||||
AssetBase mesh = m_assetService.Get(meshID.ToString());
|
||||
|
@ -86,31 +83,32 @@ namespace OpenSim.Capabilities.Handlers
|
|||
{
|
||||
if (mesh.Type == (SByte)AssetType.Mesh)
|
||||
{
|
||||
responsedata["str_response_string"] = Convert.ToBase64String(mesh.Data);
|
||||
responsedata["content_type"] = "application/vnd.ll.mesh";
|
||||
responsedata["int_response_code"] = 200;
|
||||
byte[] data = mesh.Data;
|
||||
httpResponse.Body.Write(data, 0, data.Length);
|
||||
httpResponse.ContentType = "application/vnd.ll.mesh";
|
||||
httpResponse.StatusCode = 200;
|
||||
}
|
||||
// Optionally add additional mesh types here
|
||||
else
|
||||
{
|
||||
responsedata["int_response_code"] = 404; //501; //410; //404;
|
||||
responsedata["content_type"] = "text/plain";
|
||||
responsedata["keepalive"] = false;
|
||||
responsedata["str_response_string"] = "Unfortunately, this asset isn't a mesh.";
|
||||
return responsedata;
|
||||
httpResponse.StatusCode = 404;
|
||||
httpResponse.ContentType = "text/plain";
|
||||
byte[] data = Encoding.UTF8.GetBytes("Unfortunately, this asset isn't a mesh.");
|
||||
httpResponse.Body.Write(data, 0, data.Length);
|
||||
httpResponse.KeepAlive = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
responsedata["int_response_code"] = 404; //501; //410; //404;
|
||||
responsedata["content_type"] = "text/plain";
|
||||
responsedata["keepalive"] = false;
|
||||
responsedata["str_response_string"] = "Your Mesh wasn't found. Sorry!";
|
||||
return responsedata;
|
||||
httpResponse.StatusCode = 404;
|
||||
httpResponse.ContentType = "text/plain";
|
||||
byte[] data = Encoding.UTF8.GetBytes("Your Mesh wasn't found. Sorry!");
|
||||
httpResponse.Body.Write(data, 0, data.Length);
|
||||
httpResponse.KeepAlive = false;
|
||||
}
|
||||
}
|
||||
|
||||
return responsedata;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -65,15 +65,8 @@ namespace OpenSim.Capabilities.Handlers
|
|||
if (m_AssetService == null)
|
||||
throw new Exception(String.Format("Failed to load AssetService from {0}; config is {1}", assetService, m_ConfigName));
|
||||
|
||||
GetMeshHandler gmeshHandler = new GetMeshHandler(m_AssetService);
|
||||
IRequestHandler reqHandler
|
||||
= new RestHTTPHandler(
|
||||
"GET",
|
||||
"/CAPS/" + UUID.Random(),
|
||||
httpMethod => gmeshHandler.ProcessGetMesh(httpMethod, UUID.Zero, null),
|
||||
"GetMesh",
|
||||
null);
|
||||
server.AddStreamHandler(reqHandler);
|
||||
server.AddStreamHandler(
|
||||
new GetMeshHandler("/CAPS/GetMesh/" /*+ UUID.Random() */, m_AssetService, "GetMesh", null));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,7 +37,6 @@ using OpenSim.Framework;
|
|||
using OpenSim.Framework.Servers.HttpServer;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Capabilities.Handlers.GetTexture.Tests
|
||||
{
|
||||
|
|
|
@ -54,6 +54,10 @@ namespace OpenSim.Framework
|
|||
public int assetThrottle;
|
||||
public int textureThrottle;
|
||||
public int totalThrottle;
|
||||
|
||||
// Used by adaptive only
|
||||
public int targetThrottle;
|
||||
|
||||
public int maxThrottle;
|
||||
|
||||
public Dictionary<string, int> SyncRequests = new Dictionary<string,int>();
|
||||
|
|
|
@ -483,7 +483,7 @@ namespace OpenSim.Framework.Communications
|
|||
/// In case, we are invoked asynchroneously this object will keep track of the state
|
||||
/// </summary>
|
||||
AsyncResult<Stream> ar = new AsyncResult<Stream>(callback, state);
|
||||
Util.FireAndForget(RequestHelper, ar);
|
||||
Util.FireAndForget(RequestHelper, ar, "RestClient.BeginRequest");
|
||||
return ar;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,320 @@
|
|||
/*
|
||||
* 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.Concurrent;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using log4net;
|
||||
using OpenSim.Framework;
|
||||
|
||||
namespace OpenSim.Framework.Monitoring
|
||||
{
|
||||
public class Job
|
||||
{
|
||||
public string Name;
|
||||
public WaitCallback Callback;
|
||||
public object O;
|
||||
|
||||
public Job(string name, WaitCallback callback, object o)
|
||||
{
|
||||
Name = name;
|
||||
Callback = callback;
|
||||
O = o;
|
||||
}
|
||||
}
|
||||
|
||||
public class JobEngine
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
public int LogLevel { get; set; }
|
||||
|
||||
public bool IsRunning { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The timeout in milliseconds to wait for at least one event to be written when the recorder is stopping.
|
||||
/// </summary>
|
||||
public int RequestProcessTimeoutOnStop { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controls whether we need to warn in the log about exceeding the max queue size.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is flipped to false once queue max has been exceeded and back to true when it falls below max, in
|
||||
/// order to avoid spamming the log with lots of warnings.
|
||||
/// </remarks>
|
||||
private bool m_warnOverMaxQueue = true;
|
||||
|
||||
private BlockingCollection<Job> m_requestQueue;
|
||||
|
||||
private CancellationTokenSource m_cancelSource = new CancellationTokenSource();
|
||||
|
||||
private Stat m_requestsWaitingStat;
|
||||
|
||||
private Job m_currentJob;
|
||||
|
||||
/// <summary>
|
||||
/// Used to signal that we are ready to complete stop.
|
||||
/// </summary>
|
||||
private ManualResetEvent m_finishedProcessingAfterStop = new ManualResetEvent(false);
|
||||
|
||||
public JobEngine()
|
||||
{
|
||||
RequestProcessTimeoutOnStop = 5000;
|
||||
|
||||
MainConsole.Instance.Commands.AddCommand(
|
||||
"Debug",
|
||||
false,
|
||||
"debug jobengine",
|
||||
"debug jobengine <start|stop|status|log>",
|
||||
"Start, stop, get status or set logging level of the job engine.",
|
||||
"If stopped then all outstanding jobs are processed immediately.",
|
||||
HandleControlCommand);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (IsRunning)
|
||||
return;
|
||||
|
||||
IsRunning = true;
|
||||
|
||||
m_finishedProcessingAfterStop.Reset();
|
||||
|
||||
m_requestQueue = new BlockingCollection<Job>(new ConcurrentQueue<Job>(), 5000);
|
||||
|
||||
m_requestsWaitingStat =
|
||||
new Stat(
|
||||
"JobsWaiting",
|
||||
"Number of jobs waiting for processing.",
|
||||
"",
|
||||
"",
|
||||
"server",
|
||||
"jobengine",
|
||||
StatType.Pull,
|
||||
MeasuresOfInterest.None,
|
||||
stat => stat.Value = m_requestQueue.Count,
|
||||
StatVerbosity.Debug);
|
||||
|
||||
StatsManager.RegisterStat(m_requestsWaitingStat);
|
||||
|
||||
Watchdog.StartThread(
|
||||
ProcessRequests,
|
||||
"JobEngineThread",
|
||||
ThreadPriority.Normal,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
int.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsRunning)
|
||||
return;
|
||||
|
||||
IsRunning = false;
|
||||
|
||||
int requestsLeft = m_requestQueue.Count;
|
||||
|
||||
if (requestsLeft <= 0)
|
||||
{
|
||||
m_cancelSource.Cancel();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log.InfoFormat("[JOB ENGINE]: Waiting to write {0} events after stop.", requestsLeft);
|
||||
|
||||
while (requestsLeft > 0)
|
||||
{
|
||||
if (!m_finishedProcessingAfterStop.WaitOne(RequestProcessTimeoutOnStop))
|
||||
{
|
||||
// After timeout no events have been written
|
||||
if (requestsLeft == m_requestQueue.Count)
|
||||
{
|
||||
m_log.WarnFormat(
|
||||
"[JOB ENGINE]: No requests processed after {0} ms wait. Discarding remaining {1} requests",
|
||||
RequestProcessTimeoutOnStop, requestsLeft);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
requestsLeft = m_requestQueue.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_cancelSource.Dispose();
|
||||
StatsManager.DeregisterStat(m_requestsWaitingStat);
|
||||
m_requestsWaitingStat = null;
|
||||
m_requestQueue = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool QueueRequest(string name, WaitCallback req, object o)
|
||||
{
|
||||
if (LogLevel >= 1)
|
||||
m_log.DebugFormat("[JOB ENGINE]: Queued job {0}", name);
|
||||
|
||||
if (m_requestQueue.Count < m_requestQueue.BoundedCapacity)
|
||||
{
|
||||
// m_log.DebugFormat(
|
||||
// "[OUTGOING QUEUE REFILL ENGINE]: Adding request for categories {0} for {1} in {2}",
|
||||
// categories, client.AgentID, m_udpServer.Scene.Name);
|
||||
|
||||
m_requestQueue.Add(new Job(name, req, o));
|
||||
|
||||
if (!m_warnOverMaxQueue)
|
||||
m_warnOverMaxQueue = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_warnOverMaxQueue)
|
||||
{
|
||||
// m_log.WarnFormat(
|
||||
// "[JOB ENGINE]: Request queue at maximum capacity, not recording request from {0} in {1}",
|
||||
// client.AgentID, m_udpServer.Scene.Name);
|
||||
|
||||
m_log.WarnFormat("[JOB ENGINE]: Request queue at maximum capacity, not recording job");
|
||||
|
||||
m_warnOverMaxQueue = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessRequests()
|
||||
{
|
||||
try
|
||||
{
|
||||
while (IsRunning || m_requestQueue.Count > 0)
|
||||
{
|
||||
m_currentJob = m_requestQueue.Take(m_cancelSource.Token);
|
||||
|
||||
// QueueEmpty callback = req.Client.OnQueueEmpty;
|
||||
//
|
||||
// if (callback != null)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// callback(req.Categories);
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// m_log.Error("[OUTGOING QUEUE REFILL ENGINE]: ProcessRequests(" + req.Categories + ") threw an exception: " + e.Message, e);
|
||||
// }
|
||||
// }
|
||||
|
||||
if (LogLevel >= 1)
|
||||
m_log.DebugFormat("[JOB ENGINE]: Processing job {0}", m_currentJob.Name);
|
||||
|
||||
try
|
||||
{
|
||||
m_currentJob.Callback.Invoke(m_currentJob.O);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.Error(
|
||||
string.Format(
|
||||
"[JOB ENGINE]: Job {0} failed, continuing. Exception ", m_currentJob.Name), e);
|
||||
}
|
||||
|
||||
if (LogLevel >= 1)
|
||||
m_log.DebugFormat("[JOB ENGINE]: Processed job {0}", m_currentJob.Name);
|
||||
|
||||
m_currentJob = null;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
|
||||
m_finishedProcessingAfterStop.Set();
|
||||
}
|
||||
|
||||
private void HandleControlCommand(string module, string[] args)
|
||||
{
|
||||
// if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
// return;
|
||||
|
||||
if (args.Length < 3)
|
||||
{
|
||||
MainConsole.Instance.Output("Usage: debug jobengine <stop|start|status|log>");
|
||||
return;
|
||||
}
|
||||
|
||||
string subCommand = args[2];
|
||||
|
||||
if (subCommand == "stop")
|
||||
{
|
||||
Stop();
|
||||
MainConsole.Instance.OutputFormat("Stopped job engine.");
|
||||
}
|
||||
else if (subCommand == "start")
|
||||
{
|
||||
Start();
|
||||
MainConsole.Instance.OutputFormat("Started job engine.");
|
||||
}
|
||||
else if (subCommand == "status")
|
||||
{
|
||||
MainConsole.Instance.OutputFormat("Job engine running: {0}", IsRunning);
|
||||
MainConsole.Instance.OutputFormat("Current job {0}", m_currentJob != null ? m_currentJob.Name : "none");
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Jobs waiting: {0}", IsRunning ? m_requestQueue.Count.ToString() : "n/a");
|
||||
MainConsole.Instance.OutputFormat("Log Level: {0}", LogLevel);
|
||||
}
|
||||
else if (subCommand == "log")
|
||||
{
|
||||
// int logLevel;
|
||||
int logLevel = int.Parse(args[3]);
|
||||
// if (ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out logLevel))
|
||||
// {
|
||||
LogLevel = logLevel;
|
||||
MainConsole.Instance.OutputFormat("Set debug log level to {0}", LogLevel);
|
||||
// }
|
||||
}
|
||||
else
|
||||
{
|
||||
MainConsole.Instance.OutputFormat("Unrecognized job engine subcommand {0}", subCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -171,7 +171,8 @@ namespace OpenSim.Framework.Monitoring
|
|||
foreach (char c in DisallowedShortNameCharacters)
|
||||
{
|
||||
if (shortName.IndexOf(c) != -1)
|
||||
throw new Exception(string.Format("Stat name {0} cannot contain character {1}", shortName, c));
|
||||
shortName = shortName.Replace(c, '#');
|
||||
// throw new Exception(string.Format("Stat name {0} cannot contain character {1}", shortName, c));
|
||||
}
|
||||
|
||||
ShortName = shortName;
|
||||
|
|
|
@ -133,6 +133,8 @@ namespace OpenSim.Framework.Monitoring
|
|||
/// /summary>
|
||||
public static event Action<ThreadWatchdogInfo> OnWatchdogTimeout;
|
||||
|
||||
public static JobEngine JobEngine { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this watchdog active?
|
||||
/// </summary>
|
||||
|
@ -173,6 +175,7 @@ namespace OpenSim.Framework.Monitoring
|
|||
|
||||
static Watchdog()
|
||||
{
|
||||
JobEngine = new JobEngine();
|
||||
m_threads = new Dictionary<int, ThreadWatchdogInfo>();
|
||||
m_watchdogTimer = new System.Timers.Timer(WATCHDOG_INTERVAL_MS);
|
||||
m_watchdogTimer.AutoReset = false;
|
||||
|
@ -449,5 +452,55 @@ namespace OpenSim.Framework.Monitoring
|
|||
|
||||
m_watchdogTimer.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run a job.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This differs from direct scheduling (e.g. Util.FireAndForget) in that a job can be run in the job
|
||||
/// engine if it is running, where all jobs are currently performed in sequence on a single thread. This is
|
||||
/// to prevent observed overload and server freeze problems when there are hundreds of connections which all attempt to
|
||||
/// perform work at once (e.g. in conference situations). With lower numbers of connections, the small
|
||||
/// delay in performing jobs in sequence rather than concurrently has not been notiecable in testing, though a future more
|
||||
/// sophisticated implementation could perform jobs concurrently when the server is under low load.
|
||||
///
|
||||
/// However, be advised that some callers of this function rely on all jobs being performed in sequence if any
|
||||
/// jobs are performed in sequence (i.e. if jobengine is active or not). Therefore, expanding the jobengine
|
||||
/// beyond a single thread will require considerable thought.
|
||||
///
|
||||
/// Also, any jobs submitted must be guaranteed to complete within a reasonable timeframe (e.g. they cannot
|
||||
/// incorporate a network delay with a long timeout). At the moment, work that could suffer such issues
|
||||
/// should still be run directly with RunInThread(), Util.FireAndForget(), etc. This is another area where
|
||||
/// the job engine could be improved and so CPU utilization improved by better management of concurrency within
|
||||
/// OpenSimulator.
|
||||
/// </remarks>
|
||||
/// <param name="jobType">General classification for the job (e.g. "RezAttachments").</param>
|
||||
/// <param name="callback">Callback for job.</param>
|
||||
/// <param name="name">Specific name of job (e.g. "RezAttachments for Joe Bloggs"</param>
|
||||
/// <param name="obj">Object to pass to callback when run</param>
|
||||
/// <param name="canRunInThisThread">If set to true then the job may be run in ths calling thread.</param>
|
||||
/// <param name="mustNotTimeout">If the true then the job must never timeout.</param>
|
||||
/// <param name="log">If set to true then extra logging is performed.</param>
|
||||
public static void RunJob(
|
||||
string jobType, WaitCallback callback, string name, object obj,
|
||||
bool canRunInThisThread = false, bool mustNotTimeout = false,
|
||||
bool log = false)
|
||||
{
|
||||
if (Util.FireAndForgetMethod == FireAndForgetMethod.RegressionTest)
|
||||
{
|
||||
Culture.SetCurrentCulture();
|
||||
callback(obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (JobEngine.IsRunning)
|
||||
JobEngine.QueueRequest(name, callback, obj);
|
||||
else if (canRunInThisThread)
|
||||
callback(obj);
|
||||
else if (mustNotTimeout)
|
||||
RunInThread(callback, name, obj, log);
|
||||
else
|
||||
Util.FireAndForget(callback, obj, name);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -127,7 +127,6 @@ namespace OpenSim.Framework
|
|||
private int m_objectCapacity = 0;
|
||||
private int m_maxPrimsPerUser = -1;
|
||||
private int m_linksetCapacity = 0;
|
||||
private int m_agentCapacity = 0;
|
||||
private string m_regionType = String.Empty;
|
||||
private RegionLightShareData m_windlight = new RegionLightShareData();
|
||||
protected uint m_httpPort;
|
||||
|
@ -351,10 +350,7 @@ namespace OpenSim.Framework
|
|||
get { return m_linksetCapacity; }
|
||||
}
|
||||
|
||||
public int AgentCapacity
|
||||
{
|
||||
get { return m_agentCapacity; }
|
||||
}
|
||||
public int AgentCapacity { get; set; }
|
||||
|
||||
public byte AccessLevel
|
||||
{
|
||||
|
@ -748,7 +744,7 @@ namespace OpenSim.Framework
|
|||
|
||||
#endregion
|
||||
|
||||
m_agentCapacity = config.GetInt("MaxAgents", 100);
|
||||
AgentCapacity = config.GetInt("MaxAgents", 100);
|
||||
allKeys.Remove("MaxAgents");
|
||||
|
||||
// Multi-tenancy
|
||||
|
@ -864,8 +860,8 @@ namespace OpenSim.Framework
|
|||
if (m_linksetCapacity > 0)
|
||||
config.Set("LinksetPrims", m_linksetCapacity);
|
||||
|
||||
if (m_agentCapacity > 0)
|
||||
config.Set("MaxAgents", m_agentCapacity);
|
||||
if (AgentCapacity > 0)
|
||||
config.Set("MaxAgents", AgentCapacity);
|
||||
|
||||
if (ScopeID != UUID.Zero)
|
||||
config.Set("ScopeID", ScopeID.ToString());
|
||||
|
|
|
@ -29,6 +29,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
@ -291,6 +292,18 @@ namespace OpenSim.Framework.Servers
|
|||
+ " 3 = full stack trace, including common threads\n",
|
||||
HandleDebugThreadpoolLevel);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Debug", false, "show threadpool calls active",
|
||||
"show threadpool calls active",
|
||||
"Show details about threadpool calls that are still active (currently waiting or in progress)",
|
||||
HandleShowThreadpoolCallsActive);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Debug", false, "show threadpool calls complete",
|
||||
"show threadpool calls complete",
|
||||
"Show details about threadpool calls that have been completed.",
|
||||
HandleShowThreadpoolCallsComplete);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Debug", false, "force gc",
|
||||
"force gc",
|
||||
|
@ -354,6 +367,57 @@ namespace OpenSim.Framework.Servers
|
|||
Notice("serialosdreq is now {0}", setSerializeOsdRequests);
|
||||
}
|
||||
|
||||
private void HandleShowThreadpoolCallsActive(string module, string[] args)
|
||||
{
|
||||
List<KeyValuePair<string, int>> calls = Util.GetFireAndForgetCallsInProgress().ToList();
|
||||
calls.Sort((kvp1, kvp2) => kvp2.Value.CompareTo(kvp1.Value));
|
||||
int namedCalls = 0;
|
||||
|
||||
ConsoleDisplayList cdl = new ConsoleDisplayList();
|
||||
foreach (KeyValuePair<string, int> kvp in calls)
|
||||
{
|
||||
if (kvp.Value > 0)
|
||||
{
|
||||
cdl.AddRow(kvp.Key, kvp.Value);
|
||||
namedCalls += kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
cdl.AddRow("TOTAL NAMED", namedCalls);
|
||||
|
||||
long allQueuedCalls = Util.TotalQueuedFireAndForgetCalls;
|
||||
long allRunningCalls = Util.TotalRunningFireAndForgetCalls;
|
||||
|
||||
cdl.AddRow("TOTAL QUEUED", allQueuedCalls);
|
||||
cdl.AddRow("TOTAL RUNNING", allRunningCalls);
|
||||
cdl.AddRow("TOTAL ANONYMOUS", allQueuedCalls + allRunningCalls - namedCalls);
|
||||
cdl.AddRow("TOTAL ALL", allQueuedCalls + allRunningCalls);
|
||||
|
||||
MainConsole.Instance.Output(cdl.ToString());
|
||||
}
|
||||
|
||||
private void HandleShowThreadpoolCallsComplete(string module, string[] args)
|
||||
{
|
||||
List<KeyValuePair<string, int>> calls = Util.GetFireAndForgetCallsMade().ToList();
|
||||
calls.Sort((kvp1, kvp2) => kvp2.Value.CompareTo(kvp1.Value));
|
||||
int namedCallsMade = 0;
|
||||
|
||||
ConsoleDisplayList cdl = new ConsoleDisplayList();
|
||||
foreach (KeyValuePair<string, int> kvp in calls)
|
||||
{
|
||||
cdl.AddRow(kvp.Key, kvp.Value);
|
||||
namedCallsMade += kvp.Value;
|
||||
}
|
||||
|
||||
cdl.AddRow("TOTAL NAMED", namedCallsMade);
|
||||
|
||||
long allCallsMade = Util.TotalFireAndForgetCallsMade;
|
||||
cdl.AddRow("TOTAL ANONYMOUS", allCallsMade - namedCallsMade);
|
||||
cdl.AddRow("TOTAL ALL", allCallsMade);
|
||||
|
||||
MainConsole.Instance.Output(cdl.ToString());
|
||||
}
|
||||
|
||||
private void HandleDebugThreadpoolStatus(string module, string[] args)
|
||||
{
|
||||
int workerThreads, iocpThreads;
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace OpenSim.Framework.ServiceAuth
|
|||
private string m_Username, m_Password;
|
||||
private string m_CredentialsB64;
|
||||
|
||||
private string remove_me;
|
||||
// private string remove_me;
|
||||
|
||||
public string Credentials
|
||||
{
|
||||
|
@ -24,7 +24,7 @@ namespace OpenSim.Framework.ServiceAuth
|
|||
|
||||
public BasicHttpAuthentication(IConfigSource config, string section)
|
||||
{
|
||||
remove_me = section;
|
||||
// remove_me = section;
|
||||
m_Username = Util.GetConfigVarFromSections<string>(config, "HttpAuthUsername", new string[] { "Network", section }, string.Empty);
|
||||
m_Password = Util.GetConfigVarFromSections<string>(config, "HttpAuthPassword", new string[] { "Network", section }, string.Empty);
|
||||
string str = m_Username + ":" + m_Password;
|
||||
|
|
|
@ -32,7 +32,6 @@ using OpenMetaverse;
|
|||
using OpenMetaverse.StructuredData;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
using Animation = OpenSim.Framework.Animation;
|
||||
|
||||
namespace OpenSim.Framework.Tests
|
||||
|
|
|
@ -1928,11 +1928,6 @@ namespace OpenSim.Framework
|
|||
}
|
||||
}
|
||||
|
||||
public static void FireAndForget(System.Threading.WaitCallback callback)
|
||||
{
|
||||
FireAndForget(callback, null, null);
|
||||
}
|
||||
|
||||
public static void InitThreadPool(int minThreads, int maxThreads)
|
||||
{
|
||||
if (maxThreads < 2)
|
||||
|
@ -1977,8 +1972,7 @@ namespace OpenSim.Framework
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Additional information about threads in the main thread pool. Used to time how long the
|
||||
/// thread has been running, and abort it if it has timed-out.
|
||||
|
@ -2052,12 +2046,15 @@ namespace OpenSim.Framework
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private static long nextThreadFuncNum = 0;
|
||||
private static long numQueuedThreadFuncs = 0;
|
||||
private static long numRunningThreadFuncs = 0;
|
||||
private static long numTotalThreadFuncsCalled = 0;
|
||||
private static Int32 threadFuncOverloadMode = 0;
|
||||
|
||||
public static long TotalQueuedFireAndForgetCalls { get { return numQueuedThreadFuncs; } }
|
||||
public static long TotalRunningFireAndForgetCalls { get { return numRunningThreadFuncs; } }
|
||||
|
||||
// Maps (ThreadFunc number -> Thread)
|
||||
private static ConcurrentDictionary<long, ThreadInfo> activeThreads = new ConcurrentDictionary<long, ThreadInfo>();
|
||||
|
||||
|
@ -2086,14 +2083,49 @@ namespace OpenSim.Framework
|
|||
}
|
||||
}
|
||||
|
||||
public static long TotalFireAndForgetCallsMade { get { return numTotalThreadFuncsCalled; } }
|
||||
|
||||
public static Dictionary<string, int> GetFireAndForgetCallsMade()
|
||||
{
|
||||
return new Dictionary<string, int>(m_fireAndForgetCallsMade);
|
||||
}
|
||||
|
||||
private static Dictionary<string, int> m_fireAndForgetCallsMade = new Dictionary<string, int>();
|
||||
|
||||
public static Dictionary<string, int> GetFireAndForgetCallsInProgress()
|
||||
{
|
||||
return new Dictionary<string, int>(m_fireAndForgetCallsInProgress);
|
||||
}
|
||||
|
||||
private static Dictionary<string, int> m_fireAndForgetCallsInProgress = new Dictionary<string, int>();
|
||||
|
||||
public static void FireAndForget(System.Threading.WaitCallback callback)
|
||||
{
|
||||
FireAndForget(callback, null, null);
|
||||
}
|
||||
|
||||
public static void FireAndForget(System.Threading.WaitCallback callback, object obj)
|
||||
{
|
||||
FireAndForget(callback, obj, null);
|
||||
}
|
||||
|
||||
|
||||
public static void FireAndForget(System.Threading.WaitCallback callback, object obj, string context)
|
||||
{
|
||||
Interlocked.Increment(ref numTotalThreadFuncsCalled);
|
||||
|
||||
if (context != null)
|
||||
{
|
||||
if (!m_fireAndForgetCallsMade.ContainsKey(context))
|
||||
m_fireAndForgetCallsMade[context] = 1;
|
||||
else
|
||||
m_fireAndForgetCallsMade[context]++;
|
||||
|
||||
if (!m_fireAndForgetCallsInProgress.ContainsKey(context))
|
||||
m_fireAndForgetCallsInProgress[context] = 1;
|
||||
else
|
||||
m_fireAndForgetCallsInProgress[context]++;
|
||||
}
|
||||
|
||||
WaitCallback realCallback;
|
||||
|
||||
bool loggingEnabled = LogThreadPool > 0;
|
||||
|
@ -2104,7 +2136,15 @@ namespace OpenSim.Framework
|
|||
if (FireAndForgetMethod == FireAndForgetMethod.RegressionTest)
|
||||
{
|
||||
// If we're running regression tests, then we want any exceptions to rise up to the test code.
|
||||
realCallback = o => { Culture.SetCurrentCulture(); callback(o); };
|
||||
realCallback =
|
||||
o =>
|
||||
{
|
||||
Culture.SetCurrentCulture();
|
||||
callback(o);
|
||||
|
||||
if (context != null)
|
||||
m_fireAndForgetCallsInProgress[context]--;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2143,6 +2183,9 @@ namespace OpenSim.Framework
|
|||
activeThreads.TryRemove(threadFuncNum, out dummy);
|
||||
if ((loggingEnabled || (threadFuncOverloadMode == 1)) && threadInfo.LogThread)
|
||||
m_log.DebugFormat("Exit threadfunc {0} ({1})", threadFuncNum, FormatDuration(threadInfo.Elapsed()));
|
||||
|
||||
if (context != null)
|
||||
m_fireAndForgetCallsInProgress[context]--;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -337,6 +337,10 @@ namespace OpenSim
|
|||
{
|
||||
// Called from base.StartUp()
|
||||
|
||||
IConfig startupConfig = Config.Configs["Startup"];
|
||||
if (startupConfig == null || startupConfig.GetBoolean("JobEngineEnabled", true))
|
||||
Watchdog.JobEngine.Start();
|
||||
|
||||
m_httpServerPort = m_networkServersInfo.HttpListenerPort;
|
||||
SceneManager.OnRestartSim += HandleRestartRegion;
|
||||
|
||||
|
|
|
@ -268,8 +268,8 @@ namespace OpenSim.Region.ClientStack.Linden
|
|||
public string SeedCapRequest(string request, string path, string param,
|
||||
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
|
||||
{
|
||||
m_log.DebugFormat(
|
||||
"[CAPS]: Received SEED caps request in {0} for agent {1}", m_regionName, m_HostCapsObj.AgentID);
|
||||
// m_log.DebugFormat(
|
||||
// "[CAPS]: Received SEED caps request in {0} for agent {1}", m_regionName, m_HostCapsObj.AgentID);
|
||||
|
||||
if (!m_Scene.CheckClient(m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint))
|
||||
{
|
||||
|
|
|
@ -43,7 +43,6 @@ using OpenSim.Region.CoreModules.Framework;
|
|||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Region.OptionalModules.World.NPC;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.ClientStack.Linden.Tests
|
||||
{
|
||||
|
|
|
@ -110,32 +110,29 @@ namespace OpenSim.Region.ClientStack.Linden
|
|||
|
||||
#endregion
|
||||
|
||||
|
||||
public void RegisterCaps(UUID agentID, Caps caps)
|
||||
{
|
||||
// UUID capID = UUID.Random();
|
||||
|
||||
//caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture));
|
||||
if (m_URL == "localhost")
|
||||
{
|
||||
// m_log.DebugFormat("[GETMESH]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName);
|
||||
GetMeshHandler gmeshHandler = new GetMeshHandler(m_AssetService);
|
||||
IRequestHandler reqHandler
|
||||
= new RestHTTPHandler(
|
||||
"GET",
|
||||
"/CAPS/" + UUID.Random(),
|
||||
httpMethod => gmeshHandler.ProcessGetMesh(httpMethod, UUID.Zero, null),
|
||||
"GetMesh",
|
||||
agentID.ToString());
|
||||
// m_log.DebugFormat("[GETTEXTURE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName);
|
||||
|
||||
caps.RegisterHandler("GetMesh", reqHandler);
|
||||
UUID capID = UUID.Random();
|
||||
|
||||
caps.RegisterHandler(
|
||||
"GetMesh",
|
||||
new GetMeshHandler("/CAPS/" + capID + "/", m_AssetService, "GetMesh", agentID.ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// m_log.DebugFormat("[GETMESH]: {0} in region {1}", m_URL, m_scene.RegionInfo.RegionName);
|
||||
caps.RegisterHandler("GetMesh", m_URL);
|
||||
// m_log.DebugFormat("[GETTEXTURE]: {0} in region {1}", m_URL, m_scene.RegionInfo.RegionName);
|
||||
IExternalCapsModule handler = m_scene.RequestModuleInterface<IExternalCapsModule>();
|
||||
if (handler != null)
|
||||
handler.RegisterExternalUserCapsHandler(agentID, caps, "GetMesh", m_URL);
|
||||
else
|
||||
caps.RegisterHandler("GetMesh", m_URL);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -57,8 +57,8 @@ namespace OpenSim.Region.ClientStack.Linden
|
|||
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimulatorFeaturesModule")]
|
||||
public class SimulatorFeaturesModule : ISharedRegionModule, ISimulatorFeaturesModule
|
||||
{
|
||||
private static readonly ILog m_log =
|
||||
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
// private static readonly ILog m_log =
|
||||
// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
public event SimulatorFeaturesRequestDelegate OnSimulatorFeaturesRequest;
|
||||
|
||||
|
|
|
@ -47,7 +47,6 @@ using OpenSim.Region.CoreModules.Framework;
|
|||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
using OSDArray = OpenMetaverse.StructuredData.OSDArray;
|
||||
using OSDMap = OpenMetaverse.StructuredData.OSDMap;
|
||||
|
||||
|
|
|
@ -0,0 +1,328 @@
|
|||
/*
|
||||
* 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.Concurrent;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using log4net;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Monitoring;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
|
||||
namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
public class Job
|
||||
{
|
||||
public string Name;
|
||||
public WaitCallback Callback;
|
||||
public object O;
|
||||
|
||||
public Job(string name, WaitCallback callback, object o)
|
||||
{
|
||||
Name = name;
|
||||
Callback = callback;
|
||||
O = o;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: These kinds of classes MUST be generalized with JobEngine, etc.
|
||||
public class IncomingPacketAsyncHandlingEngine
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
public int LogLevel { get; set; }
|
||||
|
||||
public bool IsRunning { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The timeout in milliseconds to wait for at least one event to be written when the recorder is stopping.
|
||||
/// </summary>
|
||||
public int RequestProcessTimeoutOnStop { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controls whether we need to warn in the log about exceeding the max queue size.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is flipped to false once queue max has been exceeded and back to true when it falls below max, in
|
||||
/// order to avoid spamming the log with lots of warnings.
|
||||
/// </remarks>
|
||||
private bool m_warnOverMaxQueue = true;
|
||||
|
||||
private BlockingCollection<Job> m_requestQueue;
|
||||
|
||||
private CancellationTokenSource m_cancelSource = new CancellationTokenSource();
|
||||
|
||||
private LLUDPServer m_udpServer;
|
||||
|
||||
private Stat m_requestsWaitingStat;
|
||||
|
||||
private Job m_currentJob;
|
||||
|
||||
/// <summary>
|
||||
/// Used to signal that we are ready to complete stop.
|
||||
/// </summary>
|
||||
private ManualResetEvent m_finishedProcessingAfterStop = new ManualResetEvent(false);
|
||||
|
||||
public IncomingPacketAsyncHandlingEngine(LLUDPServer server)
|
||||
{
|
||||
//LogLevel = 1;
|
||||
m_udpServer = server;
|
||||
RequestProcessTimeoutOnStop = 5000;
|
||||
|
||||
// MainConsole.Instance.Commands.AddCommand(
|
||||
// "Debug",
|
||||
// false,
|
||||
// "debug jobengine",
|
||||
// "debug jobengine <start|stop|status>",
|
||||
// "Start, stop or get status of the job engine.",
|
||||
// "If stopped then all jobs are processed immediately.",
|
||||
// HandleControlCommand);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (IsRunning)
|
||||
return;
|
||||
|
||||
IsRunning = true;
|
||||
|
||||
m_finishedProcessingAfterStop.Reset();
|
||||
|
||||
m_requestQueue = new BlockingCollection<Job>(new ConcurrentQueue<Job>(), 5000);
|
||||
|
||||
m_requestsWaitingStat =
|
||||
new Stat(
|
||||
"IncomingPacketAsyncRequestsWaiting",
|
||||
"Number of incoming packets waiting for async processing in engine.",
|
||||
"",
|
||||
"",
|
||||
"clientstack",
|
||||
m_udpServer.Scene.Name,
|
||||
StatType.Pull,
|
||||
MeasuresOfInterest.None,
|
||||
stat => stat.Value = m_requestQueue.Count,
|
||||
StatVerbosity.Debug);
|
||||
|
||||
StatsManager.RegisterStat(m_requestsWaitingStat);
|
||||
|
||||
Watchdog.StartThread(
|
||||
ProcessRequests,
|
||||
string.Format("Incoming Packet Async Handling Engine Thread ({0})", m_udpServer.Scene.Name),
|
||||
ThreadPriority.Normal,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
int.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsRunning)
|
||||
return;
|
||||
|
||||
IsRunning = false;
|
||||
|
||||
int requestsLeft = m_requestQueue.Count;
|
||||
|
||||
if (requestsLeft <= 0)
|
||||
{
|
||||
m_cancelSource.Cancel();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log.InfoFormat("[INCOMING PACKET ASYNC HANDLING ENGINE]: Waiting to write {0} events after stop.", requestsLeft);
|
||||
|
||||
while (requestsLeft > 0)
|
||||
{
|
||||
if (!m_finishedProcessingAfterStop.WaitOne(RequestProcessTimeoutOnStop))
|
||||
{
|
||||
// After timeout no events have been written
|
||||
if (requestsLeft == m_requestQueue.Count)
|
||||
{
|
||||
m_log.WarnFormat(
|
||||
"[INCOMING PACKET ASYNC HANDLING ENGINE]: No requests processed after {0} ms wait. Discarding remaining {1} requests",
|
||||
RequestProcessTimeoutOnStop, requestsLeft);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
requestsLeft = m_requestQueue.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_cancelSource.Dispose();
|
||||
StatsManager.DeregisterStat(m_requestsWaitingStat);
|
||||
m_requestsWaitingStat = null;
|
||||
m_requestQueue = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool QueueRequest(string name, WaitCallback req, object o)
|
||||
{
|
||||
if (LogLevel >= 1)
|
||||
m_log.DebugFormat("[INCOMING PACKET ASYNC HANDLING ENGINE]: Queued job {0}", name);
|
||||
|
||||
if (m_requestQueue.Count < m_requestQueue.BoundedCapacity)
|
||||
{
|
||||
// m_log.DebugFormat(
|
||||
// "[OUTGOING QUEUE REFILL ENGINE]: Adding request for categories {0} for {1} in {2}",
|
||||
// categories, client.AgentID, m_udpServer.Scene.Name);
|
||||
|
||||
m_requestQueue.Add(new Job(name, req, o));
|
||||
|
||||
if (!m_warnOverMaxQueue)
|
||||
m_warnOverMaxQueue = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_warnOverMaxQueue)
|
||||
{
|
||||
// m_log.WarnFormat(
|
||||
// "[JOB ENGINE]: Request queue at maximum capacity, not recording request from {0} in {1}",
|
||||
// client.AgentID, m_udpServer.Scene.Name);
|
||||
|
||||
m_log.WarnFormat("[INCOMING PACKET ASYNC HANDLING ENGINE]: Request queue at maximum capacity, not recording job");
|
||||
|
||||
m_warnOverMaxQueue = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessRequests()
|
||||
{
|
||||
try
|
||||
{
|
||||
while (IsRunning || m_requestQueue.Count > 0)
|
||||
{
|
||||
m_currentJob = m_requestQueue.Take(m_cancelSource.Token);
|
||||
|
||||
// QueueEmpty callback = req.Client.OnQueueEmpty;
|
||||
//
|
||||
// if (callback != null)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// callback(req.Categories);
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// m_log.Error("[OUTGOING QUEUE REFILL ENGINE]: ProcessRequests(" + req.Categories + ") threw an exception: " + e.Message, e);
|
||||
// }
|
||||
// }
|
||||
|
||||
if (LogLevel >= 1)
|
||||
m_log.DebugFormat("[INCOMING PACKET ASYNC HANDLING ENGINE]: Processing job {0}", m_currentJob.Name);
|
||||
|
||||
try
|
||||
{
|
||||
m_currentJob.Callback.Invoke(m_currentJob.O);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.Error(
|
||||
string.Format(
|
||||
"[INCOMING PACKET ASYNC HANDLING ENGINE]: Job {0} failed, continuing. Exception ", m_currentJob.Name), e);
|
||||
}
|
||||
|
||||
if (LogLevel >= 1)
|
||||
m_log.DebugFormat("[INCOMING PACKET ASYNC HANDLING ENGINE]: Processed job {0}", m_currentJob.Name);
|
||||
|
||||
m_currentJob = null;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
|
||||
m_finishedProcessingAfterStop.Set();
|
||||
}
|
||||
|
||||
// private void HandleControlCommand(string module, string[] args)
|
||||
// {
|
||||
// // if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
// // return;
|
||||
//
|
||||
// if (args.Length < 3)
|
||||
// {
|
||||
// MainConsole.Instance.Output("Usage: debug jobengine <stop|start|status|loglevel>");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// string subCommand = args[2];
|
||||
//
|
||||
// if (subCommand == "stop")
|
||||
// {
|
||||
// Stop();
|
||||
// MainConsole.Instance.OutputFormat("Stopped job engine.");
|
||||
// }
|
||||
// else if (subCommand == "start")
|
||||
// {
|
||||
// Start();
|
||||
// MainConsole.Instance.OutputFormat("Started job engine.");
|
||||
// }
|
||||
// else if (subCommand == "status")
|
||||
// {
|
||||
// MainConsole.Instance.OutputFormat("Job engine running: {0}", IsRunning);
|
||||
// MainConsole.Instance.OutputFormat("Current job {0}", m_currentJob != null ? m_currentJob.Name : "none");
|
||||
// MainConsole.Instance.OutputFormat(
|
||||
// "Jobs waiting: {0}", IsRunning ? m_requestQueue.Count.ToString() : "n/a");
|
||||
// MainConsole.Instance.OutputFormat("Log Level: {0}", LogLevel);
|
||||
// }
|
||||
//
|
||||
// else if (subCommand == "loglevel")
|
||||
// {
|
||||
// // int logLevel;
|
||||
// int logLevel = int.Parse(args[3]);
|
||||
// // if (ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out logLevel))
|
||||
// // {
|
||||
// LogLevel = logLevel;
|
||||
// MainConsole.Instance.OutputFormat("Set log level to {0}", LogLevel);
|
||||
// // }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// MainConsole.Instance.OutputFormat("Unrecognized job engine subcommand {0}", subCommand);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
|
@ -647,13 +647,37 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
/// </param>
|
||||
/// <returns>true if the handler was added. This is currently always the case.</returns>
|
||||
public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync)
|
||||
{
|
||||
return AddLocalPacketHandler(packetType, handler, doAsync, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a handler for the given packet type.
|
||||
/// </summary>
|
||||
/// <param name="packetType"></param>
|
||||
/// <param name="handler"></param>
|
||||
/// <param name="doAsync">
|
||||
/// If true, when the packet is received handle it on a different thread. Whether this is given direct to
|
||||
/// a threadpool thread or placed in a queue depends on the inEngine parameter.
|
||||
/// </param>
|
||||
/// <param name="inEngine">
|
||||
/// If async is false then this parameter is ignored.
|
||||
/// If async is true and inEngine is false, then the packet is sent directly to a
|
||||
/// threadpool thread.
|
||||
/// If async is true and inEngine is true, then the packet is sent to the IncomingPacketAsyncHandlingEngine.
|
||||
/// This may result in slower handling but reduces the risk of overloading the simulator when there are many
|
||||
/// simultaneous async requests.
|
||||
/// </param>
|
||||
/// <returns>true if the handler was added. This is currently always the case.</returns>
|
||||
public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync, bool inEngine)
|
||||
{
|
||||
bool result = false;
|
||||
lock (m_packetHandlers)
|
||||
{
|
||||
if (!m_packetHandlers.ContainsKey(packetType))
|
||||
{
|
||||
m_packetHandlers.Add(packetType, new PacketProcessor() { method = handler, Async = doAsync });
|
||||
m_packetHandlers.Add(
|
||||
packetType, new PacketProcessor() { method = handler, Async = doAsync, InEngine = inEngine });
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
@ -688,21 +712,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
PacketProcessor pprocessor;
|
||||
if (m_packetHandlers.TryGetValue(packet.Type, out pprocessor))
|
||||
{
|
||||
ClientInfo cinfo = UDPClient.GetClientInfo();
|
||||
|
||||
//there is a local handler for this packet type
|
||||
if (pprocessor.Async)
|
||||
{
|
||||
ClientInfo cinfo = UDPClient.GetClientInfo();
|
||||
if (!cinfo.AsyncRequests.ContainsKey(packet.Type.ToString()))
|
||||
cinfo.AsyncRequests[packet.Type.ToString()] = 0;
|
||||
cinfo.AsyncRequests[packet.Type.ToString()]++;
|
||||
|
||||
object obj = new AsyncPacketProcess(this, pprocessor.method, packet);
|
||||
Util.FireAndForget(ProcessSpecificPacketAsync, obj, packet.Type.ToString());
|
||||
|
||||
if (pprocessor.InEngine)
|
||||
m_udpServer.IpahEngine.QueueRequest(
|
||||
packet.Type.ToString(),
|
||||
ProcessSpecificPacketAsync,
|
||||
obj);
|
||||
else
|
||||
Util.FireAndForget(ProcessSpecificPacketAsync, obj, packet.Type.ToString());
|
||||
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ClientInfo cinfo = UDPClient.GetClientInfo();
|
||||
if (!cinfo.SyncRequests.ContainsKey(packet.Type.ToString()))
|
||||
cinfo.SyncRequests[packet.Type.ToString()] = 0;
|
||||
cinfo.SyncRequests[packet.Type.ToString()]++;
|
||||
|
@ -1161,7 +1193,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
/// <param name="map">heightmap</param>
|
||||
public virtual void SendLayerData(float[] map)
|
||||
{
|
||||
Util.FireAndForget(DoSendLayerData, m_scene.Heightmap.GetTerrainData());
|
||||
Util.FireAndForget(DoSendLayerData, m_scene.Heightmap.GetTerrainData(), "LLClientView.DoSendLayerData");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1373,7 +1405,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
/// <param name="windSpeeds">16x16 array of wind speeds</param>
|
||||
public virtual void SendWindData(Vector2[] windSpeeds)
|
||||
{
|
||||
Util.FireAndForget(DoSendWindData, windSpeeds);
|
||||
Util.FireAndForget(DoSendWindData, windSpeeds, "LLClientView.SendWindData");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1382,7 +1414,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
/// <param name="windSpeeds">16x16 array of cloud densities</param>
|
||||
public virtual void SendCloudData(float[] cloudDensity)
|
||||
{
|
||||
Util.FireAndForget(DoSendCloudData, cloudDensity);
|
||||
Util.FireAndForget(DoSendCloudData, cloudDensity, "LLClientView.SendCloudData");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -3834,11 +3866,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
/// </summary>
|
||||
public void SendEntityUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags)
|
||||
{
|
||||
//double priority = m_prioritizer.GetUpdatePriority(this, entity);
|
||||
uint priority = m_prioritizer.GetUpdatePriority(this, entity);
|
||||
if (entity.UUID == m_agentId && !updateFlags.HasFlag(PrimUpdateFlags.FullUpdate))
|
||||
{
|
||||
ImprovedTerseObjectUpdatePacket packet
|
||||
= (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate);
|
||||
|
||||
lock (m_entityUpdates.SyncRoot)
|
||||
m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation));
|
||||
packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
|
||||
packet.RegionData.TimeDilation = Utils.FloatToUInt16(1, 0.0f, 1.0f);
|
||||
packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1];
|
||||
packet.ObjectData[0] = CreateImprovedTerseBlock(entity, false);
|
||||
OutPacket(packet, ThrottleOutPacketType.Unknown, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
//double priority = m_prioritizer.GetUpdatePriority(this, entity);
|
||||
uint priority = m_prioritizer.GetUpdatePriority(this, entity);
|
||||
|
||||
lock (m_entityUpdates.SyncRoot)
|
||||
m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -5540,10 +5586,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
AddLocalPacketHandler(PacketType.ParcelBuy, HandleParcelBuyRequest, false);
|
||||
AddLocalPacketHandler(PacketType.UUIDGroupNameRequest, HandleUUIDGroupNameRequest);
|
||||
AddLocalPacketHandler(PacketType.ObjectGroup, HandleObjectGroupRequest);
|
||||
AddLocalPacketHandler(PacketType.GenericMessage, HandleGenericMessage);
|
||||
AddLocalPacketHandler(PacketType.AvatarPropertiesRequest, HandleAvatarPropertiesRequest);
|
||||
AddLocalPacketHandler(PacketType.GenericMessage, HandleGenericMessage, true, true);
|
||||
AddLocalPacketHandler(PacketType.AvatarPropertiesRequest, HandleAvatarPropertiesRequest, true, true);
|
||||
AddLocalPacketHandler(PacketType.ChatFromViewer, HandleChatFromViewer);
|
||||
AddLocalPacketHandler(PacketType.AvatarPropertiesUpdate, HandlerAvatarPropertiesUpdate);
|
||||
AddLocalPacketHandler(PacketType.AvatarPropertiesUpdate, HandlerAvatarPropertiesUpdate, true, true);
|
||||
AddLocalPacketHandler(PacketType.ScriptDialogReply, HandlerScriptDialogReply);
|
||||
AddLocalPacketHandler(PacketType.ImprovedInstantMessage, HandlerImprovedInstantMessage);
|
||||
AddLocalPacketHandler(PacketType.AcceptFriendship, HandlerAcceptFriendship);
|
||||
|
@ -5728,8 +5774,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
AddLocalPacketHandler(PacketType.PickDelete, HandlePickDelete);
|
||||
AddLocalPacketHandler(PacketType.PickGodDelete, HandlePickGodDelete);
|
||||
AddLocalPacketHandler(PacketType.PickInfoUpdate, HandlePickInfoUpdate);
|
||||
AddLocalPacketHandler(PacketType.AvatarNotesUpdate, HandleAvatarNotesUpdate);
|
||||
AddLocalPacketHandler(PacketType.AvatarInterestsUpdate, HandleAvatarInterestsUpdate);
|
||||
AddLocalPacketHandler(PacketType.AvatarNotesUpdate, HandleAvatarNotesUpdate, true, true);
|
||||
AddLocalPacketHandler(PacketType.AvatarInterestsUpdate, HandleAvatarInterestsUpdate, true, true);
|
||||
AddLocalPacketHandler(PacketType.GrantUserRights, HandleGrantUserRights);
|
||||
AddLocalPacketHandler(PacketType.PlacesQuery, HandlePlacesQuery);
|
||||
AddLocalPacketHandler(PacketType.UpdateMuteListEntry, HandleUpdateMuteListEntry);
|
||||
|
@ -8079,7 +8125,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
{
|
||||
// This requests the asset if needed
|
||||
HandleSimInventoryTransferRequestWithPermsCheck(sender, transfer);
|
||||
});
|
||||
}, null, "LLClientView.HandleTransferRequest");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -12786,8 +12833,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
public struct PacketProcessor
|
||||
{
|
||||
public PacketMethod method;
|
||||
public bool Async;
|
||||
/// <summary>
|
||||
/// Packet handling method.
|
||||
/// </summary>
|
||||
public PacketMethod method { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should this packet be handled asynchronously?
|
||||
/// </summary>
|
||||
public bool Async { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If async is true, should this packet be handled in the async engine or given directly to a threadpool
|
||||
/// thread?
|
||||
/// </summary>
|
||||
public bool InEngine { get; set; }
|
||||
}
|
||||
|
||||
public class AsyncPacketProcess
|
||||
|
|
|
@ -76,6 +76,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
/// or removed, this number must also change</summary>
|
||||
const int THROTTLE_CATEGORY_COUNT = 8;
|
||||
|
||||
/// <summary>
|
||||
/// Controls whether information is logged about each outbound packet immediately before it is sent. For debug purposes.
|
||||
/// </summary>
|
||||
/// <remarks>Any level above 0 will turn on logging.</remarks>
|
||||
public int DebugDataOutLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controls whether information is logged about each outbound packet immediately before it is sent. For debug purposes.
|
||||
/// </summary>
|
||||
/// <remarks>Any level above 0 will turn on logging.</remarks>
|
||||
public int ThrottleDebugLevel
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_throttleDebugLevel;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
m_throttleDebugLevel = value;
|
||||
m_throttleClient.DebugLevel = m_throttleDebugLevel;
|
||||
foreach (TokenBucket tb in m_throttleCategories)
|
||||
tb.DebugLevel = m_throttleDebugLevel;
|
||||
}
|
||||
}
|
||||
private int m_throttleDebugLevel;
|
||||
|
||||
/// <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
|
||||
|
@ -144,8 +171,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
get { return m_throttleClient; }
|
||||
}
|
||||
|
||||
/// <summary>Throttle bucket for this agent's connection</summary>
|
||||
private readonly TokenBucket m_throttleCategory;
|
||||
/// <summary>Throttle buckets for each packet category</summary>
|
||||
private readonly TokenBucket[] m_throttleCategories;
|
||||
/// <summary>Outgoing queues for throttled packets</summary>
|
||||
|
@ -201,9 +226,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
m_maxRTO = maxRTO;
|
||||
|
||||
// Create a token bucket throttle for this client that has the scene token bucket as a parent
|
||||
m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.Total, rates.AdaptiveThrottlesEnabled);
|
||||
// Create a token bucket throttle for the total category with the client bucket as a throttle
|
||||
m_throttleCategory = new TokenBucket(m_throttleClient, 0);
|
||||
m_throttleClient
|
||||
= new AdaptiveTokenBucket(
|
||||
string.Format("adaptive throttle for {0} in {1}", AgentID, server.Scene.Name),
|
||||
parentThrottle, 0, rates.Total, rates.AdaptiveThrottlesEnabled);
|
||||
|
||||
// Create an array of token buckets for this clients different throttle categories
|
||||
m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
|
||||
|
||||
|
@ -215,8 +242,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
// Initialize the packet outboxes, where packets sit while they are waiting for tokens
|
||||
m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
|
||||
|
||||
// Initialize the token buckets that control the throttling for each category
|
||||
m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetRate(type));
|
||||
m_throttleCategories[i]
|
||||
= new TokenBucket(
|
||||
string.Format("{0} throttle for {1} in {2}", type, AgentID, server.Scene.Name),
|
||||
m_throttleClient, rates.GetRate(type), 0);
|
||||
}
|
||||
|
||||
// Default the retransmission timeout to one second
|
||||
|
@ -261,7 +292,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
m_info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
|
||||
m_info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
|
||||
m_info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
|
||||
m_info.totalThrottle = (int)m_throttleCategory.DripRate;
|
||||
m_info.totalThrottle = (int)m_throttleClient.DripRate;
|
||||
m_info.targetThrottle = (int)m_throttleClient.TargetDripRate;
|
||||
m_info.maxThrottle = (int)m_throttleClient.MaxDripRate;
|
||||
|
||||
return m_info;
|
||||
|
@ -279,6 +311,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the total number of pakcets queued for this client.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int GetTotalPacketsQueuedCount()
|
||||
{
|
||||
int total = 0;
|
||||
|
||||
for (int i = 0; i <= (int)ThrottleOutPacketType.Asset; i++)
|
||||
total += m_packetOutboxes[i].Count;
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of packets queued for the given throttle type.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <param name="throttleType"></param>
|
||||
public int GetPacketsQueuedCount(ThrottleOutPacketType throttleType)
|
||||
{
|
||||
if ((int)throttleType > 0)
|
||||
return m_packetOutboxes[(int)throttleType].Count;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return statistics information about client packet queues.
|
||||
/// </summary>
|
||||
|
@ -348,6 +407,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
|
||||
int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
|
||||
|
||||
if (ThrottleDebugLevel > 0)
|
||||
{
|
||||
long total = resend + land + wind + cloud + task + texture + asset;
|
||||
m_log.DebugFormat(
|
||||
"[LLUDPCLIENT]: {0} is setting throttles in {1} to Resend={2}, Land={3}, Wind={4}, Cloud={5}, Task={6}, Texture={7}, Asset={8}, TOTAL = {9}",
|
||||
AgentID, m_udpServer.Scene.Name, resend, land, wind, cloud, task, texture, asset, total);
|
||||
}
|
||||
|
||||
// Make sure none of the throttles are set below our packet MTU,
|
||||
// otherwise a throttle could become permanently clogged
|
||||
resend = Math.Max(resend, LLUDPServer.MTU);
|
||||
|
@ -365,10 +432,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
texture = (int)((1 - m_cannibalrate) * texture);
|
||||
|
||||
//int total = resend + land + wind + cloud + task + texture + asset;
|
||||
//m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, Total={8}",
|
||||
// AgentID, resend, land, wind, cloud, task, texture, asset, total);
|
||||
|
||||
if (ThrottleDebugLevel > 0)
|
||||
{
|
||||
long total = resend + land + wind + cloud + task + texture + asset;
|
||||
m_log.DebugFormat(
|
||||
"[LLUDPCLIENT]: {0} is setting throttles in {1} to Resend={2}, Land={3}, Wind={4}, Cloud={5}, Task={6}, Texture={7}, Asset={8}, TOTAL = {9}",
|
||||
AgentID, m_udpServer.Scene.Name, resend, land, wind, cloud, task, texture, asset, total);
|
||||
}
|
||||
|
||||
// Update the token buckets with new throttle values
|
||||
if (m_throttleClient.AdaptiveEnabled)
|
||||
{
|
||||
long total = resend + land + wind + cloud + task + texture + asset;
|
||||
m_throttleClient.TargetDripRate = total;
|
||||
}
|
||||
|
||||
TokenBucket bucket;
|
||||
|
||||
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
|
||||
|
@ -653,7 +732,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
if (!m_udpServer.OqrEngine.IsRunning)
|
||||
{
|
||||
// Asynchronously run the callback
|
||||
Util.FireAndForget(FireQueueEmpty, categories);
|
||||
Util.FireAndForget(FireQueueEmpty, categories, "LLUDPClient.BeginFireQueueEmpty");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -34,7 +34,6 @@ using System.Net.Sockets;
|
|||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using log4net;
|
||||
using NDesk.Options;
|
||||
using Nini.Config;
|
||||
using OpenMetaverse.Packets;
|
||||
using OpenSim.Framework;
|
||||
|
@ -222,6 +221,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
/// </summary>
|
||||
public int DefaultClientPacketDebugLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If set then all inbound agent updates are discarded. For debugging purposes.
|
||||
/// discard agent update.
|
||||
/// </summary>
|
||||
public bool DiscardInboundAgentUpdates { get; set; }
|
||||
|
||||
/// <summary>The measured resolution of Environment.TickCount</summary>
|
||||
public readonly float TickCountResolution;
|
||||
|
||||
|
@ -238,12 +243,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
/// <summary>Incoming packets that are awaiting handling</summary>
|
||||
private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>();
|
||||
|
||||
/// <summary></summary>
|
||||
//private UDPClientCollection m_clients = new UDPClientCollection();
|
||||
/// <summary>Bandwidth throttle for this UDP server</summary>
|
||||
protected TokenBucket m_throttle;
|
||||
public TokenBucket Throttle { get; private set; }
|
||||
|
||||
/// <summary>Bandwidth throttle rates for this UDP server</summary>
|
||||
/// <summary>Per client throttle rates enforced by this server</summary>
|
||||
/// <remarks>
|
||||
/// If the total rate is non-zero, then this is the maximum total throttle setting that any client can ever have.
|
||||
/// The other rates (resend, asset, etc.) are the defaults for a new client and can be changed (and usually
|
||||
/// do get changed immediately). They do not need to sum to the total.
|
||||
/// </remarks>
|
||||
public ThrottleRates ThrottleRates { get; private set; }
|
||||
|
||||
/// <summary>Manages authentication for agent circuits</summary>
|
||||
|
@ -355,6 +363,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
/// </summary>
|
||||
private IClientAPI m_currentIncomingClient;
|
||||
|
||||
/// <summary>
|
||||
/// Queue some low priority but potentially high volume async requests so that they don't overwhelm available
|
||||
/// threadpool threads.
|
||||
/// </summary>
|
||||
public IncomingPacketAsyncHandlingEngine IpahEngine { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Experimental facility to run queue empty processing within a controlled number of threads rather than
|
||||
/// requiring massive numbers of short-lived threads from the threadpool when there are a high number of
|
||||
|
@ -434,12 +448,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
}
|
||||
#endregion BinaryStats
|
||||
|
||||
m_throttle = new TokenBucket(null, sceneThrottleBps);
|
||||
// FIXME: Can't add info here because don't know scene yet.
|
||||
// m_throttle
|
||||
// = new TokenBucket(
|
||||
// string.Format("server throttle bucket for {0}", Scene.Name), null, sceneThrottleBps);
|
||||
|
||||
Throttle = new TokenBucket("server throttle bucket", null, 0, sceneThrottleBps);
|
||||
|
||||
ThrottleRates = new ThrottleRates(configSource);
|
||||
|
||||
if (usePools)
|
||||
EnablePools();
|
||||
|
||||
IpahEngine = new IncomingPacketAsyncHandlingEngine(this);
|
||||
OqrEngine = new OutgoingQueueRefillEngine(this);
|
||||
}
|
||||
|
||||
|
@ -447,12 +468,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
{
|
||||
StartInbound();
|
||||
StartOutbound();
|
||||
IpahEngine.Start();
|
||||
OqrEngine.Start();
|
||||
|
||||
m_elapsedMSSinceLastStatReport = Environment.TickCount;
|
||||
}
|
||||
|
||||
private void StartInbound()
|
||||
public void StartInbound()
|
||||
{
|
||||
m_log.InfoFormat(
|
||||
"[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server in {0} mode with UsePools = {1}",
|
||||
|
@ -471,7 +493,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
private new void StartOutbound()
|
||||
public override void StartOutbound()
|
||||
{
|
||||
m_log.Info("[LLUDPSERVER]: Starting outbound packet processing for the LLUDP server");
|
||||
|
||||
|
@ -492,10 +514,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + Scene.Name);
|
||||
base.StopOutbound();
|
||||
base.StopInbound();
|
||||
IpahEngine.Stop();
|
||||
OqrEngine.Stop();
|
||||
}
|
||||
|
||||
protected override bool EnablePools()
|
||||
public override bool EnablePools()
|
||||
{
|
||||
if (!UsePools)
|
||||
{
|
||||
|
@ -509,7 +532,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
return false;
|
||||
}
|
||||
|
||||
protected override bool DisablePools()
|
||||
public override bool DisablePools()
|
||||
{
|
||||
if (UsePools)
|
||||
{
|
||||
|
@ -529,7 +552,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
/// This is a seperate method so that it can be called once we have an m_scene to distinguish different scene
|
||||
/// stats.
|
||||
/// </summary>
|
||||
private void EnablePoolStats()
|
||||
protected internal void EnablePoolStats()
|
||||
{
|
||||
m_poolCountStat
|
||||
= new Stat(
|
||||
|
@ -563,7 +586,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
/// <summary>
|
||||
/// Disables pool stats.
|
||||
/// </summary>
|
||||
private void DisablePoolStats()
|
||||
protected internal void DisablePoolStats()
|
||||
{
|
||||
StatsManager.DeregisterStat(m_poolCountStat);
|
||||
m_poolCountStat = null;
|
||||
|
@ -677,312 +700,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
StatType.Pull,
|
||||
stat => stat.Value = PacketPool.Instance.BlocksPooled,
|
||||
StatVerbosity.Debug));
|
||||
|
||||
StatsManager.RegisterStat(
|
||||
new Stat(
|
||||
"OutgoingPacketsQueuedCount",
|
||||
"Packets queued for outgoing send",
|
||||
"Number of queued outgoing packets across all connections",
|
||||
"",
|
||||
"clientstack",
|
||||
Scene.Name,
|
||||
StatType.Pull,
|
||||
MeasuresOfInterest.AverageChangeOverTime,
|
||||
stat => stat.Value = GetTotalQueuedOutgoingPackets(),
|
||||
StatVerbosity.Info));
|
||||
|
||||
// We delay enabling pool stats to AddScene() instead of Initialize() so that we can distinguish pool stats by
|
||||
// scene name
|
||||
if (UsePools)
|
||||
EnablePoolStats();
|
||||
|
||||
MainConsole.Instance.Commands.AddCommand(
|
||||
"Debug", false, "debug lludp packet",
|
||||
"debug lludp packet [--default | --all] <level> [<avatar-first-name> <avatar-last-name>]",
|
||||
"Turn on packet debugging",
|
||||
"If level > 255 then all incoming and outgoing packets are logged.\n"
|
||||
+ "If level <= 255 then incoming AgentUpdate and outgoing SimStats and SimulatorViewerTimeMessage packets are not logged.\n"
|
||||
+ "If level <= 200 then incoming RequestImage and outgoing ImagePacket, ImageData, LayerData and CoarseLocationUpdate packets are not logged.\n"
|
||||
+ "If level <= 100 then incoming ViewerEffect and AgentAnimation and outgoing ViewerEffect and AvatarAnimation packets are not logged.\n"
|
||||
+ "If level <= 50 then outgoing ImprovedTerseObjectUpdate packets are not logged.\n"
|
||||
+ "If level <= 0 then no packets are logged.\n"
|
||||
+ "If --default is specified then the level becomes the default logging level for all subsequent agents.\n"
|
||||
+ "If --all is specified then the level becomes the default logging level for all current and subsequent agents.\n"
|
||||
+ "In these cases, you cannot also specify an avatar name.\n"
|
||||
+ "If an avatar name is given then only packets from that avatar are logged.",
|
||||
HandlePacketCommand);
|
||||
|
||||
MainConsole.Instance.Commands.AddCommand(
|
||||
"Debug", false, "debug lludp drop",
|
||||
"debug lludp drop <in|out> <add|remove> <packet-name>",
|
||||
"Drop all in or outbound packets that match the given name",
|
||||
"For test purposes.",
|
||||
HandleDropCommand);
|
||||
|
||||
MainConsole.Instance.Commands.AddCommand(
|
||||
"Debug",
|
||||
false,
|
||||
"debug lludp start",
|
||||
"debug lludp start <in|out|all>",
|
||||
"Control LLUDP packet processing.",
|
||||
"No effect if packet processing has already started.\n"
|
||||
+ "in - start inbound processing.\n"
|
||||
+ "out - start outbound processing.\n"
|
||||
+ "all - start in and outbound processing.\n",
|
||||
HandleStartCommand);
|
||||
|
||||
MainConsole.Instance.Commands.AddCommand(
|
||||
"Debug",
|
||||
false,
|
||||
"debug lludp stop",
|
||||
"debug lludp stop <in|out|all>",
|
||||
"Stop LLUDP packet processing.",
|
||||
"No effect if packet processing has already stopped.\n"
|
||||
+ "in - stop inbound processing.\n"
|
||||
+ "out - stop outbound processing.\n"
|
||||
+ "all - stop in and outbound processing.\n",
|
||||
HandleStopCommand);
|
||||
|
||||
MainConsole.Instance.Commands.AddCommand(
|
||||
"Debug",
|
||||
false,
|
||||
"debug lludp pool",
|
||||
"debug lludp pool <on|off>",
|
||||
"Turn object pooling within the lludp component on or off.",
|
||||
HandlePoolCommand);
|
||||
|
||||
MainConsole.Instance.Commands.AddCommand(
|
||||
"Debug",
|
||||
false,
|
||||
"debug lludp status",
|
||||
"debug lludp status",
|
||||
"Return status of LLUDP packet processing.",
|
||||
HandleStatusCommand);
|
||||
|
||||
MainConsole.Instance.Commands.AddCommand(
|
||||
"Debug",
|
||||
false,
|
||||
"debug lludp toggle agentupdate",
|
||||
"debug lludp toggle agentupdate",
|
||||
"Toggle whether agentupdate packets are processed or simply discarded.",
|
||||
HandleAgentUpdateCommand);
|
||||
}
|
||||
|
||||
private void HandlePacketCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
|
||||
return;
|
||||
|
||||
bool setAsDefaultLevel = false;
|
||||
bool setAll = false;
|
||||
OptionSet optionSet = new OptionSet()
|
||||
.Add("default", o => setAsDefaultLevel = (o != null))
|
||||
.Add("all", o => setAll = (o != null));
|
||||
List<string> filteredArgs = optionSet.Parse(args);
|
||||
|
||||
string name = null;
|
||||
|
||||
if (filteredArgs.Count == 6)
|
||||
{
|
||||
if (!(setAsDefaultLevel || setAll))
|
||||
{
|
||||
name = string.Format("{0} {1}", filteredArgs[4], filteredArgs[5]);
|
||||
}
|
||||
else
|
||||
{
|
||||
MainConsole.Instance.OutputFormat("ERROR: Cannot specify a user name when setting default/all logging level");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (filteredArgs.Count > 3)
|
||||
{
|
||||
int newDebug;
|
||||
if (int.TryParse(filteredArgs[3], out newDebug))
|
||||
{
|
||||
if (setAsDefaultLevel || setAll)
|
||||
{
|
||||
DefaultClientPacketDebugLevel = newDebug;
|
||||
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Packet debug for {0} clients set to {1} in {2}",
|
||||
(setAll ? "all" : "future"), DefaultClientPacketDebugLevel, Scene.Name);
|
||||
|
||||
if (setAll)
|
||||
{
|
||||
Scene.ForEachScenePresence(sp =>
|
||||
{
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Packet debug for {0} ({1}) set to {2} in {3}",
|
||||
sp.Name, sp.IsChildAgent ? "child" : "root", newDebug, Scene.Name);
|
||||
|
||||
sp.ControllingClient.DebugPacketLevel = newDebug;
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Scene.ForEachScenePresence(sp =>
|
||||
{
|
||||
if (name == null || sp.Name == name)
|
||||
{
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Packet debug for {0} ({1}) set to {2} in {3}",
|
||||
sp.Name, sp.IsChildAgent ? "child" : "root", newDebug, Scene.Name);
|
||||
|
||||
sp.ControllingClient.DebugPacketLevel = newDebug;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MainConsole.Instance.Output("Usage: debug lludp packet [--default | --all] 0..255 [<first-name> <last-name>]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDropCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
|
||||
return;
|
||||
|
||||
if (args.Length != 6)
|
||||
{
|
||||
MainConsole.Instance.Output("Usage: debug lludp drop <in|out> <add|remove> <packet-name>");
|
||||
return;
|
||||
}
|
||||
|
||||
string direction = args[3];
|
||||
string subCommand = args[4];
|
||||
string packetName = args[5];
|
||||
|
||||
if (subCommand == "add")
|
||||
{
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Adding packet {0} to {1} drop list for all connections in {2}", direction, packetName, Scene.Name);
|
||||
|
||||
Scene.ForEachScenePresence(
|
||||
sp =>
|
||||
{
|
||||
LLClientView llcv = (LLClientView)sp.ControllingClient;
|
||||
|
||||
if (direction == "in")
|
||||
llcv.AddInPacketToDropSet(packetName);
|
||||
else if (direction == "out")
|
||||
llcv.AddOutPacketToDropSet(packetName);
|
||||
}
|
||||
);
|
||||
}
|
||||
else if (subCommand == "remove")
|
||||
{
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Removing packet {0} from {1} drop list for all connections in {2}", direction, packetName, Scene.Name);
|
||||
|
||||
Scene.ForEachScenePresence(
|
||||
sp =>
|
||||
{
|
||||
LLClientView llcv = (LLClientView)sp.ControllingClient;
|
||||
|
||||
if (direction == "in")
|
||||
llcv.RemoveInPacketFromDropSet(packetName);
|
||||
else if (direction == "out")
|
||||
llcv.RemoveOutPacketFromDropSet(packetName);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleStartCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
|
||||
return;
|
||||
|
||||
if (args.Length != 4)
|
||||
{
|
||||
MainConsole.Instance.Output("Usage: debug lludp start <in|out|all>");
|
||||
return;
|
||||
}
|
||||
|
||||
string subCommand = args[3];
|
||||
|
||||
if (subCommand == "in" || subCommand == "all")
|
||||
StartInbound();
|
||||
|
||||
if (subCommand == "out" || subCommand == "all")
|
||||
StartOutbound();
|
||||
}
|
||||
|
||||
private void HandleStopCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
|
||||
return;
|
||||
|
||||
if (args.Length != 4)
|
||||
{
|
||||
MainConsole.Instance.Output("Usage: debug lludp stop <in|out|all>");
|
||||
return;
|
||||
}
|
||||
|
||||
string subCommand = args[3];
|
||||
|
||||
if (subCommand == "in" || subCommand == "all")
|
||||
StopInbound();
|
||||
|
||||
if (subCommand == "out" || subCommand == "all")
|
||||
StopOutbound();
|
||||
}
|
||||
|
||||
private void HandlePoolCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
|
||||
return;
|
||||
|
||||
if (args.Length != 4)
|
||||
{
|
||||
MainConsole.Instance.Output("Usage: debug lludp pool <on|off>");
|
||||
return;
|
||||
}
|
||||
|
||||
string enabled = args[3];
|
||||
|
||||
if (enabled == "on")
|
||||
{
|
||||
if (EnablePools())
|
||||
{
|
||||
EnablePoolStats();
|
||||
MainConsole.Instance.OutputFormat("Packet pools enabled on {0}", Scene.Name);
|
||||
}
|
||||
}
|
||||
else if (enabled == "off")
|
||||
{
|
||||
if (DisablePools())
|
||||
{
|
||||
DisablePoolStats();
|
||||
MainConsole.Instance.OutputFormat("Packet pools disabled on {0}", Scene.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MainConsole.Instance.Output("Usage: debug lludp pool <on|off>");
|
||||
}
|
||||
}
|
||||
|
||||
bool m_discardAgentUpdates;
|
||||
|
||||
private void HandleAgentUpdateCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
|
||||
return;
|
||||
|
||||
m_discardAgentUpdates = !m_discardAgentUpdates;
|
||||
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Discard AgentUpdates now {0} for {1}", m_discardAgentUpdates, Scene.Name);
|
||||
}
|
||||
|
||||
private void HandleStatusCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != Scene)
|
||||
return;
|
||||
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"IN LLUDP packet processing for {0} is {1}", Scene.Name, IsRunningInbound ? "enabled" : "disabled");
|
||||
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"OUT LLUDP packet processing for {0} is {1}", Scene.Name, IsRunningOutbound ? "enabled" : "disabled");
|
||||
|
||||
MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", Scene.Name, UsePools ? "on" : "off");
|
||||
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Packet debug level for new clients is {0}", DefaultClientPacketDebugLevel);
|
||||
LLUDPServerCommands commands = new LLUDPServerCommands(MainConsole.Instance, this);
|
||||
commands.Register();
|
||||
}
|
||||
|
||||
public bool HandlesRegion(Location x)
|
||||
|
@ -990,6 +728,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
return x == m_location;
|
||||
}
|
||||
|
||||
public int GetTotalQueuedOutgoingPackets()
|
||||
{
|
||||
int total = 0;
|
||||
|
||||
foreach (ScenePresence sp in Scene.GetScenePresences())
|
||||
{
|
||||
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
|
||||
total += udpClient.GetTotalPacketsQueuedCount();
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
// public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting)
|
||||
// {
|
||||
// // CoarseLocationUpdate and AvatarGroupsReply packets cannot be split in an automated way
|
||||
|
@ -1156,6 +907,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
// packet so that it isn't sent before a queued update packet.
|
||||
bool forceQueue = (type == PacketType.KillObject);
|
||||
|
||||
// if (type == PacketType.ImprovedTerseObjectUpdate)
|
||||
// {
|
||||
// m_log.DebugFormat("Direct send ITOU to {0} in {1}", udpClient.AgentID, Scene.Name);
|
||||
// SendPacketFinal(outgoingPacket);
|
||||
// return false;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, forceQueue))
|
||||
{
|
||||
SendPacketFinal(outgoingPacket);
|
||||
|
@ -1165,6 +924,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
{
|
||||
return false;
|
||||
}
|
||||
// }
|
||||
|
||||
#endregion Queue or Send
|
||||
}
|
||||
|
@ -1240,7 +1000,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
// Fire this out on a different thread so that we don't hold up outgoing packet processing for
|
||||
// everybody else if this is being called due to an ack timeout.
|
||||
// This is the same as processing as the async process of a logout request.
|
||||
Util.FireAndForget(o => DeactivateClientDueToTimeout(client, timeoutTicks));
|
||||
Util.FireAndForget(
|
||||
o => DeactivateClientDueToTimeout(client, timeoutTicks), null, "LLUDPServer.DeactivateClientDueToTimeout");
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1360,6 +1121,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
// is 100% correct
|
||||
PacketsSentCount++;
|
||||
|
||||
if (udpClient.DebugDataOutLevel > 0)
|
||||
m_log.DebugFormat(
|
||||
"[LLUDPSERVER]: Sending packet #{0} (rel: {1}, res: {2}) to {3} from {4}",
|
||||
outgoingPacket.SequenceNumber, isReliable, isResend, udpClient.AgentID, Scene.Name);
|
||||
|
||||
// Put the UDP payload on the wire
|
||||
AsyncBeginSend(buffer);
|
||||
|
||||
|
@ -1469,7 +1235,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
// buffer.
|
||||
object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
|
||||
|
||||
Util.FireAndForget(HandleUseCircuitCode, array);
|
||||
Util.FireAndForget(HandleUseCircuitCode, array, "LLUDPServer.HandleUseCircuitCode");
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1482,7 +1248,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
// buffer.
|
||||
object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
|
||||
|
||||
Util.FireAndForget(HandleCompleteMovementIntoRegion, array);
|
||||
Util.FireAndForget(
|
||||
HandleCompleteMovementIntoRegion, array, "LLUDPServer.HandleCompleteMovementIntoRegion");
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1604,7 +1371,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
if (packet.Type == PacketType.AgentUpdate)
|
||||
{
|
||||
if (m_discardAgentUpdates)
|
||||
if (DiscardInboundAgentUpdates)
|
||||
return;
|
||||
|
||||
((LLClientView)client).TotalAgentUpdates++;
|
||||
|
@ -2012,7 +1779,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
{
|
||||
if (!Scene.TryGetClient(agentID, out client))
|
||||
{
|
||||
LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO);
|
||||
LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, Throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO);
|
||||
|
||||
client = new LLClientView(Scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode);
|
||||
client.OnLogout += LogoutHandler;
|
||||
|
@ -2055,6 +1822,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
private void IncomingPacketHandler()
|
||||
{
|
||||
Thread.CurrentThread.Priority = ThreadPriority.Highest;
|
||||
|
||||
// Set this culture for the thread that incoming packets are received
|
||||
// on to en-US to avoid number parsing issues
|
||||
Culture.SetCurrentCulture();
|
||||
|
@ -2100,6 +1869,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
private void OutgoingPacketHandler()
|
||||
{
|
||||
Thread.CurrentThread.Priority = ThreadPriority.Highest;
|
||||
|
||||
// Set this culture for the thread that outgoing packets are sent
|
||||
// on to en-US to avoid number parsing issues
|
||||
Culture.SetCurrentCulture();
|
||||
|
|
|
@ -0,0 +1,762 @@
|
|||
/*
|
||||
* 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.Text;
|
||||
using NDesk.Options;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Console;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
|
||||
namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
public class LLUDPServerCommands
|
||||
{
|
||||
private ICommandConsole m_console;
|
||||
private LLUDPServer m_udpServer;
|
||||
|
||||
public LLUDPServerCommands(ICommandConsole console, LLUDPServer udpServer)
|
||||
{
|
||||
m_console = console;
|
||||
m_udpServer = udpServer;
|
||||
}
|
||||
|
||||
public void Register()
|
||||
{
|
||||
m_console.Commands.AddCommand(
|
||||
"Comms", false, "show server throttles",
|
||||
"show server throttles",
|
||||
"Show information about server throttles",
|
||||
HandleShowServerThrottlesCommand);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Debug", false, "debug lludp packet",
|
||||
"debug lludp packet [--default | --all] <level> [<avatar-first-name> <avatar-last-name>]",
|
||||
"Turn on packet debugging. This logs information when the client stack hands a processed packet off to downstream code or when upstream code first requests that a certain packet be sent.",
|
||||
"If level > 255 then all incoming and outgoing packets are logged.\n"
|
||||
+ "If level <= 255 then incoming AgentUpdate and outgoing SimStats and SimulatorViewerTimeMessage packets are not logged.\n"
|
||||
+ "If level <= 200 then incoming RequestImage and outgoing ImagePacket, ImageData, LayerData and CoarseLocationUpdate packets are not logged.\n"
|
||||
+ "If level <= 100 then incoming ViewerEffect and AgentAnimation and outgoing ViewerEffect and AvatarAnimation packets are not logged.\n"
|
||||
+ "If level <= 50 then outgoing ImprovedTerseObjectUpdate packets are not logged.\n"
|
||||
+ "If level <= 0 then no packets are logged.\n"
|
||||
+ "If --default is specified then the level becomes the default logging level for all subsequent agents.\n"
|
||||
+ "If --all is specified then the level becomes the default logging level for all current and subsequent agents.\n"
|
||||
+ "In these cases, you cannot also specify an avatar name.\n"
|
||||
+ "If an avatar name is given then only packets from that avatar are logged.",
|
||||
HandlePacketCommand);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Debug", false, "debug lludp data out",
|
||||
"debug lludp data out <level> <avatar-first-name> <avatar-last-name>\"",
|
||||
"Turn on debugging for final outgoing data to the given user's client.",
|
||||
"This operates at a much lower level than the packet command and prints out available details when the data is actually sent.\n"
|
||||
+ "If level > 0 then information about all outgoing UDP data for this avatar is logged.\n"
|
||||
+ "If level <= 0 then no information about outgoing UDP data for this avatar is logged.",
|
||||
HandleDataCommand);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Debug", false, "debug lludp drop",
|
||||
"debug lludp drop <in|out> <add|remove> <packet-name>",
|
||||
"Drop all in or outbound packets that match the given name",
|
||||
"For test purposes.",
|
||||
HandleDropCommand);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Debug",
|
||||
false,
|
||||
"debug lludp start",
|
||||
"debug lludp start <in|out|all>",
|
||||
"Control LLUDP packet processing.",
|
||||
"No effect if packet processing has already started.\n"
|
||||
+ "in - start inbound processing.\n"
|
||||
+ "out - start outbound processing.\n"
|
||||
+ "all - start in and outbound processing.\n",
|
||||
HandleStartCommand);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Debug",
|
||||
false,
|
||||
"debug lludp stop",
|
||||
"debug lludp stop <in|out|all>",
|
||||
"Stop LLUDP packet processing.",
|
||||
"No effect if packet processing has already stopped.\n"
|
||||
+ "in - stop inbound processing.\n"
|
||||
+ "out - stop outbound processing.\n"
|
||||
+ "all - stop in and outbound processing.\n",
|
||||
HandleStopCommand);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Debug",
|
||||
false,
|
||||
"debug lludp pool",
|
||||
"debug lludp pool <on|off>",
|
||||
"Turn object pooling within the lludp component on or off.",
|
||||
HandlePoolCommand);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Debug",
|
||||
false,
|
||||
"debug lludp status",
|
||||
"debug lludp status",
|
||||
"Return status of LLUDP packet processing.",
|
||||
HandleStatusCommand);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Debug",
|
||||
false,
|
||||
"debug lludp throttles log",
|
||||
"debug lludp throttles log <level> [<avatar-first-name> <avatar-last-name>]",
|
||||
"Change debug logging level for throttles.",
|
||||
"If level >= 0 then throttle debug logging is performed.\n"
|
||||
+ "If level <= 0 then no throttle debug logging is performed.",
|
||||
HandleThrottleCommand);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Debug",
|
||||
false,
|
||||
"debug lludp throttles get",
|
||||
"debug lludp throttles get [<avatar-first-name> <avatar-last-name>]",
|
||||
"Return debug settings for throttles.",
|
||||
"adaptive - true/false, controls adaptive throttle setting.\n"
|
||||
+ "request - request drip rate in kbps.\n"
|
||||
+ "max - the max kbps throttle allowed for the specified existing clients. Use 'debug lludp get new-client-throttle-max' to see the setting for new clients.\n",
|
||||
HandleThrottleGetCommand);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Debug",
|
||||
false,
|
||||
"debug lludp throttles set",
|
||||
"debug lludp throttles set <param> <value> [<avatar-first-name> <avatar-last-name>]",
|
||||
"Set a throttle parameter for the given client.",
|
||||
"adaptive - true/false, controls adaptive throttle setting.\n"
|
||||
+ "current - current drip rate in kbps.\n"
|
||||
+ "request - requested drip rate in kbps.\n"
|
||||
+ "max - the max kbps throttle allowed for the specified existing clients. Use 'debug lludp set new-client-throttle-max' to change the settings for new clients.\n",
|
||||
HandleThrottleSetCommand);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Debug",
|
||||
false,
|
||||
"debug lludp get",
|
||||
"debug lludp get",
|
||||
"Get debug parameters for the server.",
|
||||
"max-scene-throttle - the current max cumulative kbps provided for this scene to clients.\n"
|
||||
+ "max-new-client-throttle - the max kbps throttle allowed to new clients. Use 'debug lludp throttles get max' to see the settings for existing clients.",
|
||||
HandleGetCommand);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Debug",
|
||||
false,
|
||||
"debug lludp set",
|
||||
"debug lludp set <param> <value>",
|
||||
"Set a parameter for the server.",
|
||||
"max-scene-throttle - the current max cumulative kbps provided for this scene to clients.\n"
|
||||
+ "max-new-client-throttle - the max kbps throttle allowed to each new client. Use 'debug lludp throttles set max' to set for existing clients.",
|
||||
HandleSetCommand);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Debug",
|
||||
false,
|
||||
"debug lludp toggle agentupdate",
|
||||
"debug lludp toggle agentupdate",
|
||||
"Toggle whether agentupdate packets are processed or simply discarded.",
|
||||
HandleAgentUpdateCommand);
|
||||
}
|
||||
|
||||
private void HandleShowServerThrottlesCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
return;
|
||||
|
||||
m_console.OutputFormat("Throttles for {0}", m_udpServer.Scene.Name);
|
||||
ConsoleDisplayList cdl = new ConsoleDisplayList();
|
||||
cdl.AddRow("Adaptive throttles", m_udpServer.ThrottleRates.AdaptiveThrottlesEnabled);
|
||||
|
||||
long maxSceneDripRate = m_udpServer.Throttle.MaxDripRate;
|
||||
cdl.AddRow(
|
||||
"Max scene throttle",
|
||||
maxSceneDripRate != 0 ? string.Format("{0} kbps", maxSceneDripRate * 8 / 1000) : "unset");
|
||||
|
||||
int maxClientDripRate = m_udpServer.ThrottleRates.Total;
|
||||
cdl.AddRow(
|
||||
"Max new client throttle",
|
||||
maxClientDripRate != 0 ? string.Format("{0} kbps", maxClientDripRate * 8 / 1000) : "unset");
|
||||
|
||||
m_console.Output(cdl.ToString());
|
||||
|
||||
m_console.OutputFormat("{0}\n", GetServerThrottlesReport(m_udpServer));
|
||||
}
|
||||
|
||||
private string GetServerThrottlesReport(LLUDPServer udpServer)
|
||||
{
|
||||
StringBuilder report = new StringBuilder();
|
||||
|
||||
report.AppendFormat(
|
||||
"{0,7} {1,8} {2,7} {3,7} {4,7} {5,7} {6,9} {7,7}\n",
|
||||
"Total",
|
||||
"Resend",
|
||||
"Land",
|
||||
"Wind",
|
||||
"Cloud",
|
||||
"Task",
|
||||
"Texture",
|
||||
"Asset");
|
||||
|
||||
report.AppendFormat(
|
||||
"{0,7} {1,8} {2,7} {3,7} {4,7} {5,7} {6,9} {7,7}\n",
|
||||
"kb/s",
|
||||
"kb/s",
|
||||
"kb/s",
|
||||
"kb/s",
|
||||
"kb/s",
|
||||
"kb/s",
|
||||
"kb/s",
|
||||
"kb/s");
|
||||
|
||||
ThrottleRates throttleRates = udpServer.ThrottleRates;
|
||||
report.AppendFormat(
|
||||
"{0,7} {1,8} {2,7} {3,7} {4,7} {5,7} {6,9} {7,7}",
|
||||
(throttleRates.Total * 8) / 1000,
|
||||
(throttleRates.Resend * 8) / 1000,
|
||||
(throttleRates.Land * 8) / 1000,
|
||||
(throttleRates.Wind * 8) / 1000,
|
||||
(throttleRates.Cloud * 8) / 1000,
|
||||
(throttleRates.Task * 8) / 1000,
|
||||
(throttleRates.Texture * 8) / 1000,
|
||||
(throttleRates.Asset * 8) / 1000);
|
||||
|
||||
return report.ToString();
|
||||
}
|
||||
|
||||
protected string GetColumnEntry(string entry, int maxLength, int columnPadding)
|
||||
{
|
||||
return string.Format(
|
||||
"{0,-" + maxLength + "}{1,-" + columnPadding + "}",
|
||||
entry.Length > maxLength ? entry.Substring(0, maxLength) : entry,
|
||||
"");
|
||||
}
|
||||
|
||||
private void HandleDataCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
return;
|
||||
|
||||
if (args.Length != 7)
|
||||
{
|
||||
MainConsole.Instance.OutputFormat("Usage: debug lludp data out <true|false> <avatar-first-name> <avatar-last-name>");
|
||||
return;
|
||||
}
|
||||
|
||||
int level;
|
||||
if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out level))
|
||||
return;
|
||||
|
||||
string firstName = args[5];
|
||||
string lastName = args[6];
|
||||
|
||||
m_udpServer.Scene.ForEachScenePresence(sp =>
|
||||
{
|
||||
if (sp.Firstname == firstName && sp.Lastname == lastName)
|
||||
{
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Data debug for {0} ({1}) set to {2} in {3}",
|
||||
sp.Name, sp.IsChildAgent ? "child" : "root", level, m_udpServer.Scene.Name);
|
||||
|
||||
((LLClientView)sp.ControllingClient).UDPClient.DebugDataOutLevel = level;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void HandleThrottleCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
return;
|
||||
|
||||
bool all = args.Length == 5;
|
||||
bool one = args.Length == 7;
|
||||
|
||||
if (!all && !one)
|
||||
{
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Usage: debug lludp throttles log <level> [<avatar-first-name> <avatar-last-name>]");
|
||||
return;
|
||||
}
|
||||
|
||||
int level;
|
||||
if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out level))
|
||||
return;
|
||||
|
||||
string firstName = null;
|
||||
string lastName = null;
|
||||
|
||||
if (one)
|
||||
{
|
||||
firstName = args[5];
|
||||
lastName = args[6];
|
||||
}
|
||||
|
||||
m_udpServer.Scene.ForEachScenePresence(sp =>
|
||||
{
|
||||
if (all || (sp.Firstname == firstName && sp.Lastname == lastName))
|
||||
{
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Throttle log level for {0} ({1}) set to {2} in {3}",
|
||||
sp.Name, sp.IsChildAgent ? "child" : "root", level, m_udpServer.Scene.Name);
|
||||
|
||||
((LLClientView)sp.ControllingClient).UDPClient.ThrottleDebugLevel = level;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void HandleThrottleSetCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
return;
|
||||
|
||||
bool all = args.Length == 6;
|
||||
bool one = args.Length == 8;
|
||||
|
||||
if (!all && !one)
|
||||
{
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Usage: debug lludp throttles set <param> <value> [<avatar-first-name> <avatar-last-name>]");
|
||||
return;
|
||||
}
|
||||
|
||||
string param = args[4];
|
||||
string rawValue = args[5];
|
||||
|
||||
string firstName = null;
|
||||
string lastName = null;
|
||||
|
||||
if (one)
|
||||
{
|
||||
firstName = args[6];
|
||||
lastName = args[7];
|
||||
}
|
||||
|
||||
if (param == "adaptive")
|
||||
{
|
||||
bool newValue;
|
||||
if (!ConsoleUtil.TryParseConsoleBool(MainConsole.Instance, rawValue, out newValue))
|
||||
return;
|
||||
|
||||
m_udpServer.Scene.ForEachScenePresence(sp =>
|
||||
{
|
||||
if (all || (sp.Firstname == firstName && sp.Lastname == lastName))
|
||||
{
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Setting param {0} to {1} for {2} ({3}) in {4}",
|
||||
param, newValue, sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name);
|
||||
|
||||
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
|
||||
udpClient.FlowThrottle.AdaptiveEnabled = newValue;
|
||||
// udpClient.FlowThrottle.MaxDripRate = 0;
|
||||
// udpClient.FlowThrottle.AdjustedDripRate = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (param == "request")
|
||||
{
|
||||
int newValue;
|
||||
if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, rawValue, out newValue))
|
||||
return;
|
||||
|
||||
int newCurrentThrottleKbps = newValue * 1000 / 8;
|
||||
|
||||
m_udpServer.Scene.ForEachScenePresence(sp =>
|
||||
{
|
||||
if (all || (sp.Firstname == firstName && sp.Lastname == lastName))
|
||||
{
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Setting param {0} to {1} for {2} ({3}) in {4}",
|
||||
param, newValue, sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name);
|
||||
|
||||
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
|
||||
udpClient.FlowThrottle.RequestedDripRate = newCurrentThrottleKbps;
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (param == "max")
|
||||
{
|
||||
int newValue;
|
||||
if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, rawValue, out newValue))
|
||||
return;
|
||||
|
||||
int newThrottleMaxKbps = newValue * 1000 / 8;
|
||||
|
||||
m_udpServer.Scene.ForEachScenePresence(sp =>
|
||||
{
|
||||
if (all || (sp.Firstname == firstName && sp.Lastname == lastName))
|
||||
{
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Setting param {0} to {1} for {2} ({3}) in {4}",
|
||||
param, newValue, sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name);
|
||||
|
||||
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
|
||||
udpClient.FlowThrottle.MaxDripRate = newThrottleMaxKbps;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleThrottleGetCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
return;
|
||||
|
||||
bool all = args.Length == 4;
|
||||
bool one = args.Length == 6;
|
||||
|
||||
if (!all && !one)
|
||||
{
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Usage: debug lludp throttles get [<avatar-first-name> <avatar-last-name>]");
|
||||
return;
|
||||
}
|
||||
|
||||
string firstName = null;
|
||||
string lastName = null;
|
||||
|
||||
if (one)
|
||||
{
|
||||
firstName = args[4];
|
||||
lastName = args[5];
|
||||
}
|
||||
|
||||
m_udpServer.Scene.ForEachScenePresence(sp =>
|
||||
{
|
||||
if (all || (sp.Firstname == firstName && sp.Lastname == lastName))
|
||||
{
|
||||
m_console.OutputFormat(
|
||||
"Status for {0} ({1}) in {2}",
|
||||
sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name);
|
||||
|
||||
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
|
||||
|
||||
ConsoleDisplayList cdl = new ConsoleDisplayList();
|
||||
cdl.AddRow("adaptive", udpClient.FlowThrottle.AdaptiveEnabled);
|
||||
cdl.AddRow("current", string.Format("{0} kbps", udpClient.FlowThrottle.DripRate * 8 / 1000));
|
||||
cdl.AddRow("request", string.Format("{0} kbps", udpClient.FlowThrottle.RequestedDripRate * 8 / 1000));
|
||||
cdl.AddRow("max", string.Format("{0} kbps", udpClient.FlowThrottle.MaxDripRate * 8 / 1000));
|
||||
|
||||
m_console.Output(cdl.ToString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void HandleGetCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
return;
|
||||
|
||||
m_console.OutputFormat("Debug settings for {0}", m_udpServer.Scene.Name);
|
||||
ConsoleDisplayList cdl = new ConsoleDisplayList();
|
||||
|
||||
long maxSceneDripRate = m_udpServer.Throttle.MaxDripRate;
|
||||
cdl.AddRow(
|
||||
"max-scene-throttle",
|
||||
maxSceneDripRate != 0 ? string.Format("{0} kbps", maxSceneDripRate * 8 / 1000) : "unset");
|
||||
|
||||
int maxClientDripRate = m_udpServer.ThrottleRates.Total;
|
||||
cdl.AddRow(
|
||||
"max-new-client-throttle",
|
||||
maxClientDripRate != 0 ? string.Format("{0} kbps", maxClientDripRate * 8 / 1000) : "unset");
|
||||
|
||||
m_console.Output(cdl.ToString());
|
||||
}
|
||||
|
||||
private void HandleSetCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
return;
|
||||
|
||||
if (args.Length != 5)
|
||||
{
|
||||
MainConsole.Instance.OutputFormat("Usage: debug lludp set <param> <value>");
|
||||
return;
|
||||
}
|
||||
|
||||
string param = args[3];
|
||||
string rawValue = args[4];
|
||||
|
||||
int newValue;
|
||||
|
||||
if (param == "max-scene-throttle")
|
||||
{
|
||||
if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, rawValue, out newValue))
|
||||
return;
|
||||
|
||||
m_udpServer.Throttle.MaxDripRate = newValue * 1000 / 8;
|
||||
}
|
||||
else if (param == "max-new-client-throttle")
|
||||
{
|
||||
if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, rawValue, out newValue))
|
||||
return;
|
||||
|
||||
m_udpServer.ThrottleRates.Total = newValue * 1000 / 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_console.OutputFormat("{0} set to {1} in {2}", param, rawValue, m_udpServer.Scene.Name);
|
||||
}
|
||||
|
||||
private void HandlePacketCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
return;
|
||||
|
||||
bool setAsDefaultLevel = false;
|
||||
bool setAll = false;
|
||||
OptionSet optionSet = new OptionSet()
|
||||
.Add("default", o => setAsDefaultLevel = (o != null))
|
||||
.Add("all", o => setAll = (o != null));
|
||||
List<string> filteredArgs = optionSet.Parse(args);
|
||||
|
||||
string name = null;
|
||||
|
||||
if (filteredArgs.Count == 6)
|
||||
{
|
||||
if (!(setAsDefaultLevel || setAll))
|
||||
{
|
||||
name = string.Format("{0} {1}", filteredArgs[4], filteredArgs[5]);
|
||||
}
|
||||
else
|
||||
{
|
||||
MainConsole.Instance.OutputFormat("ERROR: Cannot specify a user name when setting default/all logging level");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (filteredArgs.Count > 3)
|
||||
{
|
||||
int newDebug;
|
||||
if (int.TryParse(filteredArgs[3], out newDebug))
|
||||
{
|
||||
if (setAsDefaultLevel || setAll)
|
||||
{
|
||||
m_udpServer.DefaultClientPacketDebugLevel = newDebug;
|
||||
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Packet debug for {0} clients set to {1} in {2}",
|
||||
(setAll ? "all" : "future"), m_udpServer.DefaultClientPacketDebugLevel, m_udpServer.Scene.Name);
|
||||
|
||||
if (setAll)
|
||||
{
|
||||
m_udpServer.Scene.ForEachScenePresence(sp =>
|
||||
{
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Packet debug for {0} ({1}) set to {2} in {3}",
|
||||
sp.Name, sp.IsChildAgent ? "child" : "root", newDebug, m_udpServer.Scene.Name);
|
||||
|
||||
sp.ControllingClient.DebugPacketLevel = newDebug;
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_udpServer.Scene.ForEachScenePresence(sp =>
|
||||
{
|
||||
if (name == null || sp.Name == name)
|
||||
{
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Packet debug for {0} ({1}) set to {2} in {3}",
|
||||
sp.Name, sp.IsChildAgent ? "child" : "root", newDebug, m_udpServer.Scene.Name);
|
||||
|
||||
sp.ControllingClient.DebugPacketLevel = newDebug;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MainConsole.Instance.Output("Usage: debug lludp packet [--default | --all] 0..255 [<first-name> <last-name>]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDropCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
return;
|
||||
|
||||
if (args.Length != 6)
|
||||
{
|
||||
MainConsole.Instance.Output("Usage: debug lludp drop <in|out> <add|remove> <packet-name>");
|
||||
return;
|
||||
}
|
||||
|
||||
string direction = args[3];
|
||||
string subCommand = args[4];
|
||||
string packetName = args[5];
|
||||
|
||||
if (subCommand == "add")
|
||||
{
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Adding packet {0} to {1} drop list for all connections in {2}",
|
||||
direction, packetName, m_udpServer.Scene.Name);
|
||||
|
||||
m_udpServer.Scene.ForEachScenePresence(
|
||||
sp =>
|
||||
{
|
||||
LLClientView llcv = (LLClientView)sp.ControllingClient;
|
||||
|
||||
if (direction == "in")
|
||||
llcv.AddInPacketToDropSet(packetName);
|
||||
else if (direction == "out")
|
||||
llcv.AddOutPacketToDropSet(packetName);
|
||||
}
|
||||
);
|
||||
}
|
||||
else if (subCommand == "remove")
|
||||
{
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Removing packet {0} from {1} drop list for all connections in {2}",
|
||||
direction, packetName, m_udpServer.Scene.Name);
|
||||
|
||||
m_udpServer.Scene.ForEachScenePresence(
|
||||
sp =>
|
||||
{
|
||||
LLClientView llcv = (LLClientView)sp.ControllingClient;
|
||||
|
||||
if (direction == "in")
|
||||
llcv.RemoveInPacketFromDropSet(packetName);
|
||||
else if (direction == "out")
|
||||
llcv.RemoveOutPacketFromDropSet(packetName);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleStartCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
return;
|
||||
|
||||
if (args.Length != 4)
|
||||
{
|
||||
MainConsole.Instance.Output("Usage: debug lludp start <in|out|all>");
|
||||
return;
|
||||
}
|
||||
|
||||
string subCommand = args[3];
|
||||
|
||||
if (subCommand == "in" || subCommand == "all")
|
||||
m_udpServer.StartInbound();
|
||||
|
||||
if (subCommand == "out" || subCommand == "all")
|
||||
m_udpServer.StartOutbound();
|
||||
}
|
||||
|
||||
private void HandleStopCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
return;
|
||||
|
||||
if (args.Length != 4)
|
||||
{
|
||||
MainConsole.Instance.Output("Usage: debug lludp stop <in|out|all>");
|
||||
return;
|
||||
}
|
||||
|
||||
string subCommand = args[3];
|
||||
|
||||
if (subCommand == "in" || subCommand == "all")
|
||||
m_udpServer.StopInbound();
|
||||
|
||||
if (subCommand == "out" || subCommand == "all")
|
||||
m_udpServer.StopOutbound();
|
||||
}
|
||||
|
||||
private void HandlePoolCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
return;
|
||||
|
||||
if (args.Length != 4)
|
||||
{
|
||||
MainConsole.Instance.Output("Usage: debug lludp pool <on|off>");
|
||||
return;
|
||||
}
|
||||
|
||||
string enabled = args[3];
|
||||
|
||||
if (enabled == "on")
|
||||
{
|
||||
if (m_udpServer.EnablePools())
|
||||
{
|
||||
m_udpServer.EnablePoolStats();
|
||||
MainConsole.Instance.OutputFormat("Packet pools enabled on {0}", m_udpServer.Scene.Name);
|
||||
}
|
||||
}
|
||||
else if (enabled == "off")
|
||||
{
|
||||
if (m_udpServer.DisablePools())
|
||||
{
|
||||
m_udpServer.DisablePoolStats();
|
||||
MainConsole.Instance.OutputFormat("Packet pools disabled on {0}", m_udpServer.Scene.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MainConsole.Instance.Output("Usage: debug lludp pool <on|off>");
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleAgentUpdateCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
return;
|
||||
|
||||
m_udpServer.DiscardInboundAgentUpdates = !m_udpServer.DiscardInboundAgentUpdates;
|
||||
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Discard AgentUpdates now {0} for {1}", m_udpServer.DiscardInboundAgentUpdates, m_udpServer.Scene.Name);
|
||||
}
|
||||
|
||||
private void HandleStatusCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
return;
|
||||
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"IN LLUDP packet processing for {0} is {1}", m_udpServer.Scene.Name, m_udpServer.IsRunningInbound ? "enabled" : "disabled");
|
||||
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"OUT LLUDP packet processing for {0} is {1}", m_udpServer.Scene.Name, m_udpServer.IsRunningOutbound ? "enabled" : "disabled");
|
||||
|
||||
MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", m_udpServer.Scene.Name, m_udpServer.UsePools ? "on" : "off");
|
||||
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Packet debug level for new clients is {0}", m_udpServer.DefaultClientPacketDebugLevel);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -135,7 +135,7 @@ namespace OpenMetaverse
|
|||
/// manner (not throwing an exception when the remote side resets the
|
||||
/// connection). This call is ignored on Mono where the flag is not
|
||||
/// necessary</remarks>
|
||||
public void StartInbound(int recvBufferSize, bool asyncPacketHandling)
|
||||
public virtual void StartInbound(int recvBufferSize, bool asyncPacketHandling)
|
||||
{
|
||||
m_asyncPacketHandling = asyncPacketHandling;
|
||||
|
||||
|
@ -168,6 +168,12 @@ namespace OpenMetaverse
|
|||
m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring");
|
||||
}
|
||||
|
||||
// On at least Mono 3.2.8, multiple UDP sockets can bind to the same port by default. At the moment
|
||||
// we never want two regions to listen on the same port as they cannot demultiplex each other's messages,
|
||||
// leading to a confusing bug.
|
||||
// By default, Windows does not allow two sockets to bind to the same port.
|
||||
m_udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, false);
|
||||
|
||||
if (recvBufferSize != 0)
|
||||
m_udpSocket.ReceiveBufferSize = recvBufferSize;
|
||||
|
||||
|
@ -185,14 +191,14 @@ namespace OpenMetaverse
|
|||
/// <summary>
|
||||
/// Start outbound UDP packet handling.
|
||||
/// </summary>
|
||||
public void StartOutbound()
|
||||
public virtual void StartOutbound()
|
||||
{
|
||||
m_log.DebugFormat("[UDPBASE]: Starting outbound UDP loop");
|
||||
|
||||
IsRunningOutbound = true;
|
||||
}
|
||||
|
||||
public void StopInbound()
|
||||
public virtual void StopInbound()
|
||||
{
|
||||
if (IsRunningInbound)
|
||||
{
|
||||
|
@ -203,14 +209,14 @@ namespace OpenMetaverse
|
|||
}
|
||||
}
|
||||
|
||||
public void StopOutbound()
|
||||
public virtual void StopOutbound()
|
||||
{
|
||||
m_log.DebugFormat("[UDPBASE]: Stopping outbound UDP loop");
|
||||
|
||||
IsRunningOutbound = false;
|
||||
}
|
||||
|
||||
protected virtual bool EnablePools()
|
||||
public virtual bool EnablePools()
|
||||
{
|
||||
if (!UsePools)
|
||||
{
|
||||
|
@ -224,7 +230,7 @@ namespace OpenMetaverse
|
|||
return false;
|
||||
}
|
||||
|
||||
protected virtual bool DisablePools()
|
||||
public virtual bool DisablePools()
|
||||
{
|
||||
if (UsePools)
|
||||
{
|
||||
|
|
|
@ -217,6 +217,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
private void ProcessRequests()
|
||||
{
|
||||
Thread.CurrentThread.Priority = ThreadPriority.Highest;
|
||||
|
||||
try
|
||||
{
|
||||
while (IsRunning || m_requestQueue.Count > 0)
|
||||
|
|
|
@ -36,7 +36,6 @@ using OpenSim.Framework;
|
|||
using OpenSim.Framework.Monitoring;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
||||
{
|
||||
|
@ -47,7 +46,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
|||
public class BasicCircuitTests : OpenSimTestCase
|
||||
{
|
||||
private Scene m_scene;
|
||||
private TestLLUDPServer m_udpServer;
|
||||
|
||||
[TestFixtureSetUp]
|
||||
public void FixtureInit()
|
||||
|
@ -73,72 +71,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
|||
StatsManager.SimExtraStats = new SimExtraStatsCollector();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build an object name packet for test purposes
|
||||
/// </summary>
|
||||
/// <param name="objectLocalId"></param>
|
||||
/// <param name="objectName"></param>
|
||||
private ObjectNamePacket BuildTestObjectNamePacket(uint objectLocalId, string objectName)
|
||||
{
|
||||
ObjectNamePacket onp = new ObjectNamePacket();
|
||||
ObjectNamePacket.ObjectDataBlock odb = new ObjectNamePacket.ObjectDataBlock();
|
||||
odb.LocalID = objectLocalId;
|
||||
odb.Name = Utils.StringToBytes(objectName);
|
||||
onp.ObjectData = new ObjectNamePacket.ObjectDataBlock[] { odb };
|
||||
onp.Header.Zerocoded = false;
|
||||
|
||||
return onp;
|
||||
}
|
||||
|
||||
private void AddUdpServer()
|
||||
{
|
||||
AddUdpServer(new IniConfigSource());
|
||||
}
|
||||
|
||||
private void AddUdpServer(IniConfigSource configSource)
|
||||
{
|
||||
uint port = 0;
|
||||
AgentCircuitManager acm = m_scene.AuthenticateHandler;
|
||||
|
||||
m_udpServer = new TestLLUDPServer(IPAddress.Any, ref port, 0, false, configSource, acm);
|
||||
m_udpServer.AddScene(m_scene);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by tests that aren't testing this stage.
|
||||
/// </summary>
|
||||
private ScenePresence AddClient()
|
||||
{
|
||||
UUID myAgentUuid = TestHelpers.ParseTail(0x1);
|
||||
UUID mySessionUuid = TestHelpers.ParseTail(0x2);
|
||||
uint myCircuitCode = 123456;
|
||||
IPEndPoint testEp = new IPEndPoint(IPAddress.Loopback, 999);
|
||||
|
||||
UseCircuitCodePacket uccp = new UseCircuitCodePacket();
|
||||
|
||||
UseCircuitCodePacket.CircuitCodeBlock uccpCcBlock
|
||||
= new UseCircuitCodePacket.CircuitCodeBlock();
|
||||
uccpCcBlock.Code = myCircuitCode;
|
||||
uccpCcBlock.ID = myAgentUuid;
|
||||
uccpCcBlock.SessionID = mySessionUuid;
|
||||
uccp.CircuitCode = uccpCcBlock;
|
||||
|
||||
byte[] uccpBytes = uccp.ToBytes();
|
||||
UDPPacketBuffer upb = new UDPPacketBuffer(testEp, uccpBytes.Length);
|
||||
upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor.
|
||||
Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length);
|
||||
|
||||
AgentCircuitData acd = new AgentCircuitData();
|
||||
acd.AgentID = myAgentUuid;
|
||||
acd.SessionID = mySessionUuid;
|
||||
|
||||
m_scene.AuthenticateHandler.AddNewCircuit(myCircuitCode, acd);
|
||||
|
||||
m_udpServer.PacketReceived(upb);
|
||||
|
||||
return m_scene.GetScenePresence(myAgentUuid);
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// Build an object name packet for test purposes
|
||||
// /// </summary>
|
||||
// /// <param name="objectLocalId"></param>
|
||||
// /// <param name="objectName"></param>
|
||||
// private ObjectNamePacket BuildTestObjectNamePacket(uint objectLocalId, string objectName)
|
||||
// {
|
||||
// ObjectNamePacket onp = new ObjectNamePacket();
|
||||
// ObjectNamePacket.ObjectDataBlock odb = new ObjectNamePacket.ObjectDataBlock();
|
||||
// odb.LocalID = objectLocalId;
|
||||
// odb.Name = Utils.StringToBytes(objectName);
|
||||
// onp.ObjectData = new ObjectNamePacket.ObjectDataBlock[] { odb };
|
||||
// onp.Header.Zerocoded = false;
|
||||
//
|
||||
// return onp;
|
||||
// }
|
||||
//
|
||||
/// <summary>
|
||||
/// Test adding a client to the stack
|
||||
/// </summary>
|
||||
|
@ -148,7 +97,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
|||
TestHelpers.InMethod();
|
||||
// TestHelpers.EnableLogging();
|
||||
|
||||
AddUdpServer();
|
||||
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(m_scene);
|
||||
|
||||
UUID myAgentUuid = TestHelpers.ParseTail(0x1);
|
||||
UUID mySessionUuid = TestHelpers.ParseTail(0x2);
|
||||
|
@ -169,7 +118,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
|||
upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor.
|
||||
Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length);
|
||||
|
||||
m_udpServer.PacketReceived(upb);
|
||||
udpServer.PacketReceived(upb);
|
||||
|
||||
// Presence shouldn't exist since the circuit manager doesn't know about this circuit for authentication yet
|
||||
Assert.That(m_scene.GetScenePresence(myAgentUuid), Is.Null);
|
||||
|
@ -180,15 +129,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
|||
|
||||
m_scene.AuthenticateHandler.AddNewCircuit(myCircuitCode, acd);
|
||||
|
||||
m_udpServer.PacketReceived(upb);
|
||||
udpServer.PacketReceived(upb);
|
||||
|
||||
// Should succeed now
|
||||
ScenePresence sp = m_scene.GetScenePresence(myAgentUuid);
|
||||
Assert.That(sp.UUID, Is.EqualTo(myAgentUuid));
|
||||
|
||||
Assert.That(m_udpServer.PacketsSent.Count, Is.EqualTo(1));
|
||||
Assert.That(udpServer.PacketsSent.Count, Is.EqualTo(1));
|
||||
|
||||
Packet packet = m_udpServer.PacketsSent[0];
|
||||
Packet packet = udpServer.PacketsSent[0];
|
||||
Assert.That(packet, Is.InstanceOf(typeof(PacketAckPacket)));
|
||||
|
||||
PacketAckPacket ackPacket = packet as PacketAckPacket;
|
||||
|
@ -200,15 +149,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
|||
public void TestLogoutClientDueToAck()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
TestHelpers.EnableLogging();
|
||||
// TestHelpers.EnableLogging();
|
||||
|
||||
IniConfigSource ics = new IniConfigSource();
|
||||
IConfig config = ics.AddConfig("ClientStack.LindenUDP");
|
||||
config.Set("AckTimeout", -1);
|
||||
AddUdpServer(ics);
|
||||
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(m_scene, ics);
|
||||
|
||||
ScenePresence sp = AddClient();
|
||||
m_udpServer.ClientOutgoingPacketHandler(sp.ControllingClient, true, false, false);
|
||||
ScenePresence sp
|
||||
= ClientStackHelpers.AddChildClient(
|
||||
m_scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
|
||||
|
||||
udpServer.ClientOutgoingPacketHandler(sp.ControllingClient, true, false, false);
|
||||
|
||||
ScenePresence spAfterAckTimeout = m_scene.GetScenePresence(sp.UUID);
|
||||
Assert.That(spAfterAckTimeout, Is.Null);
|
||||
|
|
|
@ -39,7 +39,6 @@ using OpenSim.Framework;
|
|||
using OpenSim.Region.CoreModules.Agent.TextureSender;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
||||
{
|
||||
|
|
|
@ -30,7 +30,6 @@ using NUnit.Framework;
|
|||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
using OpenSim.Tests.Common;
|
||||
|
||||
namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
||||
|
|
|
@ -0,0 +1,425 @@
|
|||
/*
|
||||
* 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;
|
||||
using NUnit.Framework;
|
||||
using OpenMetaverse.Packets;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
|
||||
namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class ThrottleTests : OpenSimTestCase
|
||||
{
|
||||
[TestFixtureSetUp]
|
||||
public void FixtureInit()
|
||||
{
|
||||
// Don't allow tests to be bamboozled by asynchronous events. Execute everything on the same thread.
|
||||
Util.FireAndForgetMethod = FireAndForgetMethod.RegressionTest;
|
||||
}
|
||||
|
||||
[TestFixtureTearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
// We must set this back afterwards, otherwise later tests will fail since they're expecting multiple
|
||||
// threads. Possibly, later tests should be rewritten so none of them require async stuff (which regression
|
||||
// tests really shouldn't).
|
||||
Util.FireAndForgetMethod = Util.DefaultFireAndForgetMethod;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSetRequestDripRate()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
|
||||
TokenBucket tb = new TokenBucket("tb", null, 5000, 0);
|
||||
AssertRates(tb, 5000, 0, 5000, 0);
|
||||
|
||||
tb.RequestedDripRate = 4000;
|
||||
AssertRates(tb, 4000, 0, 4000, 0);
|
||||
|
||||
tb.RequestedDripRate = 6000;
|
||||
AssertRates(tb, 6000, 0, 6000, 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSetRequestDripRateWithMax()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
|
||||
TokenBucket tb = new TokenBucket("tb", null, 5000, 10000);
|
||||
AssertRates(tb, 5000, 0, 5000, 10000);
|
||||
|
||||
tb.RequestedDripRate = 4000;
|
||||
AssertRates(tb, 4000, 0, 4000, 10000);
|
||||
|
||||
tb.RequestedDripRate = 6000;
|
||||
AssertRates(tb, 6000, 0, 6000, 10000);
|
||||
|
||||
tb.RequestedDripRate = 12000;
|
||||
AssertRates(tb, 10000, 0, 10000, 10000);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSetRequestDripRateWithChildren()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
|
||||
TokenBucket tbParent = new TokenBucket("tbParent", null, 0, 0);
|
||||
TokenBucket tbChild1 = new TokenBucket("tbChild1", tbParent, 3000, 0);
|
||||
TokenBucket tbChild2 = new TokenBucket("tbChild2", tbParent, 5000, 0);
|
||||
|
||||
AssertRates(tbParent, 8000, 8000, 8000, 0);
|
||||
AssertRates(tbChild1, 3000, 0, 3000, 0);
|
||||
AssertRates(tbChild2, 5000, 0, 5000, 0);
|
||||
|
||||
// Test: Setting a parent request greater than total children requests.
|
||||
tbParent.RequestedDripRate = 10000;
|
||||
|
||||
AssertRates(tbParent, 10000, 8000, 8000, 0);
|
||||
AssertRates(tbChild1, 3000, 0, 3000, 0);
|
||||
AssertRates(tbChild2, 5000, 0, 5000, 0);
|
||||
|
||||
// Test: Setting a parent request lower than total children requests.
|
||||
tbParent.RequestedDripRate = 6000;
|
||||
|
||||
AssertRates(tbParent, 6000, 8000, 6000, 0);
|
||||
AssertRates(tbChild1, 3000, 0, 6000 / 8 * 3, 0);
|
||||
AssertRates(tbChild2, 5000, 0, 6000 / 8 * 5, 0);
|
||||
}
|
||||
|
||||
private void AssertRates(
|
||||
TokenBucket tb, double requestedDripRate, double totalDripRequest, double dripRate, double maxDripRate)
|
||||
{
|
||||
Assert.AreEqual((int)requestedDripRate, tb.RequestedDripRate, "Requested drip rate");
|
||||
Assert.AreEqual((int)totalDripRequest, tb.TotalDripRequest, "Total drip request");
|
||||
Assert.AreEqual((int)dripRate, tb.DripRate, "Drip rate");
|
||||
Assert.AreEqual((int)maxDripRate, tb.MaxDripRate, "Max drip rate");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestClientThrottleSetNoLimit()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
// TestHelpers.EnableLogging();
|
||||
|
||||
Scene scene = new SceneHelpers().SetupScene();
|
||||
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene);
|
||||
|
||||
ScenePresence sp
|
||||
= ClientStackHelpers.AddChildClient(
|
||||
scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
|
||||
|
||||
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
|
||||
|
||||
udpServer.Throttle.DebugLevel = 1;
|
||||
udpClient.ThrottleDebugLevel = 1;
|
||||
|
||||
int resendBytes = 1000;
|
||||
int landBytes = 2000;
|
||||
int windBytes = 3000;
|
||||
int cloudBytes = 4000;
|
||||
int taskBytes = 5000;
|
||||
int textureBytes = 6000;
|
||||
int assetBytes = 7000;
|
||||
|
||||
SetThrottles(
|
||||
udpClient, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
|
||||
|
||||
// We expect this to be lower because of the minimum bound set by MTU
|
||||
int totalBytes = LLUDPServer.MTU + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes;
|
||||
|
||||
AssertThrottles(
|
||||
udpClient,
|
||||
LLUDPServer.MTU, landBytes, windBytes, cloudBytes, taskBytes,
|
||||
textureBytes, assetBytes, totalBytes, 0, 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestClientThrottleAdaptiveNoLimit()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
// TestHelpers.EnableLogging();
|
||||
|
||||
Scene scene = new SceneHelpers().SetupScene();
|
||||
|
||||
IniConfigSource ics = new IniConfigSource();
|
||||
IConfig config = ics.AddConfig("ClientStack.LindenUDP");
|
||||
config.Set("enable_adaptive_throttles", true);
|
||||
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene, ics);
|
||||
|
||||
ScenePresence sp
|
||||
= ClientStackHelpers.AddChildClient(
|
||||
scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
|
||||
|
||||
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
|
||||
|
||||
udpServer.Throttle.DebugLevel = 1;
|
||||
udpClient.ThrottleDebugLevel = 1;
|
||||
|
||||
// Total is 280000
|
||||
int resendBytes = 10000;
|
||||
int landBytes = 20000;
|
||||
int windBytes = 30000;
|
||||
int cloudBytes = 40000;
|
||||
int taskBytes = 50000;
|
||||
int textureBytes = 60000;
|
||||
int assetBytes = 70000;
|
||||
int totalBytes = resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes;
|
||||
|
||||
SetThrottles(
|
||||
udpClient, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
|
||||
|
||||
// Ratio of current adaptive drip rate to requested bytes
|
||||
// XXX: Should hard code this as below so we don't rely on values given by tested code to construct
|
||||
// expected values.
|
||||
double commitRatio = (double)udpClient.FlowThrottle.AdjustedDripRate / udpClient.FlowThrottle.TargetDripRate;
|
||||
|
||||
AssertThrottles(
|
||||
udpClient,
|
||||
LLUDPServer.MTU, landBytes * commitRatio, windBytes * commitRatio, cloudBytes * commitRatio, taskBytes * commitRatio,
|
||||
textureBytes * commitRatio, assetBytes * commitRatio, udpClient.FlowThrottle.AdjustedDripRate, totalBytes, 0);
|
||||
|
||||
// Test an increase in target throttle
|
||||
udpClient.FlowThrottle.AcknowledgePackets(35000);
|
||||
commitRatio = 0.2;
|
||||
|
||||
AssertThrottles(
|
||||
udpClient,
|
||||
resendBytes * commitRatio, landBytes * commitRatio, windBytes * commitRatio, cloudBytes * commitRatio, taskBytes * commitRatio,
|
||||
textureBytes * commitRatio, assetBytes * commitRatio, udpClient.FlowThrottle.AdjustedDripRate, totalBytes, 0);
|
||||
|
||||
// Test a decrease in target throttle
|
||||
udpClient.FlowThrottle.ExpirePackets(1);
|
||||
commitRatio = 0.1;
|
||||
|
||||
AssertThrottles(
|
||||
udpClient,
|
||||
LLUDPServer.MTU, landBytes * commitRatio, windBytes * commitRatio, cloudBytes * commitRatio, taskBytes * commitRatio,
|
||||
textureBytes * commitRatio, assetBytes * commitRatio, udpClient.FlowThrottle.AdjustedDripRate, totalBytes, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test throttle setttings where max client throttle has been limited server side.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSingleClientThrottleRegionLimited()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
// TestHelpers.EnableLogging();
|
||||
|
||||
int resendBytes = 6000;
|
||||
int landBytes = 8000;
|
||||
int windBytes = 10000;
|
||||
int cloudBytes = 12000;
|
||||
int taskBytes = 14000;
|
||||
int textureBytes = 16000;
|
||||
int assetBytes = 18000;
|
||||
int totalBytes
|
||||
= (int)((resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes) / 2);
|
||||
|
||||
Scene scene = new SceneHelpers().SetupScene();
|
||||
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene);
|
||||
udpServer.Throttle.RequestedDripRate = totalBytes;
|
||||
|
||||
ScenePresence sp1
|
||||
= ClientStackHelpers.AddChildClient(
|
||||
scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
|
||||
|
||||
LLUDPClient udpClient1 = ((LLClientView)sp1.ControllingClient).UDPClient;
|
||||
|
||||
SetThrottles(
|
||||
udpClient1, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
|
||||
|
||||
AssertThrottles(
|
||||
udpClient1,
|
||||
resendBytes / 2, landBytes / 2, windBytes / 2, cloudBytes / 2, taskBytes / 2,
|
||||
textureBytes / 2, assetBytes / 2, totalBytes, 0, 0);
|
||||
|
||||
// Test: Now add another client
|
||||
ScenePresence sp2
|
||||
= ClientStackHelpers.AddChildClient(
|
||||
scene, udpServer, TestHelpers.ParseTail(0x10), TestHelpers.ParseTail(0x20), 123457);
|
||||
|
||||
LLUDPClient udpClient2 = ((LLClientView)sp2.ControllingClient).UDPClient;
|
||||
// udpClient.ThrottleDebugLevel = 1;
|
||||
|
||||
SetThrottles(
|
||||
udpClient2, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
|
||||
|
||||
AssertThrottles(
|
||||
udpClient1,
|
||||
resendBytes / 4, landBytes / 4, windBytes / 4, cloudBytes / 4, taskBytes / 4,
|
||||
textureBytes / 4, assetBytes / 4, totalBytes / 2, 0, 0);
|
||||
|
||||
AssertThrottles(
|
||||
udpClient2,
|
||||
resendBytes / 4, landBytes / 4, windBytes / 4, cloudBytes / 4, taskBytes / 4,
|
||||
textureBytes / 4, assetBytes / 4, totalBytes / 2, 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test throttle setttings where max client throttle has been limited server side.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestClientThrottlePerClientLimited()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
// TestHelpers.EnableLogging();
|
||||
|
||||
int resendBytes = 4000;
|
||||
int landBytes = 6000;
|
||||
int windBytes = 8000;
|
||||
int cloudBytes = 10000;
|
||||
int taskBytes = 12000;
|
||||
int textureBytes = 14000;
|
||||
int assetBytes = 16000;
|
||||
int totalBytes
|
||||
= (int)((resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes) / 2);
|
||||
|
||||
Scene scene = new SceneHelpers().SetupScene();
|
||||
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene);
|
||||
udpServer.ThrottleRates.Total = totalBytes;
|
||||
|
||||
ScenePresence sp
|
||||
= ClientStackHelpers.AddChildClient(
|
||||
scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
|
||||
|
||||
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
|
||||
// udpClient.ThrottleDebugLevel = 1;
|
||||
|
||||
SetThrottles(
|
||||
udpClient, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
|
||||
|
||||
AssertThrottles(
|
||||
udpClient,
|
||||
resendBytes / 2, landBytes / 2, windBytes / 2, cloudBytes / 2, taskBytes / 2,
|
||||
textureBytes / 2, assetBytes / 2, totalBytes, 0, totalBytes);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestClientThrottlePerClientAndRegionLimited()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
//TestHelpers.EnableLogging();
|
||||
|
||||
int resendBytes = 4000;
|
||||
int landBytes = 6000;
|
||||
int windBytes = 8000;
|
||||
int cloudBytes = 10000;
|
||||
int taskBytes = 12000;
|
||||
int textureBytes = 14000;
|
||||
int assetBytes = 16000;
|
||||
|
||||
// current total 70000
|
||||
int totalBytes = resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes;
|
||||
|
||||
Scene scene = new SceneHelpers().SetupScene();
|
||||
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene);
|
||||
udpServer.ThrottleRates.Total = (int)(totalBytes * 1.1);
|
||||
udpServer.Throttle.RequestedDripRate = (int)(totalBytes * 1.5);
|
||||
|
||||
ScenePresence sp1
|
||||
= ClientStackHelpers.AddChildClient(
|
||||
scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
|
||||
|
||||
LLUDPClient udpClient1 = ((LLClientView)sp1.ControllingClient).UDPClient;
|
||||
udpClient1.ThrottleDebugLevel = 1;
|
||||
|
||||
SetThrottles(
|
||||
udpClient1, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
|
||||
|
||||
AssertThrottles(
|
||||
udpClient1,
|
||||
resendBytes, landBytes, windBytes, cloudBytes, taskBytes,
|
||||
textureBytes, assetBytes, totalBytes, 0, totalBytes * 1.1);
|
||||
|
||||
// Now add another client
|
||||
ScenePresence sp2
|
||||
= ClientStackHelpers.AddChildClient(
|
||||
scene, udpServer, TestHelpers.ParseTail(0x10), TestHelpers.ParseTail(0x20), 123457);
|
||||
|
||||
LLUDPClient udpClient2 = ((LLClientView)sp2.ControllingClient).UDPClient;
|
||||
udpClient2.ThrottleDebugLevel = 1;
|
||||
|
||||
SetThrottles(
|
||||
udpClient2, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
|
||||
|
||||
AssertThrottles(
|
||||
udpClient1,
|
||||
resendBytes * 0.75, landBytes * 0.75, windBytes * 0.75, cloudBytes * 0.75, taskBytes * 0.75,
|
||||
textureBytes * 0.75, assetBytes * 0.75, totalBytes * 0.75, 0, totalBytes * 1.1);
|
||||
|
||||
AssertThrottles(
|
||||
udpClient2,
|
||||
resendBytes * 0.75, landBytes * 0.75, windBytes * 0.75, cloudBytes * 0.75, taskBytes * 0.75,
|
||||
textureBytes * 0.75, assetBytes * 0.75, totalBytes * 0.75, 0, totalBytes * 1.1);
|
||||
}
|
||||
|
||||
private void AssertThrottles(
|
||||
LLUDPClient udpClient,
|
||||
double resendBytes, double landBytes, double windBytes, double cloudBytes, double taskBytes, double textureBytes, double assetBytes,
|
||||
double totalBytes, double targetBytes, double maxBytes)
|
||||
{
|
||||
ClientInfo ci = udpClient.GetClientInfo();
|
||||
|
||||
// Console.WriteLine(
|
||||
// "Resend={0}, Land={1}, Wind={2}, Cloud={3}, Task={4}, Texture={5}, Asset={6}, TOTAL = {7}",
|
||||
// ci.resendThrottle, ci.landThrottle, ci.windThrottle, ci.cloudThrottle, ci.taskThrottle, ci.textureThrottle, ci.assetThrottle, ci.totalThrottle);
|
||||
|
||||
Assert.AreEqual((int)resendBytes, ci.resendThrottle, "Resend");
|
||||
Assert.AreEqual((int)landBytes, ci.landThrottle, "Land");
|
||||
Assert.AreEqual((int)windBytes, ci.windThrottle, "Wind");
|
||||
Assert.AreEqual((int)cloudBytes, ci.cloudThrottle, "Cloud");
|
||||
Assert.AreEqual((int)taskBytes, ci.taskThrottle, "Task");
|
||||
Assert.AreEqual((int)textureBytes, ci.textureThrottle, "Texture");
|
||||
Assert.AreEqual((int)assetBytes, ci.assetThrottle, "Asset");
|
||||
Assert.AreEqual((int)totalBytes, ci.totalThrottle, "Total");
|
||||
Assert.AreEqual((int)targetBytes, ci.targetThrottle, "Target");
|
||||
Assert.AreEqual((int)maxBytes, ci.maxThrottle, "Max");
|
||||
}
|
||||
|
||||
private void SetThrottles(
|
||||
LLUDPClient udpClient, int resendBytes, int landBytes, int windBytes, int cloudBytes, int taskBytes, int textureBytes, int assetBytes)
|
||||
{
|
||||
byte[] throttles = new byte[28];
|
||||
|
||||
Array.Copy(BitConverter.GetBytes((float)resendBytes * 8), 0, throttles, 0, 4);
|
||||
Array.Copy(BitConverter.GetBytes((float)landBytes * 8), 0, throttles, 4, 4);
|
||||
Array.Copy(BitConverter.GetBytes((float)windBytes * 8), 0, throttles, 8, 4);
|
||||
Array.Copy(BitConverter.GetBytes((float)cloudBytes * 8), 0, throttles, 12, 4);
|
||||
Array.Copy(BitConverter.GetBytes((float)taskBytes * 8), 0, throttles, 16, 4);
|
||||
Array.Copy(BitConverter.GetBytes((float)textureBytes * 8), 0, throttles, 20, 4);
|
||||
Array.Copy(BitConverter.GetBytes((float)assetBytes * 8), 0, throttles, 24, 4);
|
||||
|
||||
udpClient.SetThrottles(throttles);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -72,6 +72,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
{
|
||||
IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"];
|
||||
|
||||
// Current default total is 66750
|
||||
Resend = throttleConfig.GetInt("resend_default", 6625);
|
||||
Land = throttleConfig.GetInt("land_default", 9125);
|
||||
Wind = throttleConfig.GetInt("wind_default", 1750);
|
||||
|
|
|
@ -42,9 +42,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
public class TokenBucket
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
private static Int32 m_counter = 0;
|
||||
|
||||
// private Int32 m_identifier;
|
||||
|
||||
public string Identifier { get; private set; }
|
||||
|
||||
public int DebugLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of ticks (ms) per quantum, drip rate and max burst
|
||||
|
@ -75,20 +76,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
/// Map of children buckets and their requested maximum burst rate
|
||||
/// </summary>
|
||||
protected Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>();
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// The parent bucket of this bucket, or null if this bucket has no
|
||||
/// parent. The parent bucket will limit the aggregate bandwidth of all
|
||||
/// of its children buckets
|
||||
/// </summary>
|
||||
protected TokenBucket m_parent;
|
||||
public TokenBucket Parent
|
||||
{
|
||||
get { return m_parent; }
|
||||
set { m_parent = value; }
|
||||
}
|
||||
public TokenBucket Parent { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum burst rate in bytes per second. This is the maximum number
|
||||
|
@ -114,77 +108,106 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// The speed limit of this bucket in bytes per second. This is the
|
||||
/// number of tokens that are added to the bucket per quantum
|
||||
/// The requested drip rate for this particular bucket.
|
||||
/// </summary>
|
||||
/// <remarks>Tokens are added to the bucket any time
|
||||
/// <remarks>
|
||||
/// 0 then TotalDripRequest is used instead.
|
||||
/// Can never be above MaxDripRate.
|
||||
/// Tokens are added to the bucket at any time
|
||||
/// <seealso cref="RemoveTokens"/> is called, at the granularity of
|
||||
/// the system tick interval (typically around 15-22ms)</remarks>
|
||||
protected Int64 m_dripRate;
|
||||
/// the system tick interval (typically around 15-22ms)
|
||||
/// FIXME: It is extremely confusing to be able to set a RequestedDripRate of 0 and then receive a positive
|
||||
/// number on get if TotalDripRequest is sent. This also stops us being able to retrieve the fact that
|
||||
/// RequestedDripRate is set to 0. Really, this should always return m_dripRate and then we can get
|
||||
/// (m_dripRate == 0 ? TotalDripRequest : m_dripRate) on some other properties.
|
||||
/// </remarks>
|
||||
public virtual Int64 RequestedDripRate
|
||||
{
|
||||
get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); }
|
||||
set {
|
||||
m_dripRate = (value < 0 ? 0 : value);
|
||||
get { return (m_dripRate == 0 ? TotalDripRequest : m_dripRate); }
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
m_dripRate = 0;
|
||||
else if (MaxDripRate > 0 && value > MaxDripRate)
|
||||
m_dripRate = MaxDripRate;
|
||||
else
|
||||
m_dripRate = value;
|
||||
|
||||
m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
|
||||
m_totalDripRequest = m_dripRate;
|
||||
if (m_parent != null)
|
||||
m_parent.RegisterRequest(this,m_dripRate);
|
||||
|
||||
if (Parent != null)
|
||||
Parent.RegisterRequest(this, m_dripRate);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the drip rate.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// DripRate can never be above max drip rate or below min drip rate.
|
||||
/// If we are a child bucket then the drip rate return is modifed by the total load on the capacity of the
|
||||
/// parent bucket.
|
||||
/// </value>
|
||||
public virtual Int64 DripRate
|
||||
{
|
||||
get {
|
||||
if (m_parent == null)
|
||||
return Math.Min(RequestedDripRate,TotalDripRequest);
|
||||
|
||||
double rate = (double)RequestedDripRate * m_parent.DripRateModifier();
|
||||
get
|
||||
{
|
||||
double rate;
|
||||
|
||||
// FIXME: This doesn't properly work if we have a parent and children and a requested drip rate set
|
||||
// on ourselves which is not equal to the child drip rates.
|
||||
if (Parent == null)
|
||||
{
|
||||
if (TotalDripRequest > 0)
|
||||
rate = Math.Min(RequestedDripRate, TotalDripRequest);
|
||||
else
|
||||
rate = RequestedDripRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
rate = (double)RequestedDripRate * Parent.DripRateModifier();
|
||||
}
|
||||
|
||||
if (rate < m_minimumDripRate)
|
||||
rate = m_minimumDripRate;
|
||||
else if (MaxDripRate > 0 && rate > MaxDripRate)
|
||||
rate = MaxDripRate;
|
||||
|
||||
return (Int64)rate;
|
||||
}
|
||||
}
|
||||
protected Int64 m_dripRate;
|
||||
|
||||
// <summary>
|
||||
// The maximum rate for flow control. Drip rate can never be greater than this.
|
||||
// </summary>
|
||||
public Int64 MaxDripRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current total of the requested maximum burst rates of
|
||||
/// this bucket's children buckets.
|
||||
/// The current total of the requested maximum burst rates of children buckets.
|
||||
/// </summary>
|
||||
protected Int64 m_totalDripRequest;
|
||||
public Int64 TotalDripRequest
|
||||
{
|
||||
get { return m_totalDripRequest; }
|
||||
set { m_totalDripRequest = value; }
|
||||
}
|
||||
|
||||
#endregion Properties
|
||||
|
||||
#region Constructor
|
||||
public Int64 TotalDripRequest { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
/// <param name="identifier">Identifier for this token bucket</param>
|
||||
/// <param name="parent">Parent bucket if this is a child bucket, or
|
||||
/// null if this is a root bucket</param>
|
||||
/// <param name="maxBurst">Maximum size of the bucket in bytes, or
|
||||
/// zero if this bucket has no maximum capacity</param>
|
||||
/// <param name="dripRate">Rate that the bucket fills, in bytes per
|
||||
/// second. If zero, the bucket always remains full</param>
|
||||
public TokenBucket(TokenBucket parent, Int64 dripRate)
|
||||
/// <param name="requestedDripRate">
|
||||
/// Requested rate that the bucket fills, in bytes per
|
||||
/// second. If zero, the bucket always remains full.
|
||||
/// </param>
|
||||
public TokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate)
|
||||
{
|
||||
// m_identifier = m_counter++;
|
||||
m_counter++;
|
||||
Identifier = identifier;
|
||||
|
||||
Parent = parent;
|
||||
RequestedDripRate = dripRate;
|
||||
// TotalDripRequest = dripRate; // this will be overwritten when a child node registers
|
||||
// MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst);
|
||||
RequestedDripRate = requestedDripRate;
|
||||
MaxDripRate = maxDripRate;
|
||||
m_lastDrip = Util.EnvironmentTickCount();
|
||||
}
|
||||
|
||||
#endregion Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Compute a modifier for the MaxBurst rate. This is 1.0, meaning
|
||||
/// no modification if the requested bandwidth is less than the
|
||||
|
@ -195,7 +218,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
protected double DripRateModifier()
|
||||
{
|
||||
Int64 driprate = DripRate;
|
||||
return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest;
|
||||
double modifier = driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest;
|
||||
|
||||
// if (DebugLevel > 0)
|
||||
// m_log.DebugFormat(
|
||||
// "[TOKEN BUCKET]: Returning drip modifier {0}/{1} = {2} from {3}",
|
||||
// driprate, TotalDripRequest, modifier, Identifier);
|
||||
|
||||
return modifier;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -217,16 +247,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
lock (m_children)
|
||||
{
|
||||
m_children[child] = request;
|
||||
// m_totalDripRequest = m_children.Values.Sum();
|
||||
|
||||
m_totalDripRequest = 0;
|
||||
TotalDripRequest = 0;
|
||||
foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
|
||||
m_totalDripRequest += cref.Value;
|
||||
TotalDripRequest += cref.Value;
|
||||
}
|
||||
|
||||
// Pass the new values up to the parent
|
||||
if (m_parent != null)
|
||||
m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
|
||||
if (Parent != null)
|
||||
{
|
||||
Int64 effectiveDripRate;
|
||||
|
||||
if (RequestedDripRate > 0)
|
||||
effectiveDripRate = Math.Min(RequestedDripRate, TotalDripRequest);
|
||||
else
|
||||
effectiveDripRate = TotalDripRequest;
|
||||
|
||||
Parent.RegisterRequest(this, effectiveDripRate);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -238,17 +276,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
lock (m_children)
|
||||
{
|
||||
m_children.Remove(child);
|
||||
// m_totalDripRequest = m_children.Values.Sum();
|
||||
|
||||
m_totalDripRequest = 0;
|
||||
TotalDripRequest = 0;
|
||||
foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
|
||||
m_totalDripRequest += cref.Value;
|
||||
TotalDripRequest += cref.Value;
|
||||
}
|
||||
|
||||
|
||||
// Pass the new values up to the parent
|
||||
if (m_parent != null)
|
||||
m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
|
||||
if (Parent != null)
|
||||
Parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -301,7 +337,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
// with no drip rate...
|
||||
if (DripRate == 0)
|
||||
{
|
||||
m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0");
|
||||
m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0 for {0}", Identifier);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -321,8 +357,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
public class AdaptiveTokenBucket : TokenBucket
|
||||
{
|
||||
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
public bool AdaptiveEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Target drip rate for this bucket.
|
||||
/// </summary>
|
||||
/// <remarks>Usually set by the client. If adaptive is enabled then throttles will increase until we reach this.</remarks>
|
||||
public Int64 TargetDripRate
|
||||
{
|
||||
get { return m_targetDripRate; }
|
||||
set
|
||||
{
|
||||
m_targetDripRate = Math.Max(value, m_minimumFlow);
|
||||
}
|
||||
}
|
||||
protected Int64 m_targetDripRate;
|
||||
|
||||
// <summary>
|
||||
// Adjust drip rate in response to network conditions.
|
||||
// </summary>
|
||||
public virtual Int64 AdjustedDripRate
|
||||
{
|
||||
get { return m_dripRate; }
|
||||
set
|
||||
{
|
||||
m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value, m_minimumFlow, TargetDripRate);
|
||||
m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
|
||||
|
||||
if (Parent != null)
|
||||
Parent.RegisterRequest(this, m_dripRate);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The minimum rate for flow control. Minimum drip rate is one
|
||||
/// packet per second. Open the throttle to 15 packets per second
|
||||
|
@ -330,65 +398,42 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
/// </summary>
|
||||
protected const Int64 m_minimumFlow = m_minimumDripRate * 15;
|
||||
|
||||
// <summary>
|
||||
// The maximum rate for flow control. Drip rate can never be
|
||||
// greater than this.
|
||||
// </summary>
|
||||
protected Int64 m_maxDripRate = 0;
|
||||
public Int64 MaxDripRate
|
||||
public AdaptiveTokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate, bool enabled)
|
||||
: base(identifier, parent, requestedDripRate, maxDripRate)
|
||||
{
|
||||
get { return (m_maxDripRate == 0 ? m_totalDripRequest : m_maxDripRate); }
|
||||
protected set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); }
|
||||
}
|
||||
AdaptiveEnabled = enabled;
|
||||
|
||||
public bool Enabled { get; private set; }
|
||||
|
||||
// <summary>
|
||||
//
|
||||
// </summary>
|
||||
public virtual Int64 AdjustedDripRate
|
||||
{
|
||||
get { return m_dripRate; }
|
||||
set {
|
||||
m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value,m_minimumFlow,MaxDripRate);
|
||||
m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
|
||||
if (m_parent != null)
|
||||
m_parent.RegisterRequest(this,m_dripRate);
|
||||
}
|
||||
}
|
||||
|
||||
// <summary>
|
||||
//
|
||||
// </summary>
|
||||
public AdaptiveTokenBucket(TokenBucket parent, Int64 maxDripRate, bool enabled) : base(parent,maxDripRate)
|
||||
{
|
||||
Enabled = enabled;
|
||||
|
||||
if (Enabled)
|
||||
if (AdaptiveEnabled)
|
||||
{
|
||||
// m_log.DebugFormat("[TOKENBUCKET] Adaptive throttle enabled");
|
||||
MaxDripRate = maxDripRate;
|
||||
// m_log.DebugFormat("[TOKENBUCKET]: Adaptive throttle enabled");
|
||||
TargetDripRate = m_minimumFlow;
|
||||
AdjustedDripRate = m_minimumFlow;
|
||||
}
|
||||
}
|
||||
|
||||
// <summary>
|
||||
//
|
||||
// Reliable packets sent to the client for which we never received an ack adjust the drip rate down.
|
||||
// </summary>
|
||||
public void ExpirePackets(Int32 count)
|
||||
{
|
||||
// m_log.WarnFormat("[ADAPTIVEBUCKET] drop {0} by {1} expired packets",AdjustedDripRate,count);
|
||||
if (Enabled)
|
||||
if (AdaptiveEnabled)
|
||||
{
|
||||
if (DebugLevel > 0)
|
||||
m_log.WarnFormat(
|
||||
"[ADAPTIVEBUCKET] drop {0} by {1} expired packets for {2}",
|
||||
AdjustedDripRate, count, Identifier);
|
||||
|
||||
AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,count));
|
||||
}
|
||||
}
|
||||
|
||||
// <summary>
|
||||
//
|
||||
// Reliable packets acked by the client adjust the drip rate up.
|
||||
// </summary>
|
||||
public void AcknowledgePackets(Int32 count)
|
||||
{
|
||||
if (Enabled)
|
||||
if (AdaptiveEnabled)
|
||||
AdjustedDripRate = AdjustedDripRate + count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -167,7 +167,7 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender
|
|||
|
||||
// Do Decode!
|
||||
if (decode)
|
||||
Util.FireAndForget(delegate { Decode(assetID, j2kData); });
|
||||
Util.FireAndForget(delegate { Decode(assetID, j2kData); }, null, "J2KDecoderModule.BeginDecode");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -302,7 +302,7 @@ namespace OpenSim.Region.CoreModules.Asset
|
|||
}
|
||||
|
||||
Util.FireAndForget(
|
||||
delegate { WriteFileCache(filename, asset); });
|
||||
delegate { WriteFileCache(filename, asset); }, null, "FlotsamAssetCache.UpdateFileCache");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
|
@ -39,7 +39,6 @@ using OpenSim.Framework;
|
|||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Region.Framework.Scenes.Serialization;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Asset.Tests
|
||||
{
|
||||
|
|
|
@ -387,7 +387,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
|
|||
if (!Enabled)
|
||||
return false;
|
||||
|
||||
return AttachObjectInternal(sp, group, attachmentPt, silent, addToInventory, false, append);
|
||||
group.DetachFromBackup();
|
||||
|
||||
bool success = AttachObjectInternal(sp, group, attachmentPt, silent, addToInventory, false, append);
|
||||
|
||||
if (!success)
|
||||
group.AttachToBackup();
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -815,8 +822,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
|
|||
"[ATTACHMENTS MODULE]: Adding attachment {0} to avatar {1} in pt {2} pos {3} {4}",
|
||||
so.Name, sp.Name, attachmentpoint, attachOffset, so.RootPart.AttachedPos);
|
||||
|
||||
so.DetachFromBackup();
|
||||
|
||||
// Remove from database and parcel prim count
|
||||
m_scene.DeleteFromStorage(so.UUID);
|
||||
m_scene.EventManager.TriggerParcelPrimCountTainted();
|
||||
|
|
|
@ -53,7 +53,6 @@ using OpenSim.Region.ScriptEngine.Interfaces;
|
|||
using OpenSim.Region.ScriptEngine.XEngine;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
|
||||
{
|
||||
|
@ -199,6 +198,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
|
|||
string attName = "att";
|
||||
|
||||
SceneObjectGroup so = SceneHelpers.AddSceneObject(scene, attName, sp.UUID);
|
||||
Assert.That(so.Backup, Is.True);
|
||||
|
||||
m_numberOfAttachEventsFired = 0;
|
||||
scene.AttachmentsModule.AttachObject(sp, so, (uint)AttachmentPoint.Chest, false, true, false);
|
||||
|
@ -213,6 +213,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
|
|||
Assert.That(attSo.IsAttachment);
|
||||
Assert.That(attSo.UsesPhysics, Is.False);
|
||||
Assert.That(attSo.IsTemporary, Is.False);
|
||||
Assert.That(attSo.Backup, Is.False);
|
||||
|
||||
// Check item status
|
||||
Assert.That(
|
||||
|
@ -385,7 +386,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
|
|||
public void TestRezAttachmentFromInventory()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
// log4net.Config.XmlConfigurator.Configure();
|
||||
// TestHelpers.EnableLogging();
|
||||
|
||||
Scene scene = CreateTestScene();
|
||||
UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1);
|
||||
|
@ -407,6 +408,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
|
|||
Assert.That(attSo.IsAttachment);
|
||||
Assert.That(attSo.UsesPhysics, Is.False);
|
||||
Assert.That(attSo.IsTemporary, Is.False);
|
||||
Assert.IsFalse(attSo.Backup);
|
||||
|
||||
// Check appearance status
|
||||
Assert.That(sp.Appearance.GetAttachments().Count, Is.EqualTo(1));
|
||||
|
@ -601,7 +603,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
|
|||
Assert.That(scene.InventoryService.GetItem(new InventoryItemBase(attItem.ID)), Is.Null);
|
||||
|
||||
// Check object in scene
|
||||
Assert.That(scene.GetSceneObjectGroup("att"), Is.Not.Null);
|
||||
SceneObjectGroup soInScene = scene.GetSceneObjectGroup("att");
|
||||
Assert.That(soInScene, Is.Not.Null);
|
||||
Assert.IsTrue(soInScene.Backup);
|
||||
|
||||
// Check events
|
||||
Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
|
||||
|
@ -755,6 +759,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
|
|||
Assert.That(attSo.IsAttachment);
|
||||
Assert.That(attSo.UsesPhysics, Is.False);
|
||||
Assert.That(attSo.IsTemporary, Is.False);
|
||||
Assert.IsFalse(attSo.Backup);
|
||||
|
||||
// Check appearance status
|
||||
List<AvatarAttachment> retreivedAttachments = presence.Appearance.GetAttachments();
|
||||
|
@ -884,6 +889,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
|
|||
SceneObjectGroup actualSceneBAtt = actualSceneBAttachments[0];
|
||||
Assert.That(actualSceneBAtt.Name, Is.EqualTo(attItem.Name));
|
||||
Assert.That(actualSceneBAtt.AttachmentPoint, Is.EqualTo((uint)AttachmentPoint.Chest));
|
||||
Assert.IsFalse(actualSceneBAtt.Backup);
|
||||
|
||||
Assert.That(sceneB.GetSceneObjectGroups().Count, Is.EqualTo(1));
|
||||
|
||||
|
@ -994,6 +1000,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
|
|||
SceneObjectGroup actualSceneBAtt = actualSceneBAttachments[0];
|
||||
Assert.That(actualSceneBAtt.Name, Is.EqualTo(attItem.Name));
|
||||
Assert.That(actualSceneBAtt.AttachmentPoint, Is.EqualTo((uint)AttachmentPoint.Chest));
|
||||
Assert.IsFalse(actualSceneBAtt.Backup);
|
||||
|
||||
Assert.That(sceneB.GetSceneObjectGroups().Count, Is.EqualTo(1));
|
||||
|
||||
|
|
|
@ -593,7 +593,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
|
||||
if (sendTime < now)
|
||||
{
|
||||
Util.FireAndForget(o => SendAppearance(avatarID));
|
||||
Util.FireAndForget(o => SendAppearance(avatarID), null, "AvatarFactoryModule.SendAppearance");
|
||||
m_sendqueue.Remove(avatarID);
|
||||
}
|
||||
}
|
||||
|
@ -611,7 +611,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
|
||||
if (sendTime < now)
|
||||
{
|
||||
Util.FireAndForget(o => SaveAppearance(avatarID));
|
||||
Util.FireAndForget(o => SaveAppearance(avatarID), null, "AvatarFactoryModule.SaveAppearance");
|
||||
m_savequeue.Remove(avatarID);
|
||||
}
|
||||
}
|
||||
|
@ -1038,7 +1038,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
client.SendWearables(sp.Appearance.Wearables, sp.Appearance.Serial++);
|
||||
else
|
||||
m_log.WarnFormat("[AVFACTORY]: Client_OnRequestWearables unable to find presence for {0}", client.AgentId);
|
||||
});
|
||||
}, null, "AvatarFactoryModule.OnClientRequestWearables");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -34,7 +34,6 @@ using OpenSim.Framework;
|
|||
using OpenSim.Region.CoreModules.Asset;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
||||
{
|
||||
|
|
|
@ -187,7 +187,7 @@ namespace OpenSim.Region.CoreModules.Avatar.BakedTextures
|
|||
{
|
||||
rc.Request(reqStream, m_Auth);
|
||||
m_log.DebugFormat("[XBakes]: stored {0} textures for user {1}", data.Length, agentId);
|
||||
}
|
||||
}, null, "XBakesModule.Store"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation;
|
|||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Avatar.Chat.Tests
|
||||
{
|
||||
|
|
|
@ -511,7 +511,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
|
|||
|
||||
// Notify about this user status
|
||||
StatusNotify(friendList, agentID, online);
|
||||
}
|
||||
}, null, "FriendsModule.StatusChange"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -660,7 +660,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
|
|||
FriendsService.Delete(friendUUI, agentID.ToString());
|
||||
|
||||
// notify the exfriend's service
|
||||
Util.FireAndForget(delegate { Delete(exfriendID, agentID, friendUUI); });
|
||||
Util.FireAndForget(
|
||||
delegate { Delete(exfriendID, agentID, friendUUI); }, null, "HGFriendsModule.DeleteFriendshipForeignFriend");
|
||||
|
||||
m_log.DebugFormat("[HGFRIENDS MODULE]: {0} terminated {1}", agentID, friendUUI);
|
||||
return true;
|
||||
|
@ -678,7 +679,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
|
|||
FriendsService.Delete(agentUUI, exfriendID.ToString());
|
||||
|
||||
// notify the agent's service?
|
||||
Util.FireAndForget(delegate { Delete(agentID, exfriendID, agentUUI); });
|
||||
Util.FireAndForget(
|
||||
delegate { Delete(agentID, exfriendID, agentUUI); }, null, "HGFriendsModule.DeleteFriendshipLocalFriend");
|
||||
|
||||
m_log.DebugFormat("[HGFRIENDS MODULE]: {0} terminated {1}", agentUUI, exfriendID);
|
||||
return true;
|
||||
|
|
|
@ -35,7 +35,6 @@ using OpenSim.Framework;
|
|||
using OpenSim.Region.CoreModules.Avatar.Friends;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Avatar.Friends.Tests
|
||||
{
|
||||
|
|
|
@ -213,7 +213,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
|
|||
HandleUndeliverableMessage(im, result);
|
||||
else
|
||||
result(success);
|
||||
});
|
||||
}, null, "HGMessageTransferModule.SendInstantMessage");
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -43,7 +43,6 @@ using OpenSim.Region.Framework.Scenes;
|
|||
using OpenSim.Region.Framework.Scenes.Serialization;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
|
||||
{
|
||||
|
|
|
@ -43,7 +43,6 @@ using OpenSim.Region.Framework.Scenes;
|
|||
using OpenSim.Region.Framework.Scenes.Serialization;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
|
||||
{
|
||||
|
|
|
@ -43,7 +43,6 @@ using OpenSim.Region.Framework.Scenes;
|
|||
using OpenSim.Region.Framework.Scenes.Serialization;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
|
||||
{
|
||||
|
|
|
@ -43,7 +43,6 @@ using OpenSim.Region.Framework.Scenes;
|
|||
using OpenSim.Region.Framework.Scenes.Serialization;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
|
||||
{
|
||||
|
|
|
@ -39,7 +39,6 @@ using OpenSim.Region.Framework.Interfaces;
|
|||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer.Tests
|
||||
{
|
||||
|
|
|
@ -181,7 +181,7 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
Util.FireAndForget(delegate
|
||||
{
|
||||
GetImageAssets(((IScenePresence)obj).UUID);
|
||||
});
|
||||
}, null, "UserProfileModule.GetImageAssets");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -871,8 +871,8 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
object Pref = (object)pref;
|
||||
if(!rpc.JsonRpcRequest(ref Pref, "user_preferences_request", serverURI, UUID.Random().ToString()))
|
||||
{
|
||||
m_log.InfoFormat("[PROFILES]: UserPreferences request error");
|
||||
remoteClient.SendAgentAlertMessage("Error requesting preferences", false);
|
||||
// m_log.InfoFormat("[PROFILES]: UserPreferences request error");
|
||||
// remoteClient.SendAgentAlertMessage("Error requesting preferences", false);
|
||||
return;
|
||||
}
|
||||
pref = (UserPreferences) Pref;
|
||||
|
|
|
@ -804,8 +804,6 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
// once we reach here...
|
||||
//avatar.Scene.RemoveCapsHandler(avatar.UUID);
|
||||
|
||||
string capsPath = String.Empty;
|
||||
|
||||
AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode);
|
||||
AgentCircuitData agentCircuit = sp.ControllingClient.RequestClientInfo();
|
||||
agentCircuit.startpos = position;
|
||||
|
@ -2702,5 +2700,69 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
}
|
||||
#endregion
|
||||
|
||||
public virtual bool HandleIncomingSceneObject(SceneObjectGroup so, Vector3 newPosition)
|
||||
{
|
||||
// If the user is banned, we won't let any of their objects
|
||||
// enter. Period.
|
||||
//
|
||||
if (Scene.RegionInfo.EstateSettings.IsBanned(so.OwnerID))
|
||||
{
|
||||
m_log.DebugFormat(
|
||||
"[ENTITY TRANSFER MODULE]: Denied prim crossing of {0} {1} into {2} for banned avatar {3}",
|
||||
so.Name, so.UUID, Scene.Name, so.OwnerID);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newPosition != Vector3.Zero)
|
||||
so.RootPart.GroupPosition = newPosition;
|
||||
|
||||
if (!Scene.AddSceneObject(so))
|
||||
{
|
||||
m_log.DebugFormat(
|
||||
"[ENTITY TRANSFER MODULE]: Problem adding scene object {0} {1} into {2} ",
|
||||
so.Name, so.UUID, Scene.Name);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!so.IsAttachment)
|
||||
{
|
||||
// FIXME: It would be better to never add the scene object at all rather than add it and then delete
|
||||
// it
|
||||
if (!Scene.Permissions.CanObjectEntry(so.UUID, true, so.AbsolutePosition))
|
||||
{
|
||||
// Deny non attachments based on parcel settings
|
||||
//
|
||||
m_log.Info("[ENTITY TRANSFER MODULE]: Denied prim crossing because of parcel settings");
|
||||
|
||||
Scene.DeleteSceneObject(so, false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// For attachments, we need to wait until the agent is root
|
||||
// before we restart the scripts, or else some functions won't work.
|
||||
so.RootPart.ParentGroup.CreateScriptInstances(
|
||||
0, false, Scene.DefaultScriptEngine, GetStateSource(so));
|
||||
|
||||
so.ResumeScripts();
|
||||
|
||||
if (so.RootPart.KeyframeMotion != null)
|
||||
so.RootPart.KeyframeMotion.UpdateSceneObject(so);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private int GetStateSource(SceneObjectGroup sog)
|
||||
{
|
||||
ScenePresence sp = Scene.GetScenePresence(sog.OwnerID);
|
||||
|
||||
if (sp != null)
|
||||
return sp.GetStateSource();
|
||||
|
||||
return 2; // StateSource.PrimCrossing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,6 +110,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used for processing analysis of incoming attachments in a controlled fashion.
|
||||
/// </summary>
|
||||
private HGIncomingSceneObjectEngine m_incomingSceneObjectEngine;
|
||||
|
||||
#region ISharedRegionModule
|
||||
|
||||
public override string Name
|
||||
|
@ -153,33 +158,10 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
if (m_Enabled)
|
||||
{
|
||||
scene.RegisterModuleInterface<IUserAgentVerificationModule>(this);
|
||||
scene.EventManager.OnIncomingSceneObject += OnIncomingSceneObject;
|
||||
}
|
||||
}
|
||||
//scene.EventManager.OnIncomingSceneObject += OnIncomingSceneObject;
|
||||
|
||||
void OnIncomingSceneObject(SceneObjectGroup so)
|
||||
{
|
||||
if (!so.IsAttachment)
|
||||
return;
|
||||
|
||||
if (so.AttachedAvatar == UUID.Zero || Scene.UserManagementModule.IsLocalGridUser(so.AttachedAvatar))
|
||||
return;
|
||||
|
||||
// foreign user
|
||||
AgentCircuitData aCircuit = Scene.AuthenticateHandler.GetAgentCircuitData(so.AttachedAvatar);
|
||||
if (aCircuit != null && (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0)
|
||||
{
|
||||
if (aCircuit.ServiceURLs != null && aCircuit.ServiceURLs.ContainsKey("AssetServerURI"))
|
||||
{
|
||||
string url = aCircuit.ServiceURLs["AssetServerURI"].ToString();
|
||||
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Incoming attachment {0} for HG user {1} with asset server {2}", so.Name, so.AttachedAvatar, url);
|
||||
Dictionary<UUID, sbyte> ids = new Dictionary<UUID, sbyte>();
|
||||
HGUuidGatherer uuidGatherer = new HGUuidGatherer(Scene.AssetService, url);
|
||||
uuidGatherer.GatherAssetUuids(so, ids);
|
||||
|
||||
foreach (KeyValuePair<UUID, sbyte> kvp in ids)
|
||||
uuidGatherer.FetchAsset(kvp.Key);
|
||||
}
|
||||
m_incomingSceneObjectEngine = new HGIncomingSceneObjectEngine(scene.Name);
|
||||
m_incomingSceneObjectEngine.Start();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,12 +191,15 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
base.RemoveRegion(scene);
|
||||
|
||||
if (m_Enabled)
|
||||
{
|
||||
scene.UnregisterModuleInterface<IUserAgentVerificationModule>(this);
|
||||
m_incomingSceneObjectEngine.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HG overrides of IEntiryTransferModule
|
||||
#region HG overrides of IEntityTransferModule
|
||||
|
||||
protected override GridRegion GetFinalDestination(GridRegion region, UUID agentID, string agentHomeURI, out string message)
|
||||
{
|
||||
|
@ -554,6 +539,132 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
}
|
||||
}
|
||||
|
||||
private void RemoveIncomingSceneObjectJobs(string commonIdToRemove)
|
||||
{
|
||||
List<Job> jobsToReinsert = new List<Job>();
|
||||
int jobsRemoved = 0;
|
||||
|
||||
Job job;
|
||||
while ((job = m_incomingSceneObjectEngine.RemoveNextRequest()) != null)
|
||||
{
|
||||
if (job.CommonId != commonIdToRemove)
|
||||
jobsToReinsert.Add(job);
|
||||
else
|
||||
jobsRemoved++;
|
||||
}
|
||||
|
||||
m_log.DebugFormat(
|
||||
"[HG ENTITY TRANSFER]: Removing {0} jobs with common ID {1} and reinserting {2} other jobs",
|
||||
jobsRemoved, commonIdToRemove, jobsToReinsert.Count);
|
||||
|
||||
if (jobsToReinsert.Count > 0)
|
||||
{
|
||||
foreach (Job jobToReinsert in jobsToReinsert)
|
||||
m_incomingSceneObjectEngine.QueueRequest(jobToReinsert);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool HandleIncomingSceneObject(SceneObjectGroup so, Vector3 newPosition)
|
||||
{
|
||||
// FIXME: We must make it so that we can use SOG.IsAttachment here. At the moment it is always null!
|
||||
if (!so.IsAttachmentCheckFull())
|
||||
return base.HandleIncomingSceneObject(so, newPosition);
|
||||
|
||||
// Equally, we can't use so.AttachedAvatar here.
|
||||
if (so.OwnerID == UUID.Zero || Scene.UserManagementModule.IsLocalGridUser(so.OwnerID))
|
||||
return base.HandleIncomingSceneObject(so, newPosition);
|
||||
|
||||
// foreign user
|
||||
AgentCircuitData aCircuit = Scene.AuthenticateHandler.GetAgentCircuitData(so.OwnerID);
|
||||
if (aCircuit != null)
|
||||
{
|
||||
if ((aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) == 0)
|
||||
{
|
||||
// We have already pulled the necessary attachments from the source grid.
|
||||
base.HandleIncomingSceneObject(so, newPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (aCircuit.ServiceURLs != null && aCircuit.ServiceURLs.ContainsKey("AssetServerURI"))
|
||||
{
|
||||
m_incomingSceneObjectEngine.QueueRequest(
|
||||
string.Format("HG UUID Gather for attachment {0} for {1}", so.Name, aCircuit.Name),
|
||||
so.OwnerID.ToString(),
|
||||
o =>
|
||||
{
|
||||
string url = aCircuit.ServiceURLs["AssetServerURI"].ToString();
|
||||
// m_log.DebugFormat(
|
||||
// "[HG ENTITY TRANSFER MODULE]: Incoming attachment {0} for HG user {1} with asset service {2}",
|
||||
// so.Name, so.AttachedAvatar, url);
|
||||
|
||||
IteratingHGUuidGatherer uuidGatherer = new IteratingHGUuidGatherer(Scene.AssetService, url);
|
||||
uuidGatherer.RecordAssetUuids(so);
|
||||
|
||||
while (!uuidGatherer.Complete)
|
||||
{
|
||||
int tickStart = Util.EnvironmentTickCount();
|
||||
|
||||
UUID? nextUuid = uuidGatherer.NextUuidToInspect;
|
||||
uuidGatherer.GatherNext();
|
||||
|
||||
// m_log.DebugFormat(
|
||||
// "[HG ENTITY TRANSFER]: Gathered attachment asset uuid {0} for object {1} for HG user {2} took {3} ms with asset service {4}",
|
||||
// nextUuid, so.Name, so.OwnerID, Util.EnvironmentTickCountSubtract(tickStart), url);
|
||||
|
||||
int ticksElapsed = Util.EnvironmentTickCountSubtract(tickStart);
|
||||
|
||||
if (ticksElapsed > 30000)
|
||||
{
|
||||
m_log.WarnFormat(
|
||||
"[HG ENTITY TRANSFER]: Removing incoming scene object jobs for HG user {0} as gather of {1} from {2} took {3} ms to respond (> {4} ms)",
|
||||
so.OwnerID, so.Name, url, ticksElapsed, 30000);
|
||||
|
||||
RemoveIncomingSceneObjectJobs(so.OwnerID.ToString());
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
IDictionary<UUID, sbyte> ids = uuidGatherer.GetGatheredUuids();
|
||||
|
||||
// m_log.DebugFormat(
|
||||
// "[HG ENTITY TRANSFER]: Fetching {0} assets for attachment {1} for HG user {2} with asset service {3}",
|
||||
// ids.Count, so.Name, so.OwnerID, url);
|
||||
|
||||
foreach (KeyValuePair<UUID, sbyte> kvp in ids)
|
||||
{
|
||||
int tickStart = Util.EnvironmentTickCount();
|
||||
|
||||
uuidGatherer.FetchAsset(kvp.Key);
|
||||
|
||||
int ticksElapsed = Util.EnvironmentTickCountSubtract(tickStart);
|
||||
|
||||
if (ticksElapsed > 30000)
|
||||
{
|
||||
m_log.WarnFormat(
|
||||
"[HG ENTITY TRANSFER]: Removing incoming scene object jobs for HG user {0} as fetch of {1} from {2} took {3} ms to respond (> {4} ms)",
|
||||
so.OwnerID, kvp.Key, url, ticksElapsed, 30000);
|
||||
|
||||
RemoveIncomingSceneObjectJobs(so.OwnerID.ToString());
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
base.HandleIncomingSceneObject(so, newPosition);
|
||||
|
||||
// m_log.DebugFormat(
|
||||
// "[HG ENTITY TRANSFER MODULE]: Completed incoming attachment {0} for HG user {1} with asset server {2}",
|
||||
// so.Name, so.OwnerID, url);
|
||||
},
|
||||
null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IUserAgentVerificationModule
|
||||
|
|
|
@ -0,0 +1,344 @@
|
|||
/*
|
||||
* 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.Concurrent;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using log4net;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Monitoring;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||
{
|
||||
public class Job
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
public string CommonId { get; private set; }
|
||||
public WaitCallback Callback { get; private set; }
|
||||
public object O { get; private set; }
|
||||
|
||||
public Job(string name, string commonId, WaitCallback callback, object o)
|
||||
{
|
||||
Name = name;
|
||||
CommonId = commonId;
|
||||
Callback = callback;
|
||||
O = o;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: These kinds of classes MUST be generalized with JobEngine, etc.
|
||||
public class HGIncomingSceneObjectEngine
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
public int LogLevel { get; set; }
|
||||
|
||||
public bool IsRunning { get; private set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The timeout in milliseconds to wait for at least one event to be written when the recorder is stopping.
|
||||
/// </summary>
|
||||
public int RequestProcessTimeoutOnStop { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controls whether we need to warn in the log about exceeding the max queue size.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is flipped to false once queue max has been exceeded and back to true when it falls below max, in
|
||||
/// order to avoid spamming the log with lots of warnings.
|
||||
/// </remarks>
|
||||
private bool m_warnOverMaxQueue = true;
|
||||
|
||||
private BlockingCollection<Job> m_requestQueue;
|
||||
|
||||
private CancellationTokenSource m_cancelSource = new CancellationTokenSource();
|
||||
|
||||
private Stat m_requestsWaitingStat;
|
||||
|
||||
private Job m_currentJob;
|
||||
|
||||
/// <summary>
|
||||
/// Used to signal that we are ready to complete stop.
|
||||
/// </summary>
|
||||
private ManualResetEvent m_finishedProcessingAfterStop = new ManualResetEvent(false);
|
||||
|
||||
public HGIncomingSceneObjectEngine(string name)
|
||||
{
|
||||
// LogLevel = 1;
|
||||
Name = name;
|
||||
RequestProcessTimeoutOnStop = 5000;
|
||||
|
||||
// MainConsole.Instance.Commands.AddCommand(
|
||||
// "Debug",
|
||||
// false,
|
||||
// "debug jobengine",
|
||||
// "debug jobengine <start|stop|status>",
|
||||
// "Start, stop or get status of the job engine.",
|
||||
// "If stopped then all jobs are processed immediately.",
|
||||
// HandleControlCommand);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (IsRunning)
|
||||
return;
|
||||
|
||||
IsRunning = true;
|
||||
|
||||
m_finishedProcessingAfterStop.Reset();
|
||||
|
||||
m_requestQueue = new BlockingCollection<Job>(new ConcurrentQueue<Job>(), 5000);
|
||||
|
||||
m_requestsWaitingStat =
|
||||
new Stat(
|
||||
"HGIncomingAttachmentsWaiting",
|
||||
"Number of incoming attachments waiting for processing.",
|
||||
"",
|
||||
"",
|
||||
"entitytransfer",
|
||||
Name,
|
||||
StatType.Pull,
|
||||
MeasuresOfInterest.None,
|
||||
stat => stat.Value = m_requestQueue.Count,
|
||||
StatVerbosity.Debug);
|
||||
|
||||
StatsManager.RegisterStat(m_requestsWaitingStat);
|
||||
|
||||
Watchdog.StartThread(
|
||||
ProcessRequests,
|
||||
string.Format("HG Incoming Scene Object Engine Thread ({0})", Name),
|
||||
ThreadPriority.Normal,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
int.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsRunning)
|
||||
return;
|
||||
|
||||
IsRunning = false;
|
||||
|
||||
int requestsLeft = m_requestQueue.Count;
|
||||
|
||||
if (requestsLeft <= 0)
|
||||
{
|
||||
m_cancelSource.Cancel();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log.InfoFormat("[HG INCOMING SCENE OBJECT ENGINE]: Waiting to write {0} events after stop.", requestsLeft);
|
||||
|
||||
while (requestsLeft > 0)
|
||||
{
|
||||
if (!m_finishedProcessingAfterStop.WaitOne(RequestProcessTimeoutOnStop))
|
||||
{
|
||||
// After timeout no events have been written
|
||||
if (requestsLeft == m_requestQueue.Count)
|
||||
{
|
||||
m_log.WarnFormat(
|
||||
"[HG INCOMING SCENE OBJECT ENGINE]: No requests processed after {0} ms wait. Discarding remaining {1} requests",
|
||||
RequestProcessTimeoutOnStop, requestsLeft);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
requestsLeft = m_requestQueue.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_cancelSource.Dispose();
|
||||
StatsManager.DeregisterStat(m_requestsWaitingStat);
|
||||
m_requestsWaitingStat = null;
|
||||
m_requestQueue = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Job RemoveNextRequest()
|
||||
{
|
||||
Job nextRequest;
|
||||
m_requestQueue.TryTake(out nextRequest);
|
||||
|
||||
return nextRequest;
|
||||
}
|
||||
|
||||
public bool QueueRequest(string name, string commonId, WaitCallback req, object o)
|
||||
{
|
||||
return QueueRequest(new Job(name, commonId, req, o));
|
||||
}
|
||||
|
||||
public bool QueueRequest(Job job)
|
||||
{
|
||||
if (LogLevel >= 1)
|
||||
m_log.DebugFormat(
|
||||
"[HG INCOMING SCENE OBJECT ENGINE]: Queued job {0}, common ID {1}", job.Name, job.CommonId);
|
||||
|
||||
if (m_requestQueue.Count < m_requestQueue.BoundedCapacity)
|
||||
{
|
||||
// m_log.DebugFormat(
|
||||
// "[OUTGOING QUEUE REFILL ENGINE]: Adding request for categories {0} for {1} in {2}",
|
||||
// categories, client.AgentID, m_udpServer.Scene.Name);
|
||||
|
||||
m_requestQueue.Add(job);
|
||||
|
||||
if (!m_warnOverMaxQueue)
|
||||
m_warnOverMaxQueue = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_warnOverMaxQueue)
|
||||
{
|
||||
// m_log.WarnFormat(
|
||||
// "[JOB ENGINE]: Request queue at maximum capacity, not recording request from {0} in {1}",
|
||||
// client.AgentID, m_udpServer.Scene.Name);
|
||||
|
||||
m_log.WarnFormat("[HG INCOMING SCENE OBJECT ENGINE]: Request queue at maximum capacity, not recording job");
|
||||
|
||||
m_warnOverMaxQueue = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessRequests()
|
||||
{
|
||||
try
|
||||
{
|
||||
while (IsRunning || m_requestQueue.Count > 0)
|
||||
{
|
||||
m_currentJob = m_requestQueue.Take(m_cancelSource.Token);
|
||||
|
||||
// QueueEmpty callback = req.Client.OnQueueEmpty;
|
||||
//
|
||||
// if (callback != null)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// callback(req.Categories);
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// m_log.Error("[OUTGOING QUEUE REFILL ENGINE]: ProcessRequests(" + req.Categories + ") threw an exception: " + e.Message, e);
|
||||
// }
|
||||
// }
|
||||
|
||||
if (LogLevel >= 1)
|
||||
m_log.DebugFormat("[HG INCOMING SCENE OBJECT ENGINE]: Processing job {0}", m_currentJob.Name);
|
||||
|
||||
try
|
||||
{
|
||||
m_currentJob.Callback.Invoke(m_currentJob.O);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.Error(
|
||||
string.Format(
|
||||
"[HG INCOMING SCENE OBJECT ENGINE]: Job {0} failed, continuing. Exception ", m_currentJob.Name), e);
|
||||
}
|
||||
|
||||
if (LogLevel >= 1)
|
||||
m_log.DebugFormat("[HG INCOMING SCENE OBJECT ENGINE]: Processed job {0}", m_currentJob.Name);
|
||||
|
||||
m_currentJob = null;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
|
||||
m_finishedProcessingAfterStop.Set();
|
||||
}
|
||||
|
||||
// private void HandleControlCommand(string module, string[] args)
|
||||
// {
|
||||
// // if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
// // return;
|
||||
//
|
||||
// if (args.Length < 3)
|
||||
// {
|
||||
// MainConsole.Instance.Output("Usage: debug jobengine <stop|start|status|loglevel>");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// string subCommand = args[2];
|
||||
//
|
||||
// if (subCommand == "stop")
|
||||
// {
|
||||
// Stop();
|
||||
// MainConsole.Instance.OutputFormat("Stopped job engine.");
|
||||
// }
|
||||
// else if (subCommand == "start")
|
||||
// {
|
||||
// Start();
|
||||
// MainConsole.Instance.OutputFormat("Started job engine.");
|
||||
// }
|
||||
// else if (subCommand == "status")
|
||||
// {
|
||||
// MainConsole.Instance.OutputFormat("Job engine running: {0}", IsRunning);
|
||||
// MainConsole.Instance.OutputFormat("Current job {0}", m_currentJob != null ? m_currentJob.Name : "none");
|
||||
// MainConsole.Instance.OutputFormat(
|
||||
// "Jobs waiting: {0}", IsRunning ? m_requestQueue.Count.ToString() : "n/a");
|
||||
// MainConsole.Instance.OutputFormat("Log Level: {0}", LogLevel);
|
||||
// }
|
||||
//
|
||||
// else if (subCommand == "loglevel")
|
||||
// {
|
||||
// // int logLevel;
|
||||
// int logLevel = int.Parse(args[3]);
|
||||
// // if (ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out logLevel))
|
||||
// // {
|
||||
// LogLevel = logLevel;
|
||||
// MainConsole.Instance.OutputFormat("Set log level to {0}", LogLevel);
|
||||
// // }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// MainConsole.Instance.OutputFormat("Unrecognized job engine subcommand {0}", subCommand);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
|
@ -189,50 +189,203 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
|
|||
return Utils.StringToBytes(RewriteSOP(xml));
|
||||
}
|
||||
|
||||
protected string RewriteSOP(string xml)
|
||||
protected void TransformXml(XmlReader reader, XmlWriter writer)
|
||||
{
|
||||
XmlDocument doc = new XmlDocument();
|
||||
doc.LoadXml(xml);
|
||||
XmlNodeList sops = doc.GetElementsByTagName("SceneObjectPart");
|
||||
// m_log.DebugFormat("[HG ASSET MAPPER]: Transforming XML");
|
||||
|
||||
foreach (XmlNode sop in sops)
|
||||
int sopDepth = -1;
|
||||
UserAccount creator = null;
|
||||
bool hasCreatorData = false;
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
UserAccount creator = null;
|
||||
bool hasCreatorData = false;
|
||||
XmlNodeList nodes = sop.ChildNodes;
|
||||
foreach (XmlNode node in nodes)
|
||||
//Console.WriteLine("Depth: {0}", reader.Depth);
|
||||
|
||||
switch (reader.NodeType)
|
||||
{
|
||||
if (node.Name == "CreatorID")
|
||||
case XmlNodeType.Attribute:
|
||||
writer.WriteAttributeString(reader.Prefix, reader.Name, reader.NamespaceURI, reader.Value);
|
||||
break;
|
||||
|
||||
case XmlNodeType.CDATA:
|
||||
writer.WriteCData(reader.Value);
|
||||
break;
|
||||
|
||||
case XmlNodeType.Comment:
|
||||
writer.WriteComment(reader.Value);
|
||||
break;
|
||||
|
||||
case XmlNodeType.DocumentType:
|
||||
writer.WriteDocType(reader.Name, reader.Value, null, null);
|
||||
break;
|
||||
|
||||
case XmlNodeType.Element:
|
||||
// m_log.DebugFormat("Depth {0} at element {1}", reader.Depth, reader.Name);
|
||||
|
||||
writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
|
||||
|
||||
if (reader.LocalName == "SceneObjectPart")
|
||||
{
|
||||
UUID uuid = UUID.Zero;
|
||||
UUID.TryParse(node.InnerText, out uuid);
|
||||
creator = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, uuid);
|
||||
if (sopDepth < 0)
|
||||
{
|
||||
sopDepth = reader.Depth;
|
||||
// m_log.DebugFormat("[HG ASSET MAPPER]: Set sopDepth to {0}", sopDepth);
|
||||
}
|
||||
}
|
||||
if (node.Name == "CreatorData" && node.InnerText != null && node.InnerText != string.Empty)
|
||||
hasCreatorData = true;
|
||||
else
|
||||
{
|
||||
if (sopDepth >= 0 && reader.Depth == sopDepth + 1)
|
||||
{
|
||||
if (reader.Name == "CreatorID")
|
||||
{
|
||||
reader.Read();
|
||||
if (reader.NodeType == XmlNodeType.Element && reader.Name == "Guid" || reader.Name == "UUID")
|
||||
{
|
||||
reader.Read();
|
||||
|
||||
//if (node.Name == "OwnerID")
|
||||
//{
|
||||
// UserAccount owner = GetUser(node.InnerText);
|
||||
// if (owner != null)
|
||||
// node.InnerText = m_ProfileServiceURL + "/" + node.InnerText + "/" + owner.FirstName + " " + owner.LastName;
|
||||
//}
|
||||
}
|
||||
if (reader.NodeType == XmlNodeType.Text)
|
||||
{
|
||||
UUID uuid = UUID.Zero;
|
||||
UUID.TryParse(reader.Value, out uuid);
|
||||
creator = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, uuid);
|
||||
writer.WriteElementString("UUID", reader.Value);
|
||||
reader.Read();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we unexpected run across mixed content in this node, still carry on
|
||||
// transforming the subtree (this replicates earlier behaviour).
|
||||
TransformXml(reader, writer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we unexpected run across mixed content in this node, still carry on
|
||||
// transforming the subtree (this replicates earlier behaviour).
|
||||
TransformXml(reader, writer);
|
||||
}
|
||||
}
|
||||
else if (reader.Name == "CreatorData")
|
||||
{
|
||||
reader.Read();
|
||||
if (reader.NodeType == XmlNodeType.Text)
|
||||
{
|
||||
hasCreatorData = true;
|
||||
writer.WriteString(reader.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we unexpected run across mixed content in this node, still carry on
|
||||
// transforming the subtree (this replicates earlier behaviour).
|
||||
TransformXml(reader, writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reader.IsEmptyElement)
|
||||
{
|
||||
// m_log.DebugFormat("[HG ASSET MAPPER]: Writing end for empty element {0}", reader.Name);
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
if (!hasCreatorData && creator != null)
|
||||
{
|
||||
XmlElement creatorData = doc.CreateElement("CreatorData");
|
||||
creatorData.InnerText = m_HomeURI + ";" + creator.FirstName + " " + creator.LastName;
|
||||
sop.AppendChild(creatorData);
|
||||
break;
|
||||
|
||||
case XmlNodeType.EndElement:
|
||||
// m_log.DebugFormat("Depth {0} at EndElement", reader.Depth);
|
||||
if (sopDepth == reader.Depth)
|
||||
{
|
||||
if (!hasCreatorData && creator != null)
|
||||
writer.WriteElementString(reader.Prefix, "CreatorData", reader.NamespaceURI, string.Format("{0};{1} {2}", m_HomeURI, creator.FirstName, creator.LastName));
|
||||
|
||||
// m_log.DebugFormat("[HG ASSET MAPPER]: Reset sopDepth");
|
||||
sopDepth = -1;
|
||||
creator = null;
|
||||
hasCreatorData = false;
|
||||
}
|
||||
writer.WriteEndElement();
|
||||
break;
|
||||
|
||||
case XmlNodeType.EntityReference:
|
||||
writer.WriteEntityRef(reader.Name);
|
||||
break;
|
||||
|
||||
case XmlNodeType.ProcessingInstruction:
|
||||
writer.WriteProcessingInstruction(reader.Name, reader.Value);
|
||||
break;
|
||||
|
||||
case XmlNodeType.Text:
|
||||
writer.WriteString(reader.Value);
|
||||
break;
|
||||
|
||||
default:
|
||||
m_log.WarnFormat("[HG ASSET MAPPER]: Unrecognized node in asset XML transform in {0}", m_scene.Name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using (StringWriter wr = new StringWriter())
|
||||
protected string RewriteSOP(string xmlData)
|
||||
{
|
||||
// Console.WriteLine("Input XML [{0}]", xmlData);
|
||||
|
||||
using (StringWriter sw = new StringWriter())
|
||||
using (XmlTextWriter writer = new XmlTextWriter(sw))
|
||||
using (XmlTextReader wrappedReader = new XmlTextReader(xmlData, XmlNodeType.Element, null))
|
||||
using (XmlReader reader = XmlReader.Create(wrappedReader, new XmlReaderSettings() { IgnoreWhitespace = true, ConformanceLevel = ConformanceLevel.Fragment }))
|
||||
{
|
||||
doc.Save(wr);
|
||||
return wr.ToString();
|
||||
TransformXml(reader, writer);
|
||||
|
||||
writer.WriteEndDocument();
|
||||
|
||||
// Console.WriteLine("Output: [{0}]", sw.ToString());
|
||||
|
||||
return sw.ToString();
|
||||
}
|
||||
|
||||
// We are now taking the more complex streaming approach above because some assets can be very large
|
||||
// and can trigger higher CPU use or possibly memory problems.
|
||||
// XmlDocument doc = new XmlDocument();
|
||||
// doc.LoadXml(xml);
|
||||
// XmlNodeList sops = doc.GetElementsByTagName("SceneObjectPart");
|
||||
//
|
||||
// foreach (XmlNode sop in sops)
|
||||
// {
|
||||
// UserAccount creator = null;
|
||||
// bool hasCreatorData = false;
|
||||
// XmlNodeList nodes = sop.ChildNodes;
|
||||
// foreach (XmlNode node in nodes)
|
||||
// {
|
||||
// if (node.Name == "CreatorID")
|
||||
// {
|
||||
// UUID uuid = UUID.Zero;
|
||||
// UUID.TryParse(node.InnerText, out uuid);
|
||||
// creator = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, uuid);
|
||||
// }
|
||||
// if (node.Name == "CreatorData" && node.InnerText != null && node.InnerText != string.Empty)
|
||||
// hasCreatorData = true;
|
||||
//
|
||||
// //if (node.Name == "OwnerID")
|
||||
// //{
|
||||
// // UserAccount owner = GetUser(node.InnerText);
|
||||
// // if (owner != null)
|
||||
// // node.InnerText = m_ProfileServiceURL + "/" + node.InnerText + "/" + owner.FirstName + " " + owner.LastName;
|
||||
// //}
|
||||
// }
|
||||
//
|
||||
// if (!hasCreatorData && creator != null)
|
||||
// {
|
||||
// XmlElement creatorData = doc.CreateElement("CreatorData");
|
||||
// creatorData.InnerText = m_HomeURI + ";" + creator.FirstName + " " + creator.LastName;
|
||||
// sop.AppendChild(creatorData);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// using (StringWriter wr = new StringWriter())
|
||||
// {
|
||||
// doc.Save(wr);
|
||||
// return wr.ToString();
|
||||
// }
|
||||
}
|
||||
|
||||
// TODO: unused
|
||||
|
|
|
@ -912,7 +912,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
|
|||
// one full update during the attachment
|
||||
// process causes some clients to fail to display the
|
||||
// attachment properly.
|
||||
m_Scene.AddNewSceneObject(group, true, false);
|
||||
m_Scene.AddNewSceneObject(group, !attachment, false);
|
||||
|
||||
// if attachment we set it's asset id so object updates
|
||||
// can reflect that, if not, we set it's position in world.
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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.Xml;
|
||||
using NUnit.Framework;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.CoreModules.Framework.InventoryAccess;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using OpenSim.Tests.Common;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class HGAssetMapperTests : OpenSimTestCase
|
||||
{
|
||||
[Test]
|
||||
public void TestPostAssetRewrite()
|
||||
{
|
||||
TestHelpers.InMethod();
|
||||
// TestHelpers.EnableLogging();
|
||||
|
||||
string homeUrl = "http://hg.HomeTestPostAssetRewriteGrid.com";
|
||||
string foreignUrl = "http://hg.ForeignTestPostAssetRewriteGrid.com";
|
||||
UUID assetId = TestHelpers.ParseTail(0x1);
|
||||
UUID userId = TestHelpers.ParseTail(0x10);
|
||||
string userFirstName = "TestPostAsset";
|
||||
string userLastName = "Rewrite";
|
||||
int soPartsCount = 3;
|
||||
|
||||
Scene scene = new SceneHelpers().SetupScene();
|
||||
HGAssetMapper hgam = new HGAssetMapper(scene, homeUrl);
|
||||
UserAccount ua
|
||||
= UserAccountHelpers.CreateUserWithInventory(scene, userFirstName, userLastName, userId, "password");
|
||||
|
||||
//AssetBase ncAssetSet = AssetHelpers.CreateNotecardAsset(assetId, "TestPostAssetRewriteNotecard");
|
||||
SceneObjectGroup so = SceneHelpers.CreateSceneObject(soPartsCount, ua.PrincipalID);
|
||||
AssetBase ncAssetSet = AssetHelpers.CreateAsset(assetId, so);
|
||||
ncAssetSet.CreatorID = foreignUrl;
|
||||
hgam.PostAsset(foreignUrl, ncAssetSet);
|
||||
|
||||
AssetBase ncAssetGet = scene.AssetService.Get(assetId.ToString());
|
||||
Assert.AreEqual(foreignUrl, ncAssetGet.CreatorID);
|
||||
string xmlData = Utils.BytesToString(ncAssetGet.Data);
|
||||
XmlDocument ncAssetGetXmlDoc = new XmlDocument();
|
||||
ncAssetGetXmlDoc.LoadXml(xmlData);
|
||||
XmlNodeList creatorDataNodes = ncAssetGetXmlDoc.GetElementsByTagName("CreatorData");
|
||||
|
||||
Assert.AreEqual(soPartsCount, creatorDataNodes.Count);
|
||||
//Console.WriteLine("creatorDataNodes {0}", creatorDataNodes.Count);
|
||||
|
||||
foreach (XmlNode creatorDataNode in creatorDataNodes)
|
||||
{
|
||||
Assert.AreEqual(
|
||||
string.Format("{0};{1} {2}", homeUrl, ua.FirstName, ua.LastName), creatorDataNode.InnerText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -44,7 +44,6 @@ using OpenSim.Region.Framework.Scenes;
|
|||
using OpenSim.Region.Framework.Scenes.Serialization;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests
|
||||
{
|
||||
|
|
|
@ -32,7 +32,6 @@ using OpenMetaverse;
|
|||
using OpenSim.Framework;
|
||||
using OpenSim.Region.CoreModules.Framework.UserManagement;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Framework.UserManagement.Tests
|
||||
{
|
||||
|
|
|
@ -41,7 +41,6 @@ using OpenSim.Framework;
|
|||
using OpenSim.Region.CoreModules.Scripting.HttpRequest;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Scripting.HttpRequest.Tests
|
||||
{
|
||||
|
|
|
@ -40,7 +40,6 @@ using OpenSim.Region.CoreModules.Scripting.VectorRender;
|
|||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Region.Framework.Scenes.Serialization;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
|
||||
{
|
||||
|
|
|
@ -69,6 +69,13 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
|
|||
get { return "HGAssetBroker"; }
|
||||
}
|
||||
|
||||
public HGAssetBroker() {}
|
||||
|
||||
public HGAssetBroker(IConfigSource config)
|
||||
{
|
||||
Initialise(config);
|
||||
}
|
||||
|
||||
public void Initialise(IConfigSource source)
|
||||
{
|
||||
IConfig moduleConfig = source.Configs["Modules"];
|
||||
|
@ -288,7 +295,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
|
|||
|
||||
if (asset != null)
|
||||
{
|
||||
Util.FireAndForget(delegate { handler(id, sender, asset); });
|
||||
Util.FireAndForget(delegate { handler(id, sender, asset); }, null, "HGAssetBroker.GotFromCache");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -236,7 +236,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
|
|||
|
||||
if (asset != null)
|
||||
{
|
||||
Util.FireAndForget(delegate { handler(id, sender, asset); });
|
||||
Util.FireAndForget(
|
||||
o => handler(id, sender, asset), null, "LocalAssetServiceConnector.GotFromCacheCallback");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -249,7 +250,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
|
|||
// if (null == a)
|
||||
// m_log.WarnFormat("[LOCAL ASSET SERVICES CONNECTOR]: Could not asynchronously find asset with id {0}", id);
|
||||
|
||||
Util.FireAndForget(delegate { handler(assetID, s, a); });
|
||||
Util.FireAndForget(
|
||||
o => handler(assetID, s, a), null, "LocalAssetServiceConnector.GotFromServiceCallback");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -198,8 +198,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
|
|||
public GridRegion GetRegionByPosition(UUID scopeID, int x, int y)
|
||||
{
|
||||
GridRegion region = null;
|
||||
uint regionX = Util.WorldToRegionLoc((uint)x);
|
||||
uint regionY = Util.WorldToRegionLoc((uint)y);
|
||||
// uint regionX = Util.WorldToRegionLoc((uint)x);
|
||||
// uint regionY = Util.WorldToRegionLoc((uint)y);
|
||||
|
||||
// First see if it's a neighbour, even if it isn't on this sim.
|
||||
// Neighbour data is cached in memory, so this is fast
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage
|
|||
/// </remarks>
|
||||
|
||||
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MapImageServiceModule")]
|
||||
public class MapImageServiceModule : ISharedRegionModule
|
||||
public class MapImageServiceModule : IMapImageUploadModule, ISharedRegionModule
|
||||
{
|
||||
private static readonly ILog m_log =
|
||||
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
@ -152,6 +152,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage
|
|||
m_scenes[scene.RegionInfo.RegionID] = scene;
|
||||
|
||||
scene.EventManager.OnRegionReadyStatusChange += s => { if (s.Ready) UploadMapTile(s); };
|
||||
|
||||
scene.RegisterModuleInterface<IMapImageUploadModule>(this);
|
||||
}
|
||||
|
||||
///<summary>
|
||||
|
@ -198,14 +200,53 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage
|
|||
m_lastrefresh = Util.EnvironmentTickCount();
|
||||
}
|
||||
|
||||
public void UploadMapTile(IScene scene, Bitmap mapTile)
|
||||
{
|
||||
m_log.DebugFormat("{0} Upload maptile for {1}", LogHeader, scene.Name);
|
||||
|
||||
// mapTile.Save( // DEBUG DEBUG
|
||||
// String.Format("maptiles/raw-{0}-{1}-{2}.jpg", regionName, scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY),
|
||||
// ImageFormat.Jpeg);
|
||||
// If the region/maptile is legacy sized, just upload the one tile like it has always been done
|
||||
if (mapTile.Width == Constants.RegionSize && mapTile.Height == Constants.RegionSize)
|
||||
{
|
||||
ConvertAndUploadMaptile(mapTile,
|
||||
scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY,
|
||||
scene.RegionInfo.RegionName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For larger regions (varregion) we must cut the region image into legacy sized
|
||||
// pieces since that is how the maptile system works.
|
||||
// Note the assumption that varregions are always a multiple of legacy size.
|
||||
for (uint xx = 0; xx < mapTile.Width; xx += Constants.RegionSize)
|
||||
{
|
||||
for (uint yy = 0; yy < mapTile.Height; yy += Constants.RegionSize)
|
||||
{
|
||||
// Images are addressed from the upper left corner so have to do funny
|
||||
// math to pick out the sub-tile since regions are numbered from
|
||||
// the lower left.
|
||||
Rectangle rect = new Rectangle(
|
||||
(int)xx,
|
||||
mapTile.Height - (int)yy - (int)Constants.RegionSize,
|
||||
(int)Constants.RegionSize, (int)Constants.RegionSize);
|
||||
using (Bitmap subMapTile = mapTile.Clone(rect, mapTile.PixelFormat))
|
||||
{
|
||||
ConvertAndUploadMaptile(subMapTile,
|
||||
scene.RegionInfo.RegionLocX + (xx / Constants.RegionSize),
|
||||
scene.RegionInfo.RegionLocY + (yy / Constants.RegionSize),
|
||||
scene.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///<summary>
|
||||
///
|
||||
///</summary>
|
||||
private void UploadMapTile(IScene scene)
|
||||
{
|
||||
m_log.DebugFormat("{0} Upload maptile for {1}", LogHeader, scene.RegionInfo.RegionName);
|
||||
string regionName = scene.RegionInfo.RegionName;
|
||||
|
||||
// Create a JPG map tile and upload it to the AddMapTile API
|
||||
IMapImageGenerator tileGenerator = scene.RequestModuleInterface<IMapImageGenerator>();
|
||||
if (tileGenerator == null)
|
||||
|
@ -213,46 +254,12 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage
|
|||
m_log.WarnFormat("{0} Cannot upload map tile without an ImageGenerator", LogHeader);
|
||||
return;
|
||||
}
|
||||
|
||||
using (Bitmap mapTile = tileGenerator.CreateMapTile())
|
||||
{
|
||||
if (mapTile != null)
|
||||
{
|
||||
// mapTile.Save( // DEBUG DEBUG
|
||||
// String.Format("maptiles/raw-{0}-{1}-{2}.jpg", regionName, scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY),
|
||||
// ImageFormat.Jpeg);
|
||||
// If the region/maptile is legacy sized, just upload the one tile like it has always been done
|
||||
if (mapTile.Width == Constants.RegionSize && mapTile.Height == Constants.RegionSize)
|
||||
{
|
||||
ConvertAndUploadMaptile(mapTile,
|
||||
scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY,
|
||||
scene.RegionInfo.RegionName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For larger regions (varregion) we must cut the region image into legacy sized
|
||||
// pieces since that is how the maptile system works.
|
||||
// Note the assumption that varregions are always a multiple of legacy size.
|
||||
for (uint xx = 0; xx < mapTile.Width; xx += Constants.RegionSize)
|
||||
{
|
||||
for (uint yy = 0; yy < mapTile.Height; yy += Constants.RegionSize)
|
||||
{
|
||||
// Images are addressed from the upper left corner so have to do funny
|
||||
// math to pick out the sub-tile since regions are numbered from
|
||||
// the lower left.
|
||||
Rectangle rect = new Rectangle(
|
||||
(int)xx,
|
||||
mapTile.Height - (int)yy - (int)Constants.RegionSize,
|
||||
(int)Constants.RegionSize, (int)Constants.RegionSize);
|
||||
using (Bitmap subMapTile = mapTile.Clone(rect, mapTile.PixelFormat))
|
||||
{
|
||||
ConvertAndUploadMaptile(subMapTile,
|
||||
scene.RegionInfo.RegionLocX + (xx / Constants.RegionSize),
|
||||
scene.RegionInfo.RegionLocY + (yy / Constants.RegionSize),
|
||||
regionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
UploadMapTile(scene, mapTile);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -45,7 +45,6 @@ using OpenSim.Region.Framework.Scenes;
|
|||
using OpenSim.Region.Framework.Scenes.Serialization;
|
||||
using OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
using ArchiveConstants = OpenSim.Framework.Serialization.ArchiveConstants;
|
||||
using TarArchiveReader = OpenSim.Framework.Serialization.TarArchiveReader;
|
||||
using TarArchiveWriter = OpenSim.Framework.Serialization.TarArchiveWriter;
|
||||
|
|
|
@ -31,7 +31,6 @@ using OpenMetaverse;
|
|||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.World.Land.Tests
|
||||
{
|
||||
|
|
|
@ -36,7 +36,6 @@ using OpenSim.Framework;
|
|||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.World.Land.Tests
|
||||
{
|
||||
|
|
|
@ -39,7 +39,6 @@ using OpenSim.Region.CoreModules.World.Media.Moap;
|
|||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Region.Framework.Scenes.Serialization;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.World.Media.Moap.Tests
|
||||
{
|
||||
|
|
|
@ -141,6 +141,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.BuySell
|
|||
|
||||
part.ObjectSaleType = 0;
|
||||
part.SalePrice = 10;
|
||||
part.ClickAction = Convert.ToByte(0);
|
||||
|
||||
group.HasGroupChanged = true;
|
||||
part.SendPropertiesToClient(remoteClient);
|
||||
|
|
|
@ -87,7 +87,26 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
"Regions", false, "show region",
|
||||
"show region",
|
||||
"Show control information for the currently selected region (host name, max physical prim size, etc).",
|
||||
"A synonym for \"region get\"",
|
||||
HandleShowRegion);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Regions", false, "region get",
|
||||
"region get",
|
||||
"Show control information for the currently selected region (host name, max physical prim size, etc).",
|
||||
"Some parameters can be set with the \"region set\" command.\n"
|
||||
+ "Others must be changed via a viewer (usually via the region/estate dialog box).",
|
||||
HandleShowRegion);
|
||||
|
||||
m_console.Commands.AddCommand(
|
||||
"Regions", false, "region set",
|
||||
"region get",
|
||||
"Set control information for the currently selected region.",
|
||||
"Currently, the following parameters can be set:\n"
|
||||
+ "agent-limit <int> - Current root agent limit. This is persisted over restart.\n"
|
||||
+ "max-agent-limit <int> - Maximum root agent limit. agent-limit cannot exceed this."
|
||||
+ " This is not persisted over restart - to set it every time you must add a MaxAgents entry to your regions file.",
|
||||
HandleRegionSet);
|
||||
}
|
||||
|
||||
public void RemoveRegion(Scene scene)
|
||||
|
@ -123,8 +142,8 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
dispList.AddRow("External endpoint", ri.ExternalEndPoint);
|
||||
dispList.AddRow("Internal endpoint", ri.InternalEndPoint);
|
||||
dispList.AddRow("Access level", ri.AccessLevel);
|
||||
dispList.AddRow("Agent limit", rs.AgentLimit);
|
||||
dispList.AddRow("Max agent limit", ri.AgentCapacity);
|
||||
dispList.AddRow("Current agent limit", rs.AgentLimit);
|
||||
dispList.AddRow("Linkset capacity", ri.LinksetCapacity <= 0 ? "not set" : ri.LinksetCapacity.ToString());
|
||||
dispList.AddRow("Prim capacity", ri.ObjectCapacity);
|
||||
dispList.AddRow("Prim bonus", rs.ObjectBonus);
|
||||
|
@ -166,6 +185,73 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
|||
MainConsole.Instance.Output(sb.ToString());
|
||||
}
|
||||
|
||||
private void HandleRegionSet(string module, string[] args)
|
||||
{
|
||||
if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_scene))
|
||||
return;
|
||||
|
||||
if (args.Length != 4)
|
||||
{
|
||||
MainConsole.Instance.OutputFormat("Usage: region set <param> <value>");
|
||||
return;
|
||||
}
|
||||
|
||||
string param = args[2];
|
||||
string rawValue = args[3];
|
||||
|
||||
if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_scene))
|
||||
return;
|
||||
|
||||
RegionInfo ri = m_scene.RegionInfo;
|
||||
RegionSettings rs = ri.RegionSettings;
|
||||
|
||||
if (param == "agent-limit")
|
||||
{
|
||||
int newValue;
|
||||
|
||||
if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, rawValue, out newValue))
|
||||
return;
|
||||
|
||||
if (newValue > ri.AgentCapacity)
|
||||
{
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Cannot set {0} to {1} in {2} as max-agent-limit is {3}", "agent-limit",
|
||||
newValue, m_scene.Name, ri.AgentCapacity);
|
||||
}
|
||||
else
|
||||
{
|
||||
rs.AgentLimit = newValue;
|
||||
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"{0} set to {1} in {2}", "agent-limit", newValue, m_scene.Name);
|
||||
}
|
||||
|
||||
rs.Save();
|
||||
}
|
||||
else if (param == "max-agent-limit")
|
||||
{
|
||||
int newValue;
|
||||
|
||||
if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, rawValue, out newValue))
|
||||
return;
|
||||
|
||||
ri.AgentCapacity = newValue;
|
||||
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"{0} set to {1} in {2}", "max-agent-limit", newValue, m_scene.Name);
|
||||
|
||||
if (ri.AgentCapacity < rs.AgentLimit)
|
||||
{
|
||||
rs.AgentLimit = ri.AgentCapacity;
|
||||
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Reducing {0} to {1} in {2}", "agent-limit", rs.AgentLimit, m_scene.Name);
|
||||
}
|
||||
|
||||
rs.Save();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleShowScene(string module, string[] cmd)
|
||||
{
|
||||
if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_scene))
|
||||
|
|
|
@ -68,6 +68,9 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
|
|||
private static readonly UUID STOP_UUID = UUID.Random();
|
||||
private static readonly string m_mapLayerPath = "0001/";
|
||||
|
||||
private IMapImageGenerator m_mapImageGenerator;
|
||||
private IMapImageUploadModule m_mapImageServiceModule;
|
||||
|
||||
private OpenSim.Framework.BlockingQueue<MapRequestState> requests = new OpenSim.Framework.BlockingQueue<MapRequestState>();
|
||||
|
||||
protected Scene m_scene;
|
||||
|
@ -100,7 +103,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
|
|||
= Util.GetConfigVarFromSections<int>(config, "BlacklistTimeout", configSections, 10 * 60) * 1000;
|
||||
}
|
||||
|
||||
public virtual void AddRegion (Scene scene)
|
||||
public virtual void AddRegion(Scene scene)
|
||||
{
|
||||
if (!m_Enabled)
|
||||
return;
|
||||
|
@ -144,8 +147,10 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
|
|||
return;
|
||||
|
||||
m_ServiceThrottle = scene.RequestModuleInterface<IServiceThrottleModule>();
|
||||
}
|
||||
|
||||
m_mapImageGenerator = m_scene.RequestModuleInterface<IMapImageGenerator>();
|
||||
m_mapImageServiceModule = m_scene.RequestModuleInterface<IMapImageUploadModule>();
|
||||
}
|
||||
|
||||
public virtual void Close()
|
||||
{
|
||||
|
@ -1315,7 +1320,17 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
|
|||
if (consoleScene != null && consoleScene != m_scene)
|
||||
return;
|
||||
|
||||
GenerateMaptile();
|
||||
if (m_mapImageGenerator == null)
|
||||
{
|
||||
Console.WriteLine("No map image generator available for {0}", m_scene.Name);
|
||||
return;
|
||||
}
|
||||
|
||||
using (Bitmap mapbmp = m_mapImageGenerator.CreateMapTile())
|
||||
{
|
||||
GenerateMaptile(mapbmp);
|
||||
m_mapImageServiceModule.UploadMapTile(m_scene, mapbmp);
|
||||
}
|
||||
}
|
||||
|
||||
public OSD HandleRemoteMapItemRequest(string path, OSD request, string endpoint)
|
||||
|
@ -1444,16 +1459,25 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
|
|||
if (m_scene.Heightmap == null)
|
||||
return;
|
||||
|
||||
//create a texture asset of the terrain
|
||||
IMapImageGenerator terrain = m_scene.RequestModuleInterface<IMapImageGenerator>();
|
||||
if (terrain == null)
|
||||
return;
|
||||
m_log.DebugFormat("[WORLD MAP]: Generating map image for {0}", m_scene.Name);
|
||||
|
||||
m_log.DebugFormat("[WORLD MAP]: Generating map image for {0}", m_scene.RegionInfo.RegionName);
|
||||
using (Bitmap mapbmp = m_mapImageGenerator.CreateMapTile())
|
||||
GenerateMaptile(mapbmp);
|
||||
}
|
||||
|
||||
byte[] data = terrain.WriteJpeg2000Image();
|
||||
if (data == null)
|
||||
private void GenerateMaptile(Bitmap mapbmp)
|
||||
{
|
||||
byte[] data;
|
||||
|
||||
try
|
||||
{
|
||||
data = OpenJPEG.EncodeFromImage(mapbmp, true);
|
||||
}
|
||||
catch (Exception e) // LEGIT: Catching problems caused by OpenJPEG p/invoke
|
||||
{
|
||||
m_log.Error("[WORLD MAP]: Failed generating terrain map: " + e);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] overlay = GenerateOverlay();
|
||||
|
||||
|
|
|
@ -98,6 +98,8 @@ namespace OpenSim.Region.Framework.Interfaces
|
|||
void Cross(SceneObjectGroup sog, Vector3 position, bool silent);
|
||||
|
||||
ScenePresence CrossAgentToNewRegionAsync(ScenePresence agent, Vector3 pos, GridRegion neighbourRegion, bool isFlying, string version);
|
||||
|
||||
bool HandleIncomingSceneObject(SceneObjectGroup so, Vector3 newPosition);
|
||||
}
|
||||
|
||||
public interface IUserAgentVerificationModule
|
||||
|
|
|
@ -25,55 +25,13 @@
|
|||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System.Net;
|
||||
using OpenMetaverse;
|
||||
using System.Drawing;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using GridRegion = OpenSim.Services.Interfaces.GridRegion;
|
||||
|
||||
namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
||||
namespace OpenSim.Region.Framework.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Mock scene for unit tests
|
||||
/// </summary>
|
||||
public class MockScene : SceneBase
|
||||
public interface IMapImageUploadModule
|
||||
{
|
||||
public int ObjectNameCallsReceived
|
||||
{
|
||||
get { return m_objectNameCallsReceived; }
|
||||
}
|
||||
protected int m_objectNameCallsReceived;
|
||||
|
||||
public MockScene() : base(new RegionInfo(1000, 1000, null, null))
|
||||
{
|
||||
m_regStatus = RegionStatus.Up;
|
||||
}
|
||||
|
||||
public override bool Update(int frames) { return true; }
|
||||
public override void LoadWorldMap() {}
|
||||
|
||||
public override ISceneAgent AddNewAgent(IClientAPI client, PresenceType type)
|
||||
{
|
||||
client.OnObjectName += RecordObjectNameCall;
|
||||
|
||||
// FIXME
|
||||
return null;
|
||||
}
|
||||
|
||||
public override bool CloseAgent(UUID agentID, bool force) { return true; }
|
||||
|
||||
public override bool CheckClient(UUID clientId, IPEndPoint endPoint) { return true; }
|
||||
|
||||
public override void OtherRegionUp(GridRegion otherRegion) { }
|
||||
|
||||
public override bool TryGetScenePresence(UUID uuid, out ScenePresence sp) { sp = null; return false; }
|
||||
|
||||
/// <summary>
|
||||
/// Doesn't really matter what the call is - we're using this to test that a packet has actually been received
|
||||
/// </summary>
|
||||
protected void RecordObjectNameCall(IClientAPI remoteClient, uint localID, string message)
|
||||
{
|
||||
m_objectNameCallsReceived++;
|
||||
}
|
||||
void UploadMapTile(IScene scene, Bitmap mapTile);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1227,16 +1227,21 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
agentItem.BasePermissions = taskItem.BasePermissions & (taskItem.NextPermissions | (uint)PermissionMask.Move);
|
||||
if (taskItem.InvType == (int)InventoryType.Object)
|
||||
{
|
||||
uint perms = taskItem.CurrentPermissions;
|
||||
// Bake the new base permissions from folded permissions
|
||||
// The folded perms are in the lowest 3 bits of the current perms
|
||||
// We use base permissions here to avoid baking the "Locked" status
|
||||
// into the item as it is passed.
|
||||
uint perms = taskItem.BasePermissions & taskItem.NextPermissions;
|
||||
PermissionsUtil.ApplyFoldedPermissions(taskItem.CurrentPermissions, ref perms);
|
||||
// Avoid the "lock trap" - move must always be enabled but the above may remove it
|
||||
// Add it back here.
|
||||
agentItem.BasePermissions = perms | (uint)PermissionMask.Move;
|
||||
agentItem.CurrentPermissions = agentItem.BasePermissions;
|
||||
}
|
||||
else
|
||||
{
|
||||
agentItem.CurrentPermissions = agentItem.BasePermissions & taskItem.CurrentPermissions;
|
||||
// Newly given items cannot be "locked" on rez. Make sure by
|
||||
// setting current equal to base.
|
||||
}
|
||||
|
||||
agentItem.CurrentPermissions = agentItem.BasePermissions;
|
||||
|
||||
agentItem.Flags |= (uint)InventoryItemFlags.ObjectSlamPerm;
|
||||
agentItem.NextPermissions = taskItem.NextPermissions;
|
||||
agentItem.EveryOnePermissions = taskItem.EveryonePermissions & (taskItem.NextPermissions | (uint)PermissionMask.Move);
|
||||
|
@ -1935,8 +1940,11 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
/// Rez a script into a prim's inventory from another prim
|
||||
/// </summary>
|
||||
/// <param name="remoteClient"></param>
|
||||
/// <param name="itemID"> </param>
|
||||
/// <param name="localID"></param>
|
||||
/// <param name="srcPart"> </param>
|
||||
/// <param name="destId"> </param>
|
||||
/// <param name="pin"></param>
|
||||
/// <param name="running"></param>
|
||||
/// <param name="start_param"></param>
|
||||
public void RezScriptFromPrim(UUID srcId, SceneObjectPart srcPart, UUID destId, int pin, int running, int start_param)
|
||||
{
|
||||
TaskInventoryItem srcTaskItem = srcPart.Inventory.GetInventoryItem(srcId);
|
||||
|
@ -1956,12 +1964,11 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
if (destPart == null)
|
||||
{
|
||||
m_log.ErrorFormat(
|
||||
"[PRIM INVENTORY]: " +
|
||||
"Could not find script for ID {0}",
|
||||
destId);
|
||||
"[PRIM INVENTORY]: Could not find part {0} to insert script item {1} from {2} {3} in {4}",
|
||||
destId, srcId, srcPart.Name, srcPart.UUID, Name);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Must own the object, and have modify rights
|
||||
if (srcPart.OwnerID != destPart.OwnerID)
|
||||
{
|
||||
|
@ -1969,12 +1976,14 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
if ((destPart.GroupID == UUID.Zero) || (destPart.GroupID != srcPart.GroupID) ||
|
||||
((destPart.GroupMask & (uint)PermissionMask.Modify) == 0))
|
||||
return;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((destPart.OwnerMask & (uint)PermissionMask.Modify) == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
if (destPart.ScriptAccessPin != pin)
|
||||
if (destPart.ScriptAccessPin == 0 || destPart.ScriptAccessPin != pin)
|
||||
{
|
||||
m_log.WarnFormat(
|
||||
"[PRIM INVENTORY]: " +
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* 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 copyrightD
|
||||
* * 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
|
||||
|
@ -360,30 +360,52 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
public uint MaintenanceRun { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The minimum length of time in seconds that will be taken for a scene frame. If the frame takes less time then we
|
||||
/// The minimum length of time in milliseconds that will be taken for a scene frame. If the frame takes less time then we
|
||||
/// will sleep for the remaining period.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// One can tweak this number to experiment. One current effect of reducing it is to make avatar animations
|
||||
/// occur too quickly (viewer 1) or with even more slide (viewer 2).
|
||||
/// </remarks>
|
||||
public float MinFrameTime { get; private set; }
|
||||
public int MinFrameTicks
|
||||
{
|
||||
get { return m_minFrameTicks; }
|
||||
private set
|
||||
{
|
||||
m_minFrameTicks = value;
|
||||
MinFrameSeconds = (float)m_minFrameTicks / 1000;
|
||||
}
|
||||
}
|
||||
private int m_minFrameTicks;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum length of time in seconds that will be taken for a maintenance run.
|
||||
/// The minimum length of time in seconds that will be taken for a scene frame.
|
||||
/// </summary>
|
||||
public float MinMaintenanceTime { get; private set; }
|
||||
/// <remarks>
|
||||
/// Always derived from MinFrameTicks.
|
||||
/// </remarks>
|
||||
public float MinFrameSeconds { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The minimum length of time in milliseconds that will be taken for a scene frame. If the frame takes less time then we
|
||||
/// will sleep for the remaining period.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// One can tweak this number to experiment. One current effect of reducing it is to make avatar animations
|
||||
/// occur too quickly (viewer 1) or with even more slide (viewer 2).
|
||||
/// </remarks>
|
||||
public int MinMaintenanceTicks { get; set; }
|
||||
|
||||
private int m_update_physics = 1;
|
||||
private int m_update_entitymovement = 1;
|
||||
private int m_update_objects = 1;
|
||||
private int m_update_temp_cleaning = 1000;
|
||||
private int m_update_presences = 1; // Update scene presence movements
|
||||
private int m_update_events = 1;
|
||||
private int m_update_backup = 200;
|
||||
private int m_update_terrain = 50;
|
||||
// private int m_update_land = 1;
|
||||
private int m_update_coarse_locations = 50;
|
||||
private int m_update_temp_cleaning = 180;
|
||||
|
||||
private int agentMS;
|
||||
private int frameMS;
|
||||
|
@ -412,8 +434,16 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
/// asynchronously from the update loop.
|
||||
/// </summary>
|
||||
private bool m_cleaningTemps = false;
|
||||
|
||||
/// <summary>
|
||||
/// Used to control main scene thread looping time when not updating via timer.
|
||||
/// </summary>
|
||||
private ManualResetEvent m_updateWaitEvent = new ManualResetEvent(false);
|
||||
|
||||
// private Object m_heartbeatLock = new Object();
|
||||
/// <summary>
|
||||
/// Used to control maintenance thread runs.
|
||||
/// </summary>
|
||||
private ManualResetEvent m_maintenanceWaitEvent = new ManualResetEvent(false);
|
||||
|
||||
// TODO: Possibly stop other classes being able to manipulate this directly.
|
||||
private SceneGraph m_sceneGraph;
|
||||
|
@ -782,8 +812,8 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
: this(regInfo, physicsScene)
|
||||
{
|
||||
m_config = config;
|
||||
MinFrameTime = 0.089f;
|
||||
MinMaintenanceTime = 1;
|
||||
MinFrameTicks = 89;
|
||||
MinMaintenanceTicks = 1000;
|
||||
SeeIntoRegion = true;
|
||||
|
||||
Random random = new Random();
|
||||
|
@ -1005,7 +1035,9 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
}
|
||||
}
|
||||
|
||||
MinFrameTime = startupConfig.GetFloat( "MinFrameTime", MinFrameTime);
|
||||
if (startupConfig.Contains("MinFrameTime"))
|
||||
MinFrameTicks = (int)(startupConfig.GetFloat("MinFrameTime") * 1000);
|
||||
|
||||
m_update_backup = startupConfig.GetInt( "UpdateStorageEveryNFrames", m_update_backup);
|
||||
m_update_coarse_locations = startupConfig.GetInt( "UpdateCoarseLocationsEveryNFrames", m_update_coarse_locations);
|
||||
m_update_entitymovement = startupConfig.GetInt( "UpdateEntityMovementEveryNFrames", m_update_entitymovement);
|
||||
|
@ -1014,11 +1046,11 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
m_update_physics = startupConfig.GetInt( "UpdatePhysicsEveryNFrames", m_update_physics);
|
||||
m_update_presences = startupConfig.GetInt( "UpdateAgentsEveryNFrames", m_update_presences);
|
||||
m_update_terrain = startupConfig.GetInt( "UpdateTerrainEveryNFrames", m_update_terrain);
|
||||
m_update_temp_cleaning = startupConfig.GetInt( "UpdateTempCleaningEveryNFrames", m_update_temp_cleaning);
|
||||
m_update_temp_cleaning = startupConfig.GetInt( "UpdateTempCleaningEveryNSeconds", m_update_temp_cleaning);
|
||||
}
|
||||
|
||||
// FIXME: Ultimately this should be in a module.
|
||||
SendPeriodicAppearanceUpdates = true;
|
||||
SendPeriodicAppearanceUpdates = false;
|
||||
|
||||
IConfig appearanceConfig = m_config.Configs["Appearance"];
|
||||
if (appearanceConfig != null)
|
||||
|
@ -1444,13 +1476,14 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
if (UpdateOnTimer)
|
||||
{
|
||||
m_sceneUpdateTimer = new Timer(MinFrameTime * 1000);
|
||||
m_sceneUpdateTimer = new Timer(MinFrameTicks);
|
||||
m_sceneUpdateTimer.AutoReset = true;
|
||||
m_sceneUpdateTimer.Elapsed += Update;
|
||||
m_sceneUpdateTimer.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.CurrentThread.Priority = ThreadPriority.Highest;
|
||||
Update(-1);
|
||||
Watchdog.RemoveThread();
|
||||
m_isRunning = false;
|
||||
|
@ -1490,7 +1523,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
public void DoMaintenance(int runs)
|
||||
{
|
||||
long? endRun = null;
|
||||
int runtc;
|
||||
int runtc, tmpMS;
|
||||
int previousMaintenanceTick;
|
||||
|
||||
if (runs >= 0)
|
||||
|
@ -1504,6 +1537,8 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
runtc = Util.EnvironmentTickCount();
|
||||
++MaintenanceRun;
|
||||
|
||||
// m_log.DebugFormat("[SCENE]: Maintenance run {0} in {1}", MaintenanceRun, Name);
|
||||
|
||||
// Coarse locations relate to positions of green dots on the mini-map (on a SecondLife client)
|
||||
if (MaintenanceRun % (m_update_coarse_locations / 10) == 0)
|
||||
{
|
||||
|
@ -1525,24 +1560,39 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
}
|
||||
}
|
||||
|
||||
// Delete temp-on-rez stuff
|
||||
if (MaintenanceRun % m_update_temp_cleaning == 0 && !m_cleaningTemps)
|
||||
{
|
||||
// m_log.DebugFormat("[SCENE]: Running temp-on-rez cleaning in {0}", Name);
|
||||
tmpMS = Util.EnvironmentTickCount();
|
||||
m_cleaningTemps = true;
|
||||
|
||||
Watchdog.RunInThread(
|
||||
delegate { CleanTempObjects(); m_cleaningTemps = false; },
|
||||
string.Format("CleanTempObjects ({0})", Name),
|
||||
null);
|
||||
|
||||
tempOnRezMS = Util.EnvironmentTickCountSubtract(tmpMS);
|
||||
}
|
||||
|
||||
Watchdog.UpdateThread();
|
||||
|
||||
previousMaintenanceTick = m_lastMaintenanceTick;
|
||||
m_lastMaintenanceTick = Util.EnvironmentTickCount();
|
||||
runtc = Util.EnvironmentTickCountSubtract(m_lastMaintenanceTick, runtc);
|
||||
runtc = (int)(MinMaintenanceTime * 1000) - runtc;
|
||||
runtc = MinMaintenanceTicks - runtc;
|
||||
|
||||
if (runtc > 0)
|
||||
Thread.Sleep(runtc);
|
||||
m_maintenanceWaitEvent.WaitOne(runtc);
|
||||
|
||||
// Optionally warn if a frame takes double the amount of time that it should.
|
||||
if (DebugUpdates
|
||||
&& Util.EnvironmentTickCountSubtract(
|
||||
m_lastMaintenanceTick, previousMaintenanceTick) > (int)(MinMaintenanceTime * 1000 * 2))
|
||||
m_lastMaintenanceTick, previousMaintenanceTick) > MinMaintenanceTicks * 2)
|
||||
m_log.WarnFormat(
|
||||
"[SCENE]: Maintenance took {0} ms (desired max {1} ms) in {2}",
|
||||
Util.EnvironmentTickCountSubtract(m_lastMaintenanceTick, previousMaintenanceTick),
|
||||
MinMaintenanceTime * 1000,
|
||||
MinMaintenanceTicks,
|
||||
RegionInfo.RegionName);
|
||||
}
|
||||
}
|
||||
|
@ -1563,7 +1613,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
// m_log.DebugFormat("[SCENE]: Processing frame {0} in {1}", Frame, RegionInfo.RegionName);
|
||||
|
||||
agentMS = tempOnRezMS = eventMS = backupMS = terrainMS = landMS = spareMS = 0;
|
||||
agentMS = eventMS = backupMS = terrainMS = landMS = spareMS = 0;
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -1594,7 +1644,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
if (Frame % m_update_physics == 0)
|
||||
{
|
||||
if (PhysicsEnabled)
|
||||
physicsFPS = m_sceneGraph.UpdatePhysics(MinFrameTime);
|
||||
physicsFPS = m_sceneGraph.UpdatePhysics(MinFrameSeconds);
|
||||
|
||||
if (SynchronizeScene != null)
|
||||
SynchronizeScene(this);
|
||||
|
@ -1616,21 +1666,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
if (Frame % m_update_presences == 0)
|
||||
m_sceneGraph.UpdatePresences();
|
||||
|
||||
agentMS += Util.EnvironmentTickCountSubtract(tmpMS);
|
||||
|
||||
// Delete temp-on-rez stuff
|
||||
if (Frame % m_update_temp_cleaning == 0 && !m_cleaningTemps)
|
||||
{
|
||||
tmpMS = Util.EnvironmentTickCount();
|
||||
m_cleaningTemps = true;
|
||||
|
||||
Watchdog.RunInThread(
|
||||
delegate { CleanTempObjects(); m_cleaningTemps = false; },
|
||||
string.Format("CleanTempObjects ({0})", Name),
|
||||
null);
|
||||
|
||||
tempOnRezMS = Util.EnvironmentTickCountSubtract(tmpMS);
|
||||
}
|
||||
agentMS += Util.EnvironmentTickCountSubtract(tmpMS);
|
||||
|
||||
if (Frame % m_update_events == 0)
|
||||
{
|
||||
|
@ -1700,24 +1736,22 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
}
|
||||
|
||||
EventManager.TriggerRegionHeartbeatEnd(this);
|
||||
otherMS = tempOnRezMS + eventMS + backupMS + terrainMS + landMS;
|
||||
otherMS = eventMS + backupMS + terrainMS + landMS;
|
||||
|
||||
if (!UpdateOnTimer)
|
||||
{
|
||||
Watchdog.UpdateThread();
|
||||
|
||||
tmpMS = Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), m_lastFrameTick);
|
||||
tmpMS = (int)(MinFrameTime * 1000) - tmpMS;
|
||||
spareMS = MinFrameTicks - Util.EnvironmentTickCountSubtract(m_lastFrameTick);
|
||||
|
||||
if (tmpMS > 0)
|
||||
{
|
||||
spareMS = tmpMS;
|
||||
Thread.Sleep(tmpMS);
|
||||
}
|
||||
if (spareMS > 0)
|
||||
m_updateWaitEvent.WaitOne(spareMS);
|
||||
else
|
||||
spareMS = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
spareMS = Math.Max(0, (int)(MinFrameTime * 1000) - physicsMS2 - agentMS - physicsMS -otherMS);
|
||||
spareMS = Math.Max(0, MinFrameTicks - physicsMS2 - agentMS - physicsMS - otherMS);
|
||||
}
|
||||
|
||||
previousFrameTick = m_lastFrameTick;
|
||||
|
@ -1740,11 +1774,11 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
// Optionally warn if a frame takes double the amount of time that it should.
|
||||
if (DebugUpdates
|
||||
&& Util.EnvironmentTickCountSubtract(
|
||||
m_lastFrameTick, previousFrameTick) > (int)(MinFrameTime * 1000 * 2))
|
||||
m_lastFrameTick, previousFrameTick) > MinFrameTicks * 2)
|
||||
m_log.WarnFormat(
|
||||
"[SCENE]: Frame took {0} ms (desired max {1} ms) in {2}",
|
||||
Util.EnvironmentTickCountSubtract(m_lastFrameTick, previousFrameTick),
|
||||
MinFrameTime * 1000,
|
||||
MinFrameTicks,
|
||||
RegionInfo.RegionName);
|
||||
}
|
||||
|
||||
|
@ -2588,48 +2622,8 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
return false;
|
||||
}
|
||||
|
||||
// If the user is banned, we won't let any of their objects
|
||||
// enter. Period.
|
||||
//
|
||||
if (RegionInfo.EstateSettings.IsBanned(newObject.OwnerID))
|
||||
{
|
||||
m_log.InfoFormat("[INTERREGION]: Denied prim crossing for banned avatar {0}", newObject.OwnerID);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newPosition != Vector3.Zero)
|
||||
newObject.RootPart.GroupPosition = newPosition;
|
||||
|
||||
if (!AddSceneObject(newObject))
|
||||
{
|
||||
m_log.DebugFormat(
|
||||
"[INTERREGION]: Problem adding scene object {0} in {1} ", newObject.UUID, RegionInfo.RegionName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!newObject.IsAttachment)
|
||||
{
|
||||
// FIXME: It would be better to never add the scene object at all rather than add it and then delete
|
||||
// it
|
||||
if (!Permissions.CanObjectEntry(newObject.UUID, true, newObject.AbsolutePosition))
|
||||
{
|
||||
// Deny non attachments based on parcel settings
|
||||
//
|
||||
m_log.Info("[INTERREGION]: Denied prim crossing because of parcel settings");
|
||||
|
||||
DeleteSceneObject(newObject, false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// For attachments, we need to wait until the agent is root
|
||||
// before we restart the scripts, or else some functions won't work.
|
||||
newObject.RootPart.ParentGroup.CreateScriptInstances(0, false, DefaultScriptEngine, GetStateSource(newObject));
|
||||
newObject.ResumeScripts();
|
||||
|
||||
if (newObject.RootPart.KeyframeMotion != null)
|
||||
newObject.RootPart.KeyframeMotion.UpdateSceneObject(newObject);
|
||||
}
|
||||
if (!EntityTransferModule.HandleIncomingSceneObject(newObject, newPosition))
|
||||
return false;
|
||||
|
||||
// Do this as late as possible so that listeners have full access to the incoming object
|
||||
EventManager.TriggerOnIncomingSceneObject(newObject);
|
||||
|
@ -2698,16 +2692,6 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
return true;
|
||||
}
|
||||
|
||||
private int GetStateSource(SceneObjectGroup sog)
|
||||
{
|
||||
ScenePresence sp = GetScenePresence(sog.OwnerID);
|
||||
|
||||
if (sp != null)
|
||||
return sp.GetStateSource();
|
||||
|
||||
return 2; // StateSource.PrimCrossing
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Add/Remove Avatar Methods
|
||||
|
@ -2754,29 +2738,41 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
m_log.DebugFormat(
|
||||
"[SCENE]: Adding new child scene presence {0} {1} to scene {2} at pos {3}",
|
||||
client.Name, client.AgentId, RegionInfo.RegionName, client.StartPos);
|
||||
|
||||
|
||||
sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type);
|
||||
|
||||
// We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the
|
||||
// client is for a root or child agent.
|
||||
// We must also set this before adding the client to the client manager so that an exception later on
|
||||
// does not leave a client manager entry without the scene agent set, which will cause other code
|
||||
// to fail since any entry in the client manager should have a ScenePresence
|
||||
//
|
||||
// XXX: This may be better set for a new client before that client is added to the client manager.
|
||||
// But need to know what happens in the case where a ScenePresence is already present (and if this
|
||||
// actually occurs).
|
||||
client.SceneAgent = sp;
|
||||
|
||||
m_clientManager.Add(client);
|
||||
SubscribeToClientEvents(client);
|
||||
|
||||
sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type);
|
||||
m_eventManager.TriggerOnNewPresence(sp);
|
||||
|
||||
sp.TeleportFlags = (TPFlags)aCircuit.teleportFlags;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the
|
||||
// client is for a root or child agent.
|
||||
// XXX: This may be better set for a new client before that client is added to the client manager.
|
||||
// But need to know what happens in the case where a ScenePresence is already present (and if this
|
||||
// actually occurs).
|
||||
client.SceneAgent = sp;
|
||||
|
||||
m_log.WarnFormat(
|
||||
"[SCENE]: Already found {0} scene presence for {1} in {2} when asked to add new scene presence",
|
||||
sp.IsChildAgent ? "child" : "root", sp.Name, RegionInfo.RegionName);
|
||||
|
||||
reallyNew = false;
|
||||
}
|
||||
|
||||
// We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the
|
||||
// client is for a root or child agent.
|
||||
// XXX: This may be better set for a new client before that client is added to the client manager.
|
||||
// But need to know what happens in the case where a ScenePresence is already present (and if this
|
||||
// actually occurs).
|
||||
client.SceneAgent = sp;
|
||||
}
|
||||
|
||||
// This is currently also being done earlier in NewUserConnection for real users to see if this
|
||||
// resolves problems where HG agents are occasionally seen by others as "Unknown user" in chat and other
|
||||
|
@ -4466,14 +4462,9 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
sp.LifecycleState = ScenePresenceState.Removing;
|
||||
}
|
||||
|
||||
if (sp != null)
|
||||
{
|
||||
sp.ControllingClient.Close(force);
|
||||
return true;
|
||||
}
|
||||
sp.ControllingClient.Close(force);
|
||||
|
||||
// Agent not here
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -226,7 +226,10 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
// We must take a copy here since handle is acts like a reference when used in an iterator.
|
||||
// This leads to race conditions if directly passed to SendCloseChildAgent with more than one neighbour region.
|
||||
ulong handleCopy = handle;
|
||||
Util.FireAndForget((o) => { SendCloseChildAgent(agentID, handleCopy, auth_code); });
|
||||
Util.FireAndForget(
|
||||
o => SendCloseChildAgent(agentID, handleCopy, auth_code),
|
||||
null,
|
||||
"SceneCommunicationService.SendCloseChildAgentConnections");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -943,8 +943,8 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
{
|
||||
if (CanBeBackedUp)
|
||||
{
|
||||
//m_log.DebugFormat(
|
||||
// "[SCENE OBJECT GROUP]: Attaching object {0} {1} to scene presistence sweep", Name, UUID);
|
||||
// m_log.DebugFormat(
|
||||
// "[SCENE OBJECT GROUP]: Attaching object {0} {1} to scene presistence sweep", Name, UUID);
|
||||
|
||||
if (!Backup)
|
||||
m_scene.EventManager.OnBackup += ProcessBackup;
|
||||
|
|
|
@ -1589,20 +1589,29 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
public void AddTextureAnimation(Primitive.TextureAnimation pTexAnim)
|
||||
{
|
||||
byte[] data = new byte[16];
|
||||
int pos = 0;
|
||||
byte[] data;
|
||||
|
||||
// The flags don't like conversion from uint to byte, so we have to do
|
||||
// it the crappy way. See the above function :(
|
||||
if (pTexAnim.Flags == Primitive.TextureAnimMode.ANIM_OFF)
|
||||
{
|
||||
data = Utils.EmptyBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = new byte[16];
|
||||
int pos = 0;
|
||||
|
||||
data[pos] = ConvertScriptUintToByte((uint)pTexAnim.Flags); pos++;
|
||||
data[pos] = (byte)pTexAnim.Face; pos++;
|
||||
data[pos] = (byte)pTexAnim.SizeX; pos++;
|
||||
data[pos] = (byte)pTexAnim.SizeY; pos++;
|
||||
// The flags don't like conversion from uint to byte, so we have to do
|
||||
// it the crappy way. See the above function :(
|
||||
|
||||
Utils.FloatToBytes(pTexAnim.Start).CopyTo(data, pos);
|
||||
Utils.FloatToBytes(pTexAnim.Length).CopyTo(data, pos + 4);
|
||||
Utils.FloatToBytes(pTexAnim.Rate).CopyTo(data, pos + 8);
|
||||
data[pos] = ConvertScriptUintToByte((uint)pTexAnim.Flags); pos++;
|
||||
data[pos] = (byte)pTexAnim.Face; pos++;
|
||||
data[pos] = (byte)pTexAnim.SizeX; pos++;
|
||||
data[pos] = (byte)pTexAnim.SizeY; pos++;
|
||||
|
||||
Utils.FloatToBytes(pTexAnim.Start).CopyTo(data, pos);
|
||||
Utils.FloatToBytes(pTexAnim.Length).CopyTo(data, pos + 4);
|
||||
Utils.FloatToBytes(pTexAnim.Rate).CopyTo(data, pos + 8);
|
||||
}
|
||||
|
||||
m_TextureAnimation = data;
|
||||
}
|
||||
|
|
|
@ -1228,19 +1228,15 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
// viewers without (e.g. v1 viewers) will not, so we still need to make this call.
|
||||
if (Scene.AttachmentsModule != null)
|
||||
{
|
||||
Util.FireAndForget(o =>
|
||||
{
|
||||
Scene.AttachmentsModule.RezAttachments(this);
|
||||
});
|
||||
Watchdog.RunJob(
|
||||
"RezAttachments",
|
||||
o => Scene.AttachmentsModule.RezAttachments(this),
|
||||
string.Format("Rez attachments for {0} in {1}", Name, Scene.Name),
|
||||
null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We need to restart scripts here so that they receive the correct changed events (CHANGED_TELEPORT
|
||||
// and CHANGED_REGION) when the attachments have been rezzed in the new region. This cannot currently
|
||||
// be done in AttachmentsModule.CopyAttachments(AgentData ad, IScenePresence sp) itself since we are
|
||||
// not transporting the required data.
|
||||
//
|
||||
// We need to restart scripts here so that they receive the correct changed events (CHANGED_TELEPORT
|
||||
// and CHANGED_REGION) when the attachments have been rezzed in the new region. This cannot currently
|
||||
// be done in AttachmentsModule.CopyAttachments(AgentData ad, IScenePresence sp) itself since we are
|
||||
|
@ -1254,23 +1250,16 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
// But XEngine starts all scripts unsuspended. Starting them suspended will not currently work because script rezzing
|
||||
// is placed in an asynchronous queue in XEngine and so the ResumeScripts() call will almost certainly execute before the
|
||||
// script is rezzed. This means the ResumeScripts() does absolutely nothing when using XEngine.
|
||||
//
|
||||
// One cannot simply iterate over attachments in a fire and forget thread because this would no longer
|
||||
// be locked, allowing race conditions if other code changes the attachments list.
|
||||
List<SceneObjectGroup> attachments = GetAttachments();
|
||||
|
||||
if (attachments.Count > 0)
|
||||
{
|
||||
m_log.DebugFormat(
|
||||
"[SCENE PRESENCE]: Restarting scripts in attachments for {0} in {1}", Name, Scene.Name);
|
||||
|
||||
// Resume scripts
|
||||
foreach (SceneObjectGroup sog in attachments)
|
||||
{
|
||||
sog.ScheduleGroupForFullUpdate();
|
||||
sog.RootPart.ParentGroup.CreateScriptInstances(0, false, m_scene.DefaultScriptEngine, GetStateSource());
|
||||
sog.ResumeScripts();
|
||||
}
|
||||
Watchdog.RunJob(
|
||||
"StartAttachmentScripts",
|
||||
o => RestartAttachmentScripts(attachments),
|
||||
string.Format("Start attachment scripts for {0} in {1}", Name, Scene.Name),
|
||||
null,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1294,6 +1283,20 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
return true;
|
||||
}
|
||||
|
||||
private void RestartAttachmentScripts(List<SceneObjectGroup> attachments)
|
||||
{
|
||||
m_log.DebugFormat(
|
||||
"[SCENE PRESENCE]: Restarting scripts in attachments for {0} in {1}", Name, Scene.Name);
|
||||
|
||||
// Resume scripts
|
||||
foreach (SceneObjectGroup sog in attachments)
|
||||
{
|
||||
sog.ScheduleGroupForFullUpdate();
|
||||
sog.RootPart.ParentGroup.CreateScriptInstances(0, false, m_scene.DefaultScriptEngine, GetStateSource());
|
||||
sog.ResumeScripts();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsRealLogin(TeleportFlags teleportFlags)
|
||||
{
|
||||
return ((teleportFlags & TeleportFlags.ViaLogin) != 0) && ((teleportFlags & TeleportFlags.ViaHGLogin) == 0);
|
||||
|
@ -1326,7 +1329,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
UseFakeGroupTitle = false;
|
||||
SendAvatarDataToAllClients(false);
|
||||
});
|
||||
}, null, "Scenepresence.ForceViewersUpdateName");
|
||||
}
|
||||
|
||||
public int GetStateSource()
|
||||
|
@ -1809,14 +1812,15 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
}
|
||||
|
||||
// XXX: If we force an update here, then multiple attachments do appear correctly on a destination region
|
||||
// XXX: If we force an update after activity has completed, then multiple attachments do appear correctly on a destination region
|
||||
// If we do it a little bit earlier (e.g. when converting the child to a root agent) then this does not work.
|
||||
// This may be due to viewer code or it may be something we're not doing properly simulator side.
|
||||
lock (m_attachments)
|
||||
{
|
||||
foreach (SceneObjectGroup sog in m_attachments)
|
||||
sog.ScheduleGroupForFullUpdate();
|
||||
}
|
||||
Watchdog.RunJob(
|
||||
"ScheduleAttachmentsForFullUpdate",
|
||||
o => ScheduleAttachmentsForFullUpdate(),
|
||||
string.Format("Schedule attachments for full update for {0} in {1}", Name, Scene.Name),
|
||||
null,
|
||||
true);
|
||||
|
||||
// m_log.DebugFormat(
|
||||
// "[SCENE PRESENCE]: Completing movement of {0} into region {1} took {2}ms",
|
||||
|
@ -1828,6 +1832,15 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
}
|
||||
}
|
||||
|
||||
private void ScheduleAttachmentsForFullUpdate()
|
||||
{
|
||||
lock (m_attachments)
|
||||
{
|
||||
foreach (SceneObjectGroup sog in m_attachments)
|
||||
sog.ScheduleGroupForFullUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback for the Camera view block check. Gets called with the results of the camera view block test
|
||||
/// hitYN is true when there's something in the way.
|
||||
|
@ -2618,7 +2631,6 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
}
|
||||
}
|
||||
|
||||
Vector3 sitPartWorldPosition = part.GetWorldPosition();
|
||||
ControllingClient.SendClearFollowCamProperties(part.ParentUUID);
|
||||
|
||||
ParentID = 0;
|
||||
|
@ -2647,13 +2659,13 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
// Vector3 standPositionAdjustment
|
||||
// = part.SitTargetPosition + new Vector3(0.5f, 0f, m_sitAvatarHeight / 2f);
|
||||
Vector3 adjustmentForSitPosition = (part.SitTargetPosition + OffsetPosition) * part.GetWorldRotation();
|
||||
Vector3 adjustmentForSitPosition = (OffsetPosition - SIT_TARGET_ADJUSTMENT) * part.GetWorldRotation();
|
||||
|
||||
// XXX: This is based on the physics capsule sizes. Need to find a better way to read this rather than
|
||||
// hardcoding here.
|
||||
Vector3 adjustmentForSitPose = new Vector3(0.74f, 0f, 0f) * standRotation;
|
||||
|
||||
Vector3 standPos = sitPartWorldPosition + adjustmentForSitPosition + adjustmentForSitPose;
|
||||
Vector3 standPos = part.ParentGroup.AbsolutePosition + adjustmentForSitPosition + adjustmentForSitPose;
|
||||
|
||||
// m_log.DebugFormat(
|
||||
// "[SCENE PRESENCE]: Setting stand to pos {0}, (adjustmentForSitPosition {1}, adjustmentForSitPose {2}) rotation {3} for {4} in {5}",
|
||||
|
@ -3363,7 +3375,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
SentInitialDataToClient = true;
|
||||
|
||||
// Send all scene object to the new client
|
||||
Watchdog.RunInThread(delegate
|
||||
Watchdog.RunJob("SendInitialDataToClient", delegate
|
||||
{
|
||||
// m_log.DebugFormat(
|
||||
// "[SCENE PRESENCE]: Sending initial data to {0} agent {1} in {2}, tp flags {3}",
|
||||
|
@ -3381,7 +3393,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
if (e != null && e is SceneObjectGroup)
|
||||
((SceneObjectGroup)e).SendFullUpdateToClient(ControllingClient);
|
||||
}
|
||||
}, string.Format("SendInitialDataToClient ({0} in {1})", Name, Scene.Name), null);
|
||||
}, string.Format("SendInitialDataToClient ({0} in {1})", Name, Scene.Name),null, false, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -3618,7 +3630,8 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
agentpos.CopyFrom(cadu, ControllingClient.SessionId);
|
||||
|
||||
// Let's get this out of the update loop
|
||||
Util.FireAndForget(delegate { m_scene.SendOutChildAgentUpdates(agentpos, this); });
|
||||
Util.FireAndForget(
|
||||
o => m_scene.SendOutChildAgentUpdates(agentpos, this), null, "ScenePresence.SendOutChildAgentUpdates");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4036,9 +4049,23 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
Animator.Animations.SetImplicitDefaultAnimation(cAgent.AnimState.AnimID, cAgent.AnimState.SequenceNum, UUID.Zero);
|
||||
|
||||
if (Scene.AttachmentsModule != null)
|
||||
Scene.AttachmentsModule.CopyAttachments(cAgent, this);
|
||||
{
|
||||
// If the JobEngine is running we can schedule this job now and continue rather than waiting for all
|
||||
// attachments to copy, which might take a long time in the Hypergrid case as the entire inventory
|
||||
// graph is inspected for each attachments and assets possibly fetched.
|
||||
//
|
||||
// We don't need to worry about a race condition as the job to later start the scripts is also
|
||||
// JobEngine scheduled and so will always occur after this task.
|
||||
// XXX: This will not be true if JobEngine ever gets more than one thread.
|
||||
Watchdog.RunJob(
|
||||
"CopyAttachments",
|
||||
o => Scene.AttachmentsModule.CopyAttachments(cAgent, this),
|
||||
string.Format("Copy attachments for {0} entering {1}", Name, Scene.Name),
|
||||
null,
|
||||
true);
|
||||
}
|
||||
|
||||
// This must occur after attachments are copied, as it releases the CompleteMovement() calling thread
|
||||
// This must occur after attachments are copied or scheduled to be copied, as it releases the CompleteMovement() calling thread
|
||||
// originating from the client completing a teleport. Otherwise, CompleteMovement() code to restart
|
||||
// script attachments can outrace this thread.
|
||||
lock (m_originRegionIDAccessLock)
|
||||
|
@ -4472,7 +4499,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}, null, "ScenePresence.SendScriptEventToAttachments");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -224,7 +224,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
public SimStatsReporter(Scene scene)
|
||||
{
|
||||
m_scene = scene;
|
||||
m_reportedFpsCorrectionFactor = scene.MinFrameTime * m_nominalReportedFps;
|
||||
m_reportedFpsCorrectionFactor = scene.MinFrameSeconds * m_nominalReportedFps;
|
||||
m_statsUpdateFactor = (float)(m_statsUpdatesEveryMS / 1000);
|
||||
ReportingRegion = scene.RegionInfo;
|
||||
|
||||
|
@ -239,7 +239,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
/// At the moment, we'll only report if a frame is over 120% of target, since commonly frames are a bit
|
||||
/// longer than ideal (which in itself is a concern).
|
||||
SlowFramesStatReportThreshold = (int)Math.Ceiling(m_scene.MinFrameTime * 1000 * 1.2);
|
||||
SlowFramesStatReportThreshold = (int)Math.Ceiling(scene.MinFrameTicks * 1.2);
|
||||
|
||||
SlowFramesStat
|
||||
= new Stat(
|
||||
|
|
|
@ -35,7 +35,6 @@ using OpenSim.Framework;
|
|||
using OpenSim.Framework.Communications;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
{
|
||||
|
|
|
@ -36,7 +36,6 @@ using OpenSim.Framework.Communications;
|
|||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
{
|
||||
|
|
|
@ -37,7 +37,6 @@ using OpenSim.Framework.Communications;
|
|||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
{
|
||||
|
|
|
@ -37,7 +37,6 @@ using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation;
|
|||
using OpenSim.Region.CoreModules.World.Land;
|
||||
using OpenSim.Region.OptionalModules;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
{
|
||||
|
|
|
@ -40,7 +40,6 @@ using OpenSim.Region.CoreModules.World.Permissions;
|
|||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
{
|
||||
|
|
|
@ -34,7 +34,6 @@ using OpenSim.Framework;
|
|||
using OpenSim.Framework.Communications;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
using log4net;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
|
|
|
@ -33,7 +33,6 @@ using OpenSim.Framework;
|
|||
using OpenSim.Framework.Communications;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
{
|
||||
|
|
|
@ -35,7 +35,6 @@ using OpenSim.Framework.Communications;
|
|||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
{
|
||||
|
|
|
@ -34,7 +34,6 @@ using OpenSim.Framework;
|
|||
using OpenSim.Framework.Communications;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
{
|
||||
|
|
|
@ -34,7 +34,6 @@ using OpenSim.Framework;
|
|||
using OpenSim.Framework.Communications;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
{
|
||||
|
|
|
@ -33,7 +33,6 @@ using OpenSim.Framework;
|
|||
using OpenSim.Framework.Communications;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
{
|
||||
|
|
|
@ -39,7 +39,6 @@ using OpenSim.Region.Framework.Interfaces;
|
|||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
{
|
||||
|
|
|
@ -44,7 +44,6 @@ using OpenSim.Region.CoreModules.Framework.EntityTransfer;
|
|||
using OpenSim.Region.CoreModules.World.Serialiser;
|
||||
using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
using GridRegion = OpenSim.Services.Interfaces.GridRegion;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
|
|
|
@ -43,7 +43,6 @@ using OpenSim.Region.CoreModules.World.Serialiser;
|
|||
using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
{
|
||||
|
|
|
@ -37,7 +37,6 @@ using OpenSim.Framework.Communications;
|
|||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
{
|
||||
|
|
|
@ -47,7 +47,6 @@ using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation;
|
|||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
using GridRegion = OpenSim.Services.Interfaces.GridRegion;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
|
|
|
@ -40,7 +40,6 @@ using OpenSim.Region.CoreModules.Framework.EntityTransfer;
|
|||
using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation;
|
||||
using OpenSim.Region.CoreModules.World.Permissions;
|
||||
using OpenSim.Tests.Common;
|
||||
using OpenSim.Tests.Common.Mock;
|
||||
|
||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||
{
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue