aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs404
1 files changed, 387 insertions, 17 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
index f57d857..b5a70040 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,12 +60,50 @@ 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
60 private string m_URL2; 64 private string m_URL2;
61 private string m_RedirectURL = null; 65 private string m_RedirectURL = null;
62 private string m_RedirectURL2 = null; 66 private string m_RedirectURL2 = null;
67
68 struct aPollRequest
69 {
70 public PollServiceMeshEventArgs thepoll;
71 public UUID reqID;
72 public Hashtable request;
73 }
74
75 public class aPollResponse
76 {
77 public Hashtable response;
78 public int bytes;
79 public int lod;
80 }
81
82
83 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
84
85 private static GetMeshHandler m_getMeshHandler;
86
87 private IAssetService m_assetService = null;
88
89 private Dictionary<UUID, string> m_capsDict = new Dictionary<UUID, string>();
90 private static Thread[] m_workerThreads = null;
91
92 private static OpenMetaverse.BlockingQueue<aPollRequest> m_queue =
93 new OpenMetaverse.BlockingQueue<aPollRequest>();
94
95 private Dictionary<UUID, PollServiceMeshEventArgs> m_pollservices = new Dictionary<UUID, PollServiceMeshEventArgs>();
96
63 97
64 #region Region Module interfaceBase Members 98 #region Region Module interfaceBase Members
65 99
100 ~GetMeshModule()
101 {
102 foreach (Thread t in m_workerThreads)
103 Watchdog.AbortThread(t.ManagedThreadId);
104
105 }
106
66 public Type ReplaceableInterface 107 public Type ReplaceableInterface
67 { 108 {
68 get { return null; } 109 get { return null; }
@@ -87,6 +128,7 @@ namespace OpenSim.Region.ClientStack.Linden
87 if (m_URL2 != string.Empty) 128 if (m_URL2 != string.Empty)
88 { 129 {
89 m_Enabled = true; 130 m_Enabled = true;
131
90 m_RedirectURL2 = config.GetString("GetMesh2RedirectURL"); 132 m_RedirectURL2 = config.GetString("GetMesh2RedirectURL");
91 } 133 }
92 } 134 }
@@ -97,6 +139,8 @@ namespace OpenSim.Region.ClientStack.Linden
97 return; 139 return;
98 140
99 m_scene = pScene; 141 m_scene = pScene;
142
143 m_assetService = pScene.AssetService;
100 } 144 }
101 145
102 public void RemoveRegion(Scene scene) 146 public void RemoveRegion(Scene scene)
@@ -105,6 +149,9 @@ namespace OpenSim.Region.ClientStack.Linden
105 return; 149 return;
106 150
107 m_scene.EventManager.OnRegisterCaps -= RegisterCaps; 151 m_scene.EventManager.OnRegisterCaps -= RegisterCaps;
152 m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps;
153 m_scene.EventManager.OnThrottleUpdate -= ThrottleUpdate;
154
108 m_scene = null; 155 m_scene = null;
109 } 156 }
110 157
@@ -115,6 +162,27 @@ namespace OpenSim.Region.ClientStack.Linden
115 162
116 m_AssetService = m_scene.RequestModuleInterface<IAssetService>(); 163 m_AssetService = m_scene.RequestModuleInterface<IAssetService>();
117 m_scene.EventManager.OnRegisterCaps += RegisterCaps; 164 m_scene.EventManager.OnRegisterCaps += RegisterCaps;
165 // We'll reuse the same handler for all requests.
166 m_getMeshHandler = new GetMeshHandler(m_assetService);
167 m_scene.EventManager.OnDeregisterCaps += DeregisterCaps;
168 m_scene.EventManager.OnThrottleUpdate += ThrottleUpdate;
169
170 if (m_workerThreads == null)
171 {
172 m_workerThreads = new Thread[2];
173
174 for (uint i = 0; i < 2; i++)
175 {
176 m_workerThreads[i] = WorkManager.StartThread(DoMeshRequests,
177 String.Format("MeshWorkerThread{0}", i),
178 ThreadPriority.Normal,
179 false,
180 false,
181 null,
182 int.MaxValue);
183 }
184 }
185
118 } 186 }
119 187
120 188
@@ -124,44 +192,346 @@ namespace OpenSim.Region.ClientStack.Linden
124 192
125 #endregion 193 #endregion
126 194
195 private void DoMeshRequests()
196 {
197 while (true)
198 {
199 aPollRequest poolreq = m_queue.Dequeue();
127 200
128 public void RegisterCaps(UUID agentID, Caps caps) 201 poolreq.thepoll.Process(poolreq);
202 }
203 }
204
205 // 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.
206 public void ThrottleUpdate(ScenePresence p)
207 {
208 UUID user = p.UUID;
209 int imagethrottle = p.ControllingClient.GetAgentThrottleSilent((int)ThrottleOutPacketType.Asset);
210 PollServiceMeshEventArgs args;
211 if (m_pollservices.TryGetValue(user, out args))
212 {
213 args.UpdateThrottle(imagethrottle, p);
214 }
215 }
216
217 private class PollServiceMeshEventArgs : PollServiceEventArgs
129 { 218 {
130 UUID capID = UUID.Random(); 219 private List<Hashtable> requests =
131 bool getMeshRegistered = false; 220 new List<Hashtable>();
221 private Dictionary<UUID, aPollResponse> responses =
222 new Dictionary<UUID, aPollResponse>();
132 223
133 if (m_URL == string.Empty) 224 private Scene m_scene;
225 private MeshCapsDataThrottler m_throttler;
226 public PollServiceMeshEventArgs(string uri, UUID pId, Scene scene) :
227 base(null, uri, null, null, null, pId, int.MaxValue)
134 { 228 {
229 m_scene = scene;
230 m_throttler = new MeshCapsDataThrottler(100000, 1400000, 10000, scene, pId);
231 // x is request id, y is userid
232 HasEvents = (x, y) =>
233 {
234 lock (responses)
235 {
236 bool ret = m_throttler.hasEvents(x, responses);
237 m_throttler.ProcessTime();
238 return ret;
239
240 }
241 };
242 GetEvents = (x, y) =>
243 {
244 lock (responses)
245 {
246 try
247 {
248 return responses[x].response;
249 }
250 finally
251 {
252 m_throttler.ProcessTime();
253 responses.Remove(x);
254 }
255 }
256 };
257 // x is request id, y is request data hashtable
258 Request = (x, y) =>
259 {
260 aPollRequest reqinfo = new aPollRequest();
261 reqinfo.thepoll = this;
262 reqinfo.reqID = x;
263 reqinfo.request = y;
264
265 m_queue.Enqueue(reqinfo);
266 };
267
268 // this should never happen except possible on shutdown
269 NoEvents = (x, y) =>
270 {
271 /*
272 lock (requests)
273 {
274 Hashtable request = requests.Find(id => id["RequestID"].ToString() == x.ToString());
275 requests.Remove(request);
276 }
277 */
278 Hashtable response = new Hashtable();
279
280 response["int_response_code"] = 500;
281 response["str_response_string"] = "Script timeout";
282 response["content_type"] = "text/plain";
283 response["keepalive"] = false;
284 response["reusecontext"] = false;
285
286 return response;
287 };
288 }
289
290 public void Process(aPollRequest requestinfo)
291 {
292 Hashtable response;
293
294 UUID requestID = requestinfo.reqID;
295
296 // If the avatar is gone, don't bother to get the texture
297 if (m_scene.GetScenePresence(Id) == null)
298 {
299 response = new Hashtable();
300
301 response["int_response_code"] = 500;
302 response["str_response_string"] = "Script timeout";
303 response["content_type"] = "text/plain";
304 response["keepalive"] = false;
305 response["reusecontext"] = false;
306
307 lock (responses)
308 responses[requestID] = new aPollResponse() { bytes = 0, response = response, lod = 0 };
309
310 return;
311 }
312
313 response = m_getMeshHandler.Handle(requestinfo.request);
314 lock (responses)
315 {
316 responses[requestID] = new aPollResponse()
317 {
318 bytes = (int)response["int_bytes"],
319 lod = (int)response["int_lod"],
320 response = response
321 };
135 322
323 }
324 m_throttler.ProcessTime();
136 } 325 }
137 else if (m_URL == "localhost") 326
327 internal void UpdateThrottle(int pimagethrottle, ScenePresence p)
138 { 328 {
139 getMeshRegistered = true; 329 m_throttler.UpdateThrottle(pimagethrottle, p);
140 caps.RegisterHandler( 330 }
141 "GetMesh", 331 }
142 new GetMeshHandler("/CAPS/" + capID + "/", m_AssetService, "GetMesh", agentID.ToString(), m_RedirectURL)); 332
333 public void RegisterCaps(UUID agentID, Caps caps)
334 {
335// UUID capID = UUID.Random();
336 if (m_URL == "localhost")
337 {
338 string capUrl = "/CAPS/" + UUID.Random() + "/";
339
340 // Register this as a poll service
341 PollServiceMeshEventArgs args = new PollServiceMeshEventArgs(capUrl, agentID, m_scene);
342
343 args.Type = PollServiceEventArgs.EventType.Mesh;
344 MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args);
345
346 string hostName = m_scene.RegionInfo.ExternalHostName;
347 uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
348 string protocol = "http";
349
350 if (MainServer.Instance.UseSSL)
351 {
352 hostName = MainServer.Instance.SSLCommonName;
353 port = MainServer.Instance.SSLPort;
354 protocol = "https";
355 }
356 caps.RegisterHandler("GetMesh", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl));
357 m_pollservices[agentID] = args;
358 m_capsDict[agentID] = capUrl;
143 } 359 }
144 else 360 else
145 { 361 {
146 caps.RegisterHandler("GetMesh", m_URL); 362 caps.RegisterHandler("GetMesh", m_URL);
147 } 363 }
364 }
365
366 private void DeregisterCaps(UUID agentID, Caps caps)
367 {
368 string capUrl;
369 PollServiceMeshEventArgs args;
370 if (m_capsDict.TryGetValue(agentID, out capUrl))
371 {
372 MainServer.Instance.RemoveHTTPHandler("", capUrl);
373 m_capsDict.Remove(agentID);
374 }
375 if (m_pollservices.TryGetValue(agentID, out args))
376 {
377 m_pollservices.Remove(agentID);
378 }
379 }
380
381 internal sealed class MeshCapsDataThrottler
382 {
383
384 private volatile int currenttime = 0;
385 private volatile int lastTimeElapsed = 0;
386 private volatile int BytesSent = 0;
387 private int Lod3 = 0;
388 private int Lod2 = 0;
389 private int Lod1 = 0;
390 private int UserSetThrottle = 0;
391 private int UDPSetThrottle = 0;
392 private int CapSetThrottle = 0;
393 private float CapThrottleDistributon = 0.30f;
394 private readonly Scene m_scene;
395 private ThrottleOutPacketType Throttle;
396 private readonly UUID User;
397
398 public MeshCapsDataThrottler(int pBytes, int max, int min, Scene pScene, UUID puser)
399 {
400 ThrottleBytes = pBytes;
401 lastTimeElapsed = Util.EnvironmentTickCount();
402 Throttle = ThrottleOutPacketType.Asset;
403 m_scene = pScene;
404 User = puser;
405 }
406
407
408 public bool hasEvents(UUID key, Dictionary<UUID, aPollResponse> responses)
409 {
410 const float ThirtyPercent = 0.30f;
411 const float FivePercent = 0.05f;
412 PassTime();
413 // Note, this is called IN LOCK
414 bool haskey = responses.ContainsKey(key);
415
416 if (responses.Count > 2)
417 {
418 SplitThrottle(ThirtyPercent);
419 }
420 else
421 {
422 SplitThrottle(FivePercent);
423 }
148 424
149 if(m_URL2 == string.Empty) 425 if (!haskey)
426 {
427 return false;
428 }
429 aPollResponse response;
430 if (responses.TryGetValue(key, out response))
431 {
432 float LOD3Over = (((ThrottleBytes*CapThrottleDistributon)%50000) + 1);
433 float LOD2Over = (((ThrottleBytes*CapThrottleDistributon)%10000) + 1);
434 // Normal
435 if (BytesSent + response.bytes <= ThrottleBytes)
436 {
437 BytesSent += response.bytes;
438
439 return true;
440 }
441 // Lod3 Over Throttle protection to keep things processing even when the throttle bandwidth is set too little.
442 else if (response.bytes > ThrottleBytes && Lod3 <= ((LOD3Over < 1)? 1: LOD3Over) )
443 {
444 Interlocked.Increment(ref Lod3);
445 BytesSent += response.bytes;
446
447 return true;
448 }
449 // Lod2 Over Throttle protection to keep things processing even when the throttle bandwidth is set too little.
450 else if (response.bytes > ThrottleBytes && Lod2 <= ((LOD2Over < 1) ? 1 : LOD2Over))
451 {
452 Interlocked.Increment(ref Lod2);
453 BytesSent += response.bytes;
454
455 return true;
456 }
457 else
458 {
459 return false;
460 }
461 }
462
463 return haskey;
464 }
465 public void SubtractBytes(int bytes,int lod)
466 {
467 BytesSent -= bytes;
468 }
469 private void SplitThrottle(float percentMultiplier)
150 { 470 {
151 471
472 if (CapThrottleDistributon != percentMultiplier) // don't switch it if it's already set at the % multipler
473 {
474 CapThrottleDistributon = percentMultiplier;
475 ScenePresence p;
476 if (m_scene.TryGetScenePresence(User, out p)) // If we don't get a user they're not here anymore.
477 {
478// AlterThrottle(UserSetThrottle, p);
479 UpdateThrottle(UserSetThrottle, p);
480 }
481 }
152 } 482 }
153 else if (m_URL2 == "localhost") 483
484 public void ProcessTime()
154 { 485 {
155 if (!getMeshRegistered) 486 PassTime();
487 }
488
489
490 private void PassTime()
491 {
492 currenttime = Util.EnvironmentTickCount();
493 int timeElapsed = Util.EnvironmentTickCountSubtract(currenttime, lastTimeElapsed);
494 //processTimeBasedActions(responses);
495 if (currenttime - timeElapsed >= 1000)
156 { 496 {
157 caps.RegisterHandler( 497 lastTimeElapsed = Util.EnvironmentTickCount();
158 "GetMesh2", 498 BytesSent -= ThrottleBytes;
159 new GetMeshHandler("/CAPS/" + capID + "/", m_AssetService, "GetMesh2", agentID.ToString(), m_RedirectURL2)); 499 if (BytesSent < 0) BytesSent = 0;
500 if (BytesSent < ThrottleBytes)
501 {
502 Lod3 = 0;
503 Lod2 = 0;
504 Lod1 = 0;
505 }
160 } 506 }
161 } 507 }
162 else 508 private void AlterThrottle(int setting, ScenePresence p)
509 {
510 p.ControllingClient.SetAgentThrottleSilent((int)Throttle,setting);
511 }
512
513 public int ThrottleBytes
163 { 514 {
164 caps.RegisterHandler("GetMesh2", m_URL2); 515 get { return CapSetThrottle; }
516 set { CapSetThrottle = value; }
517 }
518
519 internal void UpdateThrottle(int pimagethrottle, ScenePresence p)
520 {
521 // Client set throttle !
522 UserSetThrottle = pimagethrottle;
523 CapSetThrottle = (int)(pimagethrottle*CapThrottleDistributon);
524// UDPSetThrottle = (int) (pimagethrottle*(100 - CapThrottleDistributon));
525
526 float udp = 1.0f - CapThrottleDistributon;
527 if(udp < 0.7f)
528 udp = 0.7f;
529 UDPSetThrottle = (int) ((float)pimagethrottle * udp);
530 if (CapSetThrottle < 4068)
531 CapSetThrottle = 4068; // at least two discovery mesh
532 p.ControllingClient.SetAgentThrottleSilent((int) Throttle, UDPSetThrottle);
533 ProcessTime();
534
165 } 535 }
166 } 536 }
167 537