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.cs456
1 files changed, 456 insertions, 0 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
new file mode 100644
index 0000000..ba917e39
--- /dev/null
+++ b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
@@ -0,0 +1,456 @@
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
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Collections.Specialized;
32using System.Reflection;
33using System.IO;
34using System.Threading;
35using System.Web;
36using Mono.Addins;
37using OpenSim.Framework.Monitoring;
38using log4net;
39using Nini.Config;
40using OpenMetaverse;
41using OpenMetaverse.StructuredData;
42using OpenSim.Capabilities.Handlers;
43using OpenSim.Framework;
44using OpenSim.Framework.Servers;
45using OpenSim.Framework.Servers.HttpServer;
46using OpenSim.Region.Framework.Interfaces;
47using OpenSim.Region.Framework.Scenes;
48using OpenSim.Services.Interfaces;
49using Caps = OpenSim.Framework.Capabilities.Caps;
50
51namespace OpenSim.Region.ClientStack.Linden
52{
53 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GetMeshModule")]
54 public class GetMeshModule : INonSharedRegionModule
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 OpenSim.Framework.BlockingQueue<aPollRequest> m_queue =
93 new OpenSim.Framework.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 true,
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 // This will fail on region shutdown. Its harmless.
190 // Prevent red ink.
191 try
192 {
193 m_queue.Clear();
194 }
195 catch {}
196 }
197 }
198
199 public string Name { get { return "GetMeshModule"; } }
200
201 #endregion
202
203 private static void DoMeshRequests()
204 {
205 while(true)
206 {
207 aPollRequest poolreq = m_queue.Dequeue(4500);
208 Watchdog.UpdateThread();
209 if(m_NumberScenes <= 0)
210 return;
211 if(poolreq.reqID != UUID.Zero)
212 poolreq.thepoll.Process(poolreq);
213 }
214 }
215
216 // 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.
217 public void ThrottleUpdate(ScenePresence p)
218 {
219 UUID user = p.UUID;
220 int imagethrottle = p.ControllingClient.GetAgentThrottleSilent((int)ThrottleOutPacketType.Asset);
221 PollServiceMeshEventArgs args;
222 if (m_pollservices.TryGetValue(user, out args))
223 {
224 args.UpdateThrottle(imagethrottle);
225 }
226 }
227
228 private class PollServiceMeshEventArgs : PollServiceEventArgs
229 {
230 private List<Hashtable> requests =
231 new List<Hashtable>();
232 private Dictionary<UUID, aPollResponse> responses =
233 new Dictionary<UUID, aPollResponse>();
234
235 private Scene m_scene;
236 private MeshCapsDataThrottler m_throttler;
237 public PollServiceMeshEventArgs(string uri, UUID pId, Scene scene) :
238 base(null, uri, null, null, null, pId, int.MaxValue)
239 {
240 m_scene = scene;
241 m_throttler = new MeshCapsDataThrottler(100000);
242 // x is request id, y is userid
243 HasEvents = (x, y) =>
244 {
245 lock (responses)
246 {
247 bool ret = m_throttler.hasEvents(x, responses);
248 return ret;
249
250 }
251 };
252 GetEvents = (x, y) =>
253 {
254 lock (responses)
255 {
256 try
257 {
258 return responses[x].response;
259 }
260 finally
261 {
262 responses.Remove(x);
263 m_throttler.PassTime();
264 }
265 }
266 };
267 // x is request id, y is request data hashtable
268 Request = (x, y) =>
269 {
270 aPollRequest reqinfo = new aPollRequest();
271 reqinfo.thepoll = this;
272 reqinfo.reqID = x;
273 reqinfo.request = y;
274
275 m_queue.Enqueue(reqinfo);
276 m_throttler.PassTime();
277 };
278
279 // this should never happen except possible on shutdown
280 NoEvents = (x, y) =>
281 {
282 /*
283 lock (requests)
284 {
285 Hashtable request = requests.Find(id => id["RequestID"].ToString() == x.ToString());
286 requests.Remove(request);
287 }
288 */
289 Hashtable response = new Hashtable();
290
291 response["int_response_code"] = 500;
292 response["str_response_string"] = "Script timeout";
293 response["content_type"] = "text/plain";
294 response["keepalive"] = false;
295 response["reusecontext"] = false;
296
297 return response;
298 };
299 }
300
301 public void Process(aPollRequest requestinfo)
302 {
303 Hashtable response;
304
305 UUID requestID = requestinfo.reqID;
306
307 if(m_scene.ShuttingDown)
308 return;
309
310 // If the avatar is gone, don't bother to get the texture
311 if (m_scene.GetScenePresence(Id) == null)
312 {
313 response = new Hashtable();
314
315 response["int_response_code"] = 500;
316 response["str_response_string"] = "Script timeout";
317 response["content_type"] = "text/plain";
318 response["keepalive"] = false;
319 response["reusecontext"] = false;
320
321 lock (responses)
322 responses[requestID] = new aPollResponse() { bytes = 0, response = response, lod = 0 };
323
324 return;
325 }
326
327 response = m_getMeshHandler.Handle(requestinfo.request);
328 lock (responses)
329 {
330 responses[requestID] = new aPollResponse()
331 {
332 bytes = (int)response["int_bytes"],
333 lod = (int)response["int_lod"],
334 response = response
335 };
336
337 }
338 m_throttler.PassTime();
339 }
340
341 internal void UpdateThrottle(int pthrottle)
342 {
343 int tmp = 2 * pthrottle;
344 if(tmp < 10000)
345 tmp = 10000;
346 m_throttler.ThrottleBytes = tmp;
347 }
348 }
349
350 public void RegisterCaps(UUID agentID, Caps caps)
351 {
352// UUID capID = UUID.Random();
353 if (m_URL == "localhost")
354 {
355 string capUrl = "/CAPS/" + UUID.Random() + "/";
356
357 // Register this as a poll service
358 PollServiceMeshEventArgs args = new PollServiceMeshEventArgs(capUrl, agentID, m_scene);
359
360 args.Type = PollServiceEventArgs.EventType.Mesh;
361 MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args);
362
363 string hostName = m_scene.RegionInfo.ExternalHostName;
364 uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
365 string protocol = "http";
366
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[agentID] = args;
375 m_capsDict[agentID] = capUrl;
376 }
377 else
378 {
379 caps.RegisterHandler("GetMesh", m_URL);
380 }
381 }
382
383 private void DeregisterCaps(UUID agentID, Caps caps)
384 {
385 string capUrl;
386 PollServiceMeshEventArgs args;
387 if (m_capsDict.TryGetValue(agentID, out capUrl))
388 {
389 MainServer.Instance.RemoveHTTPHandler("", capUrl);
390 m_capsDict.Remove(agentID);
391 }
392 if (m_pollservices.TryGetValue(agentID, out args))
393 {
394 m_pollservices.Remove(agentID);
395 }
396 }
397
398 internal sealed class MeshCapsDataThrottler
399 {
400 private double lastTimeElapsed = 0;
401 private double BytesSent = 0;
402
403 public MeshCapsDataThrottler(int pBytes)
404 {
405 if(pBytes < 10000)
406 pBytes = 10000;
407 ThrottleBytes = pBytes;
408 lastTimeElapsed = Util.GetTimeStampMS();
409 }
410
411 public bool hasEvents(UUID key, Dictionary<UUID, aPollResponse> responses)
412 {
413 PassTime();
414 // Note, this is called IN LOCK
415 bool haskey = responses.ContainsKey(key);
416
417 if (!haskey)
418 {
419 return false;
420 }
421 aPollResponse response;
422 if (responses.TryGetValue(key, out response))
423 {
424 // Normal
425 if (BytesSent <= ThrottleBytes)
426 {
427 BytesSent += response.bytes;
428 return true;
429 }
430 else
431 {
432 return false;
433 }
434 }
435 return haskey;
436 }
437
438 public void PassTime()
439 {
440 double currenttime = Util.GetTimeStampMS();
441 double timeElapsed = currenttime - lastTimeElapsed;
442 if(timeElapsed < 50.0)
443 return;
444 int add = (int)(ThrottleBytes * timeElapsed * 0.001);
445 if (add >= 1000)
446 {
447 lastTimeElapsed = currenttime;
448 BytesSent -= add;
449 if (BytesSent < 0) BytesSent = 0;
450 }
451 }
452
453 public int ThrottleBytes;
454 }
455 }
456}