GetMeshModule.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  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. using System;
  28. using System.Collections;
  29. using System.Collections.Generic;
  30. using System.Collections.Specialized;
  31. using System.Reflection;
  32. using System.IO;
  33. using System.Threading;
  34. using System.Web;
  35. using Mono.Addins;
  36. using OpenSim.Framework.Monitoring;
  37. using log4net;
  38. using Nini.Config;
  39. using OpenMetaverse;
  40. using OpenMetaverse.StructuredData;
  41. using OpenSim.Capabilities.Handlers;
  42. using OpenSim.Framework;
  43. using OpenSim.Framework.Servers;
  44. using OpenSim.Framework.Servers.HttpServer;
  45. using OpenSim.Region.Framework.Interfaces;
  46. using OpenSim.Region.Framework.Scenes;
  47. using OpenSim.Services.Interfaces;
  48. using Caps = OpenSim.Framework.Capabilities.Caps;
  49. namespace OpenSim.Region.ClientStack.Linden
  50. {
  51. [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GetMeshModule")]
  52. public class GetMeshModule : INonSharedRegionModule
  53. {
  54. // private static readonly ILog m_log =
  55. // LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  56. private Scene m_scene;
  57. private IAssetService m_AssetService;
  58. private bool m_Enabled = true;
  59. private string m_URL;
  60. private string m_URL2;
  61. private string m_RedirectURL = null;
  62. private string m_RedirectURL2 = null;
  63. struct aPollRequest
  64. {
  65. public PollServiceMeshEventArgs thepoll;
  66. public UUID reqID;
  67. public Hashtable request;
  68. }
  69. public class aPollResponse
  70. {
  71. public Hashtable response;
  72. public int bytes;
  73. public int lod;
  74. }
  75. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  76. private static GetMeshHandler m_getMeshHandler;
  77. private IAssetService m_assetService = null;
  78. private Dictionary<UUID, string> m_capsDict = new Dictionary<UUID, string>();
  79. private static Thread[] m_workerThreads = null;
  80. private static int m_NumberScenes = 0;
  81. private static OpenSim.Framework.BlockingQueue<aPollRequest> m_queue =
  82. new OpenSim.Framework.BlockingQueue<aPollRequest>();
  83. private Dictionary<UUID, PollServiceMeshEventArgs> m_pollservices = new Dictionary<UUID, PollServiceMeshEventArgs>();
  84. #region Region Module interfaceBase Members
  85. public Type ReplaceableInterface
  86. {
  87. get { return null; }
  88. }
  89. public void Initialise(IConfigSource source)
  90. {
  91. IConfig config = source.Configs["ClientStack.LindenCaps"];
  92. if (config == null)
  93. return;
  94. m_URL = config.GetString("Cap_GetMesh", string.Empty);
  95. // Cap doesn't exist
  96. if (m_URL != string.Empty)
  97. {
  98. m_Enabled = true;
  99. m_RedirectURL = config.GetString("GetMeshRedirectURL");
  100. }
  101. m_URL2 = config.GetString("Cap_GetMesh2", string.Empty);
  102. // Cap doesn't exist
  103. if (m_URL2 != string.Empty)
  104. {
  105. m_Enabled = true;
  106. m_RedirectURL2 = config.GetString("GetMesh2RedirectURL");
  107. }
  108. }
  109. public void AddRegion(Scene pScene)
  110. {
  111. if (!m_Enabled)
  112. return;
  113. m_scene = pScene;
  114. m_assetService = pScene.AssetService;
  115. }
  116. public void RemoveRegion(Scene scene)
  117. {
  118. if (!m_Enabled)
  119. return;
  120. m_scene.EventManager.OnRegisterCaps -= RegisterCaps;
  121. m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps;
  122. m_scene.EventManager.OnThrottleUpdate -= ThrottleUpdate;
  123. m_NumberScenes--;
  124. m_scene = null;
  125. }
  126. public void RegionLoaded(Scene scene)
  127. {
  128. if (!m_Enabled)
  129. return;
  130. m_AssetService = m_scene.RequestModuleInterface<IAssetService>();
  131. m_scene.EventManager.OnRegisterCaps += RegisterCaps;
  132. // We'll reuse the same handler for all requests.
  133. m_getMeshHandler = new GetMeshHandler(m_assetService);
  134. m_scene.EventManager.OnDeregisterCaps += DeregisterCaps;
  135. m_scene.EventManager.OnThrottleUpdate += ThrottleUpdate;
  136. m_NumberScenes++;
  137. if (m_workerThreads == null)
  138. {
  139. m_workerThreads = new Thread[2];
  140. for (uint i = 0; i < 2; i++)
  141. {
  142. m_workerThreads[i] = WorkManager.StartThread(DoMeshRequests,
  143. String.Format("GetMeshWorker{0}", i),
  144. ThreadPriority.Normal,
  145. true,
  146. false,
  147. null,
  148. int.MaxValue);
  149. }
  150. }
  151. }
  152. public void Close()
  153. {
  154. if(m_NumberScenes <= 0 && m_workerThreads != null)
  155. {
  156. m_log.DebugFormat("[GetMeshModule] Closing");
  157. foreach (Thread t in m_workerThreads)
  158. Watchdog.AbortThread(t.ManagedThreadId);
  159. // This will fail on region shutdown. Its harmless.
  160. // Prevent red ink.
  161. try
  162. {
  163. m_queue.Clear();
  164. }
  165. catch {}
  166. }
  167. }
  168. public string Name { get { return "GetMeshModule"; } }
  169. #endregion
  170. private static void DoMeshRequests()
  171. {
  172. while(true)
  173. {
  174. aPollRequest poolreq = m_queue.Dequeue(4500);
  175. Watchdog.UpdateThread();
  176. if(m_NumberScenes <= 0)
  177. return;
  178. if(poolreq.reqID != UUID.Zero)
  179. poolreq.thepoll.Process(poolreq);
  180. }
  181. }
  182. // 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.
  183. public void ThrottleUpdate(ScenePresence p)
  184. {
  185. UUID user = p.UUID;
  186. int imagethrottle = p.ControllingClient.GetAgentThrottleSilent((int)ThrottleOutPacketType.Asset);
  187. PollServiceMeshEventArgs args;
  188. if (m_pollservices.TryGetValue(user, out args))
  189. {
  190. args.UpdateThrottle(imagethrottle);
  191. }
  192. }
  193. private class PollServiceMeshEventArgs : PollServiceEventArgs
  194. {
  195. private List<Hashtable> requests =
  196. new List<Hashtable>();
  197. private Dictionary<UUID, aPollResponse> responses =
  198. new Dictionary<UUID, aPollResponse>();
  199. private Scene m_scene;
  200. private MeshCapsDataThrottler m_throttler;
  201. public PollServiceMeshEventArgs(string uri, UUID pId, Scene scene) :
  202. base(null, uri, null, null, null, pId, int.MaxValue)
  203. {
  204. m_scene = scene;
  205. m_throttler = new MeshCapsDataThrottler(100000);
  206. // x is request id, y is userid
  207. HasEvents = (x, y) =>
  208. {
  209. lock (responses)
  210. {
  211. bool ret = m_throttler.hasEvents(x, responses);
  212. return ret;
  213. }
  214. };
  215. GetEvents = (x, y) =>
  216. {
  217. lock (responses)
  218. {
  219. try
  220. {
  221. return responses[x].response;
  222. }
  223. finally
  224. {
  225. responses.Remove(x);
  226. m_throttler.PassTime();
  227. }
  228. }
  229. };
  230. // x is request id, y is request data hashtable
  231. Request = (x, y) =>
  232. {
  233. aPollRequest reqinfo = new aPollRequest();
  234. reqinfo.thepoll = this;
  235. reqinfo.reqID = x;
  236. reqinfo.request = y;
  237. m_queue.Enqueue(reqinfo);
  238. m_throttler.PassTime();
  239. };
  240. // this should never happen except possible on shutdown
  241. NoEvents = (x, y) =>
  242. {
  243. /*
  244. lock (requests)
  245. {
  246. Hashtable request = requests.Find(id => id["RequestID"].ToString() == x.ToString());
  247. requests.Remove(request);
  248. }
  249. */
  250. Hashtable response = new Hashtable();
  251. response["int_response_code"] = 500;
  252. response["str_response_string"] = "Script timeout";
  253. response["content_type"] = "text/plain";
  254. response["keepalive"] = false;
  255. response["reusecontext"] = false;
  256. return response;
  257. };
  258. }
  259. public void Process(aPollRequest requestinfo)
  260. {
  261. Hashtable response;
  262. UUID requestID = requestinfo.reqID;
  263. if(m_scene.ShuttingDown)
  264. return;
  265. // If the avatar is gone, don't bother to get the texture
  266. if (m_scene.GetScenePresence(Id) == null)
  267. {
  268. response = new Hashtable();
  269. response["int_response_code"] = 500;
  270. response["str_response_string"] = "Script timeout";
  271. response["content_type"] = "text/plain";
  272. response["keepalive"] = false;
  273. response["reusecontext"] = false;
  274. lock (responses)
  275. responses[requestID] = new aPollResponse() { bytes = 0, response = response, lod = 0 };
  276. return;
  277. }
  278. response = m_getMeshHandler.Handle(requestinfo.request);
  279. lock (responses)
  280. {
  281. responses[requestID] = new aPollResponse()
  282. {
  283. bytes = (int)response["int_bytes"],
  284. lod = (int)response["int_lod"],
  285. response = response
  286. };
  287. }
  288. m_throttler.PassTime();
  289. }
  290. internal void UpdateThrottle(int pthrottle)
  291. {
  292. int tmp = 2 * pthrottle;
  293. if(tmp < 10000)
  294. tmp = 10000;
  295. m_throttler.ThrottleBytes = tmp;
  296. }
  297. }
  298. public void RegisterCaps(UUID agentID, Caps caps)
  299. {
  300. // UUID capID = UUID.Random();
  301. if (m_URL == "localhost")
  302. {
  303. string capUrl = "/CAPS/" + UUID.Random() + "/";
  304. // Register this as a poll service
  305. PollServiceMeshEventArgs args = new PollServiceMeshEventArgs(capUrl, agentID, m_scene);
  306. args.Type = PollServiceEventArgs.EventType.Mesh;
  307. MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args);
  308. string hostName = m_scene.RegionInfo.ExternalHostName;
  309. uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
  310. string protocol = "http";
  311. if (MainServer.Instance.UseSSL)
  312. {
  313. hostName = MainServer.Instance.SSLCommonName;
  314. port = MainServer.Instance.SSLPort;
  315. protocol = "https";
  316. }
  317. caps.RegisterHandler("GetMesh", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl));
  318. m_pollservices[agentID] = args;
  319. m_capsDict[agentID] = capUrl;
  320. }
  321. else
  322. {
  323. caps.RegisterHandler("GetMesh", m_URL);
  324. }
  325. }
  326. private void DeregisterCaps(UUID agentID, Caps caps)
  327. {
  328. string capUrl;
  329. PollServiceMeshEventArgs args;
  330. if (m_capsDict.TryGetValue(agentID, out capUrl))
  331. {
  332. MainServer.Instance.RemoveHTTPHandler("", capUrl);
  333. m_capsDict.Remove(agentID);
  334. }
  335. if (m_pollservices.TryGetValue(agentID, out args))
  336. {
  337. m_pollservices.Remove(agentID);
  338. }
  339. }
  340. internal sealed class MeshCapsDataThrottler
  341. {
  342. private double lastTimeElapsed = 0;
  343. private double BytesSent = 0;
  344. public MeshCapsDataThrottler(int pBytes)
  345. {
  346. if(pBytes < 10000)
  347. pBytes = 10000;
  348. ThrottleBytes = pBytes;
  349. lastTimeElapsed = Util.GetTimeStampMS();
  350. }
  351. public bool hasEvents(UUID key, Dictionary<UUID, aPollResponse> responses)
  352. {
  353. PassTime();
  354. // Note, this is called IN LOCK
  355. bool haskey = responses.ContainsKey(key);
  356. if (!haskey)
  357. {
  358. return false;
  359. }
  360. aPollResponse response;
  361. if (responses.TryGetValue(key, out response))
  362. {
  363. // Normal
  364. if (BytesSent <= ThrottleBytes)
  365. {
  366. BytesSent += response.bytes;
  367. return true;
  368. }
  369. else
  370. {
  371. return false;
  372. }
  373. }
  374. return haskey;
  375. }
  376. public void PassTime()
  377. {
  378. double currenttime = Util.GetTimeStampMS();
  379. double timeElapsed = currenttime - lastTimeElapsed;
  380. if(timeElapsed < 50.0)
  381. return;
  382. int add = (int)(ThrottleBytes * timeElapsed * 0.001);
  383. if (add >= 1000)
  384. {
  385. lastTimeElapsed = currenttime;
  386. BytesSent -= add;
  387. if (BytesSent < 0) BytesSent = 0;
  388. }
  389. }
  390. public int ThrottleBytes;
  391. }
  392. }
  393. }