Merge branch 'teravuswork' into avination

avinationmerge
Melanie 2012-11-20 00:48:13 +01:00
commit 0bf66434bc
11 changed files with 669 additions and 83 deletions

View File

@ -45,16 +45,53 @@ namespace OpenSim.Capabilities.Handlers
{
public class GetMeshHandler
{
// private static readonly ILog m_log =
// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private IAssetService m_assetService;
public const string DefaultFormat = "vnd.ll.mesh";
public GetMeshHandler(IAssetService assService)
{
m_assetService = assService;
}
public Hashtable Handle(Hashtable request)
{
Hashtable ret = new Hashtable();
ret["int_response_code"] = (int)System.Net.HttpStatusCode.NotFound;
ret["content_type"] = "text/plain";
ret["keepalive"] = false;
ret["reusecontext"] = false;
ret["int_bytes"] = 0;
string MeshStr = (string)request["mesh_id"];
//m_log.DebugFormat("[GETMESH]: called {0}", MeshStr);
if (m_assetService == null)
{
m_log.Error("[GETMESH]: Cannot fetch mesh " + MeshStr + " without an asset service");
}
UUID meshID;
if (!String.IsNullOrEmpty(MeshStr) && UUID.TryParse(MeshStr, out meshID))
{
// m_log.DebugFormat("[GETMESH]: Received request for mesh id {0}", meshID);
ret = ProcessGetMesh(request, UUID.Zero, null);
}
else
{
m_log.Warn("[GETMESH]: Failed to parse a mesh_id from GetMesh request: " + (string)request["uri"]);
}
return ret;
}
public Hashtable ProcessGetMesh(Hashtable request, UUID AgentId, Caps cap)
{
Hashtable responsedata = new Hashtable();
@ -62,6 +99,7 @@ namespace OpenSim.Capabilities.Handlers
responsedata["content_type"] = "text/plain";
responsedata["keepalive"] = false;
responsedata["str_response_string"] = "Request wasn't what was expected";
responsedata["reusecontext"] = false;
string meshStr = string.Empty;
@ -77,6 +115,7 @@ namespace OpenSim.Capabilities.Handlers
responsedata["content_type"] = "text/plain";
responsedata["keepalive"] = false;
responsedata["str_response_string"] = "The asset service is unavailable. So is your mesh.";
responsedata["reusecontext"] = false;
return responsedata;
}
@ -86,9 +125,100 @@ namespace OpenSim.Capabilities.Handlers
{
if (mesh.Type == (SByte)AssetType.Mesh)
{
Hashtable headers = new Hashtable();
responsedata["headers"] = headers;
string range = String.Empty;
if (((Hashtable)request["headers"])["range"] != null)
range = (string)((Hashtable)request["headers"])["range"];
else if (((Hashtable)request["headers"])["Range"] != null)
range = (string)((Hashtable)request["headers"])["Range"];
if (!String.IsNullOrEmpty(range)) // Mesh Asset LOD // Physics
{
// Range request
int start, end;
if (TryParseRange(range, out start, out end))
{
// Before clamping start make sure we can satisfy it in order to avoid
// sending back the last byte instead of an error status
if (start >= mesh.Data.Length)
{
responsedata["int_response_code"] = 404; //501; //410; //404;
responsedata["content_type"] = "text/plain";
responsedata["keepalive"] = false;
responsedata["str_response_string"] = "This range doesnt exist.";
responsedata["reusecontext"] = false;
responsedata["int_lod"] = 3;
return responsedata;
}
else
{
end = Utils.Clamp(end, 0, mesh.Data.Length - 1);
start = Utils.Clamp(start, 0, end);
int len = end - start + 1;
//m_log.Debug("Serving " + start + " to " + end + " of " + texture.Data.Length + " bytes for texture " + texture.ID);
if (start > 20000)
{
responsedata["int_lod"] = 3;
}
else if (start < 4097)
{
responsedata["int_lod"] = 1;
}
else
{
responsedata["int_lod"] = 2;
}
if (start == 0 && len == mesh.Data.Length) // well redudante maybe
{
responsedata["int_response_code"] = (int) System.Net.HttpStatusCode.OK;
responsedata["bin_response_data"] = mesh.Data;
responsedata["int_bytes"] = mesh.Data.Length;
responsedata["reusecontext"] = false;
responsedata["int_lod"] = 3;
}
else
{
responsedata["int_response_code"] =
(int) System.Net.HttpStatusCode.PartialContent;
headers["Content-Range"] = String.Format("bytes {0}-{1}/{2}", start, end,
mesh.Data.Length);
byte[] d = new byte[len];
Array.Copy(mesh.Data, start, d, 0, len);
responsedata["bin_response_data"] = d;
responsedata["int_bytes"] = len;
responsedata["reusecontext"] = false;
}
}
}
else
{
m_log.Warn("[GETMESH]: Failed to parse a range from GetMesh request, sending full asset: " + (string)request["uri"]);
responsedata["str_response_string"] = Convert.ToBase64String(mesh.Data);
responsedata["content_type"] = "application/vnd.ll.mesh";
responsedata["int_response_code"] = 200;
responsedata["reusecontext"] = false;
responsedata["int_lod"] = 3;
}
}
else
{
responsedata["str_response_string"] = Convert.ToBase64String(mesh.Data);
responsedata["content_type"] = "application/vnd.ll.mesh";
responsedata["int_response_code"] = 200;
responsedata["reusecontext"] = false;
responsedata["int_lod"] = 3;
}
}
// Optionally add additional mesh types here
else
@ -97,6 +227,8 @@ namespace OpenSim.Capabilities.Handlers
responsedata["content_type"] = "text/plain";
responsedata["keepalive"] = false;
responsedata["str_response_string"] = "Unfortunately, this asset isn't a mesh.";
responsedata["reusecontext"] = false;
responsedata["int_lod"] = 1;
return responsedata;
}
}
@ -106,11 +238,28 @@ namespace OpenSim.Capabilities.Handlers
responsedata["content_type"] = "text/plain";
responsedata["keepalive"] = false;
responsedata["str_response_string"] = "Your Mesh wasn't found. Sorry!";
responsedata["reusecontext"] = false;
responsedata["int_lod"] = 0;
return responsedata;
}
}
return responsedata;
}
private bool TryParseRange(string header, out int start, out int end)
{
if (header.StartsWith("bytes="))
{
string[] rangeValues = header.Substring(6).Split('-');
if (rangeValues.Length == 2)
{
if (Int32.TryParse(rangeValues[0], out start) && Int32.TryParse(rangeValues[1], out end))
return true;
}
}
start = end = 0;
return false;
}
}
}

View File

@ -1135,6 +1135,8 @@ namespace OpenSim.Framework
void SetChildAgentThrottle(byte[] throttle);
void SetAgentThrottleSilent(int throttle, int setting);
void SendAvatarDataImmediate(ISceneEntity avatar);
/// <summary>

View File

@ -53,7 +53,8 @@ namespace OpenSim.Framework.Servers.HttpServer
Normal = 0,
LslHttp = 1,
Inventory = 2,
Texture = 3
Texture = 3,
Mesh = 4
}
public PollServiceEventArgs(

View File

@ -27,11 +27,14 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reflection;
using System.IO;
using System.Threading;
using System.Web;
using Mono.Addins;
using OpenSim.Framework.Monitoring;
using log4net;
using Nini.Config;
using OpenMetaverse;
@ -58,7 +61,43 @@ namespace OpenSim.Region.ClientStack.Linden
private bool m_Enabled = true;
private string m_URL;
#region IRegionModuleBase Members
struct aPollRequest
{
public PollServiceMeshEventArgs thepoll;
public UUID reqID;
public Hashtable request;
}
public class aPollResponse
{
public Hashtable response;
public int bytes;
public int lod;
}
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static GetMeshHandler m_getMeshHandler;
private IAssetService m_assetService = null;
private Dictionary<UUID, string> m_capsDict = new Dictionary<UUID, string>();
private static Thread[] m_workerThreads = null;
private static OpenMetaverse.BlockingQueue<aPollRequest> m_queue =
new OpenMetaverse.BlockingQueue<aPollRequest>();
private Dictionary<UUID, PollServiceMeshEventArgs> m_pollservices = new Dictionary<UUID, PollServiceMeshEventArgs>();
#region ISharedRegionModule Members
~GetMeshModule()
{
foreach (Thread t in m_workerThreads)
Watchdog.AbortThread(t.ManagedThreadId);
}
public Type ReplaceableInterface
{
@ -75,6 +114,7 @@ namespace OpenSim.Region.ClientStack.Linden
// Cap doesn't exist
if (m_URL != string.Empty)
m_Enabled = true;
}
public void AddRegion(Scene pScene)
@ -83,6 +123,8 @@ namespace OpenSim.Region.ClientStack.Linden
return;
m_scene = pScene;
m_assetService = pScene.AssetService;
}
public void RemoveRegion(Scene scene)
@ -91,6 +133,9 @@ namespace OpenSim.Region.ClientStack.Linden
return;
m_scene.EventManager.OnRegisterCaps -= RegisterCaps;
m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps;
m_scene.EventManager.OnThrottleUpdate -= ThrottleUpdate;
m_scene = null;
}
@ -101,6 +146,27 @@ namespace OpenSim.Region.ClientStack.Linden
m_AssetService = m_scene.RequestModuleInterface<IAssetService>();
m_scene.EventManager.OnRegisterCaps += RegisterCaps;
// We'll reuse the same handler for all requests.
m_getMeshHandler = new GetMeshHandler(m_assetService);
m_scene.EventManager.OnDeregisterCaps += DeregisterCaps;
m_scene.EventManager.OnThrottleUpdate += ThrottleUpdate;
if (m_workerThreads == null)
{
m_workerThreads = new Thread[2];
for (uint i = 0; i < 2; i++)
{
m_workerThreads[i] = Watchdog.StartThread(DoMeshRequests,
String.Format("MeshWorkerThread{0}", i),
ThreadPriority.Normal,
false,
false,
null,
int.MaxValue);
}
}
}
@ -110,25 +176,212 @@ namespace OpenSim.Region.ClientStack.Linden
#endregion
private void DoMeshRequests()
{
while (true)
{
aPollRequest poolreq = m_queue.Dequeue();
poolreq.thepoll.Process(poolreq);
}
}
// Now we know when the throttle is changed by the client in the case of a root agent or by a neighbor region in the case of a child agent.
public void ThrottleUpdate(ScenePresence p)
{
byte[] throttles = p.ControllingClient.GetThrottlesPacked(1);
UUID user = p.UUID;
int imagethrottle = ExtractTaskThrottle(throttles);
PollServiceMeshEventArgs args;
if (m_pollservices.TryGetValue(user, out args))
{
args.UpdateThrottle(imagethrottle, p);
}
}
private int ExtractTaskThrottle(byte[] pthrottles)
{
byte[] adjData;
int pos = 0;
if (!BitConverter.IsLittleEndian)
{
byte[] newData = new byte[7 * 4];
Buffer.BlockCopy(pthrottles, 0, newData, 0, 7 * 4);
for (int i = 0; i < 7; i++)
Array.Reverse(newData, i * 4, 4);
adjData = newData;
}
else
{
adjData = pthrottles;
}
// 0.125f converts from bits to bytes
//int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
//pos += 4;
// int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
//pos += 4;
// int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
// pos += 4;
// int cloud = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
// pos += 4;
pos += 16;
int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
// pos += 4;
//int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); //pos += 4;
//int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
return task;
}
private class PollServiceMeshEventArgs : PollServiceEventArgs
{
private List<Hashtable> requests =
new List<Hashtable>();
private Dictionary<UUID, aPollResponse> responses =
new Dictionary<UUID, aPollResponse>();
private Scene m_scene;
private MeshCapsDataThrottler m_throttler;
public PollServiceMeshEventArgs(UUID pId, Scene scene) :
base(null, null, null, null, pId, int.MaxValue)
{
m_scene = scene;
m_throttler = new MeshCapsDataThrottler(100000, 1400000, 10000, scene);
// x is request id, y is userid
HasEvents = (x, y) =>
{
lock (responses)
{
bool ret = m_throttler.hasEvents(x, responses);
m_throttler.ProcessTime();
return ret;
}
};
GetEvents = (x, y) =>
{
lock (responses)
{
try
{
return responses[x].response;
}
finally
{
m_throttler.ProcessTime();
responses.Remove(x);
}
}
};
// x is request id, y is request data hashtable
Request = (x, y) =>
{
aPollRequest reqinfo = new aPollRequest();
reqinfo.thepoll = this;
reqinfo.reqID = x;
reqinfo.request = y;
m_queue.Enqueue(reqinfo);
};
// this should never happen except possible on shutdown
NoEvents = (x, y) =>
{
/*
lock (requests)
{
Hashtable request = requests.Find(id => id["RequestID"].ToString() == x.ToString());
requests.Remove(request);
}
*/
Hashtable response = new Hashtable();
response["int_response_code"] = 500;
response["str_response_string"] = "Script timeout";
response["content_type"] = "text/plain";
response["keepalive"] = false;
response["reusecontext"] = false;
return response;
};
}
public void Process(aPollRequest requestinfo)
{
Hashtable response;
UUID requestID = requestinfo.reqID;
// If the avatar is gone, don't bother to get the texture
if (m_scene.GetScenePresence(Id) == null)
{
response = new Hashtable();
response["int_response_code"] = 500;
response["str_response_string"] = "Script timeout";
response["content_type"] = "text/plain";
response["keepalive"] = false;
response["reusecontext"] = false;
lock (responses)
responses[requestID] = new aPollResponse() { bytes = 0, response = response, lod = 0 };
return;
}
response = m_getMeshHandler.Handle(requestinfo.request);
lock (responses)
{
responses[requestID] = new aPollResponse()
{
bytes = (int)response["int_bytes"],
lod = (int)response["int_lod"],
response = response
};
}
m_throttler.ProcessTime();
}
internal void UpdateThrottle(int pimagethrottle, ScenePresence p)
{
m_throttler.UpdateThrottle(pimagethrottle, p);
}
}
public void RegisterCaps(UUID agentID, Caps caps)
{
// UUID capID = UUID.Random();
//caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture));
if (m_URL == "localhost")
{
// m_log.DebugFormat("[GETMESH]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName);
GetMeshHandler gmeshHandler = new GetMeshHandler(m_AssetService);
IRequestHandler reqHandler
= new RestHTTPHandler(
"GET",
"/CAPS/" + UUID.Random(),
httpMethod => gmeshHandler.ProcessGetMesh(httpMethod, UUID.Zero, null),
"GetMesh",
agentID.ToString());
string capUrl = "/CAPS/" + UUID.Random() + "/";
// Register this as a poll service
PollServiceMeshEventArgs args = new PollServiceMeshEventArgs(agentID, m_scene);
args.Type = PollServiceEventArgs.EventType.Mesh;
MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args);
string hostName = m_scene.RegionInfo.ExternalHostName;
uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
string protocol = "http";
if (MainServer.Instance.UseSSL)
{
hostName = MainServer.Instance.SSLCommonName;
port = MainServer.Instance.SSLPort;
protocol = "https";
}
caps.RegisterHandler("GetMesh", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl));
m_pollservices.Add(agentID, args);
m_capsDict[agentID] = capUrl;
caps.RegisterHandler("GetMesh", reqHandler);
}
else
{
@ -136,6 +389,143 @@ namespace OpenSim.Region.ClientStack.Linden
caps.RegisterHandler("GetMesh", m_URL);
}
}
private void DeregisterCaps(UUID agentID, Caps caps)
{
string capUrl;
PollServiceMeshEventArgs args;
if (m_capsDict.TryGetValue(agentID, out capUrl))
{
MainServer.Instance.RemoveHTTPHandler("", capUrl);
m_capsDict.Remove(agentID);
}
if (m_pollservices.TryGetValue(agentID, out args))
{
m_pollservices.Remove(agentID);
}
}
internal sealed class MeshCapsDataThrottler
{
private volatile int currenttime = 0;
private volatile int lastTimeElapsed = 0;
private volatile int BytesSent = 0;
private int Lod3 = 0;
private int Lod2 = 0;
private int Lod1 = 0;
private int UserSetThrottle = 0;
private int UDPSetThrottle = 0;
private int CapSetThrottle = 0;
private float CapThrottleDistributon = 0.30f;
private readonly Scene m_scene;
private ThrottleOutPacketType Throttle;
public MeshCapsDataThrottler(int pBytes, int max, int min, Scene pScene)
{
ThrottleBytes = pBytes;
lastTimeElapsed = Util.EnvironmentTickCount();
Throttle = ThrottleOutPacketType.Task;
m_scene = pScene;
}
public bool hasEvents(UUID key, Dictionary<UUID, aPollResponse> responses)
{
PassTime();
// Note, this is called IN LOCK
bool haskey = responses.ContainsKey(key);
if (!haskey)
{
return false;
}
aPollResponse response;
if (responses.TryGetValue(key, out response))
{
// Normal
if (BytesSent + response.bytes <= ThrottleBytes)
{
BytesSent += response.bytes;
return true;
}
// Lod3 Over
else if (response.bytes > ThrottleBytes && Lod3 <= (((ThrottleBytes * .30f) % 50000) + 1))
{
Interlocked.Increment(ref Lod3);
BytesSent += response.bytes;
return true;
}
// Lod2 Over
else if (response.bytes > ThrottleBytes && Lod2 <= (((ThrottleBytes * .30f) % 10000) + 1))
{
Interlocked.Increment(ref Lod2);
BytesSent += response.bytes;
return true;
}
else
{
return false;
}
}
return haskey;
}
public void SubtractBytes(int bytes,int lod)
{
BytesSent -= bytes;
}
public void ProcessTime()
{
PassTime();
}
private void PassTime()
{
currenttime = Util.EnvironmentTickCount();
int timeElapsed = Util.EnvironmentTickCountSubtract(currenttime, lastTimeElapsed);
//processTimeBasedActions(responses);
if (currenttime - timeElapsed >= 1000)
{
lastTimeElapsed = Util.EnvironmentTickCount();
BytesSent -= ThrottleBytes;
if (BytesSent < 0) BytesSent = 0;
if (BytesSent < ThrottleBytes)
{
Lod3 = 0;
Lod2 = 0;
Lod1 = 0;
}
}
}
private void AlterThrottle(int setting, ScenePresence p)
{
p.ControllingClient.SetAgentThrottleSilent((int)Throttle,setting);
}
public int ThrottleBytes
{
get { return CapSetThrottle; }
set { CapSetThrottle = value; }
}
internal void UpdateThrottle(int pimagethrottle, ScenePresence p)
{
// Client set throttle !
UserSetThrottle = pimagethrottle;
CapSetThrottle = (int)(pimagethrottle*CapThrottleDistributon);
UDPSetThrottle = (int) (pimagethrottle*(100 - CapThrottleDistributon));
if (CapSetThrottle < 4068)
CapSetThrottle = 4068; // at least two discovery mesh
p.ControllingClient.SetAgentThrottleSilent((int) Throttle, UDPSetThrottle);
ProcessTime();
}
}
}
}

View File

@ -364,8 +364,6 @@ namespace OpenSim.Region.ClientStack.Linden
poolreq.thepoll.Process(poolreq);
}
}
}
internal sealed class CapsDataThrottler
{
@ -441,3 +439,6 @@ namespace OpenSim.Region.ClientStack.Linden
public int ThrottleBytes;
}
}
}

View File

@ -11883,6 +11883,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
}
/// <summary>
/// Sets the throttles from values supplied by the client
/// </summary>
/// <param name="throttles"></param>
public void SetAgentThrottleSilent(int throttle, int setting)
{
m_udpClient.ForceThrottleSetting(throttle,setting);
//m_udpClient.SetThrottles(throttles);
}
/// <summary>
/// Get the current throttles for this client as a packed byte array
/// </summary>

View File

@ -682,6 +682,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (m_nextOnQueueEmpty == 0)
m_nextOnQueueEmpty = 1;
}
internal void ForceThrottleSetting(int throttle, int setting)
{
m_throttleCategories[throttle].RequestedDripRate = Math.Max(setting, LLUDPServer.MTU); ;
}
/// <summary>
/// Converts a <seealso cref="ThrottleOutPacketType"/> integer to a

View File

@ -1428,6 +1428,11 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server
}
public void SetAgentThrottleSilent(int throttle, int setting)
{
}
public byte[] GetThrottlesPacked(float multiplier)
{
return new byte[0];

View File

@ -596,6 +596,12 @@ namespace OpenSim.Region.OptionalModules.World.NPC
public virtual void SetChildAgentThrottle(byte[] throttle)
{
}
public void SetAgentThrottleSilent(int throttle, int setting)
{
}
public byte[] GetThrottlesPacked(float multiplier)
{

View File

@ -520,6 +520,12 @@ namespace OpenSim.Tests.Common.Mock
public virtual void SetChildAgentThrottle(byte[] throttle)
{
}
public void SetAgentThrottleSilent(int throttle, int setting)
{
}
public byte[] GetThrottlesPacked(float multiplier)
{

View File

@ -4,6 +4,8 @@
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>
<runtime>
<supportedRuntime version="v2.0.50727"/>
<gcConcurrent enabled="true" />
<gcServer enabled="true" />
</runtime>
@ -12,11 +14,14 @@
<log4net>
<appender name="Console" type="OpenSim.Framework.Console.OpenSimAppender, OpenSim.Framework.Console">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date{HH:mm:ss} - %message%newline" />
<conversionPattern value="%date{HH:mm:ss} - %message" />
<!-- console log with milliseconds. Useful for debugging -->
<!-- <conversionPattern value="%date{HH:mm:ss.fff} - %message" /> -->
</layout>
</appender>
<appender name="LogFileAppender" type="log4net.Appender.FileAppender">
<file value="OpenSim.32BitLaunch.log" />
<file value="OpenSim.log" />
<appendToFile value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %-5level - %logger %message%newline" />
@ -28,5 +33,10 @@
<appender-ref ref="Console" />
<appender-ref ref="LogFileAppender" />
</root>
<logger name="OpenSim.Region.ScriptEngine.XEngine">
<level value="INFO"/>
</logger>
</log4net>
</configuration>