29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Reflection;
32 using System.Threading;
37 using OpenSim.Framework;
38 using OpenSim.Framework.Servers;
39 using OpenSim.Framework.Servers.HttpServer;
40 using OpenSim.Region.Framework.Interfaces;
41 using OpenSim.Region.Framework.Scenes;
42 using OpenSim.Services.Interfaces;
44 using OpenSim.Capabilities.Handlers;
45 using OpenSim.Framework.Monitoring;
47 namespace OpenSim.
Region.ClientStack.Linden
50 [Extension(Path =
"/OpenSim/RegionModules", NodeName =
"RegionModule", Id =
"GetTextureModule")]
56 public PollServiceTextureEventArgs thepoll;
58 public Hashtable request;
69 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
71 private Scene m_scene;
77 private Dictionary<UUID, string> m_capsDict =
new Dictionary<UUID, string>();
78 private static Thread[] m_workerThreads = null;
79 private static int m_NumberScenes = 0;
80 private static OpenMetaverse.BlockingQueue<aPollRequest> m_queue =
81 new OpenMetaverse.BlockingQueue<aPollRequest>();
83 private Dictionary<UUID,PollServiceTextureEventArgs> m_pollservices =
new Dictionary<UUID,PollServiceTextureEventArgs>();
85 private string m_Url =
"localhost";
87 #region ISharedRegionModule Members
91 IConfig config = source.Configs[
"ClientStack.LindenCaps"];
104 m_Url = config.GetString(
"Cap_GetTexture",
"localhost");
110 m_assetService = s.AssetService;
115 m_scene.EventManager.OnRegisterCaps -= RegisterCaps;
116 m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps;
117 m_scene.EventManager.OnThrottleUpdate -= ThrottleUpdate;
127 m_scene.EventManager.OnRegisterCaps += RegisterCaps;
128 m_scene.EventManager.OnDeregisterCaps += DeregisterCaps;
129 m_scene.EventManager.OnThrottleUpdate += ThrottleUpdate;
133 if (m_workerThreads == null)
135 m_workerThreads =
new Thread[2];
137 for (uint i = 0; i < 2; i++)
139 m_workerThreads[i] = WorkManager.StartThread(DoTextureRequests,
140 String.Format(
"GetTextureWorker{0}", i),
141 ThreadPriority.Normal,
149 private int ExtractImageThrottle(byte[] pthrottles)
155 if (!BitConverter.IsLittleEndian)
157 byte[] newData =
new byte[7 * 4];
158 Buffer.BlockCopy(pthrottles, 0, newData, 0, 7 * 4);
160 for (
int i = 0; i < 7; i++)
161 Array.Reverse(newData, i * 4, 4);
167 adjData = pthrottles;
171 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
179 byte[] throttles = p.ControllingClient.GetThrottlesPacked(1);
181 int imagethrottle = ExtractImageThrottle(throttles);
182 PollServiceTextureEventArgs args;
183 if (m_pollservices.TryGetValue(user,out args))
185 args.UpdateThrottle(imagethrottle);
195 if(m_NumberScenes <= 0 && m_workerThreads != null)
197 m_log.DebugFormat(
"[GetTextureModule] Closing");
199 foreach (
Thread t
in m_workerThreads)
200 Watchdog.AbortThread(t.ManagedThreadId);
206 public string Name {
get {
return "GetTextureModule"; } }
208 public Type ReplaceableInterface
217 private List<Hashtable> requests =
218 new List<Hashtable>();
219 private Dictionary<UUID, aPollResponse> responses =
220 new Dictionary<UUID, aPollResponse>();
222 private Scene m_scene;
223 private CapsDataThrottler m_throttler =
new CapsDataThrottler(100000, 1400000,10000);
224 public PollServiceTextureEventArgs(UUID pId,
Scene scene) :
225 base(null,
"", null, null, null, pId, int.MaxValue)
229 HasEvents = (x, y) =>
233 bool ret = m_throttler.hasEvents(x, responses);
234 m_throttler.ProcessTime();
239 GetEvents = (x, y) =>
245 return responses[x].response;
256 aPollRequest reqinfo =
new aPollRequest();
257 reqinfo.thepoll =
this;
260 reqinfo.send503 =
false;
264 if (responses.Count > 0)
266 if (m_queue.Count >= 4)
269 reqinfo.send503 =
true;
273 m_queue.Enqueue(reqinfo);
286 Hashtable response =
new Hashtable();
288 response[
"int_response_code"] = 500;
289 response[
"str_response_string"] =
"Script timeout";
290 response[
"content_type"] =
"text/plain";
291 response[
"keepalive"] =
false;
292 response[
"reusecontext"] =
false;
298 public void Process(aPollRequest requestinfo)
302 UUID requestID = requestinfo.reqID;
304 if(m_scene.ShuttingDown)
307 if (requestinfo.send503)
309 response =
new Hashtable();
311 response[
"int_response_code"] = 503;
312 response[
"str_response_string"] =
"Throttled";
313 response[
"content_type"] =
"text/plain";
314 response[
"keepalive"] =
false;
315 response[
"reusecontext"] =
false;
317 Hashtable headers =
new Hashtable();
318 headers[
"Retry-After"] = 30;
319 response[
"headers"] = headers;
322 responses[requestID] = new aPollResponse() {bytes = 0, response = response};
328 if (m_scene.GetScenePresence(Id) == null)
330 response =
new Hashtable();
332 response[
"int_response_code"] = 500;
333 response[
"str_response_string"] =
"Script timeout";
334 response[
"content_type"] =
"text/plain";
335 response[
"keepalive"] =
false;
336 response[
"reusecontext"] =
false;
339 responses[requestID] = new aPollResponse() {bytes = 0, response = response};
344 response = m_getTextureHandler.Handle(requestinfo.request);
347 responses[requestID] =
new aPollResponse()
349 bytes = (int) response[
"int_bytes"],
354 m_throttler.ProcessTime();
357 internal void UpdateThrottle(
int pimagethrottle)
359 m_throttler.ThrottleBytes = pimagethrottle;
363 private void RegisterCaps(UUID agentID,
Caps caps)
365 if (m_Url ==
"localhost")
367 string capUrl =
"/CAPS/" + UUID.Random() +
"/";
370 PollServiceTextureEventArgs args =
new PollServiceTextureEventArgs(agentID, m_scene);
372 args.Type = PollServiceEventArgs.EventType.Texture;
373 MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args);
375 string hostName = m_scene.RegionInfo.ExternalHostName;
377 string protocol =
"http";
381 hostName = MainServer.Instance.SSLCommonName;
382 port = MainServer.Instance.SSLPort;
387 handler.RegisterExternalUserCapsHandler(agentID, caps,
"GetTexture", capUrl);
389 caps.RegisterHandler(
"GetTexture", String.Format(
"{0}://{1}:{2}{3}", protocol, hostName, port, capUrl));
390 m_pollservices[agentID] = args;
391 m_capsDict[agentID] = capUrl;
395 caps.RegisterHandler(
"GetTexture", m_Url);
399 private void DeregisterCaps(UUID agentID,
Caps caps)
401 PollServiceTextureEventArgs args;
403 MainServer.Instance.RemoveHTTPHandler(
"", m_Url);
404 m_capsDict.Remove(agentID);
406 if (m_pollservices.TryGetValue(agentID, out args))
408 m_pollservices.Remove(agentID);
412 private static void DoTextureRequests()
416 aPollRequest poolreq = m_queue.Dequeue();
417 Watchdog.UpdateThread();
418 poolreq.thepoll.Process(poolreq);
422 internal sealed
class CapsDataThrottler
425 private volatile int currenttime = 0;
426 private volatile int lastTimeElapsed = 0;
427 private volatile int BytesSent = 0;
428 private int oversizedImages = 0;
429 public CapsDataThrottler(
int pBytes,
int max,
int min)
431 ThrottleBytes = pBytes;
432 lastTimeElapsed = Util.EnvironmentTickCount();
434 public bool hasEvents(UUID
key, Dictionary<UUID, GetTextureModule.aPollResponse> responses)
438 bool haskey = responses.ContainsKey(
key);
443 GetTextureModule.aPollResponse response;
444 if (responses.TryGetValue(key, out response))
447 if (response.bytes == 0)
451 if (BytesSent + response.bytes <= ThrottleBytes)
453 BytesSent += response.bytes;
459 else if (response.bytes > ThrottleBytes && oversizedImages <= ((ThrottleBytes % 50000) + 1))
461 Interlocked.Increment(ref oversizedImages);
462 BytesSent += response.bytes;
476 public void ProcessTime()
481 private void PassTime()
483 currenttime = Util.EnvironmentTickCount();
484 int timeElapsed = Util.EnvironmentTickCountSubtract(currenttime, lastTimeElapsed);
486 if (Util.EnvironmentTickCountSubtract(currenttime, timeElapsed) >= 1000)
488 lastTimeElapsed = Util.EnvironmentTickCount();
489 BytesSent -= ThrottleBytes;
490 if (BytesSent < 0) BytesSent = 0;
491 if (BytesSent < ThrottleBytes)
497 public int ThrottleBytes;
void AddRegion(Scene s)
This is called whenever a Scene is added. For shared modules, this can happen several times...
OpenSim.Framework.Capabilities.Caps Caps
void Initialise(IConfigSource source)
This is called to initialize the region module. For shared modules, this is called exactly once...
void RegionLoaded(Scene s)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
OpenSim.Framework.Capabilities.Caps Caps
void ThrottleUpdate(ScenePresence p)
OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString key
static BaseHttpServer Instance
Set the main HTTP server instance.
void RemoveRegion(Scene s)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...