aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs')
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs360
1 files changed, 348 insertions, 12 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
index 8e1f63a..e6d7db8 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
@@ -27,11 +27,14 @@
27 27
28using System; 28using System;
29using System.Collections; 29using System.Collections;
30using System.Collections.Generic;
30using System.Collections.Specialized; 31using System.Collections.Specialized;
31using System.Reflection; 32using System.Reflection;
32using System.IO; 33using System.IO;
34using System.Threading;
33using System.Web; 35using System.Web;
34using Mono.Addins; 36using Mono.Addins;
37using OpenSim.Framework.Monitoring;
35using log4net; 38using log4net;
36using Nini.Config; 39using Nini.Config;
37using OpenMetaverse; 40using OpenMetaverse;
@@ -57,9 +60,43 @@ namespace OpenSim.Region.ClientStack.Linden
57 private IAssetService m_AssetService; 60 private IAssetService m_AssetService;
58 private bool m_Enabled = true; 61 private bool m_Enabled = true;
59 private string m_URL; 62 private string m_URL;
63 struct aPollRequest
64 {
65 public PollServiceMeshEventArgs thepoll;
66 public UUID reqID;
67 public Hashtable request;
68 }
69
70 public class aPollResponse
71 {
72 public Hashtable response;
73 public int bytes;
74 }
75
76
77 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
78
79 private static GetMeshHandler m_getMeshHandler;
80
81 private IAssetService m_assetService = null;
82
83 private Dictionary<UUID, string> m_capsDict = new Dictionary<UUID, string>();
84 private static Thread[] m_workerThreads = null;
85
86 private static OpenMetaverse.BlockingQueue<aPollRequest> m_queue =
87 new OpenMetaverse.BlockingQueue<aPollRequest>();
88
89 private Dictionary<UUID, PollServiceMeshEventArgs> m_pollservices = new Dictionary<UUID, PollServiceMeshEventArgs>();
60 90
61 #region Region Module interfaceBase Members 91 #region Region Module interfaceBase Members
62 92
93 ~GetMeshModule()
94 {
95 foreach (Thread t in m_workerThreads)
96 Watchdog.AbortThread(t.ManagedThreadId);
97
98 }
99
63 public Type ReplaceableInterface 100 public Type ReplaceableInterface
64 { 101 {
65 get { return null; } 102 get { return null; }
@@ -83,6 +120,8 @@ namespace OpenSim.Region.ClientStack.Linden
83 return; 120 return;
84 121
85 m_scene = pScene; 122 m_scene = pScene;
123
124 m_assetService = pScene.AssetService;
86 } 125 }
87 126
88 public void RemoveRegion(Scene scene) 127 public void RemoveRegion(Scene scene)
@@ -91,6 +130,9 @@ namespace OpenSim.Region.ClientStack.Linden
91 return; 130 return;
92 131
93 m_scene.EventManager.OnRegisterCaps -= RegisterCaps; 132 m_scene.EventManager.OnRegisterCaps -= RegisterCaps;
133 m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps;
134 m_scene.EventManager.OnThrottleUpdate -= ThrottleUpdate;
135
94 m_scene = null; 136 m_scene = null;
95 } 137 }
96 138
@@ -101,6 +143,27 @@ namespace OpenSim.Region.ClientStack.Linden
101 143
102 m_AssetService = m_scene.RequestModuleInterface<IAssetService>(); 144 m_AssetService = m_scene.RequestModuleInterface<IAssetService>();
103 m_scene.EventManager.OnRegisterCaps += RegisterCaps; 145 m_scene.EventManager.OnRegisterCaps += RegisterCaps;
146 // We'll reuse the same handler for all requests.
147 m_getMeshHandler = new GetMeshHandler(m_assetService);
148 m_scene.EventManager.OnDeregisterCaps += DeregisterCaps;
149 m_scene.EventManager.OnThrottleUpdate += ThrottleUpdate;
150
151 if (m_workerThreads == null)
152 {
153 m_workerThreads = new Thread[2];
154
155 for (uint i = 0; i < 2; i++)
156 {
157 m_workerThreads[i] = Watchdog.StartThread(DoMeshRequests,
158 String.Format("MeshWorkerThread{0}", i),
159 ThreadPriority.Normal,
160 false,
161 false,
162 null,
163 int.MaxValue);
164 }
165 }
166
104 } 167 }
105 168
106 169
@@ -110,25 +173,209 @@ namespace OpenSim.Region.ClientStack.Linden
110 173
111 #endregion 174 #endregion
112 175
176 private void DoMeshRequests()
177 {
178 while (true)
179 {
180 aPollRequest poolreq = m_queue.Dequeue();
181
182 poolreq.thepoll.Process(poolreq);
183 }
184 }
185
186 // 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.
187 public void ThrottleUpdate(ScenePresence p)
188 {
189 byte[] throttles = p.ControllingClient.GetThrottlesPacked(1);
190 UUID user = p.UUID;
191 int imagethrottle = ExtractTaskThrottle(throttles);
192 PollServiceMeshEventArgs args;
193 if (m_pollservices.TryGetValue(user, out args))
194 {
195 args.UpdateThrottle(imagethrottle);
196 }
197 }
198
199 private int ExtractTaskThrottle(byte[] pthrottles)
200 {
201
202 byte[] adjData;
203 int pos = 0;
204
205 if (!BitConverter.IsLittleEndian)
206 {
207 byte[] newData = new byte[7 * 4];
208 Buffer.BlockCopy(pthrottles, 0, newData, 0, 7 * 4);
209
210 for (int i = 0; i < 7; i++)
211 Array.Reverse(newData, i * 4, 4);
212
213 adjData = newData;
214 }
215 else
216 {
217 adjData = pthrottles;
218 }
219
220 // 0.125f converts from bits to bytes
221 //int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
222 //pos += 4;
223 // int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
224 //pos += 4;
225 // int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
226 // pos += 4;
227 // int cloud = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
228 // pos += 4;
229 pos += 16;
230 int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
231 // pos += 4;
232 //int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); //pos += 4;
233 //int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
234 return task;
235 }
236
237 private class PollServiceMeshEventArgs : PollServiceEventArgs
238 {
239 private List<Hashtable> requests =
240 new List<Hashtable>();
241 private Dictionary<UUID, aPollResponse> responses =
242 new Dictionary<UUID, aPollResponse>();
243
244 private Scene m_scene;
245 private CapsDataThrottler m_throttler = new CapsDataThrottler(100000, 1400000, 10000);
246 public PollServiceMeshEventArgs(UUID pId, Scene scene) :
247 base(null, null, null, null, pId, int.MaxValue)
248 {
249 m_scene = scene;
250 // x is request id, y is userid
251 HasEvents = (x, y) =>
252 {
253 lock (responses)
254 {
255 bool ret = m_throttler.hasEvents(x, responses);
256 m_throttler.ProcessTime();
257 return ret;
258
259 }
260 };
261 GetEvents = (x, y) =>
262 {
263 lock (responses)
264 {
265 try
266 {
267 return responses[x].response;
268 }
269 finally
270 {
271 responses.Remove(x);
272 }
273 }
274 };
275 // x is request id, y is request data hashtable
276 Request = (x, y) =>
277 {
278 aPollRequest reqinfo = new aPollRequest();
279 reqinfo.thepoll = this;
280 reqinfo.reqID = x;
281 reqinfo.request = y;
282
283 m_queue.Enqueue(reqinfo);
284 };
285
286 // this should never happen except possible on shutdown
287 NoEvents = (x, y) =>
288 {
289 /*
290 lock (requests)
291 {
292 Hashtable request = requests.Find(id => id["RequestID"].ToString() == x.ToString());
293 requests.Remove(request);
294 }
295 */
296 Hashtable response = new Hashtable();
297
298 response["int_response_code"] = 500;
299 response["str_response_string"] = "Script timeout";
300 response["content_type"] = "text/plain";
301 response["keepalive"] = false;
302 response["reusecontext"] = false;
303
304 return response;
305 };
306 }
307
308 public void Process(aPollRequest requestinfo)
309 {
310 Hashtable response;
311
312 UUID requestID = requestinfo.reqID;
313
314 // If the avatar is gone, don't bother to get the texture
315 if (m_scene.GetScenePresence(Id) == null)
316 {
317 response = new Hashtable();
318
319 response["int_response_code"] = 500;
320 response["str_response_string"] = "Script timeout";
321 response["content_type"] = "text/plain";
322 response["keepalive"] = false;
323 response["reusecontext"] = false;
324
325 lock (responses)
326 responses[requestID] = new aPollResponse() { bytes = 0, response = response };
327
328 return;
329 }
330
331 response = m_getMeshHandler.Handle(requestinfo.request);
332 lock (responses)
333 {
334 responses[requestID] = new aPollResponse()
335 {
336 bytes = (int)response["int_bytes"],
337 response = response
338 };
339
340 }
341 m_throttler.ProcessTime();
342 }
343
344 internal void UpdateThrottle(int pimagethrottle)
345 {
346 m_throttler.ThrottleBytes = pimagethrottle;
347 }
348 }
113 349
114 public void RegisterCaps(UUID agentID, Caps caps) 350 public void RegisterCaps(UUID agentID, Caps caps)
115 { 351 {
116// UUID capID = UUID.Random(); 352// UUID capID = UUID.Random();
117
118 //caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture));
119 if (m_URL == "localhost") 353 if (m_URL == "localhost")
120 { 354 {
121// m_log.DebugFormat("[GETMESH]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName); 355 string capUrl = "/CAPS/" + UUID.Random() + "/";
122 GetMeshHandler gmeshHandler = new GetMeshHandler(m_AssetService); 356
123 IRequestHandler reqHandler 357 // Register this as a poll service
124 = new RestHTTPHandler( 358 PollServiceMeshEventArgs args = new PollServiceMeshEventArgs(agentID, m_scene);
125 "GET", 359
126 "/CAPS/" + UUID.Random(), 360 args.Type = PollServiceEventArgs.EventType.Mesh;
127 httpMethod => gmeshHandler.ProcessGetMesh(httpMethod, UUID.Zero, null), 361 MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args);
128 "GetMesh", 362
129 agentID.ToString()); 363 string hostName = m_scene.RegionInfo.ExternalHostName;
364 uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
365 string protocol = "http";
130 366
131 caps.RegisterHandler("GetMesh", reqHandler); 367 if (MainServer.Instance.UseSSL)
368 {
369 hostName = MainServer.Instance.SSLCommonName;
370 port = MainServer.Instance.SSLPort;
371 protocol = "https";
372 }
373 caps.RegisterHandler("GetMesh", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl));
374 m_pollservices.Add(agentID, args);
375 m_capsDict[agentID] = capUrl;
376
377
378
132 } 379 }
133 else 380 else
134 { 381 {
@@ -136,6 +383,95 @@ namespace OpenSim.Region.ClientStack.Linden
136 caps.RegisterHandler("GetMesh", m_URL); 383 caps.RegisterHandler("GetMesh", m_URL);
137 } 384 }
138 } 385 }
386 private void DeregisterCaps(UUID agentID, Caps caps)
387 {
388 string capUrl;
389 PollServiceMeshEventArgs args;
390 if (m_capsDict.TryGetValue(agentID, out capUrl))
391 {
392 MainServer.Instance.RemoveHTTPHandler("", capUrl);
393 m_capsDict.Remove(agentID);
394 }
395 if (m_pollservices.TryGetValue(agentID, out args))
396 {
397 m_pollservices.Remove(agentID);
398 }
399 }
400
401 internal sealed class CapsDataThrottler
402 {
403
404 private volatile int currenttime = 0;
405 private volatile int lastTimeElapsed = 0;
406 private volatile int BytesSent = 0;
407 private int oversizedImages = 0;
408 public CapsDataThrottler(int pBytes, int max, int min)
409 {
410 ThrottleBytes = pBytes;
411 lastTimeElapsed = Util.EnvironmentTickCount();
412 }
413 public bool hasEvents(UUID key, Dictionary<UUID, aPollResponse> responses)
414 {
415 PassTime();
416 // Note, this is called IN LOCK
417 bool haskey = responses.ContainsKey(key);
418 if (!haskey)
419 {
420 return false;
421 }
422 aPollResponse response;
423 if (responses.TryGetValue(key, out response))
424 {
425
426 // Normal
427 if (BytesSent + response.bytes <= ThrottleBytes)
428 {
429 BytesSent += response.bytes;
430 //TimeBasedAction timeBasedAction = new TimeBasedAction { byteRemoval = response.bytes, requestId = key, timeMS = currenttime + 1000, unlockyn = false };
431 //m_actions.Add(timeBasedAction);
432 return true;
433 }
434 // Big textures
435 else if (response.bytes > ThrottleBytes && oversizedImages <= ((ThrottleBytes % 50000) + 1))
436 {
437 Interlocked.Increment(ref oversizedImages);
438 BytesSent += response.bytes;
439 //TimeBasedAction timeBasedAction = new TimeBasedAction { byteRemoval = response.bytes, requestId = key, timeMS = currenttime + (((response.bytes % ThrottleBytes)+1)*1000) , unlockyn = false };
440 //m_actions.Add(timeBasedAction);
441 return true;
442 }
443 else
444 {
445 return false;
446 }
447 }
448
449 return haskey;
450 }
451 public void ProcessTime()
452 {
453 PassTime();
454 }
455
456
457 private void PassTime()
458 {
459 currenttime = Util.EnvironmentTickCount();
460 int timeElapsed = Util.EnvironmentTickCountSubtract(currenttime, lastTimeElapsed);
461 //processTimeBasedActions(responses);
462 if (Util.EnvironmentTickCountSubtract(currenttime, timeElapsed) >= 1000)
463 {
464 lastTimeElapsed = Util.EnvironmentTickCount();
465 BytesSent -= ThrottleBytes;
466 if (BytesSent < 0) BytesSent = 0;
467 if (BytesSent < ThrottleBytes)
468 {
469 oversizedImages = 0;
470 }
471 }
472 }
473 public int ThrottleBytes;
474 }
139 475
140 } 476 }
141} 477}