OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
GetMeshModule.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.org/
3  * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the OpenSimulator Project nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Collections.Specialized;
32 using System.Reflection;
33 using System.IO;
34 using System.Threading;
35 using System.Web;
36 using Mono.Addins;
37 using OpenSim.Framework.Monitoring;
38 using log4net;
39 using Nini.Config;
40 using OpenMetaverse;
41 using OpenMetaverse.StructuredData;
42 using OpenSim.Capabilities.Handlers;
43 using OpenSim.Framework;
44 using OpenSim.Framework.Servers;
45 using OpenSim.Framework.Servers.HttpServer;
46 using OpenSim.Region.Framework.Interfaces;
47 using OpenSim.Region.Framework.Scenes;
48 using OpenSim.Services.Interfaces;
50 
51 namespace OpenSim.Region.ClientStack.Linden
52 {
53  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GetMeshModule")]
55  {
56 // private static readonly ILog m_log =
57 // LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
58 
59  private Scene m_scene;
60  private IAssetService m_AssetService;
61  private bool m_Enabled = true;
62  private string m_URL;
63 
64  private string m_URL2;
65  private string m_RedirectURL = 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  private static int m_NumberScenes = 0;
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 
97 
98  #region Region Module interfaceBase Members
99 
100  public Type ReplaceableInterface
101  {
102  get { return null; }
103  }
104 
105  public void Initialise(IConfigSource source)
106  {
107  IConfig config = source.Configs["ClientStack.LindenCaps"];
108  if (config == null)
109  return;
110 
111  m_URL = config.GetString("Cap_GetMesh", string.Empty);
112  // Cap doesn't exist
113  if (m_URL != string.Empty)
114  {
115  m_Enabled = true;
116  m_RedirectURL = config.GetString("GetMeshRedirectURL");
117  }
118 
119  m_URL2 = config.GetString("Cap_GetMesh2", string.Empty);
120  // Cap doesn't exist
121  if (m_URL2 != string.Empty)
122  {
123  m_Enabled = true;
124 
125  m_RedirectURL2 = config.GetString("GetMesh2RedirectURL");
126  }
127  }
128 
129  public void AddRegion(Scene pScene)
130  {
131  if (!m_Enabled)
132  return;
133 
134  m_scene = pScene;
135 
136  m_assetService = pScene.AssetService;
137  }
138 
139  public void RemoveRegion(Scene scene)
140  {
141  if (!m_Enabled)
142  return;
143 
144  m_scene.EventManager.OnRegisterCaps -= RegisterCaps;
145  m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps;
146  m_scene.EventManager.OnThrottleUpdate -= ThrottleUpdate;
147  m_NumberScenes--;
148  m_scene = null;
149  }
150 
151  public void RegionLoaded(Scene scene)
152  {
153  if (!m_Enabled)
154  return;
155 
156  m_AssetService = m_scene.RequestModuleInterface<IAssetService>();
157  m_scene.EventManager.OnRegisterCaps += RegisterCaps;
158  // We'll reuse the same handler for all requests.
159  m_getMeshHandler = new GetMeshHandler(m_assetService);
160  m_scene.EventManager.OnDeregisterCaps += DeregisterCaps;
161  m_scene.EventManager.OnThrottleUpdate += ThrottleUpdate;
162 
163  m_NumberScenes++;
164 
165  if (m_workerThreads == null)
166  {
167  m_workerThreads = new Thread[2];
168 
169  for (uint i = 0; i < 2; i++)
170  {
171  m_workerThreads[i] = WorkManager.StartThread(DoMeshRequests,
172  String.Format("GetMeshWorker{0}", i),
173  ThreadPriority.Normal,
174  false,
175  false,
176  null,
177  int.MaxValue);
178  }
179  }
180  }
181 
182  public void Close()
183  {
184  if(m_NumberScenes <= 0 && m_workerThreads != null)
185  {
186  m_log.DebugFormat("[GetMeshModule] Closing");
187  foreach (Thread t in m_workerThreads)
188  Watchdog.AbortThread(t.ManagedThreadId);
189  m_queue.Clear();
190  }
191  }
192 
193  public string Name { get { return "GetMeshModule"; } }
194 
195  #endregion
196 
197  private static void DoMeshRequests()
198  {
199  while(true)
200  {
201  aPollRequest poolreq = m_queue.Dequeue();
202  Watchdog.UpdateThread();
203  poolreq.thepoll.Process(poolreq);
204  }
205  }
206 
207  // 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.
209  {
210  UUID user = p.UUID;
211  int imagethrottle = p.ControllingClient.GetAgentThrottleSilent((int)ThrottleOutPacketType.Asset);
212  PollServiceMeshEventArgs args;
213  if (m_pollservices.TryGetValue(user, out args))
214  {
215  args.UpdateThrottle(imagethrottle, p);
216  }
217  }
218 
219  private class PollServiceMeshEventArgs : PollServiceEventArgs
220  {
221  private List<Hashtable> requests =
222  new List<Hashtable>();
223  private Dictionary<UUID, aPollResponse> responses =
224  new Dictionary<UUID, aPollResponse>();
225 
226  private Scene m_scene;
227  private MeshCapsDataThrottler m_throttler;
228  public PollServiceMeshEventArgs(string uri, UUID pId, Scene scene) :
229  base(null, uri, null, null, null, pId, int.MaxValue)
230  {
231  m_scene = scene;
232  m_throttler = new MeshCapsDataThrottler(100000, 1400000, 10000, scene, pId);
233  // x is request id, y is userid
234  HasEvents = (x, y) =>
235  {
236  lock (responses)
237  {
238  bool ret = m_throttler.hasEvents(x, responses);
239  m_throttler.ProcessTime();
240  return ret;
241 
242  }
243  };
244  GetEvents = (x, y) =>
245  {
246  lock (responses)
247  {
248  try
249  {
250  return responses[x].response;
251  }
252  finally
253  {
254  m_throttler.ProcessTime();
255  responses.Remove(x);
256  }
257  }
258  };
259  // x is request id, y is request data hashtable
260  Request = (x, y) =>
261  {
262  aPollRequest reqinfo = new aPollRequest();
263  reqinfo.thepoll = this;
264  reqinfo.reqID = x;
265  reqinfo.request = y;
266 
267  m_queue.Enqueue(reqinfo);
268  };
269 
270  // this should never happen except possible on shutdown
271  NoEvents = (x, y) =>
272  {
273  /*
274  lock (requests)
275  {
276  Hashtable request = requests.Find(id => id["RequestID"].ToString() == x.ToString());
277  requests.Remove(request);
278  }
279  */
280  Hashtable response = new Hashtable();
281 
282  response["int_response_code"] = 500;
283  response["str_response_string"] = "Script timeout";
284  response["content_type"] = "text/plain";
285  response["keepalive"] = false;
286  response["reusecontext"] = false;
287 
288  return response;
289  };
290  }
291 
292  public void Process(aPollRequest requestinfo)
293  {
294  Hashtable response;
295 
296  UUID requestID = requestinfo.reqID;
297 
298  if(m_scene.ShuttingDown)
299  return;
300 
301  // If the avatar is gone, don't bother to get the texture
302  if (m_scene.GetScenePresence(Id) == null)
303  {
304  response = new Hashtable();
305 
306  response["int_response_code"] = 500;
307  response["str_response_string"] = "Script timeout";
308  response["content_type"] = "text/plain";
309  response["keepalive"] = false;
310  response["reusecontext"] = false;
311 
312  lock (responses)
313  responses[requestID] = new aPollResponse() { bytes = 0, response = response, lod = 0 };
314 
315  return;
316  }
317 
318  response = m_getMeshHandler.Handle(requestinfo.request);
319  lock (responses)
320  {
321  responses[requestID] = new aPollResponse()
322  {
323  bytes = (int)response["int_bytes"],
324  lod = (int)response["int_lod"],
325  response = response
326  };
327 
328  }
329  m_throttler.ProcessTime();
330  }
331 
332  internal void UpdateThrottle(int pimagethrottle, ScenePresence p)
333  {
334  m_throttler.UpdateThrottle(pimagethrottle, p);
335  }
336  }
337 
338  public void RegisterCaps(UUID agentID, Caps caps)
339  {
340 // UUID capID = UUID.Random();
341  if (m_URL == "localhost")
342  {
343  string capUrl = "/CAPS/" + UUID.Random() + "/";
344 
345  // Register this as a poll service
346  PollServiceMeshEventArgs args = new PollServiceMeshEventArgs(capUrl, agentID, m_scene);
347 
348  args.Type = PollServiceEventArgs.EventType.Mesh;
349  MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args);
350 
351  string hostName = m_scene.RegionInfo.ExternalHostName;
352  uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
353  string protocol = "http";
354 
356  {
357  hostName = MainServer.Instance.SSLCommonName;
358  port = MainServer.Instance.SSLPort;
359  protocol = "https";
360  }
361  caps.RegisterHandler("GetMesh", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl));
362  m_pollservices[agentID] = args;
363  m_capsDict[agentID] = capUrl;
364  }
365  else
366  {
367  caps.RegisterHandler("GetMesh", m_URL);
368  }
369  }
370 
371  private void DeregisterCaps(UUID agentID, Caps caps)
372  {
373  string capUrl;
374  PollServiceMeshEventArgs args;
375  if (m_capsDict.TryGetValue(agentID, out capUrl))
376  {
377  MainServer.Instance.RemoveHTTPHandler("", capUrl);
378  m_capsDict.Remove(agentID);
379  }
380  if (m_pollservices.TryGetValue(agentID, out args))
381  {
382  m_pollservices.Remove(agentID);
383  }
384  }
385 
386  internal sealed class MeshCapsDataThrottler
387  {
388 
389  private volatile int currenttime = 0;
390  private volatile int lastTimeElapsed = 0;
391  private volatile int BytesSent = 0;
392  private int Lod3 = 0;
393  private int Lod2 = 0;
394 // private int Lod1 = 0;
395  private int UserSetThrottle = 0;
396  private int UDPSetThrottle = 0;
397  private int CapSetThrottle = 0;
398  private float CapThrottleDistributon = 0.30f;
399  private readonly Scene m_scene;
400  private ThrottleOutPacketType Throttle;
401  private readonly UUID User;
402 
403  public MeshCapsDataThrottler(int pBytes, int max, int min, Scene pScene, UUID puser)
404  {
405  ThrottleBytes = pBytes;
406  lastTimeElapsed = Util.EnvironmentTickCount();
407  Throttle = ThrottleOutPacketType.Asset;
408  m_scene = pScene;
409  User = puser;
410  }
411 
412  public bool hasEvents(UUID key, Dictionary<UUID, aPollResponse> responses)
413  {
414  const float ThirtyPercent = 0.30f;
415  const float FivePercent = 0.05f;
416  PassTime();
417  // Note, this is called IN LOCK
418  bool haskey = responses.ContainsKey(key);
419 
420  if (responses.Count > 2)
421  {
422  SplitThrottle(ThirtyPercent);
423  }
424  else
425  {
426  SplitThrottle(FivePercent);
427  }
428 
429  if (!haskey)
430  {
431  return false;
432  }
433  aPollResponse response;
434  if (responses.TryGetValue(key, out response))
435  {
436  float LOD3Over = (((ThrottleBytes*CapThrottleDistributon)%50000) + 1);
437  float LOD2Over = (((ThrottleBytes*CapThrottleDistributon)%10000) + 1);
438  // Normal
439  if (BytesSent + response.bytes <= ThrottleBytes)
440  {
441  BytesSent += response.bytes;
442 
443  return true;
444  }
445  // Lod3 Over Throttle protection to keep things processing even when the throttle bandwidth is set too little.
446  else if (response.bytes > ThrottleBytes && Lod3 <= ((LOD3Over < 1)? 1: LOD3Over) )
447  {
448  Interlocked.Increment(ref Lod3);
449  BytesSent += response.bytes;
450 
451  return true;
452  }
453  // Lod2 Over Throttle protection to keep things processing even when the throttle bandwidth is set too little.
454  else if (response.bytes > ThrottleBytes && Lod2 <= ((LOD2Over < 1) ? 1 : LOD2Over))
455  {
456  Interlocked.Increment(ref Lod2);
457  BytesSent += response.bytes;
458 
459  return true;
460  }
461  else
462  {
463  return false;
464  }
465  }
466 
467  return haskey;
468  }
469  public void SubtractBytes(int bytes,int lod)
470  {
471  BytesSent -= bytes;
472  }
473  private void SplitThrottle(float percentMultiplier)
474  {
475 
476  if (CapThrottleDistributon != percentMultiplier) // don't switch it if it's already set at the % multipler
477  {
478  CapThrottleDistributon = percentMultiplier;
479  ScenePresence p;
480  if (m_scene.TryGetScenePresence(User, out p)) // If we don't get a user they're not here anymore.
481  {
482 // AlterThrottle(UserSetThrottle, p);
483  UpdateThrottle(UserSetThrottle, p);
484  }
485  }
486  }
487 
488  public void ProcessTime()
489  {
490  PassTime();
491  }
492 
493  private void PassTime()
494  {
495  currenttime = Util.EnvironmentTickCount();
496  int timeElapsed = Util.EnvironmentTickCountSubtract(currenttime, lastTimeElapsed);
497  //processTimeBasedActions(responses);
498  if (currenttime - timeElapsed >= 1000)
499  {
500  lastTimeElapsed = Util.EnvironmentTickCount();
501  BytesSent -= ThrottleBytes;
502  if (BytesSent < 0) BytesSent = 0;
503  if (BytesSent < ThrottleBytes)
504  {
505  Lod3 = 0;
506  Lod2 = 0;
507 // Lod1 = 0;
508  }
509  }
510  }
511 
512  private void AlterThrottle(int setting, ScenePresence p)
513  {
514  p.ControllingClient.SetAgentThrottleSilent((int)Throttle,setting);
515  }
516 
517  public int ThrottleBytes
518  {
519  get { return CapSetThrottle; }
520  set { CapSetThrottle = value; }
521  }
522 
523  internal void UpdateThrottle(int pimagethrottle, ScenePresence p)
524  {
525  // Client set throttle !
526  UserSetThrottle = pimagethrottle;
527  CapSetThrottle = (int)(pimagethrottle*CapThrottleDistributon);
528 // UDPSetThrottle = (int) (pimagethrottle*(100 - CapThrottleDistributon));
529 
530  float udp = 1.0f - CapThrottleDistributon;
531  if(udp < 0.7f)
532  udp = 0.7f;
533  UDPSetThrottle = (int) ((float)pimagethrottle * udp);
534  if (CapSetThrottle < 4068)
535  CapSetThrottle = 4068; // at least two discovery mesh
536  p.ControllingClient.SetAgentThrottleSilent((int) Throttle, UDPSetThrottle);
537  ProcessTime();
538 
539  }
540  }
541  }
542 }
void Initialise(IConfigSource source)
This is called to initialize the region module. For shared modules, this is called exactly once...
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
OpenSim.Framework.Capabilities.Caps Caps
OpenSim.Framework.Capabilities.Caps Caps
OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString key
Definition: ICM_Api.cs:31
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
static BaseHttpServer Instance
Set the main HTTP server instance.
Definition: MainServer.cs:83
void RegisterCaps(UUID agentID, Caps caps)
void AddRegion(Scene pScene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...