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
|
* Allen Kerensky
|
||||||
* BigFootAg
|
* BigFootAg
|
||||||
* BlueWall Slade
|
* BlueWall Slade
|
||||||
|
* bobshaffer2
|
||||||
* brianw/Sir_Ahzz
|
* brianw/Sir_Ahzz
|
||||||
* CharlieO
|
* CharlieO
|
||||||
* ChrisDown
|
* ChrisDown
|
||||||
* Chris Yeoh (IBM)
|
* Chris Yeoh (IBM)
|
||||||
|
* cinderblocks
|
||||||
* controlbreak
|
* controlbreak
|
||||||
* coyled
|
* coyled
|
||||||
* ctrlaltdavid (David Rowe)
|
* ctrlaltdavid (David Rowe)
|
||||||
|
|
|
@ -150,7 +150,8 @@ namespace OpenSim.Groups
|
||||||
data.Data["ShowInList"] = showInList ? "1" : "0";
|
data.Data["ShowInList"] = showInList ? "1" : "0";
|
||||||
data.Data["AllowPublish"] = allowPublish ? "1" : "0";
|
data.Data["AllowPublish"] = allowPublish ? "1" : "0";
|
||||||
data.Data["MaturePublish"] = maturePublish ? "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))
|
if (!m_Database.StoreGroup(data))
|
||||||
return UUID.Zero;
|
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);
|
_AddOrUpdateGroupRole(RequestingAgentID, data.GroupID, UUID.Zero, "Everyone", "Everyone in the group", "Member of " + name, (ulong)DefaultEveryonePowers, true);
|
||||||
|
|
||||||
// Create Owner role
|
// Create Owner role
|
||||||
UUID roleID = UUID.Random();
|
|
||||||
_AddOrUpdateGroupRole(RequestingAgentID, data.GroupID, roleID, "Owners", "Owners of the group", "Owner of " + name, (ulong)OwnerPowers, true);
|
_AddOrUpdateGroupRole(RequestingAgentID, data.GroupID, roleID, "Owners", "Owners of the group", "Owner of " + name, (ulong)OwnerPowers, true);
|
||||||
|
|
||||||
// Add founder to group
|
// Add founder to group
|
||||||
|
@ -247,6 +247,9 @@ namespace OpenSim.Groups
|
||||||
if (group == null)
|
if (group == null)
|
||||||
return members;
|
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"]);
|
UUID ownerRoleID = new UUID(group.Data["OwnerRoleID"]);
|
||||||
|
|
||||||
RoleData[] roles = m_Database.RetrieveRoles(GroupID);
|
RoleData[] roles = m_Database.RetrieveRoles(GroupID);
|
||||||
|
@ -255,6 +258,11 @@ namespace OpenSim.Groups
|
||||||
return members;
|
return members;
|
||||||
List<RoleData> rolesList = new List<RoleData>(roles);
|
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?
|
// Check visibility?
|
||||||
// When we don't want to check visibility, we pass it "all" as the requestingAgentID
|
// When we don't want to check visibility, we pass it "all" as the requestingAgentID
|
||||||
bool checkVisibility = !RequestingAgentID.Equals(UUID.Zero.ToString());
|
bool checkVisibility = !RequestingAgentID.Equals(UUID.Zero.ToString());
|
||||||
|
@ -291,17 +299,17 @@ namespace OpenSim.Groups
|
||||||
{
|
{
|
||||||
m.Title = selected.Data["Title"];
|
m.Title = selected.Data["Title"];
|
||||||
m.AgentPowers = UInt64.Parse(selected.Data["Powers"]);
|
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;
|
return members;
|
||||||
|
|
|
@ -30,6 +30,7 @@ using System.Collections;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
using log4net;
|
using log4net;
|
||||||
using Nini.Config;
|
using Nini.Config;
|
||||||
|
@ -43,41 +44,37 @@ using Caps = OpenSim.Framework.Capabilities.Caps;
|
||||||
|
|
||||||
namespace OpenSim.Capabilities.Handlers
|
namespace OpenSim.Capabilities.Handlers
|
||||||
{
|
{
|
||||||
public class GetMeshHandler
|
public class GetMeshHandler : BaseStreamHandler
|
||||||
{
|
{
|
||||||
// private static readonly ILog m_log =
|
// private static readonly ILog m_log =
|
||||||
// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
private IAssetService m_assetService;
|
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;
|
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();
|
// Try to parse the texture ID from the request URL
|
||||||
responsedata["int_response_code"] = 400; //501; //410; //404;
|
NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query);
|
||||||
responsedata["content_type"] = "text/plain";
|
string meshStr = query.GetOne("mesh_id");
|
||||||
responsedata["keepalive"] = false;
|
|
||||||
responsedata["str_response_string"] = "Request wasn't what was expected";
|
|
||||||
|
|
||||||
string meshStr = string.Empty;
|
// m_log.DebugFormat("Fetching mesh {0}", meshStr);
|
||||||
|
|
||||||
if (request.ContainsKey("mesh_id"))
|
|
||||||
meshStr = request["mesh_id"].ToString();
|
|
||||||
|
|
||||||
UUID meshID = UUID.Zero;
|
UUID meshID = UUID.Zero;
|
||||||
if (!String.IsNullOrEmpty(meshStr) && UUID.TryParse(meshStr, out meshID))
|
if (!String.IsNullOrEmpty(meshStr) && UUID.TryParse(meshStr, out meshID))
|
||||||
{
|
{
|
||||||
if (m_assetService == null)
|
if (m_assetService == null)
|
||||||
{
|
{
|
||||||
responsedata["int_response_code"] = 404; //501; //410; //404;
|
httpResponse.StatusCode = 404;
|
||||||
responsedata["content_type"] = "text/plain";
|
httpResponse.ContentType = "text/plain";
|
||||||
responsedata["keepalive"] = false;
|
byte[] data = Encoding.UTF8.GetBytes("The asset service is unavailable. So is your mesh.");
|
||||||
responsedata["str_response_string"] = "The asset service is unavailable. So is your mesh.";
|
httpResponse.Body.Write(data, 0, data.Length);
|
||||||
return responsedata;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetBase mesh = m_assetService.Get(meshID.ToString());
|
AssetBase mesh = m_assetService.Get(meshID.ToString());
|
||||||
|
@ -86,31 +83,32 @@ namespace OpenSim.Capabilities.Handlers
|
||||||
{
|
{
|
||||||
if (mesh.Type == (SByte)AssetType.Mesh)
|
if (mesh.Type == (SByte)AssetType.Mesh)
|
||||||
{
|
{
|
||||||
responsedata["str_response_string"] = Convert.ToBase64String(mesh.Data);
|
byte[] data = mesh.Data;
|
||||||
responsedata["content_type"] = "application/vnd.ll.mesh";
|
httpResponse.Body.Write(data, 0, data.Length);
|
||||||
responsedata["int_response_code"] = 200;
|
httpResponse.ContentType = "application/vnd.ll.mesh";
|
||||||
|
httpResponse.StatusCode = 200;
|
||||||
}
|
}
|
||||||
// Optionally add additional mesh types here
|
// Optionally add additional mesh types here
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
responsedata["int_response_code"] = 404; //501; //410; //404;
|
httpResponse.StatusCode = 404;
|
||||||
responsedata["content_type"] = "text/plain";
|
httpResponse.ContentType = "text/plain";
|
||||||
responsedata["keepalive"] = false;
|
byte[] data = Encoding.UTF8.GetBytes("Unfortunately, this asset isn't a mesh.");
|
||||||
responsedata["str_response_string"] = "Unfortunately, this asset isn't a mesh.";
|
httpResponse.Body.Write(data, 0, data.Length);
|
||||||
return responsedata;
|
httpResponse.KeepAlive = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
responsedata["int_response_code"] = 404; //501; //410; //404;
|
httpResponse.StatusCode = 404;
|
||||||
responsedata["content_type"] = "text/plain";
|
httpResponse.ContentType = "text/plain";
|
||||||
responsedata["keepalive"] = false;
|
byte[] data = Encoding.UTF8.GetBytes("Your Mesh wasn't found. Sorry!");
|
||||||
responsedata["str_response_string"] = "Your Mesh wasn't found. Sorry!";
|
httpResponse.Body.Write(data, 0, data.Length);
|
||||||
return responsedata;
|
httpResponse.KeepAlive = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return responsedata;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -65,15 +65,8 @@ namespace OpenSim.Capabilities.Handlers
|
||||||
if (m_AssetService == null)
|
if (m_AssetService == null)
|
||||||
throw new Exception(String.Format("Failed to load AssetService from {0}; config is {1}", assetService, m_ConfigName));
|
throw new Exception(String.Format("Failed to load AssetService from {0}; config is {1}", assetService, m_ConfigName));
|
||||||
|
|
||||||
GetMeshHandler gmeshHandler = new GetMeshHandler(m_AssetService);
|
server.AddStreamHandler(
|
||||||
IRequestHandler reqHandler
|
new GetMeshHandler("/CAPS/GetMesh/" /*+ UUID.Random() */, m_AssetService, "GetMesh", null));
|
||||||
= new RestHTTPHandler(
|
|
||||||
"GET",
|
|
||||||
"/CAPS/" + UUID.Random(),
|
|
||||||
httpMethod => gmeshHandler.ProcessGetMesh(httpMethod, UUID.Zero, null),
|
|
||||||
"GetMesh",
|
|
||||||
null);
|
|
||||||
server.AddStreamHandler(reqHandler);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -37,7 +37,6 @@ using OpenSim.Framework;
|
||||||
using OpenSim.Framework.Servers.HttpServer;
|
using OpenSim.Framework.Servers.HttpServer;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Capabilities.Handlers.GetTexture.Tests
|
namespace OpenSim.Capabilities.Handlers.GetTexture.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -54,6 +54,10 @@ namespace OpenSim.Framework
|
||||||
public int assetThrottle;
|
public int assetThrottle;
|
||||||
public int textureThrottle;
|
public int textureThrottle;
|
||||||
public int totalThrottle;
|
public int totalThrottle;
|
||||||
|
|
||||||
|
// Used by adaptive only
|
||||||
|
public int targetThrottle;
|
||||||
|
|
||||||
public int maxThrottle;
|
public int maxThrottle;
|
||||||
|
|
||||||
public Dictionary<string, int> SyncRequests = new Dictionary<string,int>();
|
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
|
/// In case, we are invoked asynchroneously this object will keep track of the state
|
||||||
/// </summary>
|
/// </summary>
|
||||||
AsyncResult<Stream> ar = new AsyncResult<Stream>(callback, state);
|
AsyncResult<Stream> ar = new AsyncResult<Stream>(callback, state);
|
||||||
Util.FireAndForget(RequestHelper, ar);
|
Util.FireAndForget(RequestHelper, ar, "RestClient.BeginRequest");
|
||||||
return ar;
|
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)
|
foreach (char c in DisallowedShortNameCharacters)
|
||||||
{
|
{
|
||||||
if (shortName.IndexOf(c) != -1)
|
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;
|
ShortName = shortName;
|
||||||
|
|
|
@ -133,6 +133,8 @@ namespace OpenSim.Framework.Monitoring
|
||||||
/// /summary>
|
/// /summary>
|
||||||
public static event Action<ThreadWatchdogInfo> OnWatchdogTimeout;
|
public static event Action<ThreadWatchdogInfo> OnWatchdogTimeout;
|
||||||
|
|
||||||
|
public static JobEngine JobEngine { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Is this watchdog active?
|
/// Is this watchdog active?
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -173,6 +175,7 @@ namespace OpenSim.Framework.Monitoring
|
||||||
|
|
||||||
static Watchdog()
|
static Watchdog()
|
||||||
{
|
{
|
||||||
|
JobEngine = new JobEngine();
|
||||||
m_threads = new Dictionary<int, ThreadWatchdogInfo>();
|
m_threads = new Dictionary<int, ThreadWatchdogInfo>();
|
||||||
m_watchdogTimer = new System.Timers.Timer(WATCHDOG_INTERVAL_MS);
|
m_watchdogTimer = new System.Timers.Timer(WATCHDOG_INTERVAL_MS);
|
||||||
m_watchdogTimer.AutoReset = false;
|
m_watchdogTimer.AutoReset = false;
|
||||||
|
@ -449,5 +452,55 @@ namespace OpenSim.Framework.Monitoring
|
||||||
|
|
||||||
m_watchdogTimer.Start();
|
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_objectCapacity = 0;
|
||||||
private int m_maxPrimsPerUser = -1;
|
private int m_maxPrimsPerUser = -1;
|
||||||
private int m_linksetCapacity = 0;
|
private int m_linksetCapacity = 0;
|
||||||
private int m_agentCapacity = 0;
|
|
||||||
private string m_regionType = String.Empty;
|
private string m_regionType = String.Empty;
|
||||||
private RegionLightShareData m_windlight = new RegionLightShareData();
|
private RegionLightShareData m_windlight = new RegionLightShareData();
|
||||||
protected uint m_httpPort;
|
protected uint m_httpPort;
|
||||||
|
@ -351,10 +350,7 @@ namespace OpenSim.Framework
|
||||||
get { return m_linksetCapacity; }
|
get { return m_linksetCapacity; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public int AgentCapacity
|
public int AgentCapacity { get; set; }
|
||||||
{
|
|
||||||
get { return m_agentCapacity; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte AccessLevel
|
public byte AccessLevel
|
||||||
{
|
{
|
||||||
|
@ -748,7 +744,7 @@ namespace OpenSim.Framework
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
m_agentCapacity = config.GetInt("MaxAgents", 100);
|
AgentCapacity = config.GetInt("MaxAgents", 100);
|
||||||
allKeys.Remove("MaxAgents");
|
allKeys.Remove("MaxAgents");
|
||||||
|
|
||||||
// Multi-tenancy
|
// Multi-tenancy
|
||||||
|
@ -864,8 +860,8 @@ namespace OpenSim.Framework
|
||||||
if (m_linksetCapacity > 0)
|
if (m_linksetCapacity > 0)
|
||||||
config.Set("LinksetPrims", m_linksetCapacity);
|
config.Set("LinksetPrims", m_linksetCapacity);
|
||||||
|
|
||||||
if (m_agentCapacity > 0)
|
if (AgentCapacity > 0)
|
||||||
config.Set("MaxAgents", m_agentCapacity);
|
config.Set("MaxAgents", AgentCapacity);
|
||||||
|
|
||||||
if (ScopeID != UUID.Zero)
|
if (ScopeID != UUID.Zero)
|
||||||
config.Set("ScopeID", ScopeID.ToString());
|
config.Set("ScopeID", ScopeID.ToString());
|
||||||
|
|
|
@ -29,6 +29,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
@ -291,6 +292,18 @@ namespace OpenSim.Framework.Servers
|
||||||
+ " 3 = full stack trace, including common threads\n",
|
+ " 3 = full stack trace, including common threads\n",
|
||||||
HandleDebugThreadpoolLevel);
|
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(
|
m_console.Commands.AddCommand(
|
||||||
"Debug", false, "force gc",
|
"Debug", false, "force gc",
|
||||||
"force gc",
|
"force gc",
|
||||||
|
@ -354,6 +367,57 @@ namespace OpenSim.Framework.Servers
|
||||||
Notice("serialosdreq is now {0}", setSerializeOsdRequests);
|
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)
|
private void HandleDebugThreadpoolStatus(string module, string[] args)
|
||||||
{
|
{
|
||||||
int workerThreads, iocpThreads;
|
int workerThreads, iocpThreads;
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace OpenSim.Framework.ServiceAuth
|
||||||
private string m_Username, m_Password;
|
private string m_Username, m_Password;
|
||||||
private string m_CredentialsB64;
|
private string m_CredentialsB64;
|
||||||
|
|
||||||
private string remove_me;
|
// private string remove_me;
|
||||||
|
|
||||||
public string Credentials
|
public string Credentials
|
||||||
{
|
{
|
||||||
|
@ -24,7 +24,7 @@ namespace OpenSim.Framework.ServiceAuth
|
||||||
|
|
||||||
public BasicHttpAuthentication(IConfigSource config, string section)
|
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_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);
|
m_Password = Util.GetConfigVarFromSections<string>(config, "HttpAuthPassword", new string[] { "Network", section }, string.Empty);
|
||||||
string str = m_Username + ":" + m_Password;
|
string str = m_Username + ":" + m_Password;
|
||||||
|
|
|
@ -32,7 +32,6 @@ using OpenMetaverse;
|
||||||
using OpenMetaverse.StructuredData;
|
using OpenMetaverse.StructuredData;
|
||||||
using OpenSim.Framework;
|
using OpenSim.Framework;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
using Animation = OpenSim.Framework.Animation;
|
using Animation = OpenSim.Framework.Animation;
|
||||||
|
|
||||||
namespace OpenSim.Framework.Tests
|
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)
|
public static void InitThreadPool(int minThreads, int maxThreads)
|
||||||
{
|
{
|
||||||
if (maxThreads < 2)
|
if (maxThreads < 2)
|
||||||
|
@ -1977,8 +1972,7 @@ namespace OpenSim.Framework
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Additional information about threads in the main thread pool. Used to time how long the
|
/// 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.
|
/// 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 nextThreadFuncNum = 0;
|
||||||
private static long numQueuedThreadFuncs = 0;
|
private static long numQueuedThreadFuncs = 0;
|
||||||
private static long numRunningThreadFuncs = 0;
|
private static long numRunningThreadFuncs = 0;
|
||||||
|
private static long numTotalThreadFuncsCalled = 0;
|
||||||
private static Int32 threadFuncOverloadMode = 0;
|
private static Int32 threadFuncOverloadMode = 0;
|
||||||
|
|
||||||
|
public static long TotalQueuedFireAndForgetCalls { get { return numQueuedThreadFuncs; } }
|
||||||
|
public static long TotalRunningFireAndForgetCalls { get { return numRunningThreadFuncs; } }
|
||||||
|
|
||||||
// Maps (ThreadFunc number -> Thread)
|
// Maps (ThreadFunc number -> Thread)
|
||||||
private static ConcurrentDictionary<long, ThreadInfo> activeThreads = new ConcurrentDictionary<long, ThreadInfo>();
|
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)
|
public static void FireAndForget(System.Threading.WaitCallback callback, object obj)
|
||||||
{
|
{
|
||||||
FireAndForget(callback, obj, null);
|
FireAndForget(callback, obj, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void FireAndForget(System.Threading.WaitCallback callback, object obj, string context)
|
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;
|
WaitCallback realCallback;
|
||||||
|
|
||||||
bool loggingEnabled = LogThreadPool > 0;
|
bool loggingEnabled = LogThreadPool > 0;
|
||||||
|
@ -2104,7 +2136,15 @@ namespace OpenSim.Framework
|
||||||
if (FireAndForgetMethod == FireAndForgetMethod.RegressionTest)
|
if (FireAndForgetMethod == FireAndForgetMethod.RegressionTest)
|
||||||
{
|
{
|
||||||
// If we're running regression tests, then we want any exceptions to rise up to the test code.
|
// 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
|
else
|
||||||
{
|
{
|
||||||
|
@ -2143,6 +2183,9 @@ namespace OpenSim.Framework
|
||||||
activeThreads.TryRemove(threadFuncNum, out dummy);
|
activeThreads.TryRemove(threadFuncNum, out dummy);
|
||||||
if ((loggingEnabled || (threadFuncOverloadMode == 1)) && threadInfo.LogThread)
|
if ((loggingEnabled || (threadFuncOverloadMode == 1)) && threadInfo.LogThread)
|
||||||
m_log.DebugFormat("Exit threadfunc {0} ({1})", threadFuncNum, FormatDuration(threadInfo.Elapsed()));
|
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()
|
// Called from base.StartUp()
|
||||||
|
|
||||||
|
IConfig startupConfig = Config.Configs["Startup"];
|
||||||
|
if (startupConfig == null || startupConfig.GetBoolean("JobEngineEnabled", true))
|
||||||
|
Watchdog.JobEngine.Start();
|
||||||
|
|
||||||
m_httpServerPort = m_networkServersInfo.HttpListenerPort;
|
m_httpServerPort = m_networkServersInfo.HttpListenerPort;
|
||||||
SceneManager.OnRestartSim += HandleRestartRegion;
|
SceneManager.OnRestartSim += HandleRestartRegion;
|
||||||
|
|
||||||
|
|
|
@ -268,8 +268,8 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||||
public string SeedCapRequest(string request, string path, string param,
|
public string SeedCapRequest(string request, string path, string param,
|
||||||
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
|
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
|
||||||
{
|
{
|
||||||
m_log.DebugFormat(
|
// m_log.DebugFormat(
|
||||||
"[CAPS]: Received SEED caps request in {0} for agent {1}", m_regionName, m_HostCapsObj.AgentID);
|
// "[CAPS]: Received SEED caps request in {0} for agent {1}", m_regionName, m_HostCapsObj.AgentID);
|
||||||
|
|
||||||
if (!m_Scene.CheckClient(m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint))
|
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.Framework.Scenes;
|
||||||
using OpenSim.Region.OptionalModules.World.NPC;
|
using OpenSim.Region.OptionalModules.World.NPC;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.ClientStack.Linden.Tests
|
namespace OpenSim.Region.ClientStack.Linden.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -110,32 +110,29 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
public void RegisterCaps(UUID agentID, Caps caps)
|
public void RegisterCaps(UUID agentID, Caps caps)
|
||||||
{
|
{
|
||||||
// UUID capID = UUID.Random();
|
|
||||||
|
|
||||||
//caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture));
|
//caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture));
|
||||||
if (m_URL == "localhost")
|
if (m_URL == "localhost")
|
||||||
{
|
{
|
||||||
// m_log.DebugFormat("[GETMESH]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName);
|
// m_log.DebugFormat("[GETTEXTURE]: /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());
|
|
||||||
|
|
||||||
caps.RegisterHandler("GetMesh", reqHandler);
|
UUID capID = UUID.Random();
|
||||||
|
|
||||||
|
caps.RegisterHandler(
|
||||||
|
"GetMesh",
|
||||||
|
new GetMeshHandler("/CAPS/" + capID + "/", m_AssetService, "GetMesh", agentID.ToString()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// m_log.DebugFormat("[GETMESH]: {0} in region {1}", m_URL, m_scene.RegionInfo.RegionName);
|
// m_log.DebugFormat("[GETTEXTURE]: {0} in region {1}", m_URL, m_scene.RegionInfo.RegionName);
|
||||||
caps.RegisterHandler("GetMesh", m_URL);
|
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")]
|
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimulatorFeaturesModule")]
|
||||||
public class SimulatorFeaturesModule : ISharedRegionModule, ISimulatorFeaturesModule
|
public class SimulatorFeaturesModule : ISharedRegionModule, ISimulatorFeaturesModule
|
||||||
{
|
{
|
||||||
private static readonly ILog m_log =
|
// private static readonly ILog m_log =
|
||||||
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
public event SimulatorFeaturesRequestDelegate OnSimulatorFeaturesRequest;
|
public event SimulatorFeaturesRequestDelegate OnSimulatorFeaturesRequest;
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,6 @@ using OpenSim.Region.CoreModules.Framework;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Services.Interfaces;
|
using OpenSim.Services.Interfaces;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
using OSDArray = OpenMetaverse.StructuredData.OSDArray;
|
using OSDArray = OpenMetaverse.StructuredData.OSDArray;
|
||||||
using OSDMap = OpenMetaverse.StructuredData.OSDMap;
|
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>
|
/// </param>
|
||||||
/// <returns>true if the handler was added. This is currently always the case.</returns>
|
/// <returns>true if the handler was added. This is currently always the case.</returns>
|
||||||
public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler, bool doAsync)
|
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;
|
bool result = false;
|
||||||
lock (m_packetHandlers)
|
lock (m_packetHandlers)
|
||||||
{
|
{
|
||||||
if (!m_packetHandlers.ContainsKey(packetType))
|
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;
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -688,21 +712,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
PacketProcessor pprocessor;
|
PacketProcessor pprocessor;
|
||||||
if (m_packetHandlers.TryGetValue(packet.Type, out pprocessor))
|
if (m_packetHandlers.TryGetValue(packet.Type, out pprocessor))
|
||||||
{
|
{
|
||||||
|
ClientInfo cinfo = UDPClient.GetClientInfo();
|
||||||
|
|
||||||
//there is a local handler for this packet type
|
//there is a local handler for this packet type
|
||||||
if (pprocessor.Async)
|
if (pprocessor.Async)
|
||||||
{
|
{
|
||||||
ClientInfo cinfo = UDPClient.GetClientInfo();
|
|
||||||
if (!cinfo.AsyncRequests.ContainsKey(packet.Type.ToString()))
|
if (!cinfo.AsyncRequests.ContainsKey(packet.Type.ToString()))
|
||||||
cinfo.AsyncRequests[packet.Type.ToString()] = 0;
|
cinfo.AsyncRequests[packet.Type.ToString()] = 0;
|
||||||
cinfo.AsyncRequests[packet.Type.ToString()]++;
|
cinfo.AsyncRequests[packet.Type.ToString()]++;
|
||||||
|
|
||||||
object obj = new AsyncPacketProcess(this, pprocessor.method, packet);
|
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;
|
result = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ClientInfo cinfo = UDPClient.GetClientInfo();
|
|
||||||
if (!cinfo.SyncRequests.ContainsKey(packet.Type.ToString()))
|
if (!cinfo.SyncRequests.ContainsKey(packet.Type.ToString()))
|
||||||
cinfo.SyncRequests[packet.Type.ToString()] = 0;
|
cinfo.SyncRequests[packet.Type.ToString()] = 0;
|
||||||
cinfo.SyncRequests[packet.Type.ToString()]++;
|
cinfo.SyncRequests[packet.Type.ToString()]++;
|
||||||
|
@ -1161,7 +1193,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// <param name="map">heightmap</param>
|
/// <param name="map">heightmap</param>
|
||||||
public virtual void SendLayerData(float[] map)
|
public virtual void SendLayerData(float[] map)
|
||||||
{
|
{
|
||||||
Util.FireAndForget(DoSendLayerData, m_scene.Heightmap.GetTerrainData());
|
Util.FireAndForget(DoSendLayerData, m_scene.Heightmap.GetTerrainData(), "LLClientView.DoSendLayerData");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1373,7 +1405,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// <param name="windSpeeds">16x16 array of wind speeds</param>
|
/// <param name="windSpeeds">16x16 array of wind speeds</param>
|
||||||
public virtual void SendWindData(Vector2[] windSpeeds)
|
public virtual void SendWindData(Vector2[] windSpeeds)
|
||||||
{
|
{
|
||||||
Util.FireAndForget(DoSendWindData, windSpeeds);
|
Util.FireAndForget(DoSendWindData, windSpeeds, "LLClientView.SendWindData");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1382,7 +1414,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// <param name="windSpeeds">16x16 array of cloud densities</param>
|
/// <param name="windSpeeds">16x16 array of cloud densities</param>
|
||||||
public virtual void SendCloudData(float[] cloudDensity)
|
public virtual void SendCloudData(float[] cloudDensity)
|
||||||
{
|
{
|
||||||
Util.FireAndForget(DoSendCloudData, cloudDensity);
|
Util.FireAndForget(DoSendCloudData, cloudDensity, "LLClientView.SendCloudData");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -3834,11 +3866,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SendEntityUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags)
|
public void SendEntityUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags)
|
||||||
{
|
{
|
||||||
//double priority = m_prioritizer.GetUpdatePriority(this, entity);
|
if (entity.UUID == m_agentId && !updateFlags.HasFlag(PrimUpdateFlags.FullUpdate))
|
||||||
uint priority = m_prioritizer.GetUpdatePriority(this, entity);
|
{
|
||||||
|
ImprovedTerseObjectUpdatePacket packet
|
||||||
|
= (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate);
|
||||||
|
|
||||||
lock (m_entityUpdates.SyncRoot)
|
packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
|
||||||
m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation));
|
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>
|
/// <summary>
|
||||||
|
@ -5540,10 +5586,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
AddLocalPacketHandler(PacketType.ParcelBuy, HandleParcelBuyRequest, false);
|
AddLocalPacketHandler(PacketType.ParcelBuy, HandleParcelBuyRequest, false);
|
||||||
AddLocalPacketHandler(PacketType.UUIDGroupNameRequest, HandleUUIDGroupNameRequest);
|
AddLocalPacketHandler(PacketType.UUIDGroupNameRequest, HandleUUIDGroupNameRequest);
|
||||||
AddLocalPacketHandler(PacketType.ObjectGroup, HandleObjectGroupRequest);
|
AddLocalPacketHandler(PacketType.ObjectGroup, HandleObjectGroupRequest);
|
||||||
AddLocalPacketHandler(PacketType.GenericMessage, HandleGenericMessage);
|
AddLocalPacketHandler(PacketType.GenericMessage, HandleGenericMessage, true, true);
|
||||||
AddLocalPacketHandler(PacketType.AvatarPropertiesRequest, HandleAvatarPropertiesRequest);
|
AddLocalPacketHandler(PacketType.AvatarPropertiesRequest, HandleAvatarPropertiesRequest, true, true);
|
||||||
AddLocalPacketHandler(PacketType.ChatFromViewer, HandleChatFromViewer);
|
AddLocalPacketHandler(PacketType.ChatFromViewer, HandleChatFromViewer);
|
||||||
AddLocalPacketHandler(PacketType.AvatarPropertiesUpdate, HandlerAvatarPropertiesUpdate);
|
AddLocalPacketHandler(PacketType.AvatarPropertiesUpdate, HandlerAvatarPropertiesUpdate, true, true);
|
||||||
AddLocalPacketHandler(PacketType.ScriptDialogReply, HandlerScriptDialogReply);
|
AddLocalPacketHandler(PacketType.ScriptDialogReply, HandlerScriptDialogReply);
|
||||||
AddLocalPacketHandler(PacketType.ImprovedInstantMessage, HandlerImprovedInstantMessage);
|
AddLocalPacketHandler(PacketType.ImprovedInstantMessage, HandlerImprovedInstantMessage);
|
||||||
AddLocalPacketHandler(PacketType.AcceptFriendship, HandlerAcceptFriendship);
|
AddLocalPacketHandler(PacketType.AcceptFriendship, HandlerAcceptFriendship);
|
||||||
|
@ -5728,8 +5774,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
AddLocalPacketHandler(PacketType.PickDelete, HandlePickDelete);
|
AddLocalPacketHandler(PacketType.PickDelete, HandlePickDelete);
|
||||||
AddLocalPacketHandler(PacketType.PickGodDelete, HandlePickGodDelete);
|
AddLocalPacketHandler(PacketType.PickGodDelete, HandlePickGodDelete);
|
||||||
AddLocalPacketHandler(PacketType.PickInfoUpdate, HandlePickInfoUpdate);
|
AddLocalPacketHandler(PacketType.PickInfoUpdate, HandlePickInfoUpdate);
|
||||||
AddLocalPacketHandler(PacketType.AvatarNotesUpdate, HandleAvatarNotesUpdate);
|
AddLocalPacketHandler(PacketType.AvatarNotesUpdate, HandleAvatarNotesUpdate, true, true);
|
||||||
AddLocalPacketHandler(PacketType.AvatarInterestsUpdate, HandleAvatarInterestsUpdate);
|
AddLocalPacketHandler(PacketType.AvatarInterestsUpdate, HandleAvatarInterestsUpdate, true, true);
|
||||||
AddLocalPacketHandler(PacketType.GrantUserRights, HandleGrantUserRights);
|
AddLocalPacketHandler(PacketType.GrantUserRights, HandleGrantUserRights);
|
||||||
AddLocalPacketHandler(PacketType.PlacesQuery, HandlePlacesQuery);
|
AddLocalPacketHandler(PacketType.PlacesQuery, HandlePlacesQuery);
|
||||||
AddLocalPacketHandler(PacketType.UpdateMuteListEntry, HandleUpdateMuteListEntry);
|
AddLocalPacketHandler(PacketType.UpdateMuteListEntry, HandleUpdateMuteListEntry);
|
||||||
|
@ -8079,7 +8125,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
{
|
{
|
||||||
// This requests the asset if needed
|
// This requests the asset if needed
|
||||||
HandleSimInventoryTransferRequestWithPermsCheck(sender, transfer);
|
HandleSimInventoryTransferRequestWithPermsCheck(sender, transfer);
|
||||||
});
|
}, null, "LLClientView.HandleTransferRequest");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12786,8 +12833,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
|
|
||||||
public struct PacketProcessor
|
public struct PacketProcessor
|
||||||
{
|
{
|
||||||
public PacketMethod method;
|
/// <summary>
|
||||||
public bool Async;
|
/// 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
|
public class AsyncPacketProcess
|
||||||
|
|
|
@ -76,6 +76,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// or removed, this number must also change</summary>
|
/// or removed, this number must also change</summary>
|
||||||
const int THROTTLE_CATEGORY_COUNT = 8;
|
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>
|
/// <summary>Fired when updated networking stats are produced for this client</summary>
|
||||||
public event PacketStats OnPacketStats;
|
public event PacketStats OnPacketStats;
|
||||||
/// <summary>Fired when the queue for a packet category is empty. This event can be
|
/// <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; }
|
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>
|
/// <summary>Throttle buckets for each packet category</summary>
|
||||||
private readonly TokenBucket[] m_throttleCategories;
|
private readonly TokenBucket[] m_throttleCategories;
|
||||||
/// <summary>Outgoing queues for throttled packets</summary>
|
/// <summary>Outgoing queues for throttled packets</summary>
|
||||||
|
@ -201,9 +226,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
m_maxRTO = maxRTO;
|
m_maxRTO = maxRTO;
|
||||||
|
|
||||||
// Create a token bucket throttle for this client that has the scene token bucket as a parent
|
// 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);
|
m_throttleClient
|
||||||
// Create a token bucket throttle for the total category with the client bucket as a throttle
|
= new AdaptiveTokenBucket(
|
||||||
m_throttleCategory = new TokenBucket(m_throttleClient, 0);
|
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
|
// Create an array of token buckets for this clients different throttle categories
|
||||||
m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
|
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
|
// Initialize the packet outboxes, where packets sit while they are waiting for tokens
|
||||||
m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
|
m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
|
||||||
|
|
||||||
// Initialize the token buckets that control the throttling for each category
|
// 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
|
// 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.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
|
||||||
m_info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
|
m_info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
|
||||||
m_info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].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;
|
m_info.maxThrottle = (int)m_throttleClient.MaxDripRate;
|
||||||
|
|
||||||
return m_info;
|
return m_info;
|
||||||
|
@ -279,6 +311,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
throw new NotImplementedException();
|
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>
|
/// <summary>
|
||||||
/// Return statistics information about client packet queues.
|
/// Return statistics information about client packet queues.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -348,6 +407,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
|
int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
|
||||||
int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
|
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,
|
// Make sure none of the throttles are set below our packet MTU,
|
||||||
// otherwise a throttle could become permanently clogged
|
// otherwise a throttle could become permanently clogged
|
||||||
resend = Math.Max(resend, LLUDPServer.MTU);
|
resend = Math.Max(resend, LLUDPServer.MTU);
|
||||||
|
@ -365,10 +432,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
texture = (int)((1 - m_cannibalrate) * texture);
|
texture = (int)((1 - m_cannibalrate) * texture);
|
||||||
|
|
||||||
//int total = resend + land + wind + cloud + task + texture + asset;
|
//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
|
// 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;
|
TokenBucket bucket;
|
||||||
|
|
||||||
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
|
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
|
||||||
|
@ -653,7 +732,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
if (!m_udpServer.OqrEngine.IsRunning)
|
if (!m_udpServer.OqrEngine.IsRunning)
|
||||||
{
|
{
|
||||||
// Asynchronously run the callback
|
// Asynchronously run the callback
|
||||||
Util.FireAndForget(FireQueueEmpty, categories);
|
Util.FireAndForget(FireQueueEmpty, categories, "LLUDPClient.BeginFireQueueEmpty");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,7 +34,6 @@ using System.Net.Sockets;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using log4net;
|
using log4net;
|
||||||
using NDesk.Options;
|
|
||||||
using Nini.Config;
|
using Nini.Config;
|
||||||
using OpenMetaverse.Packets;
|
using OpenMetaverse.Packets;
|
||||||
using OpenSim.Framework;
|
using OpenSim.Framework;
|
||||||
|
@ -222,6 +221,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int DefaultClientPacketDebugLevel { get; set; }
|
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>
|
/// <summary>The measured resolution of Environment.TickCount</summary>
|
||||||
public readonly float TickCountResolution;
|
public readonly float TickCountResolution;
|
||||||
|
|
||||||
|
@ -238,12 +243,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// <summary>Incoming packets that are awaiting handling</summary>
|
/// <summary>Incoming packets that are awaiting handling</summary>
|
||||||
private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>();
|
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>
|
/// <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; }
|
public ThrottleRates ThrottleRates { get; private set; }
|
||||||
|
|
||||||
/// <summary>Manages authentication for agent circuits</summary>
|
/// <summary>Manages authentication for agent circuits</summary>
|
||||||
|
@ -355,6 +363,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private IClientAPI m_currentIncomingClient;
|
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>
|
/// <summary>
|
||||||
/// Experimental facility to run queue empty processing within a controlled number of threads rather than
|
/// 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
|
/// 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
|
#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);
|
ThrottleRates = new ThrottleRates(configSource);
|
||||||
|
|
||||||
if (usePools)
|
if (usePools)
|
||||||
EnablePools();
|
EnablePools();
|
||||||
|
|
||||||
|
IpahEngine = new IncomingPacketAsyncHandlingEngine(this);
|
||||||
OqrEngine = new OutgoingQueueRefillEngine(this);
|
OqrEngine = new OutgoingQueueRefillEngine(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,12 +468,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
{
|
{
|
||||||
StartInbound();
|
StartInbound();
|
||||||
StartOutbound();
|
StartOutbound();
|
||||||
|
IpahEngine.Start();
|
||||||
OqrEngine.Start();
|
OqrEngine.Start();
|
||||||
|
|
||||||
m_elapsedMSSinceLastStatReport = Environment.TickCount;
|
m_elapsedMSSinceLastStatReport = Environment.TickCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartInbound()
|
public void StartInbound()
|
||||||
{
|
{
|
||||||
m_log.InfoFormat(
|
m_log.InfoFormat(
|
||||||
"[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server in {0} mode with UsePools = {1}",
|
"[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);
|
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");
|
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);
|
m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + Scene.Name);
|
||||||
base.StopOutbound();
|
base.StopOutbound();
|
||||||
base.StopInbound();
|
base.StopInbound();
|
||||||
|
IpahEngine.Stop();
|
||||||
OqrEngine.Stop();
|
OqrEngine.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool EnablePools()
|
public override bool EnablePools()
|
||||||
{
|
{
|
||||||
if (!UsePools)
|
if (!UsePools)
|
||||||
{
|
{
|
||||||
|
@ -509,7 +532,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool DisablePools()
|
public override bool DisablePools()
|
||||||
{
|
{
|
||||||
if (UsePools)
|
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
|
/// This is a seperate method so that it can be called once we have an m_scene to distinguish different scene
|
||||||
/// stats.
|
/// stats.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void EnablePoolStats()
|
protected internal void EnablePoolStats()
|
||||||
{
|
{
|
||||||
m_poolCountStat
|
m_poolCountStat
|
||||||
= new Stat(
|
= new Stat(
|
||||||
|
@ -563,7 +586,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Disables pool stats.
|
/// Disables pool stats.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void DisablePoolStats()
|
protected internal void DisablePoolStats()
|
||||||
{
|
{
|
||||||
StatsManager.DeregisterStat(m_poolCountStat);
|
StatsManager.DeregisterStat(m_poolCountStat);
|
||||||
m_poolCountStat = null;
|
m_poolCountStat = null;
|
||||||
|
@ -677,312 +700,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
StatType.Pull,
|
StatType.Pull,
|
||||||
stat => stat.Value = PacketPool.Instance.BlocksPooled,
|
stat => stat.Value = PacketPool.Instance.BlocksPooled,
|
||||||
StatVerbosity.Debug));
|
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
|
// We delay enabling pool stats to AddScene() instead of Initialize() so that we can distinguish pool stats by
|
||||||
// scene name
|
// scene name
|
||||||
if (UsePools)
|
if (UsePools)
|
||||||
EnablePoolStats();
|
EnablePoolStats();
|
||||||
|
|
||||||
MainConsole.Instance.Commands.AddCommand(
|
LLUDPServerCommands commands = new LLUDPServerCommands(MainConsole.Instance, this);
|
||||||
"Debug", false, "debug lludp packet",
|
commands.Register();
|
||||||
"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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HandlesRegion(Location x)
|
public bool HandlesRegion(Location x)
|
||||||
|
@ -990,6 +728,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
return x == m_location;
|
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)
|
// public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting)
|
||||||
// {
|
// {
|
||||||
// // CoarseLocationUpdate and AvatarGroupsReply packets cannot be split in an automated way
|
// // 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.
|
// packet so that it isn't sent before a queued update packet.
|
||||||
bool forceQueue = (type == PacketType.KillObject);
|
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))
|
if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, forceQueue))
|
||||||
{
|
{
|
||||||
SendPacketFinal(outgoingPacket);
|
SendPacketFinal(outgoingPacket);
|
||||||
|
@ -1165,6 +924,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// }
|
||||||
|
|
||||||
#endregion Queue or Send
|
#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
|
// 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.
|
// 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.
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1360,6 +1121,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
// is 100% correct
|
// is 100% correct
|
||||||
PacketsSentCount++;
|
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
|
// Put the UDP payload on the wire
|
||||||
AsyncBeginSend(buffer);
|
AsyncBeginSend(buffer);
|
||||||
|
|
||||||
|
@ -1469,7 +1235,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
// buffer.
|
// buffer.
|
||||||
object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
|
object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
|
||||||
|
|
||||||
Util.FireAndForget(HandleUseCircuitCode, array);
|
Util.FireAndForget(HandleUseCircuitCode, array, "LLUDPServer.HandleUseCircuitCode");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1482,7 +1248,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
// buffer.
|
// buffer.
|
||||||
object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
|
object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
|
||||||
|
|
||||||
Util.FireAndForget(HandleCompleteMovementIntoRegion, array);
|
Util.FireAndForget(
|
||||||
|
HandleCompleteMovementIntoRegion, array, "LLUDPServer.HandleCompleteMovementIntoRegion");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1604,7 +1371,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
|
|
||||||
if (packet.Type == PacketType.AgentUpdate)
|
if (packet.Type == PacketType.AgentUpdate)
|
||||||
{
|
{
|
||||||
if (m_discardAgentUpdates)
|
if (DiscardInboundAgentUpdates)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
((LLClientView)client).TotalAgentUpdates++;
|
((LLClientView)client).TotalAgentUpdates++;
|
||||||
|
@ -2012,7 +1779,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
{
|
{
|
||||||
if (!Scene.TryGetClient(agentID, out client))
|
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 = new LLClientView(Scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode);
|
||||||
client.OnLogout += LogoutHandler;
|
client.OnLogout += LogoutHandler;
|
||||||
|
@ -2055,6 +1822,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
|
|
||||||
private void IncomingPacketHandler()
|
private void IncomingPacketHandler()
|
||||||
{
|
{
|
||||||
|
Thread.CurrentThread.Priority = ThreadPriority.Highest;
|
||||||
|
|
||||||
// Set this culture for the thread that incoming packets are received
|
// Set this culture for the thread that incoming packets are received
|
||||||
// on to en-US to avoid number parsing issues
|
// on to en-US to avoid number parsing issues
|
||||||
Culture.SetCurrentCulture();
|
Culture.SetCurrentCulture();
|
||||||
|
@ -2100,6 +1869,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
|
|
||||||
private void OutgoingPacketHandler()
|
private void OutgoingPacketHandler()
|
||||||
{
|
{
|
||||||
|
Thread.CurrentThread.Priority = ThreadPriority.Highest;
|
||||||
|
|
||||||
// Set this culture for the thread that outgoing packets are sent
|
// Set this culture for the thread that outgoing packets are sent
|
||||||
// on to en-US to avoid number parsing issues
|
// on to en-US to avoid number parsing issues
|
||||||
Culture.SetCurrentCulture();
|
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
|
/// manner (not throwing an exception when the remote side resets the
|
||||||
/// connection). This call is ignored on Mono where the flag is not
|
/// connection). This call is ignored on Mono where the flag is not
|
||||||
/// necessary</remarks>
|
/// necessary</remarks>
|
||||||
public void StartInbound(int recvBufferSize, bool asyncPacketHandling)
|
public virtual void StartInbound(int recvBufferSize, bool asyncPacketHandling)
|
||||||
{
|
{
|
||||||
m_asyncPacketHandling = asyncPacketHandling;
|
m_asyncPacketHandling = asyncPacketHandling;
|
||||||
|
|
||||||
|
@ -168,6 +168,12 @@ namespace OpenMetaverse
|
||||||
m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring");
|
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)
|
if (recvBufferSize != 0)
|
||||||
m_udpSocket.ReceiveBufferSize = recvBufferSize;
|
m_udpSocket.ReceiveBufferSize = recvBufferSize;
|
||||||
|
|
||||||
|
@ -185,14 +191,14 @@ namespace OpenMetaverse
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Start outbound UDP packet handling.
|
/// Start outbound UDP packet handling.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void StartOutbound()
|
public virtual void StartOutbound()
|
||||||
{
|
{
|
||||||
m_log.DebugFormat("[UDPBASE]: Starting outbound UDP loop");
|
m_log.DebugFormat("[UDPBASE]: Starting outbound UDP loop");
|
||||||
|
|
||||||
IsRunningOutbound = true;
|
IsRunningOutbound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StopInbound()
|
public virtual void StopInbound()
|
||||||
{
|
{
|
||||||
if (IsRunningInbound)
|
if (IsRunningInbound)
|
||||||
{
|
{
|
||||||
|
@ -203,14 +209,14 @@ namespace OpenMetaverse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StopOutbound()
|
public virtual void StopOutbound()
|
||||||
{
|
{
|
||||||
m_log.DebugFormat("[UDPBASE]: Stopping outbound UDP loop");
|
m_log.DebugFormat("[UDPBASE]: Stopping outbound UDP loop");
|
||||||
|
|
||||||
IsRunningOutbound = false;
|
IsRunningOutbound = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual bool EnablePools()
|
public virtual bool EnablePools()
|
||||||
{
|
{
|
||||||
if (!UsePools)
|
if (!UsePools)
|
||||||
{
|
{
|
||||||
|
@ -224,7 +230,7 @@ namespace OpenMetaverse
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual bool DisablePools()
|
public virtual bool DisablePools()
|
||||||
{
|
{
|
||||||
if (UsePools)
|
if (UsePools)
|
||||||
{
|
{
|
||||||
|
|
|
@ -217,6 +217,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
|
|
||||||
private void ProcessRequests()
|
private void ProcessRequests()
|
||||||
{
|
{
|
||||||
|
Thread.CurrentThread.Priority = ThreadPriority.Highest;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (IsRunning || m_requestQueue.Count > 0)
|
while (IsRunning || m_requestQueue.Count > 0)
|
||||||
|
|
|
@ -36,7 +36,6 @@ using OpenSim.Framework;
|
||||||
using OpenSim.Framework.Monitoring;
|
using OpenSim.Framework.Monitoring;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
||||||
{
|
{
|
||||||
|
@ -47,7 +46,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
||||||
public class BasicCircuitTests : OpenSimTestCase
|
public class BasicCircuitTests : OpenSimTestCase
|
||||||
{
|
{
|
||||||
private Scene m_scene;
|
private Scene m_scene;
|
||||||
private TestLLUDPServer m_udpServer;
|
|
||||||
|
|
||||||
[TestFixtureSetUp]
|
[TestFixtureSetUp]
|
||||||
public void FixtureInit()
|
public void FixtureInit()
|
||||||
|
@ -73,72 +71,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
||||||
StatsManager.SimExtraStats = new SimExtraStatsCollector();
|
StatsManager.SimExtraStats = new SimExtraStatsCollector();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
// /// <summary>
|
||||||
/// Build an object name packet for test purposes
|
// /// Build an object name packet for test purposes
|
||||||
/// </summary>
|
// /// </summary>
|
||||||
/// <param name="objectLocalId"></param>
|
// /// <param name="objectLocalId"></param>
|
||||||
/// <param name="objectName"></param>
|
// /// <param name="objectName"></param>
|
||||||
private ObjectNamePacket BuildTestObjectNamePacket(uint objectLocalId, string objectName)
|
// private ObjectNamePacket BuildTestObjectNamePacket(uint objectLocalId, string objectName)
|
||||||
{
|
// {
|
||||||
ObjectNamePacket onp = new ObjectNamePacket();
|
// ObjectNamePacket onp = new ObjectNamePacket();
|
||||||
ObjectNamePacket.ObjectDataBlock odb = new ObjectNamePacket.ObjectDataBlock();
|
// ObjectNamePacket.ObjectDataBlock odb = new ObjectNamePacket.ObjectDataBlock();
|
||||||
odb.LocalID = objectLocalId;
|
// odb.LocalID = objectLocalId;
|
||||||
odb.Name = Utils.StringToBytes(objectName);
|
// odb.Name = Utils.StringToBytes(objectName);
|
||||||
onp.ObjectData = new ObjectNamePacket.ObjectDataBlock[] { odb };
|
// onp.ObjectData = new ObjectNamePacket.ObjectDataBlock[] { odb };
|
||||||
onp.Header.Zerocoded = false;
|
// onp.Header.Zerocoded = false;
|
||||||
|
//
|
||||||
return onp;
|
// 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>
|
/// <summary>
|
||||||
/// Test adding a client to the stack
|
/// Test adding a client to the stack
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -148,7 +97,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
||||||
TestHelpers.InMethod();
|
TestHelpers.InMethod();
|
||||||
// TestHelpers.EnableLogging();
|
// TestHelpers.EnableLogging();
|
||||||
|
|
||||||
AddUdpServer();
|
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(m_scene);
|
||||||
|
|
||||||
UUID myAgentUuid = TestHelpers.ParseTail(0x1);
|
UUID myAgentUuid = TestHelpers.ParseTail(0x1);
|
||||||
UUID mySessionUuid = TestHelpers.ParseTail(0x2);
|
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.
|
upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor.
|
||||||
Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length);
|
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
|
// 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);
|
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_scene.AuthenticateHandler.AddNewCircuit(myCircuitCode, acd);
|
||||||
|
|
||||||
m_udpServer.PacketReceived(upb);
|
udpServer.PacketReceived(upb);
|
||||||
|
|
||||||
// Should succeed now
|
// Should succeed now
|
||||||
ScenePresence sp = m_scene.GetScenePresence(myAgentUuid);
|
ScenePresence sp = m_scene.GetScenePresence(myAgentUuid);
|
||||||
Assert.That(sp.UUID, Is.EqualTo(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)));
|
Assert.That(packet, Is.InstanceOf(typeof(PacketAckPacket)));
|
||||||
|
|
||||||
PacketAckPacket ackPacket = packet as PacketAckPacket;
|
PacketAckPacket ackPacket = packet as PacketAckPacket;
|
||||||
|
@ -200,15 +149,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
||||||
public void TestLogoutClientDueToAck()
|
public void TestLogoutClientDueToAck()
|
||||||
{
|
{
|
||||||
TestHelpers.InMethod();
|
TestHelpers.InMethod();
|
||||||
TestHelpers.EnableLogging();
|
// TestHelpers.EnableLogging();
|
||||||
|
|
||||||
IniConfigSource ics = new IniConfigSource();
|
IniConfigSource ics = new IniConfigSource();
|
||||||
IConfig config = ics.AddConfig("ClientStack.LindenUDP");
|
IConfig config = ics.AddConfig("ClientStack.LindenUDP");
|
||||||
config.Set("AckTimeout", -1);
|
config.Set("AckTimeout", -1);
|
||||||
AddUdpServer(ics);
|
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(m_scene, ics);
|
||||||
|
|
||||||
ScenePresence sp = AddClient();
|
ScenePresence sp
|
||||||
m_udpServer.ClientOutgoingPacketHandler(sp.ControllingClient, true, false, false);
|
= 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);
|
ScenePresence spAfterAckTimeout = m_scene.GetScenePresence(sp.UUID);
|
||||||
Assert.That(spAfterAckTimeout, Is.Null);
|
Assert.That(spAfterAckTimeout, Is.Null);
|
||||||
|
|
|
@ -39,7 +39,6 @@ using OpenSim.Framework;
|
||||||
using OpenSim.Region.CoreModules.Agent.TextureSender;
|
using OpenSim.Region.CoreModules.Agent.TextureSender;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,7 +30,6 @@ using NUnit.Framework;
|
||||||
using OpenMetaverse;
|
using OpenMetaverse;
|
||||||
using OpenMetaverse.Packets;
|
using OpenMetaverse.Packets;
|
||||||
using OpenSim.Framework;
|
using OpenSim.Framework;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
|
|
||||||
namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
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"];
|
IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"];
|
||||||
|
|
||||||
|
// Current default total is 66750
|
||||||
Resend = throttleConfig.GetInt("resend_default", 6625);
|
Resend = throttleConfig.GetInt("resend_default", 6625);
|
||||||
Land = throttleConfig.GetInt("land_default", 9125);
|
Land = throttleConfig.GetInt("land_default", 9125);
|
||||||
Wind = throttleConfig.GetInt("wind_default", 1750);
|
Wind = throttleConfig.GetInt("wind_default", 1750);
|
||||||
|
|
|
@ -42,9 +42,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
public class TokenBucket
|
public class TokenBucket
|
||||||
{
|
{
|
||||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
private static Int32 m_counter = 0;
|
|
||||||
|
public string Identifier { get; private set; }
|
||||||
// private Int32 m_identifier;
|
|
||||||
|
public int DebugLevel { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of ticks (ms) per quantum, drip rate and max burst
|
/// 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
|
/// Map of children buckets and their requested maximum burst rate
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>();
|
protected Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>();
|
||||||
|
|
||||||
#region Properties
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The parent bucket of this bucket, or null if this bucket has no
|
/// The parent bucket of this bucket, or null if this bucket has no
|
||||||
/// parent. The parent bucket will limit the aggregate bandwidth of all
|
/// parent. The parent bucket will limit the aggregate bandwidth of all
|
||||||
/// of its children buckets
|
/// of its children buckets
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected TokenBucket m_parent;
|
public TokenBucket Parent { get; protected set; }
|
||||||
public TokenBucket Parent
|
|
||||||
{
|
|
||||||
get { return m_parent; }
|
|
||||||
set { m_parent = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maximum burst rate in bytes per second. This is the maximum number
|
/// Maximum burst rate in bytes per second. This is the maximum number
|
||||||
|
@ -114,77 +108,106 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The speed limit of this bucket in bytes per second. This is the
|
/// The requested drip rate for this particular bucket.
|
||||||
/// number of tokens that are added to the bucket per quantum
|
|
||||||
/// </summary>
|
/// </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
|
/// <seealso cref="RemoveTokens"/> is called, at the granularity of
|
||||||
/// the system tick interval (typically around 15-22ms)</remarks>
|
/// the system tick interval (typically around 15-22ms)
|
||||||
protected Int64 m_dripRate;
|
/// 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
|
public virtual Int64 RequestedDripRate
|
||||||
{
|
{
|
||||||
get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); }
|
get { return (m_dripRate == 0 ? TotalDripRequest : m_dripRate); }
|
||||||
set {
|
set
|
||||||
m_dripRate = (value < 0 ? 0 : value);
|
{
|
||||||
|
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_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
|
||||||
m_totalDripRequest = m_dripRate;
|
|
||||||
if (m_parent != null)
|
if (Parent != null)
|
||||||
m_parent.RegisterRequest(this,m_dripRate);
|
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
|
public virtual Int64 DripRate
|
||||||
{
|
{
|
||||||
get {
|
get
|
||||||
if (m_parent == null)
|
{
|
||||||
return Math.Min(RequestedDripRate,TotalDripRequest);
|
double rate;
|
||||||
|
|
||||||
double rate = (double)RequestedDripRate * m_parent.DripRateModifier();
|
// 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)
|
if (rate < m_minimumDripRate)
|
||||||
rate = m_minimumDripRate;
|
rate = m_minimumDripRate;
|
||||||
|
else if (MaxDripRate > 0 && rate > MaxDripRate)
|
||||||
|
rate = MaxDripRate;
|
||||||
|
|
||||||
return (Int64)rate;
|
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>
|
/// <summary>
|
||||||
/// The current total of the requested maximum burst rates of
|
/// The current total of the requested maximum burst rates of children buckets.
|
||||||
/// this bucket's children buckets.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected Int64 m_totalDripRequest;
|
public Int64 TotalDripRequest { get; protected set; }
|
||||||
public Int64 TotalDripRequest
|
|
||||||
{
|
|
||||||
get { return m_totalDripRequest; }
|
|
||||||
set { m_totalDripRequest = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Properties
|
|
||||||
|
|
||||||
#region Constructor
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default constructor
|
/// Default constructor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="identifier">Identifier for this token bucket</param>
|
||||||
/// <param name="parent">Parent bucket if this is a child bucket, or
|
/// <param name="parent">Parent bucket if this is a child bucket, or
|
||||||
/// null if this is a root bucket</param>
|
/// null if this is a root bucket</param>
|
||||||
/// <param name="maxBurst">Maximum size of the bucket in bytes, or
|
/// <param name="requestedDripRate">
|
||||||
/// zero if this bucket has no maximum capacity</param>
|
/// Requested rate that the bucket fills, in bytes per
|
||||||
/// <param name="dripRate">Rate that the bucket fills, in bytes per
|
/// second. If zero, the bucket always remains full.
|
||||||
/// second. If zero, the bucket always remains full</param>
|
/// </param>
|
||||||
public TokenBucket(TokenBucket parent, Int64 dripRate)
|
public TokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate)
|
||||||
{
|
{
|
||||||
// m_identifier = m_counter++;
|
Identifier = identifier;
|
||||||
m_counter++;
|
|
||||||
|
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
RequestedDripRate = dripRate;
|
RequestedDripRate = requestedDripRate;
|
||||||
// TotalDripRequest = dripRate; // this will be overwritten when a child node registers
|
MaxDripRate = maxDripRate;
|
||||||
// MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst);
|
|
||||||
m_lastDrip = Util.EnvironmentTickCount();
|
m_lastDrip = Util.EnvironmentTickCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Constructor
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compute a modifier for the MaxBurst rate. This is 1.0, meaning
|
/// Compute a modifier for the MaxBurst rate. This is 1.0, meaning
|
||||||
/// no modification if the requested bandwidth is less than the
|
/// no modification if the requested bandwidth is less than the
|
||||||
|
@ -195,7 +218,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
protected double DripRateModifier()
|
protected double DripRateModifier()
|
||||||
{
|
{
|
||||||
Int64 driprate = DripRate;
|
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>
|
/// <summary>
|
||||||
|
@ -217,16 +247,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
lock (m_children)
|
lock (m_children)
|
||||||
{
|
{
|
||||||
m_children[child] = request;
|
m_children[child] = request;
|
||||||
// m_totalDripRequest = m_children.Values.Sum();
|
|
||||||
|
|
||||||
m_totalDripRequest = 0;
|
TotalDripRequest = 0;
|
||||||
foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
|
foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
|
||||||
m_totalDripRequest += cref.Value;
|
TotalDripRequest += cref.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass the new values up to the parent
|
// Pass the new values up to the parent
|
||||||
if (m_parent != null)
|
if (Parent != null)
|
||||||
m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
|
{
|
||||||
|
Int64 effectiveDripRate;
|
||||||
|
|
||||||
|
if (RequestedDripRate > 0)
|
||||||
|
effectiveDripRate = Math.Min(RequestedDripRate, TotalDripRequest);
|
||||||
|
else
|
||||||
|
effectiveDripRate = TotalDripRequest;
|
||||||
|
|
||||||
|
Parent.RegisterRequest(this, effectiveDripRate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -238,17 +276,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
lock (m_children)
|
lock (m_children)
|
||||||
{
|
{
|
||||||
m_children.Remove(child);
|
m_children.Remove(child);
|
||||||
// m_totalDripRequest = m_children.Values.Sum();
|
|
||||||
|
|
||||||
m_totalDripRequest = 0;
|
TotalDripRequest = 0;
|
||||||
foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
|
foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
|
||||||
m_totalDripRequest += cref.Value;
|
TotalDripRequest += cref.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Pass the new values up to the parent
|
// Pass the new values up to the parent
|
||||||
if (m_parent != null)
|
if (Parent != null)
|
||||||
m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
|
Parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -301,7 +337,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
// with no drip rate...
|
// with no drip rate...
|
||||||
if (DripRate == 0)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,8 +357,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
|
|
||||||
public class AdaptiveTokenBucket : TokenBucket
|
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>
|
/// <summary>
|
||||||
/// The minimum rate for flow control. Minimum drip rate is one
|
/// The minimum rate for flow control. Minimum drip rate is one
|
||||||
/// packet per second. Open the throttle to 15 packets per second
|
/// packet per second. Open the throttle to 15 packets per second
|
||||||
|
@ -330,65 +398,42 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected const Int64 m_minimumFlow = m_minimumDripRate * 15;
|
protected const Int64 m_minimumFlow = m_minimumDripRate * 15;
|
||||||
|
|
||||||
// <summary>
|
public AdaptiveTokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate, bool enabled)
|
||||||
// The maximum rate for flow control. Drip rate can never be
|
: base(identifier, parent, requestedDripRate, maxDripRate)
|
||||||
// greater than this.
|
|
||||||
// </summary>
|
|
||||||
protected Int64 m_maxDripRate = 0;
|
|
||||||
public Int64 MaxDripRate
|
|
||||||
{
|
{
|
||||||
get { return (m_maxDripRate == 0 ? m_totalDripRequest : m_maxDripRate); }
|
AdaptiveEnabled = enabled;
|
||||||
protected set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Enabled { get; private set; }
|
if (AdaptiveEnabled)
|
||||||
|
|
||||||
// <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)
|
|
||||||
{
|
{
|
||||||
// m_log.DebugFormat("[TOKENBUCKET] Adaptive throttle enabled");
|
// m_log.DebugFormat("[TOKENBUCKET]: Adaptive throttle enabled");
|
||||||
MaxDripRate = maxDripRate;
|
TargetDripRate = m_minimumFlow;
|
||||||
AdjustedDripRate = m_minimumFlow;
|
AdjustedDripRate = m_minimumFlow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// <summary>
|
// <summary>
|
||||||
//
|
// Reliable packets sent to the client for which we never received an ack adjust the drip rate down.
|
||||||
// </summary>
|
// </summary>
|
||||||
public void ExpirePackets(Int32 count)
|
public void ExpirePackets(Int32 count)
|
||||||
{
|
{
|
||||||
// m_log.WarnFormat("[ADAPTIVEBUCKET] drop {0} by {1} expired packets",AdjustedDripRate,count);
|
if (AdaptiveEnabled)
|
||||||
if (Enabled)
|
{
|
||||||
|
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));
|
AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,count));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// <summary>
|
// <summary>
|
||||||
//
|
// Reliable packets acked by the client adjust the drip rate up.
|
||||||
// </summary>
|
// </summary>
|
||||||
public void AcknowledgePackets(Int32 count)
|
public void AcknowledgePackets(Int32 count)
|
||||||
{
|
{
|
||||||
if (Enabled)
|
if (AdaptiveEnabled)
|
||||||
AdjustedDripRate = AdjustedDripRate + count;
|
AdjustedDripRate = AdjustedDripRate + count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -167,7 +167,7 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender
|
||||||
|
|
||||||
// Do Decode!
|
// Do Decode!
|
||||||
if (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(
|
Util.FireAndForget(
|
||||||
delegate { WriteFileCache(filename, asset); });
|
delegate { WriteFileCache(filename, asset); }, null, "FlotsamAssetCache.UpdateFileCache");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
|
@ -39,7 +39,6 @@ using OpenSim.Framework;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Region.Framework.Scenes.Serialization;
|
using OpenSim.Region.Framework.Scenes.Serialization;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.Asset.Tests
|
namespace OpenSim.Region.CoreModules.Asset.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -387,7 +387,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
|
||||||
if (!Enabled)
|
if (!Enabled)
|
||||||
return false;
|
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>
|
/// <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}",
|
"[ATTACHMENTS MODULE]: Adding attachment {0} to avatar {1} in pt {2} pos {3} {4}",
|
||||||
so.Name, sp.Name, attachmentpoint, attachOffset, so.RootPart.AttachedPos);
|
so.Name, sp.Name, attachmentpoint, attachOffset, so.RootPart.AttachedPos);
|
||||||
|
|
||||||
so.DetachFromBackup();
|
|
||||||
|
|
||||||
// Remove from database and parcel prim count
|
// Remove from database and parcel prim count
|
||||||
m_scene.DeleteFromStorage(so.UUID);
|
m_scene.DeleteFromStorage(so.UUID);
|
||||||
m_scene.EventManager.TriggerParcelPrimCountTainted();
|
m_scene.EventManager.TriggerParcelPrimCountTainted();
|
||||||
|
|
|
@ -53,7 +53,6 @@ using OpenSim.Region.ScriptEngine.Interfaces;
|
||||||
using OpenSim.Region.ScriptEngine.XEngine;
|
using OpenSim.Region.ScriptEngine.XEngine;
|
||||||
using OpenSim.Services.Interfaces;
|
using OpenSim.Services.Interfaces;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
|
namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
|
||||||
{
|
{
|
||||||
|
@ -199,6 +198,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
|
||||||
string attName = "att";
|
string attName = "att";
|
||||||
|
|
||||||
SceneObjectGroup so = SceneHelpers.AddSceneObject(scene, attName, sp.UUID);
|
SceneObjectGroup so = SceneHelpers.AddSceneObject(scene, attName, sp.UUID);
|
||||||
|
Assert.That(so.Backup, Is.True);
|
||||||
|
|
||||||
m_numberOfAttachEventsFired = 0;
|
m_numberOfAttachEventsFired = 0;
|
||||||
scene.AttachmentsModule.AttachObject(sp, so, (uint)AttachmentPoint.Chest, false, true, false);
|
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.IsAttachment);
|
||||||
Assert.That(attSo.UsesPhysics, Is.False);
|
Assert.That(attSo.UsesPhysics, Is.False);
|
||||||
Assert.That(attSo.IsTemporary, Is.False);
|
Assert.That(attSo.IsTemporary, Is.False);
|
||||||
|
Assert.That(attSo.Backup, Is.False);
|
||||||
|
|
||||||
// Check item status
|
// Check item status
|
||||||
Assert.That(
|
Assert.That(
|
||||||
|
@ -385,7 +386,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
|
||||||
public void TestRezAttachmentFromInventory()
|
public void TestRezAttachmentFromInventory()
|
||||||
{
|
{
|
||||||
TestHelpers.InMethod();
|
TestHelpers.InMethod();
|
||||||
// log4net.Config.XmlConfigurator.Configure();
|
// TestHelpers.EnableLogging();
|
||||||
|
|
||||||
Scene scene = CreateTestScene();
|
Scene scene = CreateTestScene();
|
||||||
UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1);
|
UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1);
|
||||||
|
@ -407,6 +408,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
|
||||||
Assert.That(attSo.IsAttachment);
|
Assert.That(attSo.IsAttachment);
|
||||||
Assert.That(attSo.UsesPhysics, Is.False);
|
Assert.That(attSo.UsesPhysics, Is.False);
|
||||||
Assert.That(attSo.IsTemporary, Is.False);
|
Assert.That(attSo.IsTemporary, Is.False);
|
||||||
|
Assert.IsFalse(attSo.Backup);
|
||||||
|
|
||||||
// Check appearance status
|
// Check appearance status
|
||||||
Assert.That(sp.Appearance.GetAttachments().Count, Is.EqualTo(1));
|
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);
|
Assert.That(scene.InventoryService.GetItem(new InventoryItemBase(attItem.ID)), Is.Null);
|
||||||
|
|
||||||
// Check object in scene
|
// 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
|
// Check events
|
||||||
Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
|
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.IsAttachment);
|
||||||
Assert.That(attSo.UsesPhysics, Is.False);
|
Assert.That(attSo.UsesPhysics, Is.False);
|
||||||
Assert.That(attSo.IsTemporary, Is.False);
|
Assert.That(attSo.IsTemporary, Is.False);
|
||||||
|
Assert.IsFalse(attSo.Backup);
|
||||||
|
|
||||||
// Check appearance status
|
// Check appearance status
|
||||||
List<AvatarAttachment> retreivedAttachments = presence.Appearance.GetAttachments();
|
List<AvatarAttachment> retreivedAttachments = presence.Appearance.GetAttachments();
|
||||||
|
@ -884,6 +889,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
|
||||||
SceneObjectGroup actualSceneBAtt = actualSceneBAttachments[0];
|
SceneObjectGroup actualSceneBAtt = actualSceneBAttachments[0];
|
||||||
Assert.That(actualSceneBAtt.Name, Is.EqualTo(attItem.Name));
|
Assert.That(actualSceneBAtt.Name, Is.EqualTo(attItem.Name));
|
||||||
Assert.That(actualSceneBAtt.AttachmentPoint, Is.EqualTo((uint)AttachmentPoint.Chest));
|
Assert.That(actualSceneBAtt.AttachmentPoint, Is.EqualTo((uint)AttachmentPoint.Chest));
|
||||||
|
Assert.IsFalse(actualSceneBAtt.Backup);
|
||||||
|
|
||||||
Assert.That(sceneB.GetSceneObjectGroups().Count, Is.EqualTo(1));
|
Assert.That(sceneB.GetSceneObjectGroups().Count, Is.EqualTo(1));
|
||||||
|
|
||||||
|
@ -994,6 +1000,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
|
||||||
SceneObjectGroup actualSceneBAtt = actualSceneBAttachments[0];
|
SceneObjectGroup actualSceneBAtt = actualSceneBAttachments[0];
|
||||||
Assert.That(actualSceneBAtt.Name, Is.EqualTo(attItem.Name));
|
Assert.That(actualSceneBAtt.Name, Is.EqualTo(attItem.Name));
|
||||||
Assert.That(actualSceneBAtt.AttachmentPoint, Is.EqualTo((uint)AttachmentPoint.Chest));
|
Assert.That(actualSceneBAtt.AttachmentPoint, Is.EqualTo((uint)AttachmentPoint.Chest));
|
||||||
|
Assert.IsFalse(actualSceneBAtt.Backup);
|
||||||
|
|
||||||
Assert.That(sceneB.GetSceneObjectGroups().Count, Is.EqualTo(1));
|
Assert.That(sceneB.GetSceneObjectGroups().Count, Is.EqualTo(1));
|
||||||
|
|
||||||
|
|
|
@ -593,7 +593,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
||||||
|
|
||||||
if (sendTime < now)
|
if (sendTime < now)
|
||||||
{
|
{
|
||||||
Util.FireAndForget(o => SendAppearance(avatarID));
|
Util.FireAndForget(o => SendAppearance(avatarID), null, "AvatarFactoryModule.SendAppearance");
|
||||||
m_sendqueue.Remove(avatarID);
|
m_sendqueue.Remove(avatarID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -611,7 +611,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
||||||
|
|
||||||
if (sendTime < now)
|
if (sendTime < now)
|
||||||
{
|
{
|
||||||
Util.FireAndForget(o => SaveAppearance(avatarID));
|
Util.FireAndForget(o => SaveAppearance(avatarID), null, "AvatarFactoryModule.SaveAppearance");
|
||||||
m_savequeue.Remove(avatarID);
|
m_savequeue.Remove(avatarID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1038,7 +1038,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
||||||
client.SendWearables(sp.Appearance.Wearables, sp.Appearance.Serial++);
|
client.SendWearables(sp.Appearance.Wearables, sp.Appearance.Serial++);
|
||||||
else
|
else
|
||||||
m_log.WarnFormat("[AVFACTORY]: Client_OnRequestWearables unable to find presence for {0}", client.AgentId);
|
m_log.WarnFormat("[AVFACTORY]: Client_OnRequestWearables unable to find presence for {0}", client.AgentId);
|
||||||
});
|
}, null, "AvatarFactoryModule.OnClientRequestWearables");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -34,7 +34,6 @@ using OpenSim.Framework;
|
||||||
using OpenSim.Region.CoreModules.Asset;
|
using OpenSim.Region.CoreModules.Asset;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
||||||
{
|
{
|
||||||
|
|
|
@ -187,7 +187,7 @@ namespace OpenSim.Region.CoreModules.Avatar.BakedTextures
|
||||||
{
|
{
|
||||||
rc.Request(reqStream, m_Auth);
|
rc.Request(reqStream, m_Auth);
|
||||||
m_log.DebugFormat("[XBakes]: stored {0} textures for user {1}", data.Length, agentId);
|
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.Region.Framework.Scenes;
|
||||||
using OpenSim.Services.Interfaces;
|
using OpenSim.Services.Interfaces;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.Avatar.Chat.Tests
|
namespace OpenSim.Region.CoreModules.Avatar.Chat.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -511,7 +511,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
|
||||||
|
|
||||||
// Notify about this user status
|
// Notify about this user status
|
||||||
StatusNotify(friendList, agentID, online);
|
StatusNotify(friendList, agentID, online);
|
||||||
}
|
}, null, "FriendsModule.StatusChange"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -660,7 +660,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
|
||||||
FriendsService.Delete(friendUUI, agentID.ToString());
|
FriendsService.Delete(friendUUI, agentID.ToString());
|
||||||
|
|
||||||
// notify the exfriend's service
|
// 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);
|
m_log.DebugFormat("[HGFRIENDS MODULE]: {0} terminated {1}", agentID, friendUUI);
|
||||||
return true;
|
return true;
|
||||||
|
@ -678,7 +679,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
|
||||||
FriendsService.Delete(agentUUI, exfriendID.ToString());
|
FriendsService.Delete(agentUUI, exfriendID.ToString());
|
||||||
|
|
||||||
// notify the agent's service?
|
// 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);
|
m_log.DebugFormat("[HGFRIENDS MODULE]: {0} terminated {1}", agentUUI, exfriendID);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -35,7 +35,6 @@ using OpenSim.Framework;
|
||||||
using OpenSim.Region.CoreModules.Avatar.Friends;
|
using OpenSim.Region.CoreModules.Avatar.Friends;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.Avatar.Friends.Tests
|
namespace OpenSim.Region.CoreModules.Avatar.Friends.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -213,7 +213,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
|
||||||
HandleUndeliverableMessage(im, result);
|
HandleUndeliverableMessage(im, result);
|
||||||
else
|
else
|
||||||
result(success);
|
result(success);
|
||||||
});
|
}, null, "HGMessageTransferModule.SendInstantMessage");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,6 @@ using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Region.Framework.Scenes.Serialization;
|
using OpenSim.Region.Framework.Scenes.Serialization;
|
||||||
using OpenSim.Services.Interfaces;
|
using OpenSim.Services.Interfaces;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
|
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.Region.Framework.Scenes.Serialization;
|
||||||
using OpenSim.Services.Interfaces;
|
using OpenSim.Services.Interfaces;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
|
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.Region.Framework.Scenes.Serialization;
|
||||||
using OpenSim.Services.Interfaces;
|
using OpenSim.Services.Interfaces;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
|
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.Region.Framework.Scenes.Serialization;
|
||||||
using OpenSim.Services.Interfaces;
|
using OpenSim.Services.Interfaces;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
|
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -39,7 +39,6 @@ using OpenSim.Region.Framework.Interfaces;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Services.Interfaces;
|
using OpenSim.Services.Interfaces;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer.Tests
|
namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -181,7 +181,7 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
||||||
Util.FireAndForget(delegate
|
Util.FireAndForget(delegate
|
||||||
{
|
{
|
||||||
GetImageAssets(((IScenePresence)obj).UUID);
|
GetImageAssets(((IScenePresence)obj).UUID);
|
||||||
});
|
}, null, "UserProfileModule.GetImageAssets");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -871,8 +871,8 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
||||||
object Pref = (object)pref;
|
object Pref = (object)pref;
|
||||||
if(!rpc.JsonRpcRequest(ref Pref, "user_preferences_request", serverURI, UUID.Random().ToString()))
|
if(!rpc.JsonRpcRequest(ref Pref, "user_preferences_request", serverURI, UUID.Random().ToString()))
|
||||||
{
|
{
|
||||||
m_log.InfoFormat("[PROFILES]: UserPreferences request error");
|
// m_log.InfoFormat("[PROFILES]: UserPreferences request error");
|
||||||
remoteClient.SendAgentAlertMessage("Error requesting preferences", false);
|
// remoteClient.SendAgentAlertMessage("Error requesting preferences", false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pref = (UserPreferences) Pref;
|
pref = (UserPreferences) Pref;
|
||||||
|
|
|
@ -804,8 +804,6 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
// once we reach here...
|
// once we reach here...
|
||||||
//avatar.Scene.RemoveCapsHandler(avatar.UUID);
|
//avatar.Scene.RemoveCapsHandler(avatar.UUID);
|
||||||
|
|
||||||
string capsPath = String.Empty;
|
|
||||||
|
|
||||||
AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode);
|
AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode);
|
||||||
AgentCircuitData agentCircuit = sp.ControllingClient.RequestClientInfo();
|
AgentCircuitData agentCircuit = sp.ControllingClient.RequestClientInfo();
|
||||||
agentCircuit.startpos = position;
|
agentCircuit.startpos = position;
|
||||||
|
@ -2702,5 +2700,69 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
}
|
}
|
||||||
#endregion
|
#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
|
#region ISharedRegionModule
|
||||||
|
|
||||||
public override string Name
|
public override string Name
|
||||||
|
@ -153,33 +158,10 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
if (m_Enabled)
|
if (m_Enabled)
|
||||||
{
|
{
|
||||||
scene.RegisterModuleInterface<IUserAgentVerificationModule>(this);
|
scene.RegisterModuleInterface<IUserAgentVerificationModule>(this);
|
||||||
scene.EventManager.OnIncomingSceneObject += OnIncomingSceneObject;
|
//scene.EventManager.OnIncomingSceneObject += OnIncomingSceneObject;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnIncomingSceneObject(SceneObjectGroup so)
|
m_incomingSceneObjectEngine = new HGIncomingSceneObjectEngine(scene.Name);
|
||||||
{
|
m_incomingSceneObjectEngine.Start();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,12 +191,15 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
||||||
base.RemoveRegion(scene);
|
base.RemoveRegion(scene);
|
||||||
|
|
||||||
if (m_Enabled)
|
if (m_Enabled)
|
||||||
|
{
|
||||||
scene.UnregisterModuleInterface<IUserAgentVerificationModule>(this);
|
scene.UnregisterModuleInterface<IUserAgentVerificationModule>(this);
|
||||||
|
m_incomingSceneObjectEngine.Stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region HG overrides of IEntiryTransferModule
|
#region HG overrides of IEntityTransferModule
|
||||||
|
|
||||||
protected override GridRegion GetFinalDestination(GridRegion region, UUID agentID, string agentHomeURI, out string message)
|
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
|
#endregion
|
||||||
|
|
||||||
#region IUserAgentVerificationModule
|
#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));
|
return Utils.StringToBytes(RewriteSOP(xml));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected string RewriteSOP(string xml)
|
protected void TransformXml(XmlReader reader, XmlWriter writer)
|
||||||
{
|
{
|
||||||
XmlDocument doc = new XmlDocument();
|
// m_log.DebugFormat("[HG ASSET MAPPER]: Transforming XML");
|
||||||
doc.LoadXml(xml);
|
|
||||||
XmlNodeList sops = doc.GetElementsByTagName("SceneObjectPart");
|
|
||||||
|
|
||||||
foreach (XmlNode sop in sops)
|
int sopDepth = -1;
|
||||||
|
UserAccount creator = null;
|
||||||
|
bool hasCreatorData = false;
|
||||||
|
|
||||||
|
while (reader.Read())
|
||||||
{
|
{
|
||||||
UserAccount creator = null;
|
//Console.WriteLine("Depth: {0}", reader.Depth);
|
||||||
bool hasCreatorData = false;
|
|
||||||
XmlNodeList nodes = sop.ChildNodes;
|
switch (reader.NodeType)
|
||||||
foreach (XmlNode node in nodes)
|
|
||||||
{
|
{
|
||||||
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;
|
if (sopDepth < 0)
|
||||||
UUID.TryParse(node.InnerText, out uuid);
|
{
|
||||||
creator = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, uuid);
|
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)
|
else
|
||||||
hasCreatorData = true;
|
{
|
||||||
|
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")
|
if (reader.NodeType == XmlNodeType.Text)
|
||||||
//{
|
{
|
||||||
// UserAccount owner = GetUser(node.InnerText);
|
UUID uuid = UUID.Zero;
|
||||||
// if (owner != null)
|
UUID.TryParse(reader.Value, out uuid);
|
||||||
// node.InnerText = m_ProfileServiceURL + "/" + node.InnerText + "/" + owner.FirstName + " " + owner.LastName;
|
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)
|
break;
|
||||||
{
|
|
||||||
XmlElement creatorData = doc.CreateElement("CreatorData");
|
case XmlNodeType.EndElement:
|
||||||
creatorData.InnerText = m_HomeURI + ";" + creator.FirstName + " " + creator.LastName;
|
// m_log.DebugFormat("Depth {0} at EndElement", reader.Depth);
|
||||||
sop.AppendChild(creatorData);
|
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);
|
TransformXml(reader, writer);
|
||||||
return wr.ToString();
|
|
||||||
|
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
|
// TODO: unused
|
||||||
|
|
|
@ -912,7 +912,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
|
||||||
// one full update during the attachment
|
// one full update during the attachment
|
||||||
// process causes some clients to fail to display the
|
// process causes some clients to fail to display the
|
||||||
// attachment properly.
|
// 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
|
// if attachment we set it's asset id so object updates
|
||||||
// can reflect that, if not, we set it's position in world.
|
// 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.Region.Framework.Scenes.Serialization;
|
||||||
using OpenSim.Services.Interfaces;
|
using OpenSim.Services.Interfaces;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests
|
namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,7 +32,6 @@ using OpenMetaverse;
|
||||||
using OpenSim.Framework;
|
using OpenSim.Framework;
|
||||||
using OpenSim.Region.CoreModules.Framework.UserManagement;
|
using OpenSim.Region.CoreModules.Framework.UserManagement;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.Framework.UserManagement.Tests
|
namespace OpenSim.Region.CoreModules.Framework.UserManagement.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,7 +41,6 @@ using OpenSim.Framework;
|
||||||
using OpenSim.Region.CoreModules.Scripting.HttpRequest;
|
using OpenSim.Region.CoreModules.Scripting.HttpRequest;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.Scripting.HttpRequest.Tests
|
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;
|
||||||
using OpenSim.Region.Framework.Scenes.Serialization;
|
using OpenSim.Region.Framework.Scenes.Serialization;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
|
namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -69,6 +69,13 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
|
||||||
get { return "HGAssetBroker"; }
|
get { return "HGAssetBroker"; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HGAssetBroker() {}
|
||||||
|
|
||||||
|
public HGAssetBroker(IConfigSource config)
|
||||||
|
{
|
||||||
|
Initialise(config);
|
||||||
|
}
|
||||||
|
|
||||||
public void Initialise(IConfigSource source)
|
public void Initialise(IConfigSource source)
|
||||||
{
|
{
|
||||||
IConfig moduleConfig = source.Configs["Modules"];
|
IConfig moduleConfig = source.Configs["Modules"];
|
||||||
|
@ -288,7 +295,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
|
||||||
|
|
||||||
if (asset != null)
|
if (asset != null)
|
||||||
{
|
{
|
||||||
Util.FireAndForget(delegate { handler(id, sender, asset); });
|
Util.FireAndForget(delegate { handler(id, sender, asset); }, null, "HGAssetBroker.GotFromCache");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -236,7 +236,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
|
||||||
|
|
||||||
if (asset != null)
|
if (asset != null)
|
||||||
{
|
{
|
||||||
Util.FireAndForget(delegate { handler(id, sender, asset); });
|
Util.FireAndForget(
|
||||||
|
o => handler(id, sender, asset), null, "LocalAssetServiceConnector.GotFromCacheCallback");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -249,7 +250,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
|
||||||
// if (null == a)
|
// if (null == a)
|
||||||
// m_log.WarnFormat("[LOCAL ASSET SERVICES CONNECTOR]: Could not asynchronously find asset with id {0}", id);
|
// 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)
|
public GridRegion GetRegionByPosition(UUID scopeID, int x, int y)
|
||||||
{
|
{
|
||||||
GridRegion region = null;
|
GridRegion region = null;
|
||||||
uint regionX = Util.WorldToRegionLoc((uint)x);
|
// uint regionX = Util.WorldToRegionLoc((uint)x);
|
||||||
uint regionY = Util.WorldToRegionLoc((uint)y);
|
// uint regionY = Util.WorldToRegionLoc((uint)y);
|
||||||
|
|
||||||
// First see if it's a neighbour, even if it isn't on this sim.
|
// 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
|
// Neighbour data is cached in memory, so this is fast
|
||||||
|
|
|
@ -53,7 +53,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
|
||||||
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MapImageServiceModule")]
|
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MapImageServiceModule")]
|
||||||
public class MapImageServiceModule : ISharedRegionModule
|
public class MapImageServiceModule : IMapImageUploadModule, ISharedRegionModule
|
||||||
{
|
{
|
||||||
private static readonly ILog m_log =
|
private static readonly ILog m_log =
|
||||||
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
@ -152,6 +152,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage
|
||||||
m_scenes[scene.RegionInfo.RegionID] = scene;
|
m_scenes[scene.RegionInfo.RegionID] = scene;
|
||||||
|
|
||||||
scene.EventManager.OnRegionReadyStatusChange += s => { if (s.Ready) UploadMapTile(s); };
|
scene.EventManager.OnRegionReadyStatusChange += s => { if (s.Ready) UploadMapTile(s); };
|
||||||
|
|
||||||
|
scene.RegisterModuleInterface<IMapImageUploadModule>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
|
@ -198,14 +200,53 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage
|
||||||
m_lastrefresh = Util.EnvironmentTickCount();
|
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>
|
||||||
///
|
///
|
||||||
///</summary>
|
///</summary>
|
||||||
private void UploadMapTile(IScene scene)
|
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
|
// Create a JPG map tile and upload it to the AddMapTile API
|
||||||
IMapImageGenerator tileGenerator = scene.RequestModuleInterface<IMapImageGenerator>();
|
IMapImageGenerator tileGenerator = scene.RequestModuleInterface<IMapImageGenerator>();
|
||||||
if (tileGenerator == null)
|
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);
|
m_log.WarnFormat("{0} Cannot upload map tile without an ImageGenerator", LogHeader);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (Bitmap mapTile = tileGenerator.CreateMapTile())
|
using (Bitmap mapTile = tileGenerator.CreateMapTile())
|
||||||
{
|
{
|
||||||
if (mapTile != null)
|
if (mapTile != null)
|
||||||
{
|
{
|
||||||
// mapTile.Save( // DEBUG DEBUG
|
UploadMapTile(scene, mapTile);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -45,7 +45,6 @@ using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Region.Framework.Scenes.Serialization;
|
using OpenSim.Region.Framework.Scenes.Serialization;
|
||||||
using OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups;
|
using OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
using ArchiveConstants = OpenSim.Framework.Serialization.ArchiveConstants;
|
using ArchiveConstants = OpenSim.Framework.Serialization.ArchiveConstants;
|
||||||
using TarArchiveReader = OpenSim.Framework.Serialization.TarArchiveReader;
|
using TarArchiveReader = OpenSim.Framework.Serialization.TarArchiveReader;
|
||||||
using TarArchiveWriter = OpenSim.Framework.Serialization.TarArchiveWriter;
|
using TarArchiveWriter = OpenSim.Framework.Serialization.TarArchiveWriter;
|
||||||
|
|
|
@ -31,7 +31,6 @@ using OpenMetaverse;
|
||||||
using OpenSim.Framework;
|
using OpenSim.Framework;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.World.Land.Tests
|
namespace OpenSim.Region.CoreModules.World.Land.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,7 +36,6 @@ using OpenSim.Framework;
|
||||||
using OpenSim.Region.Framework.Interfaces;
|
using OpenSim.Region.Framework.Interfaces;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.World.Land.Tests
|
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;
|
||||||
using OpenSim.Region.Framework.Scenes.Serialization;
|
using OpenSim.Region.Framework.Scenes.Serialization;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.CoreModules.World.Media.Moap.Tests
|
namespace OpenSim.Region.CoreModules.World.Media.Moap.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -141,6 +141,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.BuySell
|
||||||
|
|
||||||
part.ObjectSaleType = 0;
|
part.ObjectSaleType = 0;
|
||||||
part.SalePrice = 10;
|
part.SalePrice = 10;
|
||||||
|
part.ClickAction = Convert.ToByte(0);
|
||||||
|
|
||||||
group.HasGroupChanged = true;
|
group.HasGroupChanged = true;
|
||||||
part.SendPropertiesToClient(remoteClient);
|
part.SendPropertiesToClient(remoteClient);
|
||||||
|
|
|
@ -87,7 +87,26 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
||||||
"Regions", false, "show region",
|
"Regions", false, "show region",
|
||||||
"show region",
|
"show region",
|
||||||
"Show control information for the currently selected region (host name, max physical prim size, etc).",
|
"Show control information for the currently selected region (host name, max physical prim size, etc).",
|
||||||
|
"A synonym for \"region get\"",
|
||||||
HandleShowRegion);
|
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)
|
public void RemoveRegion(Scene scene)
|
||||||
|
@ -123,8 +142,8 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
||||||
dispList.AddRow("External endpoint", ri.ExternalEndPoint);
|
dispList.AddRow("External endpoint", ri.ExternalEndPoint);
|
||||||
dispList.AddRow("Internal endpoint", ri.InternalEndPoint);
|
dispList.AddRow("Internal endpoint", ri.InternalEndPoint);
|
||||||
dispList.AddRow("Access level", ri.AccessLevel);
|
dispList.AddRow("Access level", ri.AccessLevel);
|
||||||
|
dispList.AddRow("Agent limit", rs.AgentLimit);
|
||||||
dispList.AddRow("Max agent limit", ri.AgentCapacity);
|
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("Linkset capacity", ri.LinksetCapacity <= 0 ? "not set" : ri.LinksetCapacity.ToString());
|
||||||
dispList.AddRow("Prim capacity", ri.ObjectCapacity);
|
dispList.AddRow("Prim capacity", ri.ObjectCapacity);
|
||||||
dispList.AddRow("Prim bonus", rs.ObjectBonus);
|
dispList.AddRow("Prim bonus", rs.ObjectBonus);
|
||||||
|
@ -166,6 +185,73 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
|
||||||
MainConsole.Instance.Output(sb.ToString());
|
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)
|
private void HandleShowScene(string module, string[] cmd)
|
||||||
{
|
{
|
||||||
if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_scene))
|
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 UUID STOP_UUID = UUID.Random();
|
||||||
private static readonly string m_mapLayerPath = "0001/";
|
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>();
|
private OpenSim.Framework.BlockingQueue<MapRequestState> requests = new OpenSim.Framework.BlockingQueue<MapRequestState>();
|
||||||
|
|
||||||
protected Scene m_scene;
|
protected Scene m_scene;
|
||||||
|
@ -100,7 +103,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
|
||||||
= Util.GetConfigVarFromSections<int>(config, "BlacklistTimeout", configSections, 10 * 60) * 1000;
|
= Util.GetConfigVarFromSections<int>(config, "BlacklistTimeout", configSections, 10 * 60) * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void AddRegion (Scene scene)
|
public virtual void AddRegion(Scene scene)
|
||||||
{
|
{
|
||||||
if (!m_Enabled)
|
if (!m_Enabled)
|
||||||
return;
|
return;
|
||||||
|
@ -144,8 +147,10 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_ServiceThrottle = scene.RequestModuleInterface<IServiceThrottleModule>();
|
m_ServiceThrottle = scene.RequestModuleInterface<IServiceThrottleModule>();
|
||||||
}
|
|
||||||
|
|
||||||
|
m_mapImageGenerator = m_scene.RequestModuleInterface<IMapImageGenerator>();
|
||||||
|
m_mapImageServiceModule = m_scene.RequestModuleInterface<IMapImageUploadModule>();
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void Close()
|
public virtual void Close()
|
||||||
{
|
{
|
||||||
|
@ -1315,7 +1320,17 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
|
||||||
if (consoleScene != null && consoleScene != m_scene)
|
if (consoleScene != null && consoleScene != m_scene)
|
||||||
return;
|
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)
|
public OSD HandleRemoteMapItemRequest(string path, OSD request, string endpoint)
|
||||||
|
@ -1444,16 +1459,25 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
|
||||||
if (m_scene.Heightmap == null)
|
if (m_scene.Heightmap == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//create a texture asset of the terrain
|
m_log.DebugFormat("[WORLD MAP]: Generating map image for {0}", m_scene.Name);
|
||||||
IMapImageGenerator terrain = m_scene.RequestModuleInterface<IMapImageGenerator>();
|
|
||||||
if (terrain == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
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();
|
private void GenerateMaptile(Bitmap mapbmp)
|
||||||
if (data == null)
|
{
|
||||||
|
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;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
byte[] overlay = GenerateOverlay();
|
byte[] overlay = GenerateOverlay();
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,8 @@ namespace OpenSim.Region.Framework.Interfaces
|
||||||
void Cross(SceneObjectGroup sog, Vector3 position, bool silent);
|
void Cross(SceneObjectGroup sog, Vector3 position, bool silent);
|
||||||
|
|
||||||
ScenePresence CrossAgentToNewRegionAsync(ScenePresence agent, Vector3 pos, GridRegion neighbourRegion, bool isFlying, string version);
|
ScenePresence CrossAgentToNewRegionAsync(ScenePresence agent, Vector3 pos, GridRegion neighbourRegion, bool isFlying, string version);
|
||||||
|
|
||||||
|
bool HandleIncomingSceneObject(SceneObjectGroup so, Vector3 newPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IUserAgentVerificationModule
|
public interface IUserAgentVerificationModule
|
||||||
|
|
|
@ -25,55 +25,13 @@
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System.Net;
|
using System.Drawing;
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Framework;
|
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>
|
public interface IMapImageUploadModule
|
||||||
/// Mock scene for unit tests
|
|
||||||
/// </summary>
|
|
||||||
public class MockScene : SceneBase
|
|
||||||
{
|
{
|
||||||
public int ObjectNameCallsReceived
|
void UploadMapTile(IScene scene, Bitmap mapTile);
|
||||||
{
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1227,16 +1227,21 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
agentItem.BasePermissions = taskItem.BasePermissions & (taskItem.NextPermissions | (uint)PermissionMask.Move);
|
agentItem.BasePermissions = taskItem.BasePermissions & (taskItem.NextPermissions | (uint)PermissionMask.Move);
|
||||||
if (taskItem.InvType == (int)InventoryType.Object)
|
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);
|
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.BasePermissions = perms | (uint)PermissionMask.Move;
|
||||||
agentItem.CurrentPermissions = agentItem.BasePermissions;
|
// Newly given items cannot be "locked" on rez. Make sure by
|
||||||
}
|
// setting current equal to base.
|
||||||
else
|
|
||||||
{
|
|
||||||
agentItem.CurrentPermissions = agentItem.BasePermissions & taskItem.CurrentPermissions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
agentItem.CurrentPermissions = agentItem.BasePermissions;
|
||||||
|
|
||||||
agentItem.Flags |= (uint)InventoryItemFlags.ObjectSlamPerm;
|
agentItem.Flags |= (uint)InventoryItemFlags.ObjectSlamPerm;
|
||||||
agentItem.NextPermissions = taskItem.NextPermissions;
|
agentItem.NextPermissions = taskItem.NextPermissions;
|
||||||
agentItem.EveryOnePermissions = taskItem.EveryonePermissions & (taskItem.NextPermissions | (uint)PermissionMask.Move);
|
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
|
/// Rez a script into a prim's inventory from another prim
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="remoteClient"></param>
|
/// <param name="remoteClient"></param>
|
||||||
/// <param name="itemID"> </param>
|
/// <param name="srcPart"> </param>
|
||||||
/// <param name="localID"></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)
|
public void RezScriptFromPrim(UUID srcId, SceneObjectPart srcPart, UUID destId, int pin, int running, int start_param)
|
||||||
{
|
{
|
||||||
TaskInventoryItem srcTaskItem = srcPart.Inventory.GetInventoryItem(srcId);
|
TaskInventoryItem srcTaskItem = srcPart.Inventory.GetInventoryItem(srcId);
|
||||||
|
@ -1956,12 +1964,11 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
if (destPart == null)
|
if (destPart == null)
|
||||||
{
|
{
|
||||||
m_log.ErrorFormat(
|
m_log.ErrorFormat(
|
||||||
"[PRIM INVENTORY]: " +
|
"[PRIM INVENTORY]: Could not find part {0} to insert script item {1} from {2} {3} in {4}",
|
||||||
"Could not find script for ID {0}",
|
destId, srcId, srcPart.Name, srcPart.UUID, Name);
|
||||||
destId);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must own the object, and have modify rights
|
// Must own the object, and have modify rights
|
||||||
if (srcPart.OwnerID != destPart.OwnerID)
|
if (srcPart.OwnerID != destPart.OwnerID)
|
||||||
{
|
{
|
||||||
|
@ -1969,12 +1976,14 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
if ((destPart.GroupID == UUID.Zero) || (destPart.GroupID != srcPart.GroupID) ||
|
if ((destPart.GroupID == UUID.Zero) || (destPart.GroupID != srcPart.GroupID) ||
|
||||||
((destPart.GroupMask & (uint)PermissionMask.Modify) == 0))
|
((destPart.GroupMask & (uint)PermissionMask.Modify) == 0))
|
||||||
return;
|
return;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
if ((destPart.OwnerMask & (uint)PermissionMask.Modify) == 0)
|
if ((destPart.OwnerMask & (uint)PermissionMask.Modify) == 0)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (destPart.ScriptAccessPin != pin)
|
if (destPart.ScriptAccessPin == 0 || destPart.ScriptAccessPin != pin)
|
||||||
{
|
{
|
||||||
m_log.WarnFormat(
|
m_log.WarnFormat(
|
||||||
"[PRIM INVENTORY]: " +
|
"[PRIM INVENTORY]: " +
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
* * Redistributions of source code must retain the above copyright
|
* * Redistributions of source code must retain the above copyright
|
||||||
* notice, this list of conditions and the following disclaimer.
|
* 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
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
* documentation and/or other materials provided with the distribution.
|
* documentation and/or other materials provided with the distribution.
|
||||||
* * Neither the name of the OpenSimulator Project nor the
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
@ -360,30 +360,52 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
public uint MaintenanceRun { get; private set; }
|
public uint MaintenanceRun { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <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.
|
/// will sleep for the remaining period.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// One can tweak this number to experiment. One current effect of reducing it is to make avatar animations
|
/// 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).
|
/// occur too quickly (viewer 1) or with even more slide (viewer 2).
|
||||||
/// </remarks>
|
/// </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>
|
/// <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>
|
/// </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_physics = 1;
|
||||||
private int m_update_entitymovement = 1;
|
private int m_update_entitymovement = 1;
|
||||||
private int m_update_objects = 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_presences = 1; // Update scene presence movements
|
||||||
private int m_update_events = 1;
|
private int m_update_events = 1;
|
||||||
private int m_update_backup = 200;
|
private int m_update_backup = 200;
|
||||||
private int m_update_terrain = 50;
|
private int m_update_terrain = 50;
|
||||||
// private int m_update_land = 1;
|
// private int m_update_land = 1;
|
||||||
private int m_update_coarse_locations = 50;
|
private int m_update_coarse_locations = 50;
|
||||||
|
private int m_update_temp_cleaning = 180;
|
||||||
|
|
||||||
private int agentMS;
|
private int agentMS;
|
||||||
private int frameMS;
|
private int frameMS;
|
||||||
|
@ -412,8 +434,16 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
/// asynchronously from the update loop.
|
/// asynchronously from the update loop.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool m_cleaningTemps = false;
|
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.
|
// TODO: Possibly stop other classes being able to manipulate this directly.
|
||||||
private SceneGraph m_sceneGraph;
|
private SceneGraph m_sceneGraph;
|
||||||
|
@ -782,8 +812,8 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
: this(regInfo, physicsScene)
|
: this(regInfo, physicsScene)
|
||||||
{
|
{
|
||||||
m_config = config;
|
m_config = config;
|
||||||
MinFrameTime = 0.089f;
|
MinFrameTicks = 89;
|
||||||
MinMaintenanceTime = 1;
|
MinMaintenanceTicks = 1000;
|
||||||
SeeIntoRegion = true;
|
SeeIntoRegion = true;
|
||||||
|
|
||||||
Random random = new Random();
|
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_backup = startupConfig.GetInt( "UpdateStorageEveryNFrames", m_update_backup);
|
||||||
m_update_coarse_locations = startupConfig.GetInt( "UpdateCoarseLocationsEveryNFrames", m_update_coarse_locations);
|
m_update_coarse_locations = startupConfig.GetInt( "UpdateCoarseLocationsEveryNFrames", m_update_coarse_locations);
|
||||||
m_update_entitymovement = startupConfig.GetInt( "UpdateEntityMovementEveryNFrames", m_update_entitymovement);
|
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_physics = startupConfig.GetInt( "UpdatePhysicsEveryNFrames", m_update_physics);
|
||||||
m_update_presences = startupConfig.GetInt( "UpdateAgentsEveryNFrames", m_update_presences);
|
m_update_presences = startupConfig.GetInt( "UpdateAgentsEveryNFrames", m_update_presences);
|
||||||
m_update_terrain = startupConfig.GetInt( "UpdateTerrainEveryNFrames", m_update_terrain);
|
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.
|
// FIXME: Ultimately this should be in a module.
|
||||||
SendPeriodicAppearanceUpdates = true;
|
SendPeriodicAppearanceUpdates = false;
|
||||||
|
|
||||||
IConfig appearanceConfig = m_config.Configs["Appearance"];
|
IConfig appearanceConfig = m_config.Configs["Appearance"];
|
||||||
if (appearanceConfig != null)
|
if (appearanceConfig != null)
|
||||||
|
@ -1444,13 +1476,14 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
if (UpdateOnTimer)
|
if (UpdateOnTimer)
|
||||||
{
|
{
|
||||||
m_sceneUpdateTimer = new Timer(MinFrameTime * 1000);
|
m_sceneUpdateTimer = new Timer(MinFrameTicks);
|
||||||
m_sceneUpdateTimer.AutoReset = true;
|
m_sceneUpdateTimer.AutoReset = true;
|
||||||
m_sceneUpdateTimer.Elapsed += Update;
|
m_sceneUpdateTimer.Elapsed += Update;
|
||||||
m_sceneUpdateTimer.Start();
|
m_sceneUpdateTimer.Start();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Thread.CurrentThread.Priority = ThreadPriority.Highest;
|
||||||
Update(-1);
|
Update(-1);
|
||||||
Watchdog.RemoveThread();
|
Watchdog.RemoveThread();
|
||||||
m_isRunning = false;
|
m_isRunning = false;
|
||||||
|
@ -1490,7 +1523,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
public void DoMaintenance(int runs)
|
public void DoMaintenance(int runs)
|
||||||
{
|
{
|
||||||
long? endRun = null;
|
long? endRun = null;
|
||||||
int runtc;
|
int runtc, tmpMS;
|
||||||
int previousMaintenanceTick;
|
int previousMaintenanceTick;
|
||||||
|
|
||||||
if (runs >= 0)
|
if (runs >= 0)
|
||||||
|
@ -1504,6 +1537,8 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
runtc = Util.EnvironmentTickCount();
|
runtc = Util.EnvironmentTickCount();
|
||||||
++MaintenanceRun;
|
++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)
|
// Coarse locations relate to positions of green dots on the mini-map (on a SecondLife client)
|
||||||
if (MaintenanceRun % (m_update_coarse_locations / 10) == 0)
|
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();
|
Watchdog.UpdateThread();
|
||||||
|
|
||||||
previousMaintenanceTick = m_lastMaintenanceTick;
|
previousMaintenanceTick = m_lastMaintenanceTick;
|
||||||
m_lastMaintenanceTick = Util.EnvironmentTickCount();
|
m_lastMaintenanceTick = Util.EnvironmentTickCount();
|
||||||
runtc = Util.EnvironmentTickCountSubtract(m_lastMaintenanceTick, runtc);
|
runtc = Util.EnvironmentTickCountSubtract(m_lastMaintenanceTick, runtc);
|
||||||
runtc = (int)(MinMaintenanceTime * 1000) - runtc;
|
runtc = MinMaintenanceTicks - runtc;
|
||||||
|
|
||||||
if (runtc > 0)
|
if (runtc > 0)
|
||||||
Thread.Sleep(runtc);
|
m_maintenanceWaitEvent.WaitOne(runtc);
|
||||||
|
|
||||||
// Optionally warn if a frame takes double the amount of time that it should.
|
// Optionally warn if a frame takes double the amount of time that it should.
|
||||||
if (DebugUpdates
|
if (DebugUpdates
|
||||||
&& Util.EnvironmentTickCountSubtract(
|
&& Util.EnvironmentTickCountSubtract(
|
||||||
m_lastMaintenanceTick, previousMaintenanceTick) > (int)(MinMaintenanceTime * 1000 * 2))
|
m_lastMaintenanceTick, previousMaintenanceTick) > MinMaintenanceTicks * 2)
|
||||||
m_log.WarnFormat(
|
m_log.WarnFormat(
|
||||||
"[SCENE]: Maintenance took {0} ms (desired max {1} ms) in {2}",
|
"[SCENE]: Maintenance took {0} ms (desired max {1} ms) in {2}",
|
||||||
Util.EnvironmentTickCountSubtract(m_lastMaintenanceTick, previousMaintenanceTick),
|
Util.EnvironmentTickCountSubtract(m_lastMaintenanceTick, previousMaintenanceTick),
|
||||||
MinMaintenanceTime * 1000,
|
MinMaintenanceTicks,
|
||||||
RegionInfo.RegionName);
|
RegionInfo.RegionName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1563,7 +1613,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
// m_log.DebugFormat("[SCENE]: Processing frame {0} in {1}", Frame, RegionInfo.RegionName);
|
// 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
|
try
|
||||||
{
|
{
|
||||||
|
@ -1594,7 +1644,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
if (Frame % m_update_physics == 0)
|
if (Frame % m_update_physics == 0)
|
||||||
{
|
{
|
||||||
if (PhysicsEnabled)
|
if (PhysicsEnabled)
|
||||||
physicsFPS = m_sceneGraph.UpdatePhysics(MinFrameTime);
|
physicsFPS = m_sceneGraph.UpdatePhysics(MinFrameSeconds);
|
||||||
|
|
||||||
if (SynchronizeScene != null)
|
if (SynchronizeScene != null)
|
||||||
SynchronizeScene(this);
|
SynchronizeScene(this);
|
||||||
|
@ -1616,21 +1666,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
if (Frame % m_update_presences == 0)
|
if (Frame % m_update_presences == 0)
|
||||||
m_sceneGraph.UpdatePresences();
|
m_sceneGraph.UpdatePresences();
|
||||||
|
|
||||||
agentMS += Util.EnvironmentTickCountSubtract(tmpMS);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Frame % m_update_events == 0)
|
if (Frame % m_update_events == 0)
|
||||||
{
|
{
|
||||||
|
@ -1700,24 +1736,22 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
}
|
}
|
||||||
|
|
||||||
EventManager.TriggerRegionHeartbeatEnd(this);
|
EventManager.TriggerRegionHeartbeatEnd(this);
|
||||||
otherMS = tempOnRezMS + eventMS + backupMS + terrainMS + landMS;
|
otherMS = eventMS + backupMS + terrainMS + landMS;
|
||||||
|
|
||||||
if (!UpdateOnTimer)
|
if (!UpdateOnTimer)
|
||||||
{
|
{
|
||||||
Watchdog.UpdateThread();
|
Watchdog.UpdateThread();
|
||||||
|
|
||||||
tmpMS = Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), m_lastFrameTick);
|
spareMS = MinFrameTicks - Util.EnvironmentTickCountSubtract(m_lastFrameTick);
|
||||||
tmpMS = (int)(MinFrameTime * 1000) - tmpMS;
|
|
||||||
|
|
||||||
if (tmpMS > 0)
|
if (spareMS > 0)
|
||||||
{
|
m_updateWaitEvent.WaitOne(spareMS);
|
||||||
spareMS = tmpMS;
|
else
|
||||||
Thread.Sleep(tmpMS);
|
spareMS = 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
spareMS = Math.Max(0, (int)(MinFrameTime * 1000) - physicsMS2 - agentMS - physicsMS -otherMS);
|
spareMS = Math.Max(0, MinFrameTicks - physicsMS2 - agentMS - physicsMS - otherMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
previousFrameTick = m_lastFrameTick;
|
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.
|
// Optionally warn if a frame takes double the amount of time that it should.
|
||||||
if (DebugUpdates
|
if (DebugUpdates
|
||||||
&& Util.EnvironmentTickCountSubtract(
|
&& Util.EnvironmentTickCountSubtract(
|
||||||
m_lastFrameTick, previousFrameTick) > (int)(MinFrameTime * 1000 * 2))
|
m_lastFrameTick, previousFrameTick) > MinFrameTicks * 2)
|
||||||
m_log.WarnFormat(
|
m_log.WarnFormat(
|
||||||
"[SCENE]: Frame took {0} ms (desired max {1} ms) in {2}",
|
"[SCENE]: Frame took {0} ms (desired max {1} ms) in {2}",
|
||||||
Util.EnvironmentTickCountSubtract(m_lastFrameTick, previousFrameTick),
|
Util.EnvironmentTickCountSubtract(m_lastFrameTick, previousFrameTick),
|
||||||
MinFrameTime * 1000,
|
MinFrameTicks,
|
||||||
RegionInfo.RegionName);
|
RegionInfo.RegionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2588,48 +2622,8 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user is banned, we won't let any of their objects
|
if (!EntityTransferModule.HandleIncomingSceneObject(newObject, newPosition))
|
||||||
// enter. Period.
|
return false;
|
||||||
//
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do this as late as possible so that listeners have full access to the incoming object
|
// Do this as late as possible so that listeners have full access to the incoming object
|
||||||
EventManager.TriggerOnIncomingSceneObject(newObject);
|
EventManager.TriggerOnIncomingSceneObject(newObject);
|
||||||
|
@ -2698,16 +2692,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetStateSource(SceneObjectGroup sog)
|
|
||||||
{
|
|
||||||
ScenePresence sp = GetScenePresence(sog.OwnerID);
|
|
||||||
|
|
||||||
if (sp != null)
|
|
||||||
return sp.GetStateSource();
|
|
||||||
|
|
||||||
return 2; // StateSource.PrimCrossing
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Add/Remove Avatar Methods
|
#region Add/Remove Avatar Methods
|
||||||
|
@ -2754,29 +2738,41 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
m_log.DebugFormat(
|
m_log.DebugFormat(
|
||||||
"[SCENE]: Adding new child scene presence {0} {1} to scene {2} at pos {3}",
|
"[SCENE]: Adding new child scene presence {0} {1} to scene {2} at pos {3}",
|
||||||
client.Name, client.AgentId, RegionInfo.RegionName, client.StartPos);
|
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);
|
m_clientManager.Add(client);
|
||||||
SubscribeToClientEvents(client);
|
SubscribeToClientEvents(client);
|
||||||
|
|
||||||
sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type);
|
|
||||||
m_eventManager.TriggerOnNewPresence(sp);
|
m_eventManager.TriggerOnNewPresence(sp);
|
||||||
|
|
||||||
sp.TeleportFlags = (TPFlags)aCircuit.teleportFlags;
|
sp.TeleportFlags = (TPFlags)aCircuit.teleportFlags;
|
||||||
}
|
}
|
||||||
else
|
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(
|
m_log.WarnFormat(
|
||||||
"[SCENE]: Already found {0} scene presence for {1} in {2} when asked to add new scene presence",
|
"[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);
|
sp.IsChildAgent ? "child" : "root", sp.Name, RegionInfo.RegionName);
|
||||||
|
|
||||||
reallyNew = false;
|
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
|
// 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
|
// 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;
|
sp.LifecycleState = ScenePresenceState.Removing;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sp != null)
|
sp.ControllingClient.Close(force);
|
||||||
{
|
|
||||||
sp.ControllingClient.Close(force);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Agent not here
|
return true;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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.
|
// 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.
|
// This leads to race conditions if directly passed to SendCloseChildAgent with more than one neighbour region.
|
||||||
ulong handleCopy = handle;
|
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)
|
if (CanBeBackedUp)
|
||||||
{
|
{
|
||||||
//m_log.DebugFormat(
|
// m_log.DebugFormat(
|
||||||
// "[SCENE OBJECT GROUP]: Attaching object {0} {1} to scene presistence sweep", Name, UUID);
|
// "[SCENE OBJECT GROUP]: Attaching object {0} {1} to scene presistence sweep", Name, UUID);
|
||||||
|
|
||||||
if (!Backup)
|
if (!Backup)
|
||||||
m_scene.EventManager.OnBackup += ProcessBackup;
|
m_scene.EventManager.OnBackup += ProcessBackup;
|
||||||
|
|
|
@ -1589,20 +1589,29 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
public void AddTextureAnimation(Primitive.TextureAnimation pTexAnim)
|
public void AddTextureAnimation(Primitive.TextureAnimation pTexAnim)
|
||||||
{
|
{
|
||||||
byte[] data = new byte[16];
|
byte[] data;
|
||||||
int pos = 0;
|
|
||||||
|
|
||||||
// The flags don't like conversion from uint to byte, so we have to do
|
if (pTexAnim.Flags == Primitive.TextureAnimMode.ANIM_OFF)
|
||||||
// it the crappy way. See the above function :(
|
{
|
||||||
|
data = Utils.EmptyBytes;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data = new byte[16];
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
data[pos] = ConvertScriptUintToByte((uint)pTexAnim.Flags); pos++;
|
// The flags don't like conversion from uint to byte, so we have to do
|
||||||
data[pos] = (byte)pTexAnim.Face; pos++;
|
// it the crappy way. See the above function :(
|
||||||
data[pos] = (byte)pTexAnim.SizeX; pos++;
|
|
||||||
data[pos] = (byte)pTexAnim.SizeY; pos++;
|
|
||||||
|
|
||||||
Utils.FloatToBytes(pTexAnim.Start).CopyTo(data, pos);
|
data[pos] = ConvertScriptUintToByte((uint)pTexAnim.Flags); pos++;
|
||||||
Utils.FloatToBytes(pTexAnim.Length).CopyTo(data, pos + 4);
|
data[pos] = (byte)pTexAnim.Face; pos++;
|
||||||
Utils.FloatToBytes(pTexAnim.Rate).CopyTo(data, pos + 8);
|
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;
|
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.
|
// viewers without (e.g. v1 viewers) will not, so we still need to make this call.
|
||||||
if (Scene.AttachmentsModule != null)
|
if (Scene.AttachmentsModule != null)
|
||||||
{
|
{
|
||||||
Util.FireAndForget(o =>
|
Watchdog.RunJob(
|
||||||
{
|
"RezAttachments",
|
||||||
Scene.AttachmentsModule.RezAttachments(this);
|
o => Scene.AttachmentsModule.RezAttachments(this),
|
||||||
});
|
string.Format("Rez attachments for {0} in {1}", Name, Scene.Name),
|
||||||
|
null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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.
|
// 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();
|
List<SceneObjectGroup> attachments = GetAttachments();
|
||||||
|
|
||||||
if (attachments.Count > 0)
|
if (attachments.Count > 0)
|
||||||
{
|
{
|
||||||
m_log.DebugFormat(
|
Watchdog.RunJob(
|
||||||
"[SCENE PRESENCE]: Restarting scripts in attachments for {0} in {1}", Name, Scene.Name);
|
"StartAttachmentScripts",
|
||||||
|
o => RestartAttachmentScripts(attachments),
|
||||||
// Resume scripts
|
string.Format("Start attachment scripts for {0} in {1}", Name, Scene.Name),
|
||||||
foreach (SceneObjectGroup sog in attachments)
|
null,
|
||||||
{
|
true);
|
||||||
sog.ScheduleGroupForFullUpdate();
|
|
||||||
sog.RootPart.ParentGroup.CreateScriptInstances(0, false, m_scene.DefaultScriptEngine, GetStateSource());
|
|
||||||
sog.ResumeScripts();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1294,6 +1283,20 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
return true;
|
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)
|
private static bool IsRealLogin(TeleportFlags teleportFlags)
|
||||||
{
|
{
|
||||||
return ((teleportFlags & TeleportFlags.ViaLogin) != 0) && ((teleportFlags & TeleportFlags.ViaHGLogin) == 0);
|
return ((teleportFlags & TeleportFlags.ViaLogin) != 0) && ((teleportFlags & TeleportFlags.ViaHGLogin) == 0);
|
||||||
|
@ -1326,7 +1329,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
UseFakeGroupTitle = false;
|
UseFakeGroupTitle = false;
|
||||||
SendAvatarDataToAllClients(false);
|
SendAvatarDataToAllClients(false);
|
||||||
});
|
}, null, "Scenepresence.ForceViewersUpdateName");
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetStateSource()
|
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.
|
// 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.
|
// This may be due to viewer code or it may be something we're not doing properly simulator side.
|
||||||
lock (m_attachments)
|
Watchdog.RunJob(
|
||||||
{
|
"ScheduleAttachmentsForFullUpdate",
|
||||||
foreach (SceneObjectGroup sog in m_attachments)
|
o => ScheduleAttachmentsForFullUpdate(),
|
||||||
sog.ScheduleGroupForFullUpdate();
|
string.Format("Schedule attachments for full update for {0} in {1}", Name, Scene.Name),
|
||||||
}
|
null,
|
||||||
|
true);
|
||||||
|
|
||||||
// m_log.DebugFormat(
|
// m_log.DebugFormat(
|
||||||
// "[SCENE PRESENCE]: Completing movement of {0} into region {1} took {2}ms",
|
// "[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>
|
/// <summary>
|
||||||
/// Callback for the Camera view block check. Gets called with the results of the camera view block test
|
/// 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.
|
/// 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);
|
ControllingClient.SendClearFollowCamProperties(part.ParentUUID);
|
||||||
|
|
||||||
ParentID = 0;
|
ParentID = 0;
|
||||||
|
@ -2647,13 +2659,13 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
// Vector3 standPositionAdjustment
|
// Vector3 standPositionAdjustment
|
||||||
// = part.SitTargetPosition + new Vector3(0.5f, 0f, m_sitAvatarHeight / 2f);
|
// = 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
|
// XXX: This is based on the physics capsule sizes. Need to find a better way to read this rather than
|
||||||
// hardcoding here.
|
// hardcoding here.
|
||||||
Vector3 adjustmentForSitPose = new Vector3(0.74f, 0f, 0f) * standRotation;
|
Vector3 adjustmentForSitPose = new Vector3(0.74f, 0f, 0f) * standRotation;
|
||||||
|
|
||||||
Vector3 standPos = sitPartWorldPosition + adjustmentForSitPosition + adjustmentForSitPose;
|
Vector3 standPos = part.ParentGroup.AbsolutePosition + adjustmentForSitPosition + adjustmentForSitPose;
|
||||||
|
|
||||||
// m_log.DebugFormat(
|
// m_log.DebugFormat(
|
||||||
// "[SCENE PRESENCE]: Setting stand to pos {0}, (adjustmentForSitPosition {1}, adjustmentForSitPose {2}) rotation {3} for {4} in {5}",
|
// "[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;
|
SentInitialDataToClient = true;
|
||||||
|
|
||||||
// Send all scene object to the new client
|
// Send all scene object to the new client
|
||||||
Watchdog.RunInThread(delegate
|
Watchdog.RunJob("SendInitialDataToClient", delegate
|
||||||
{
|
{
|
||||||
// m_log.DebugFormat(
|
// m_log.DebugFormat(
|
||||||
// "[SCENE PRESENCE]: Sending initial data to {0} agent {1} in {2}, tp flags {3}",
|
// "[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)
|
if (e != null && e is SceneObjectGroup)
|
||||||
((SceneObjectGroup)e).SendFullUpdateToClient(ControllingClient);
|
((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>
|
/// <summary>
|
||||||
|
@ -3618,7 +3630,8 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
agentpos.CopyFrom(cadu, ControllingClient.SessionId);
|
agentpos.CopyFrom(cadu, ControllingClient.SessionId);
|
||||||
|
|
||||||
// Let's get this out of the update loop
|
// 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);
|
Animator.Animations.SetImplicitDefaultAnimation(cAgent.AnimState.AnimID, cAgent.AnimState.SequenceNum, UUID.Zero);
|
||||||
|
|
||||||
if (Scene.AttachmentsModule != null)
|
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
|
// originating from the client completing a teleport. Otherwise, CompleteMovement() code to restart
|
||||||
// script attachments can outrace this thread.
|
// script attachments can outrace this thread.
|
||||||
lock (m_originRegionIDAccessLock)
|
lock (m_originRegionIDAccessLock)
|
||||||
|
@ -4472,7 +4499,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}, null, "ScenePresence.SendScriptEventToAttachments");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -224,7 +224,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
public SimStatsReporter(Scene scene)
|
public SimStatsReporter(Scene scene)
|
||||||
{
|
{
|
||||||
m_scene = scene;
|
m_scene = scene;
|
||||||
m_reportedFpsCorrectionFactor = scene.MinFrameTime * m_nominalReportedFps;
|
m_reportedFpsCorrectionFactor = scene.MinFrameSeconds * m_nominalReportedFps;
|
||||||
m_statsUpdateFactor = (float)(m_statsUpdatesEveryMS / 1000);
|
m_statsUpdateFactor = (float)(m_statsUpdatesEveryMS / 1000);
|
||||||
ReportingRegion = scene.RegionInfo;
|
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
|
/// 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).
|
/// 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
|
SlowFramesStat
|
||||||
= new Stat(
|
= new Stat(
|
||||||
|
|
|
@ -35,7 +35,6 @@ using OpenSim.Framework;
|
||||||
using OpenSim.Framework.Communications;
|
using OpenSim.Framework.Communications;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,7 +36,6 @@ using OpenSim.Framework.Communications;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Services.Interfaces;
|
using OpenSim.Services.Interfaces;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -37,7 +37,6 @@ using OpenSim.Framework.Communications;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Services.Interfaces;
|
using OpenSim.Services.Interfaces;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
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.CoreModules.World.Land;
|
||||||
using OpenSim.Region.OptionalModules;
|
using OpenSim.Region.OptionalModules;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -40,7 +40,6 @@ using OpenSim.Region.CoreModules.World.Permissions;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Services.Interfaces;
|
using OpenSim.Services.Interfaces;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,7 +34,6 @@ using OpenSim.Framework;
|
||||||
using OpenSim.Framework.Communications;
|
using OpenSim.Framework.Communications;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
using log4net;
|
using log4net;
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||||
|
|
|
@ -33,7 +33,6 @@ using OpenSim.Framework;
|
||||||
using OpenSim.Framework.Communications;
|
using OpenSim.Framework.Communications;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,7 +35,6 @@ using OpenSim.Framework.Communications;
|
||||||
using OpenSim.Region.Framework.Interfaces;
|
using OpenSim.Region.Framework.Interfaces;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,7 +34,6 @@ using OpenSim.Framework;
|
||||||
using OpenSim.Framework.Communications;
|
using OpenSim.Framework.Communications;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,7 +34,6 @@ using OpenSim.Framework;
|
||||||
using OpenSim.Framework.Communications;
|
using OpenSim.Framework.Communications;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,7 +33,6 @@ using OpenSim.Framework;
|
||||||
using OpenSim.Framework.Communications;
|
using OpenSim.Framework.Communications;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -39,7 +39,6 @@ using OpenSim.Region.Framework.Interfaces;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups;
|
using OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
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.World.Serialiser;
|
||||||
using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation;
|
using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
using GridRegion = OpenSim.Services.Interfaces.GridRegion;
|
using GridRegion = OpenSim.Services.Interfaces.GridRegion;
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
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.CoreModules.ServiceConnectorsOut.Simulation;
|
||||||
using OpenSim.Region.Physics.Manager;
|
using OpenSim.Region.Physics.Manager;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
namespace OpenSim.Region.Framework.Scenes.Tests
|
||||||
{
|
{
|
||||||
|
|
|
@ -37,7 +37,6 @@ using OpenSim.Framework.Communications;
|
||||||
using OpenSim.Region.Framework.Interfaces;
|
using OpenSim.Region.Framework.Interfaces;
|
||||||
using OpenSim.Region.Framework.Scenes;
|
using OpenSim.Region.Framework.Scenes;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
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.Scenes;
|
||||||
using OpenSim.Region.Framework.Interfaces;
|
using OpenSim.Region.Framework.Interfaces;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
using GridRegion = OpenSim.Services.Interfaces.GridRegion;
|
using GridRegion = OpenSim.Services.Interfaces.GridRegion;
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
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.ServiceConnectorsOut.Simulation;
|
||||||
using OpenSim.Region.CoreModules.World.Permissions;
|
using OpenSim.Region.CoreModules.World.Permissions;
|
||||||
using OpenSim.Tests.Common;
|
using OpenSim.Tests.Common;
|
||||||
using OpenSim.Tests.Common.Mock;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Tests
|
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