diff options
Diffstat (limited to 'OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs')
-rw-r--r-- | OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs | 190 |
1 files changed, 173 insertions, 17 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs index d1a1583..dd87671 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs | |||
@@ -47,9 +47,6 @@ using OpenSim.Framework.Monitoring; | |||
47 | namespace OpenSim.Region.ClientStack.Linden | 47 | namespace OpenSim.Region.ClientStack.Linden |
48 | { | 48 | { |
49 | 49 | ||
50 | /// <summary> | ||
51 | /// This module implements both WebFetchTextureDescendents and FetchTextureDescendents2 capabilities. | ||
52 | /// </summary> | ||
53 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GetTextureModule")] | 50 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GetTextureModule")] |
54 | public class GetTextureModule : INonSharedRegionModule | 51 | public class GetTextureModule : INonSharedRegionModule |
55 | { | 52 | { |
@@ -61,6 +58,13 @@ namespace OpenSim.Region.ClientStack.Linden | |||
61 | public Hashtable request; | 58 | public Hashtable request; |
62 | } | 59 | } |
63 | 60 | ||
61 | public class aPollResponse | ||
62 | { | ||
63 | public Hashtable response; | ||
64 | public int bytes; | ||
65 | } | ||
66 | |||
67 | |||
64 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 68 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
65 | 69 | ||
66 | private Scene m_scene; | 70 | private Scene m_scene; |
@@ -75,6 +79,8 @@ namespace OpenSim.Region.ClientStack.Linden | |||
75 | private static OpenMetaverse.BlockingQueue<aPollRequest> m_queue = | 79 | private static OpenMetaverse.BlockingQueue<aPollRequest> m_queue = |
76 | new OpenMetaverse.BlockingQueue<aPollRequest>(); | 80 | new OpenMetaverse.BlockingQueue<aPollRequest>(); |
77 | 81 | ||
82 | private Dictionary<UUID,PollServiceTextureEventArgs> m_pollservices = new Dictionary<UUID,PollServiceTextureEventArgs>(); | ||
83 | |||
78 | #region ISharedRegionModule Members | 84 | #region ISharedRegionModule Members |
79 | 85 | ||
80 | public void Initialise(IConfigSource source) | 86 | public void Initialise(IConfigSource source) |
@@ -91,6 +97,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
91 | { | 97 | { |
92 | m_scene.EventManager.OnRegisterCaps -= RegisterCaps; | 98 | m_scene.EventManager.OnRegisterCaps -= RegisterCaps; |
93 | m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps; | 99 | m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps; |
100 | m_scene.EventManager.OnThrottleUpdate -= ThrottleUpdate; | ||
94 | m_scene = null; | 101 | m_scene = null; |
95 | } | 102 | } |
96 | 103 | ||
@@ -101,6 +108,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
101 | 108 | ||
102 | m_scene.EventManager.OnRegisterCaps += RegisterCaps; | 109 | m_scene.EventManager.OnRegisterCaps += RegisterCaps; |
103 | m_scene.EventManager.OnDeregisterCaps += DeregisterCaps; | 110 | m_scene.EventManager.OnDeregisterCaps += DeregisterCaps; |
111 | m_scene.EventManager.OnThrottleUpdate += ThrottleUpdate; | ||
104 | 112 | ||
105 | if (m_workerThreads == null) | 113 | if (m_workerThreads == null) |
106 | { | 114 | { |
@@ -118,6 +126,56 @@ namespace OpenSim.Region.ClientStack.Linden | |||
118 | } | 126 | } |
119 | } | 127 | } |
120 | } | 128 | } |
129 | private int ExtractImageThrottle(byte[] pthrottles) | ||
130 | { | ||
131 | |||
132 | byte[] adjData; | ||
133 | int pos = 0; | ||
134 | |||
135 | if (!BitConverter.IsLittleEndian) | ||
136 | { | ||
137 | byte[] newData = new byte[7 * 4]; | ||
138 | Buffer.BlockCopy(pthrottles, 0, newData, 0, 7 * 4); | ||
139 | |||
140 | for (int i = 0; i < 7; i++) | ||
141 | Array.Reverse(newData, i * 4, 4); | ||
142 | |||
143 | adjData = newData; | ||
144 | } | ||
145 | else | ||
146 | { | ||
147 | adjData = pthrottles; | ||
148 | } | ||
149 | |||
150 | // 0.125f converts from bits to bytes | ||
151 | //int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); | ||
152 | //pos += 4; | ||
153 | // int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); | ||
154 | //pos += 4; | ||
155 | // int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); | ||
156 | // pos += 4; | ||
157 | // int cloud = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); | ||
158 | // pos += 4; | ||
159 | // int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); | ||
160 | // pos += 4; | ||
161 | pos = pos + 20; | ||
162 | int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); //pos += 4; | ||
163 | //int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); | ||
164 | return texture; | ||
165 | } | ||
166 | |||
167 | // 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. | ||
168 | public void ThrottleUpdate(ScenePresence p) | ||
169 | { | ||
170 | byte[] throttles = p.ControllingClient.GetThrottlesPacked(1); | ||
171 | UUID user = p.UUID; | ||
172 | int imagethrottle = ExtractImageThrottle(throttles); | ||
173 | PollServiceTextureEventArgs args; | ||
174 | if (m_pollservices.TryGetValue(user,out args)) | ||
175 | { | ||
176 | args.UpdateThrottle(imagethrottle); | ||
177 | } | ||
178 | } | ||
121 | 179 | ||
122 | public void PostInitialise() | 180 | public void PostInitialise() |
123 | { | 181 | { |
@@ -145,20 +203,25 @@ namespace OpenSim.Region.ClientStack.Linden | |||
145 | { | 203 | { |
146 | private List<Hashtable> requests = | 204 | private List<Hashtable> requests = |
147 | new List<Hashtable>(); | 205 | new List<Hashtable>(); |
148 | private Dictionary<UUID, Hashtable> responses = | 206 | private Dictionary<UUID, aPollResponse> responses = |
149 | new Dictionary<UUID, Hashtable>(); | 207 | new Dictionary<UUID, aPollResponse>(); |
150 | 208 | ||
151 | private Scene m_scene; | 209 | private Scene m_scene; |
152 | 210 | private CapsDataThrottler m_throttler = new CapsDataThrottler(100000, 1400000,10000); | |
153 | public PollServiceTextureEventArgs(UUID pId, Scene scene) : | 211 | public PollServiceTextureEventArgs(UUID pId, Scene scene) : |
154 | base(null, null, null, null, pId, int.MaxValue) | 212 | base(null, null, null, null, pId, int.MaxValue) |
155 | { | 213 | { |
156 | m_scene = scene; | 214 | m_scene = scene; |
157 | 215 | // x is request id, y is userid | |
158 | HasEvents = (x, y) => | 216 | HasEvents = (x, y) => |
159 | { | 217 | { |
160 | lock (responses) | 218 | lock (responses) |
161 | return responses.ContainsKey(x); | 219 | { |
220 | bool ret = m_throttler.hasEvents(x, responses); | ||
221 | m_throttler.ProcessTime(); | ||
222 | return ret; | ||
223 | |||
224 | } | ||
162 | }; | 225 | }; |
163 | GetEvents = (x, y) => | 226 | GetEvents = (x, y) => |
164 | { | 227 | { |
@@ -166,7 +229,7 @@ namespace OpenSim.Region.ClientStack.Linden | |||
166 | { | 229 | { |
167 | try | 230 | try |
168 | { | 231 | { |
169 | return responses[x]; | 232 | return responses[x].response; |
170 | } | 233 | } |
171 | finally | 234 | finally |
172 | { | 235 | { |
@@ -174,14 +237,14 @@ namespace OpenSim.Region.ClientStack.Linden | |||
174 | } | 237 | } |
175 | } | 238 | } |
176 | }; | 239 | }; |
177 | 240 | // x is request id, y is request data hashtable | |
178 | Request = (x, y) => | 241 | Request = (x, y) => |
179 | { | 242 | { |
180 | aPollRequest reqinfo = new aPollRequest(); | 243 | aPollRequest reqinfo = new aPollRequest(); |
181 | reqinfo.thepoll = this; | 244 | reqinfo.thepoll = this; |
182 | reqinfo.reqID = x; | 245 | reqinfo.reqID = x; |
183 | reqinfo.request = y; | 246 | reqinfo.request = y; |
184 | 247 | ||
185 | m_queue.Enqueue(reqinfo); | 248 | m_queue.Enqueue(reqinfo); |
186 | }; | 249 | }; |
187 | 250 | ||
@@ -223,16 +286,29 @@ namespace OpenSim.Region.ClientStack.Linden | |||
223 | response["content_type"] = "text/plain"; | 286 | response["content_type"] = "text/plain"; |
224 | response["keepalive"] = false; | 287 | response["keepalive"] = false; |
225 | response["reusecontext"] = false; | 288 | response["reusecontext"] = false; |
226 | 289 | ||
227 | lock (responses) | 290 | lock (responses) |
228 | responses[requestID] = response; | 291 | responses[requestID] = new aPollResponse() {bytes = 0, response = response}; |
229 | 292 | ||
230 | return; | 293 | return; |
231 | } | 294 | } |
232 | 295 | ||
233 | response = m_getTextureHandler.Handle(requestinfo.request); | 296 | response = m_getTextureHandler.Handle(requestinfo.request); |
234 | lock (responses) | 297 | lock (responses) |
235 | responses[requestID] = response; | 298 | { |
299 | responses[requestID] = new aPollResponse() | ||
300 | { | ||
301 | bytes = (int) response["int_bytes"], | ||
302 | response = response | ||
303 | }; | ||
304 | |||
305 | } | ||
306 | m_throttler.ProcessTime(); | ||
307 | } | ||
308 | |||
309 | internal void UpdateThrottle(int pimagethrottle) | ||
310 | { | ||
311 | m_throttler.ThrottleBytes = pimagethrottle; | ||
236 | } | 312 | } |
237 | } | 313 | } |
238 | 314 | ||
@@ -257,19 +333,23 @@ namespace OpenSim.Region.ClientStack.Linden | |||
257 | protocol = "https"; | 333 | protocol = "https"; |
258 | } | 334 | } |
259 | caps.RegisterHandler("GetTexture", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl)); | 335 | caps.RegisterHandler("GetTexture", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl)); |
260 | 336 | m_pollservices.Add(agentID, args); | |
261 | m_capsDict[agentID] = capUrl; | 337 | m_capsDict[agentID] = capUrl; |
262 | } | 338 | } |
263 | 339 | ||
264 | private void DeregisterCaps(UUID agentID, Caps caps) | 340 | private void DeregisterCaps(UUID agentID, Caps caps) |
265 | { | 341 | { |
266 | string capUrl; | 342 | string capUrl; |
267 | 343 | PollServiceTextureEventArgs args; | |
268 | if (m_capsDict.TryGetValue(agentID, out capUrl)) | 344 | if (m_capsDict.TryGetValue(agentID, out capUrl)) |
269 | { | 345 | { |
270 | MainServer.Instance.RemoveHTTPHandler("", capUrl); | 346 | MainServer.Instance.RemoveHTTPHandler("", capUrl); |
271 | m_capsDict.Remove(agentID); | 347 | m_capsDict.Remove(agentID); |
272 | } | 348 | } |
349 | if (m_pollservices.TryGetValue(agentID, out args)) | ||
350 | { | ||
351 | m_pollservices.Remove(agentID); | ||
352 | } | ||
273 | } | 353 | } |
274 | 354 | ||
275 | private void DoTextureRequests() | 355 | private void DoTextureRequests() |
@@ -281,5 +361,81 @@ namespace OpenSim.Region.ClientStack.Linden | |||
281 | poolreq.thepoll.Process(poolreq); | 361 | poolreq.thepoll.Process(poolreq); |
282 | } | 362 | } |
283 | } | 363 | } |
364 | internal sealed class CapsDataThrottler | ||
365 | { | ||
366 | |||
367 | private volatile int currenttime = 0; | ||
368 | private volatile int lastTimeElapsed = 0; | ||
369 | private volatile int BytesSent = 0; | ||
370 | private int oversizedImages = 0; | ||
371 | public CapsDataThrottler(int pBytes, int max, int min) | ||
372 | { | ||
373 | ThrottleBytes = pBytes; | ||
374 | lastTimeElapsed = Util.EnvironmentTickCount(); | ||
375 | } | ||
376 | public bool hasEvents(UUID key, Dictionary<UUID, GetTextureModule.aPollResponse> responses) | ||
377 | { | ||
378 | PassTime(); | ||
379 | // Note, this is called IN LOCK | ||
380 | bool haskey = responses.ContainsKey(key); | ||
381 | if (!haskey) | ||
382 | { | ||
383 | return false; | ||
384 | } | ||
385 | GetTextureModule.aPollResponse response; | ||
386 | if (responses.TryGetValue(key, out response)) | ||
387 | { | ||
388 | |||
389 | // Normal | ||
390 | if (BytesSent + response.bytes <= ThrottleBytes) | ||
391 | { | ||
392 | BytesSent += response.bytes; | ||
393 | //TimeBasedAction timeBasedAction = new TimeBasedAction { byteRemoval = response.bytes, requestId = key, timeMS = currenttime + 1000, unlockyn = false }; | ||
394 | //m_actions.Add(timeBasedAction); | ||
395 | return true; | ||
396 | } | ||
397 | // Big textures | ||
398 | else if (response.bytes > ThrottleBytes && oversizedImages <= ((ThrottleBytes % 50000) + 1)) | ||
399 | { | ||
400 | Interlocked.Increment(ref oversizedImages); | ||
401 | BytesSent += response.bytes; | ||
402 | //TimeBasedAction timeBasedAction = new TimeBasedAction { byteRemoval = response.bytes, requestId = key, timeMS = currenttime + (((response.bytes % ThrottleBytes)+1)*1000) , unlockyn = false }; | ||
403 | //m_actions.Add(timeBasedAction); | ||
404 | return true; | ||
405 | } | ||
406 | else | ||
407 | { | ||
408 | return false; | ||
409 | } | ||
410 | } | ||
411 | |||
412 | return haskey; | ||
413 | } | ||
414 | public void ProcessTime() | ||
415 | { | ||
416 | PassTime(); | ||
417 | } | ||
418 | |||
419 | |||
420 | private void PassTime() | ||
421 | { | ||
422 | currenttime = Util.EnvironmentTickCount(); | ||
423 | int timeElapsed = Util.EnvironmentTickCountSubtract(currenttime, lastTimeElapsed); | ||
424 | //processTimeBasedActions(responses); | ||
425 | if (Util.EnvironmentTickCountSubtract(currenttime, timeElapsed) >= 1000) | ||
426 | { | ||
427 | lastTimeElapsed = Util.EnvironmentTickCount(); | ||
428 | BytesSent -= ThrottleBytes; | ||
429 | if (BytesSent < 0) BytesSent = 0; | ||
430 | if (BytesSent < ThrottleBytes) | ||
431 | { | ||
432 | oversizedImages = 0; | ||
433 | } | ||
434 | } | ||
435 | } | ||
436 | public int ThrottleBytes; | ||
437 | } | ||
284 | } | 438 | } |
439 | |||
440 | |||
285 | } | 441 | } |