diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs')
-rw-r--r-- | OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs | 1488 |
1 files changed, 791 insertions, 697 deletions
diff --git a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs index db1187e..b8645e2 100644 --- a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs +++ b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs | |||
@@ -68,30 +68,33 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
68 | private static readonly UUID STOP_UUID = UUID.Random(); | 68 | private static readonly UUID STOP_UUID = UUID.Random(); |
69 | private static readonly string m_mapLayerPath = "0001/"; | 69 | private static readonly string m_mapLayerPath = "0001/"; |
70 | 70 | ||
71 | private OpenSim.Framework.BlockingQueue<MapRequestState> requests = new OpenSim.Framework.BlockingQueue<MapRequestState>(); | ||
72 | |||
73 | private ManualResetEvent m_mapBlockRequestEvent = new ManualResetEvent(false); | ||
74 | private Dictionary<UUID, Queue<MapBlockRequestData>> m_mapBlockRequests = new Dictionary<UUID, Queue<MapBlockRequestData>>(); | ||
75 | |||
71 | private IMapImageGenerator m_mapImageGenerator; | 76 | private IMapImageGenerator m_mapImageGenerator; |
72 | private IMapImageUploadModule m_mapImageServiceModule; | 77 | private IMapImageUploadModule m_mapImageServiceModule; |
73 | 78 | ||
74 | private OpenSim.Framework.BlockingQueue<MapRequestState> requests = new OpenSim.Framework.BlockingQueue<MapRequestState>(); | ||
75 | |||
76 | protected Scene m_scene; | 79 | protected Scene m_scene; |
77 | private List<MapBlockData> cachedMapBlocks = new List<MapBlockData>(); | 80 | private List<MapBlockData> cachedMapBlocks = new List<MapBlockData>(); |
78 | private int cachedTime = 0; | ||
79 | private int blacklistTimeout = 10*60*1000; // 10 minutes | ||
80 | private byte[] myMapImageJPEG; | 81 | private byte[] myMapImageJPEG; |
81 | protected volatile bool m_Enabled = false; | 82 | protected volatile bool m_Enabled = false; |
82 | private Dictionary<UUID, MapRequestState> m_openRequests = new Dictionary<UUID, MapRequestState>(); | 83 | private ExpiringCache<string, int> m_blacklistedurls = new ExpiringCache<string, int>(); |
83 | private Dictionary<string, int> m_blacklistedurls = new Dictionary<string, int>(); | 84 | private ExpiringCache<ulong, int> m_blacklistedregions = new ExpiringCache<ulong, int>(); |
84 | private Dictionary<ulong, int> m_blacklistedregions = new Dictionary<ulong, int>(); | 85 | private ExpiringCache<ulong, string> m_cachedRegionMapItemsAddress = new ExpiringCache<ulong, string>(); |
85 | private Dictionary<ulong, string> m_cachedRegionMapItemsAddress = new Dictionary<ulong, string>(); | 86 | private ExpiringCache<ulong, OSDMap> m_cachedRegionMapItemsResponses = |
87 | new ExpiringCache<ulong, OSDMap>(); | ||
86 | private List<UUID> m_rootAgents = new List<UUID>(); | 88 | private List<UUID> m_rootAgents = new List<UUID>(); |
87 | private volatile bool threadrunning = false; | 89 | private volatile bool threadrunning = false; |
88 | 90 | // expire time for the blacklists in seconds | |
89 | private IServiceThrottleModule m_ServiceThrottle; | 91 | private double expireBlackListTime = 600.0; // 10 minutes |
90 | 92 | // expire mapItems responses time in seconds. Throttles requests to regions that do answer | |
93 | private const double expireResponsesTime = 120.0; // 2 minutes ? | ||
91 | //private int CacheRegionsDistance = 256; | 94 | //private int CacheRegionsDistance = 256; |
92 | 95 | ||
93 | #region INonSharedRegionModule Members | 96 | #region INonSharedRegionModule Members |
94 | public virtual void Initialise (IConfigSource config) | 97 | public virtual void Initialise(IConfigSource config) |
95 | { | 98 | { |
96 | string[] configSections = new string[] { "Map", "Startup" }; | 99 | string[] configSections = new string[] { "Map", "Startup" }; |
97 | 100 | ||
@@ -99,8 +102,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
99 | config, "WorldMapModule", configSections, "WorldMap") == "WorldMap") | 102 | config, "WorldMapModule", configSections, "WorldMap") == "WorldMap") |
100 | m_Enabled = true; | 103 | m_Enabled = true; |
101 | 104 | ||
102 | blacklistTimeout | 105 | expireBlackListTime = (double)Util.GetConfigVarFromSections<int>(config, "BlacklistTimeout", configSections, 10 * 60); |
103 | = Util.GetConfigVarFromSections<int>(config, "BlacklistTimeout", configSections, 10 * 60) * 1000; | ||
104 | } | 106 | } |
105 | 107 | ||
106 | public virtual void AddRegion(Scene scene) | 108 | public virtual void AddRegion(Scene scene) |
@@ -128,7 +130,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
128 | } | 130 | } |
129 | } | 131 | } |
130 | 132 | ||
131 | public virtual void RemoveRegion (Scene scene) | 133 | public virtual void RemoveRegion(Scene scene) |
132 | { | 134 | { |
133 | if (!m_Enabled) | 135 | if (!m_Enabled) |
134 | return; | 136 | return; |
@@ -141,13 +143,11 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
141 | } | 143 | } |
142 | } | 144 | } |
143 | 145 | ||
144 | public virtual void RegionLoaded (Scene scene) | 146 | public virtual void RegionLoaded(Scene scene) |
145 | { | 147 | { |
146 | if (!m_Enabled) | 148 | if (!m_Enabled) |
147 | return; | 149 | return; |
148 | 150 | ||
149 | m_ServiceThrottle = scene.RequestModuleInterface<IServiceThrottleModule>(); | ||
150 | |||
151 | m_mapImageGenerator = m_scene.RequestModuleInterface<IMapImageGenerator>(); | 151 | m_mapImageGenerator = m_scene.RequestModuleInterface<IMapImageGenerator>(); |
152 | m_mapImageServiceModule = m_scene.RequestModuleInterface<IMapImageUploadModule>(); | 152 | m_mapImageServiceModule = m_scene.RequestModuleInterface<IMapImageUploadModule>(); |
153 | } | 153 | } |
@@ -179,14 +179,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
179 | 179 | ||
180 | MainServer.Instance.AddHTTPHandler(regionimage, | 180 | MainServer.Instance.AddHTTPHandler(regionimage, |
181 | new GenericHTTPDOSProtector(OnHTTPGetMapImage, OnHTTPThrottled, new BasicDosProtectorOptions() | 181 | new GenericHTTPDOSProtector(OnHTTPGetMapImage, OnHTTPThrottled, new BasicDosProtectorOptions() |
182 | { | 182 | { |
183 | AllowXForwardedFor = false, | 183 | AllowXForwardedFor = false, |
184 | ForgetTimeSpan = TimeSpan.FromMinutes(2), | 184 | ForgetTimeSpan = TimeSpan.FromMinutes(2), |
185 | MaxRequestsInTimeframe = 4, | 185 | MaxRequestsInTimeframe = 4, |
186 | ReportingName = "MAPDOSPROTECTOR", | 186 | ReportingName = "MAPDOSPROTECTOR", |
187 | RequestTimeSpan = TimeSpan.FromSeconds(10), | 187 | RequestTimeSpan = TimeSpan.FromSeconds(10), |
188 | ThrottledAction = BasicDOSProtector.ThrottleAction.DoThrottledMethod | 188 | ThrottledAction = BasicDOSProtector.ThrottleAction.DoThrottledMethod |
189 | }).Process); | 189 | }).Process); |
190 | MainServer.Instance.AddLLSDHandler( | 190 | MainServer.Instance.AddLLSDHandler( |
191 | "/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString(), HandleRemoteMapItemRequest); | 191 | "/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString(), HandleRemoteMapItemRequest); |
192 | 192 | ||
@@ -197,13 +197,13 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
197 | m_scene.EventManager.OnMakeRootAgent += MakeRootAgent; | 197 | m_scene.EventManager.OnMakeRootAgent += MakeRootAgent; |
198 | m_scene.EventManager.OnRegionUp += OnRegionUp; | 198 | m_scene.EventManager.OnRegionUp += OnRegionUp; |
199 | 199 | ||
200 | // StartThread(new object()); | 200 | StartThread(new object()); |
201 | } | 201 | } |
202 | 202 | ||
203 | // this has to be called with a lock on m_scene | 203 | // this has to be called with a lock on m_scene |
204 | protected virtual void RemoveHandlers() | 204 | protected virtual void RemoveHandlers() |
205 | { | 205 | { |
206 | // StopThread(); | 206 | StopThread(); |
207 | 207 | ||
208 | m_scene.EventManager.OnRegionUp -= OnRegionUp; | 208 | m_scene.EventManager.OnRegionUp -= OnRegionUp; |
209 | m_scene.EventManager.OnMakeRootAgent -= MakeRootAgent; | 209 | m_scene.EventManager.OnMakeRootAgent -= MakeRootAgent; |
@@ -246,6 +246,8 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
246 | public string MapLayerRequest(string request, string path, string param, | 246 | public string MapLayerRequest(string request, string path, string param, |
247 | UUID agentID, Caps caps) | 247 | UUID agentID, Caps caps) |
248 | { | 248 | { |
249 | // not sure about this.... | ||
250 | |||
249 | //try | 251 | //try |
250 | // | 252 | // |
251 | //m_log.DebugFormat("[MAPLAYER]: path: {0}, param: {1}, agent:{2}", | 253 | //m_log.DebugFormat("[MAPLAYER]: path: {0}, param: {1}, agent:{2}", |
@@ -261,54 +263,54 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
261 | // 6/8/2011 -- I'm adding an explicit 2048 check, so that we never forget that there is | 263 | // 6/8/2011 -- I'm adding an explicit 2048 check, so that we never forget that there is |
262 | // a hack here, and so that regions below 4096 don't get spammed with unnecessary map blocks. | 264 | // a hack here, and so that regions below 4096 don't get spammed with unnecessary map blocks. |
263 | 265 | ||
264 | if (m_scene.RegionInfo.RegionLocX >= 2048 || m_scene.RegionInfo.RegionLocY >= 2048) | 266 | //if (m_scene.RegionInfo.RegionLocX >= 2048 || m_scene.RegionInfo.RegionLocY >= 2048) |
265 | { | 267 | //{ |
266 | ScenePresence avatarPresence = null; | 268 | // ScenePresence avatarPresence = null; |
267 | 269 | ||
268 | m_scene.TryGetScenePresence(agentID, out avatarPresence); | 270 | // m_scene.TryGetScenePresence(agentID, out avatarPresence); |
269 | 271 | ||
270 | if (avatarPresence != null) | 272 | // if (avatarPresence != null) |
271 | { | 273 | // { |
272 | bool lookup = false; | 274 | // bool lookup = false; |
273 | 275 | ||
274 | lock (cachedMapBlocks) | 276 | // lock (cachedMapBlocks) |
275 | { | 277 | // { |
276 | if (cachedMapBlocks.Count > 0 && ((cachedTime + 1800) > Util.UnixTimeSinceEpoch())) | 278 | // if (cachedMapBlocks.Count > 0 && ((cachedTime + 1800) > Util.UnixTimeSinceEpoch())) |
277 | { | 279 | // { |
278 | List<MapBlockData> mapBlocks; | 280 | // List<MapBlockData> mapBlocks; |
279 | 281 | ||
280 | mapBlocks = cachedMapBlocks; | 282 | // mapBlocks = cachedMapBlocks; |
281 | avatarPresence.ControllingClient.SendMapBlock(mapBlocks, 0); | 283 | // avatarPresence.ControllingClient.SendMapBlock(mapBlocks, 0); |
282 | } | 284 | // } |
283 | else | 285 | // else |
284 | { | 286 | // { |
285 | lookup = true; | 287 | // lookup = true; |
286 | } | 288 | // } |
287 | } | 289 | // } |
288 | if (lookup) | 290 | // if (lookup) |
289 | { | 291 | // { |
290 | List<MapBlockData> mapBlocks = new List<MapBlockData>(); ; | 292 | // List<MapBlockData> mapBlocks = new List<MapBlockData>(); ; |
291 | 293 | ||
292 | // Get regions that are within 8 regions of here | 294 | // List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID, |
293 | List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID, | 295 | // (int)(m_scene.RegionInfo.RegionLocX - 8) * (int)Constants.RegionSize, |
294 | (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocX - 8), | 296 | // (int)(m_scene.RegionInfo.RegionLocX + 8) * (int)Constants.RegionSize, |
295 | (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocX + 8), | 297 | // (int)(m_scene.RegionInfo.RegionLocY - 8) * (int)Constants.RegionSize, |
296 | (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocY - 8), | 298 | // (int)(m_scene.RegionInfo.RegionLocY + 8) * (int)Constants.RegionSize); |
297 | (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocY + 8) ); | 299 | // foreach (GridRegion r in regions) |
298 | foreach (GridRegion r in regions) | 300 | // { |
299 | { | 301 | // MapBlockData block = new MapBlockData(); |
300 | MapBlockData block = MapBlockFromGridRegion(r, 0); | 302 | // MapBlockFromGridRegion(block, r, 0); |
301 | mapBlocks.Add(block); | 303 | // mapBlocks.Add(block); |
302 | } | 304 | // } |
303 | avatarPresence.ControllingClient.SendMapBlock(mapBlocks, 0); | 305 | // avatarPresence.ControllingClient.SendMapBlock(mapBlocks, 0); |
304 | 306 | ||
305 | lock (cachedMapBlocks) | 307 | // lock (cachedMapBlocks) |
306 | cachedMapBlocks = mapBlocks; | 308 | // cachedMapBlocks = mapBlocks; |
307 | 309 | ||
308 | cachedTime = Util.UnixTimeSinceEpoch(); | 310 | // cachedTime = Util.UnixTimeSinceEpoch(); |
309 | } | 311 | // } |
310 | } | 312 | // } |
311 | } | 313 | //} |
312 | 314 | ||
313 | LLSDMapLayerResponse mapResponse = new LLSDMapLayerResponse(); | 315 | LLSDMapLayerResponse mapResponse = new LLSDMapLayerResponse(); |
314 | mapResponse.LayerData.Array.Add(GetOSDMapLayerResponse()); | 316 | mapResponse.LayerData.Array.Add(GetOSDMapLayerResponse()); |
@@ -334,9 +336,11 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
334 | /// <returns></returns> | 336 | /// <returns></returns> |
335 | protected static OSDMapLayer GetOSDMapLayerResponse() | 337 | protected static OSDMapLayer GetOSDMapLayerResponse() |
336 | { | 338 | { |
339 | // not sure about this.... 2048 or master 5000 and hack above? | ||
340 | |||
337 | OSDMapLayer mapLayer = new OSDMapLayer(); | 341 | OSDMapLayer mapLayer = new OSDMapLayer(); |
338 | mapLayer.Right = 5000; | 342 | mapLayer.Right = 2048; |
339 | mapLayer.Top = 5000; | 343 | mapLayer.Top = 2048; |
340 | mapLayer.ImageID = new UUID("00000000-0000-1111-9999-000000000006"); | 344 | mapLayer.ImageID = new UUID("00000000-0000-1111-9999-000000000006"); |
341 | 345 | ||
342 | return mapLayer; | 346 | return mapLayer; |
@@ -365,6 +369,11 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
365 | { | 369 | { |
366 | m_rootAgents.Remove(AgentId); | 370 | m_rootAgents.Remove(AgentId); |
367 | } | 371 | } |
372 | lock (m_mapBlockRequestEvent) | ||
373 | { | ||
374 | if (m_mapBlockRequests.ContainsKey(AgentId)) | ||
375 | m_mapBlockRequests.Remove(AgentId); | ||
376 | } | ||
368 | } | 377 | } |
369 | #endregion | 378 | #endregion |
370 | 379 | ||
@@ -379,14 +388,20 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
379 | if (threadrunning) return; | 388 | if (threadrunning) return; |
380 | threadrunning = true; | 389 | threadrunning = true; |
381 | 390 | ||
382 | // m_log.Debug("[WORLD MAP]: Starting remote MapItem request thread"); | 391 | // m_log.Debug("[WORLD MAP]: Starting remote MapItem request thread"); |
383 | 392 | ||
384 | WorkManager.StartThread( | 393 | WorkManager.StartThread( |
385 | process, | 394 | process, |
386 | string.Format("MapItemRequestThread ({0})", m_scene.RegionInfo.RegionName), | 395 | string.Format("MapItemRequestThread ({0})", m_scene.RegionInfo.RegionName), |
387 | ThreadPriority.BelowNormal, | 396 | ThreadPriority.BelowNormal, |
388 | true, | 397 | true, |
389 | true); | 398 | false); |
399 | WorkManager.StartThread( | ||
400 | MapBlockSendThread, | ||
401 | string.Format("MapBlockSendThread ({0})", m_scene.RegionInfo.RegionName), | ||
402 | ThreadPriority.BelowNormal, | ||
403 | true, | ||
404 | false); | ||
390 | } | 405 | } |
391 | 406 | ||
392 | /// <summary> | 407 | /// <summary> |
@@ -396,13 +411,29 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
396 | { | 411 | { |
397 | MapRequestState st = new MapRequestState(); | 412 | MapRequestState st = new MapRequestState(); |
398 | st.agentID = STOP_UUID; | 413 | st.agentID = STOP_UUID; |
399 | st.EstateID=0; | 414 | st.EstateID = 0; |
400 | st.flags=0; | 415 | st.flags = 0; |
401 | st.godlike=false; | 416 | st.godlike = false; |
402 | st.itemtype=0; | 417 | st.itemtype = 0; |
403 | st.regionhandle=0; | 418 | st.regionhandle = 0; |
404 | 419 | ||
405 | requests.Enqueue(st); | 420 | requests.Enqueue(st); |
421 | |||
422 | MapBlockRequestData req = new MapBlockRequestData(); | ||
423 | |||
424 | req.client = null; | ||
425 | req.minX = 0; | ||
426 | req.maxX = 0; | ||
427 | req.minY = 0; | ||
428 | req.maxY = 0; | ||
429 | req.flags = 0; | ||
430 | |||
431 | lock (m_mapBlockRequestEvent) | ||
432 | { | ||
433 | m_mapBlockRequests[UUID.Zero] = new Queue<MapBlockRequestData>(); | ||
434 | m_mapBlockRequests[UUID.Zero].Enqueue(req); | ||
435 | m_mapBlockRequestEvent.Set(); | ||
436 | } | ||
406 | } | 437 | } |
407 | 438 | ||
408 | public virtual void HandleMapItemRequest(IClientAPI remoteClient, uint flags, | 439 | public virtual void HandleMapItemRequest(IClientAPI remoteClient, uint flags, |
@@ -415,315 +446,372 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
415 | if (!m_rootAgents.Contains(remoteClient.AgentId)) | 446 | if (!m_rootAgents.Contains(remoteClient.AgentId)) |
416 | return; | 447 | return; |
417 | } | 448 | } |
449 | |||
450 | // local or remote request? | ||
451 | if (regionhandle != 0 && regionhandle != m_scene.RegionInfo.RegionHandle) | ||
452 | { | ||
453 | // its Remote Map Item Request | ||
454 | // ensures that the blockingqueue doesn't get borked if the GetAgents() timing changes. | ||
455 | RequestMapItems("", remoteClient.AgentId, flags, EstateID, godlike, itemtype, regionhandle); | ||
456 | return; | ||
457 | } | ||
458 | |||
418 | uint xstart = 0; | 459 | uint xstart = 0; |
419 | uint ystart = 0; | 460 | uint ystart = 0; |
420 | Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle, out xstart, out ystart); | 461 | Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle, out xstart, out ystart); |
421 | if (itemtype == (int)GridItemType.AgentLocations) | 462 | |
463 | // its about this region... | ||
464 | |||
465 | List<mapItemReply> mapitems = new List<mapItemReply>(); | ||
466 | mapItemReply mapitem = new mapItemReply(); | ||
467 | |||
468 | // viewers only ask for green dots to each region now | ||
469 | // except at login with regionhandle 0 | ||
470 | // possible on some other rare ocasions | ||
471 | // use previus hack of sending all items with the green dots | ||
472 | |||
473 | bool adultRegion; | ||
474 | if (regionhandle == 0) | ||
422 | { | 475 | { |
423 | if (regionhandle == 0 || regionhandle == m_scene.RegionInfo.RegionHandle) | 476 | switch (itemtype) |
424 | { | 477 | { |
425 | // Just requesting map info about the current, local region | 478 | case (int)GridItemType.AgentLocations: |
426 | int tc = Environment.TickCount; | 479 | // Service 6 right now (MAP_ITEM_AGENTS_LOCATION; green dots) |
427 | List<mapItemReply> mapitems = new List<mapItemReply>(); | 480 | |
428 | mapItemReply mapitem = new mapItemReply(); | 481 | int tc = Environment.TickCount; |
429 | if (m_scene.GetRootAgentCount() <= 1) | 482 | if (m_scene.GetRootAgentCount() <= 1) |
430 | { | 483 | { |
431 | mapitem = new mapItemReply( | 484 | mapitem = new mapItemReply( |
432 | xstart + 1, | 485 | xstart + 1, |
433 | ystart + 1, | 486 | ystart + 1, |
434 | UUID.Zero, | 487 | UUID.Zero, |
435 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), | 488 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), |
436 | 0, 0); | 489 | 0, 0); |
437 | mapitems.Add(mapitem); | 490 | mapitems.Add(mapitem); |
438 | } | 491 | } |
439 | else | 492 | else |
440 | { | ||
441 | m_scene.ForEachRootScenePresence(delegate(ScenePresence sp) | ||
442 | { | 493 | { |
494 | m_scene.ForEachRootScenePresence(delegate (ScenePresence sp) | ||
495 | { | ||
443 | // Don't send a green dot for yourself | 496 | // Don't send a green dot for yourself |
444 | if (sp.UUID != remoteClient.AgentId) | 497 | if (sp.UUID != remoteClient.AgentId) |
445 | { | 498 | { |
446 | mapitem = new mapItemReply( | 499 | mapitem = new mapItemReply( |
447 | xstart + (uint)sp.AbsolutePosition.X, | 500 | xstart + (uint)sp.AbsolutePosition.X, |
448 | ystart + (uint)sp.AbsolutePosition.Y, | 501 | ystart + (uint)sp.AbsolutePosition.Y, |
449 | UUID.Zero, | 502 | UUID.Zero, |
450 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), | 503 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), |
451 | 1, 0); | 504 | 1, 0); |
452 | mapitems.Add(mapitem); | 505 | mapitems.Add(mapitem); |
453 | } | 506 | } |
454 | }); | 507 | }); |
455 | } | 508 | } |
456 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); | 509 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); |
457 | } | 510 | break; |
458 | else | ||
459 | { | ||
460 | // Remote Map Item Request | ||
461 | 511 | ||
462 | // ensures that the blockingqueue doesn't get borked if the GetAgents() timing changes. | 512 | case (int)GridItemType.Telehub: |
463 | RequestMapItems("",remoteClient.AgentId,flags,EstateID,godlike,itemtype,regionhandle); | 513 | // Service 1 (MAP_ITEM_TELEHUB) |
464 | } | 514 | |
465 | } | 515 | SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject); |
466 | else if (itemtype == (int)GridItemType.LandForSale) // Service 7 (MAP_ITEM_LAND_FOR_SALE) | 516 | if (sog != null) |
467 | { | ||
468 | if (regionhandle == 0 || regionhandle == m_scene.RegionInfo.RegionHandle) | ||
469 | { | ||
470 | // Parcels | ||
471 | ILandChannel landChannel = m_scene.LandChannel; | ||
472 | List<ILandObject> parcels = landChannel.AllParcels(); | ||
473 | |||
474 | // Local Map Item Request | ||
475 | List<mapItemReply> mapitems = new List<mapItemReply>(); | ||
476 | mapItemReply mapitem = new mapItemReply(); | ||
477 | if ((parcels != null) && (parcels.Count >= 1)) | ||
478 | { | ||
479 | foreach (ILandObject parcel_interface in parcels) | ||
480 | { | 517 | { |
481 | // Play it safe | 518 | mapitem = new mapItemReply( |
482 | if (!(parcel_interface is LandObject)) | 519 | xstart + (uint)sog.AbsolutePosition.X, |
483 | continue; | 520 | ystart + (uint)sog.AbsolutePosition.Y, |
521 | UUID.Zero, | ||
522 | sog.Name, | ||
523 | 0, // color (not used) | ||
524 | 0 // 0 = telehub / 1 = infohub | ||
525 | ); | ||
526 | mapitems.Add(mapitem); | ||
527 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); | ||
528 | } | ||
529 | break; | ||
484 | 530 | ||
485 | LandObject land = (LandObject)parcel_interface; | 531 | case (int)GridItemType.AdultLandForSale: |
486 | LandData parcel = land.LandData; | 532 | case (int)GridItemType.LandForSale: |
487 | 533 | ||
488 | // Show land for sale | 534 | // Service 7 (MAP_ITEM_LAND_FOR_SALE) |
489 | if ((parcel.Flags & (uint)ParcelFlags.ForSale) == (uint)ParcelFlags.ForSale) | 535 | adultRegion = m_scene.RegionInfo.RegionSettings.Maturity == 2; |
490 | { | 536 | if (adultRegion) |
491 | Vector3 min = parcel.AABBMin; | 537 | { |
492 | Vector3 max = parcel.AABBMax; | 538 | if (itemtype == (int)GridItemType.LandForSale) |
493 | float x = (min.X+max.X)/2; | 539 | break; |
494 | float y = (min.Y+max.Y)/2; | 540 | } |
541 | else | ||
542 | { | ||
543 | if (itemtype == (int)GridItemType.AdultLandForSale) | ||
544 | break; | ||
545 | } | ||
495 | 546 | ||
496 | mapitem = new mapItemReply( | 547 | // Parcels |
548 | ILandChannel landChannel = m_scene.LandChannel; | ||
549 | List<ILandObject> parcels = landChannel.AllParcels(); | ||
550 | |||
551 | if ((parcels != null) && (parcels.Count >= 1)) | ||
552 | { | ||
553 | foreach (ILandObject parcel_interface in parcels) | ||
554 | { | ||
555 | // Play it safe | ||
556 | if (!(parcel_interface is LandObject)) | ||
557 | continue; | ||
558 | |||
559 | LandObject land = (LandObject)parcel_interface; | ||
560 | LandData parcel = land.LandData; | ||
561 | |||
562 | // Show land for sale | ||
563 | if ((parcel.Flags & (uint)ParcelFlags.ForSale) == (uint)ParcelFlags.ForSale) | ||
564 | { | ||
565 | Vector3 min = parcel.AABBMin; | ||
566 | Vector3 max = parcel.AABBMax; | ||
567 | float x = (min.X + max.X) / 2; | ||
568 | float y = (min.Y + max.Y) / 2; | ||
569 | mapitem = new mapItemReply( | ||
497 | xstart + (uint)x, | 570 | xstart + (uint)x, |
498 | ystart + (uint)y, | 571 | ystart + (uint)y, |
499 | parcel.GlobalID, | 572 | parcel.GlobalID, |
500 | parcel.Name, | 573 | parcel.Name, |
501 | parcel.Area, | 574 | parcel.Area, |
502 | parcel.SalePrice | 575 | parcel.SalePrice |
503 | ); | 576 | ); |
504 | mapitems.Add(mapitem); | 577 | mapitems.Add(mapitem); |
578 | } | ||
505 | } | 579 | } |
506 | } | 580 | } |
507 | } | 581 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); |
508 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); | 582 | break; |
509 | } | ||
510 | else | ||
511 | { | ||
512 | // Remote Map Item Request | ||
513 | 583 | ||
514 | // ensures that the blockingqueue doesn't get borked if the GetAgents() timing changes. | 584 | case (uint)GridItemType.PgEvent: |
515 | RequestMapItems("",remoteClient.AgentId,flags,EstateID,godlike,itemtype,regionhandle); | 585 | case (uint)GridItemType.MatureEvent: |
586 | case (uint)GridItemType.AdultEvent: | ||
587 | case (uint)GridItemType.Classified: | ||
588 | case (uint)GridItemType.Popular: | ||
589 | // TODO | ||
590 | // just dont not cry about them | ||
591 | break; | ||
592 | |||
593 | default: | ||
594 | // unkown map item type | ||
595 | m_log.DebugFormat("[WORLD MAP]: Unknown MapItem type {1}", itemtype); | ||
596 | break; | ||
516 | } | 597 | } |
517 | } | 598 | } |
518 | else if (itemtype == (int)GridItemType.Telehub) // Service 1 (MAP_ITEM_TELEHUB) | 599 | else |
519 | { | 600 | { |
520 | if (regionhandle == 0 || regionhandle == m_scene.RegionInfo.RegionHandle) | 601 | // send all items till we get a better fix |
521 | { | ||
522 | List<mapItemReply> mapitems = new List<mapItemReply>(); | ||
523 | mapItemReply mapitem = new mapItemReply(); | ||
524 | 602 | ||
525 | SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject); | 603 | // Service 6 right now (MAP_ITEM_AGENTS_LOCATION; green dots) |
526 | if (sog != null) | ||
527 | { | ||
528 | mapitem = new mapItemReply( | ||
529 | xstart + (uint)sog.AbsolutePosition.X, | ||
530 | ystart + (uint)sog.AbsolutePosition.Y, | ||
531 | UUID.Zero, | ||
532 | sog.Name, | ||
533 | 0, // color (not used) | ||
534 | 0 // 0 = telehub / 1 = infohub | ||
535 | ); | ||
536 | mapitems.Add(mapitem); | ||
537 | 604 | ||
538 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); | 605 | int tc = Environment.TickCount; |
539 | } | 606 | if (m_scene.GetRootAgentCount() <= 1) |
607 | { | ||
608 | mapitem = new mapItemReply( | ||
609 | xstart + 1, | ||
610 | ystart + 1, | ||
611 | UUID.Zero, | ||
612 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), | ||
613 | 0, 0); | ||
614 | mapitems.Add(mapitem); | ||
540 | } | 615 | } |
541 | else | 616 | else |
542 | { | 617 | { |
543 | // Remote Map Item Request | 618 | m_scene.ForEachRootScenePresence(delegate (ScenePresence sp) |
544 | RequestMapItems("",remoteClient.AgentId,flags,EstateID,godlike,itemtype,regionhandle); | 619 | { |
620 | // Don't send a green dot for yourself | ||
621 | if (sp.UUID != remoteClient.AgentId) | ||
622 | { | ||
623 | mapitem = new mapItemReply( | ||
624 | xstart + (uint)sp.AbsolutePosition.X, | ||
625 | ystart + (uint)sp.AbsolutePosition.Y, | ||
626 | UUID.Zero, | ||
627 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), | ||
628 | 1, 0); | ||
629 | mapitems.Add(mapitem); | ||
630 | } | ||
631 | }); | ||
545 | } | 632 | } |
546 | } | 633 | remoteClient.SendMapItemReply(mapitems.ToArray(), 6, flags); |
547 | } | 634 | mapitems.Clear(); |
548 | 635 | ||
549 | private int nAsyncRequests = 0; | 636 | // Service 1 (MAP_ITEM_TELEHUB) |
550 | /// <summary> | 637 | |
551 | /// Processing thread main() loop for doing remote mapitem requests | 638 | SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject); |
552 | /// </summary> | 639 | if (sog != null) |
553 | public void process() | ||
554 | { | ||
555 | //const int MAX_ASYNC_REQUESTS = 20; | ||
556 | try | ||
557 | { | ||
558 | while (true) | ||
559 | { | 640 | { |
560 | MapRequestState st = requests.Dequeue(1000); | 641 | mapitem = new mapItemReply( |
642 | xstart + (uint)sog.AbsolutePosition.X, | ||
643 | ystart + (uint)sog.AbsolutePosition.Y, | ||
644 | UUID.Zero, | ||
645 | sog.Name, | ||
646 | 0, // color (not used) | ||
647 | 0 // 0 = telehub / 1 = infohub | ||
648 | ); | ||
649 | mapitems.Add(mapitem); | ||
650 | remoteClient.SendMapItemReply(mapitems.ToArray(), 1, flags); | ||
651 | mapitems.Clear(); | ||
652 | } | ||
561 | 653 | ||
562 | // end gracefully | 654 | // Service 7 (MAP_ITEM_LAND_FOR_SALE) |
563 | if (st.agentID == STOP_UUID) | ||
564 | break; | ||
565 | 655 | ||
566 | if (st.agentID != UUID.Zero) | 656 | uint its = 7; |
657 | if (m_scene.RegionInfo.RegionSettings.Maturity == 2) | ||
658 | its = 10; | ||
659 | |||
660 | // Parcels | ||
661 | ILandChannel landChannel = m_scene.LandChannel; | ||
662 | List<ILandObject> parcels = landChannel.AllParcels(); | ||
663 | |||
664 | if ((parcels != null) && (parcels.Count >= 1)) | ||
665 | { | ||
666 | foreach (ILandObject parcel_interface in parcels) | ||
567 | { | 667 | { |
568 | bool dorequest = true; | 668 | // Play it safe |
569 | lock (m_rootAgents) | 669 | if (!(parcel_interface is LandObject)) |
570 | { | 670 | continue; |
571 | if (!m_rootAgents.Contains(st.agentID)) | 671 | |
572 | dorequest = false; | 672 | LandObject land = (LandObject)parcel_interface; |
573 | } | 673 | LandData parcel = land.LandData; |
574 | 674 | ||
575 | if (dorequest && !m_blacklistedregions.ContainsKey(st.regionhandle)) | 675 | // Show land for sale |
676 | if ((parcel.Flags & (uint)ParcelFlags.ForSale) == (uint)ParcelFlags.ForSale) | ||
576 | { | 677 | { |
577 | while (nAsyncRequests >= MAX_ASYNC_REQUESTS) // hit the break | 678 | Vector3 min = parcel.AABBMin; |
578 | Thread.Sleep(80); | 679 | Vector3 max = parcel.AABBMax; |
579 | 680 | float x = (min.X + max.X) / 2; | |
580 | RequestMapItemsDelegate d = RequestMapItemsAsync; | 681 | float y = (min.Y + max.Y) / 2; |
581 | d.BeginInvoke(st.agentID, st.flags, st.EstateID, st.godlike, st.itemtype, st.regionhandle, RequestMapItemsCompleted, null); | 682 | mapitem = new mapItemReply( |
582 | //OSDMap response = RequestMapItemsAsync(st.agentID, st.flags, st.EstateID, st.godlike, st.itemtype, st.regionhandle); | 683 | xstart + (uint)x, |
583 | //RequestMapItemsCompleted(response); | 684 | ystart + (uint)y, |
584 | Interlocked.Increment(ref nAsyncRequests); | 685 | parcel.GlobalID, |
686 | parcel.Name, | ||
687 | parcel.Area, | ||
688 | parcel.SalePrice | ||
689 | ); | ||
690 | mapitems.Add(mapitem); | ||
585 | } | 691 | } |
586 | } | 692 | } |
587 | 693 | if(mapitems.Count >0) | |
588 | Watchdog.UpdateThread(); | 694 | remoteClient.SendMapItemReply(mapitems.ToArray(), its, flags); |
695 | mapitems.Clear(); | ||
589 | } | 696 | } |
590 | } | 697 | } |
591 | catch (Exception e) | ||
592 | { | ||
593 | m_log.ErrorFormat("[WORLD MAP]: Map item request thread terminated abnormally with exception {0}", e); | ||
594 | } | ||
595 | |||
596 | threadrunning = false; | ||
597 | Watchdog.RemoveThread(); | ||
598 | } | 698 | } |
599 | 699 | ||
600 | const int MAX_ASYNC_REQUESTS = 20; | 700 | private int nAsyncRequests = 0; |
601 | |||
602 | /// <summary> | 701 | /// <summary> |
603 | /// Enqueues the map item request into the services throttle processing thread | 702 | /// Processing thread main() loop for doing remote mapitem requests |
604 | /// </summary> | 703 | /// </summary> |
605 | /// <param name="state"></param> | 704 | public void process() |
606 | public void EnqueueMapItemRequest(MapRequestState st) | ||
607 | { | 705 | { |
706 | const int MAX_ASYNC_REQUESTS = 20; | ||
707 | ScenePresence av = null; | ||
708 | MapRequestState st = null; | ||
608 | 709 | ||
609 | m_ServiceThrottle.Enqueue("map-item", st.regionhandle.ToString() + st.agentID.ToString(), delegate | 710 | try |
610 | { | 711 | { |
611 | if (st.agentID != UUID.Zero) | 712 | while (true) |
612 | { | 713 | { |
613 | bool dorequest = true; | 714 | Watchdog.UpdateThread(); |
614 | lock (m_rootAgents) | ||
615 | { | ||
616 | if (!m_rootAgents.Contains(st.agentID)) | ||
617 | dorequest = false; | ||
618 | } | ||
619 | 715 | ||
620 | if (dorequest && !m_blacklistedregions.ContainsKey(st.regionhandle)) | 716 | av = null; |
621 | { | 717 | st = null; |
622 | if (nAsyncRequests >= MAX_ASYNC_REQUESTS) // hit the break | ||
623 | { | ||
624 | // AH!!! Recursive ! | ||
625 | // Put this request back in the queue and return | ||
626 | EnqueueMapItemRequest(st); | ||
627 | return; | ||
628 | } | ||
629 | 718 | ||
630 | RequestMapItemsDelegate d = RequestMapItemsAsync; | 719 | st = requests.Dequeue(4900); // timeout to make watchdog happy |
631 | d.BeginInvoke(st.agentID, st.flags, st.EstateID, st.godlike, st.itemtype, st.regionhandle, RequestMapItemsCompleted, null); | ||
632 | //OSDMap response = RequestMapItemsAsync(st.agentID, st.flags, st.EstateID, st.godlike, st.itemtype, st.regionhandle); | ||
633 | //RequestMapItemsCompleted(response); | ||
634 | Interlocked.Increment(ref nAsyncRequests); | ||
635 | } | ||
636 | } | ||
637 | }); | ||
638 | } | ||
639 | 720 | ||
640 | /// <summary> | 721 | if (st == null || st.agentID == UUID.Zero) |
641 | /// Sends the mapitem response to the IClientAPI | 722 | continue; |
642 | /// </summary> | ||
643 | /// <param name="response">The OSDMap Response for the mapitem</param> | ||
644 | private void RequestMapItemsCompleted(IAsyncResult iar) | ||
645 | { | ||
646 | AsyncResult result = (AsyncResult)iar; | ||
647 | RequestMapItemsDelegate icon = (RequestMapItemsDelegate)result.AsyncDelegate; | ||
648 | 723 | ||
649 | OSDMap response = (OSDMap)icon.EndInvoke(iar); | 724 | // end gracefully |
725 | if (st.agentID == STOP_UUID) | ||
726 | break; | ||
650 | 727 | ||
651 | Interlocked.Decrement(ref nAsyncRequests); | 728 | // agent gone? |
729 | |||
730 | m_scene.TryGetScenePresence(st.agentID, out av); | ||
731 | if (av == null || av.IsChildAgent || av.IsDeleted || av.IsInTransit) | ||
732 | continue; | ||
652 | 733 | ||
653 | if (!response.ContainsKey("requestID")) | 734 | // region unreachable? |
654 | return; | 735 | if (m_blacklistedregions.Contains(st.regionhandle)) |
736 | continue; | ||
655 | 737 | ||
656 | UUID requestID = response["requestID"].AsUUID(); | 738 | bool dorequest = true; |
739 | OSDMap responseMap = null; | ||
657 | 740 | ||
658 | if (requestID != UUID.Zero) | 741 | // check if we are already serving this region |
659 | { | 742 | lock (m_cachedRegionMapItemsResponses) |
660 | MapRequestState mrs = new MapRequestState(); | ||
661 | mrs.agentID = UUID.Zero; | ||
662 | lock (m_openRequests) | ||
663 | { | ||
664 | if (m_openRequests.ContainsKey(requestID)) | ||
665 | { | 743 | { |
666 | mrs = m_openRequests[requestID]; | 744 | if (m_cachedRegionMapItemsResponses.Contains(st.regionhandle)) |
667 | m_openRequests.Remove(requestID); | 745 | { |
746 | m_cachedRegionMapItemsResponses.TryGetValue(st.regionhandle, out responseMap); | ||
747 | dorequest = false; | ||
748 | } | ||
749 | else | ||
750 | m_cachedRegionMapItemsResponses.Add(st.regionhandle, null, expireResponsesTime); // a bit more time for the access | ||
668 | } | 751 | } |
669 | } | ||
670 | 752 | ||
671 | if (mrs.agentID != UUID.Zero) | 753 | if (dorequest) |
672 | { | ||
673 | ScenePresence av = null; | ||
674 | m_scene.TryGetScenePresence(mrs.agentID, out av); | ||
675 | if (av != null) | ||
676 | { | 754 | { |
677 | if (response.ContainsKey(mrs.itemtype.ToString())) | 755 | // nothig for region, fire a request |
756 | Interlocked.Increment(ref nAsyncRequests); | ||
757 | MapRequestState rst = st; | ||
758 | Util.FireAndForget(x => | ||
678 | { | 759 | { |
679 | List<mapItemReply> returnitems = new List<mapItemReply>(); | 760 | RequestMapItemsAsync(rst.agentID, rst.flags, rst.EstateID, rst.godlike, rst.itemtype, rst.regionhandle); |
680 | OSDArray itemarray = (OSDArray)response[mrs.itemtype.ToString()]; | 761 | }); |
681 | for (int i = 0; i < itemarray.Count; i++) | 762 | } |
682 | { | 763 | else |
683 | OSDMap mapitem = (OSDMap)itemarray[i]; | 764 | { |
684 | mapItemReply mi = new mapItemReply(); | 765 | // do we have the response? |
685 | mi.FromOSD(mapitem); | 766 | if (responseMap != null) |
686 | returnitems.Add(mi); | ||
687 | } | ||
688 | av.ControllingClient.SendMapItemReply(returnitems.ToArray(), mrs.itemtype, mrs.flags); | ||
689 | } | ||
690 | |||
691 | // Service 7 (MAP_ITEM_LAND_FOR_SALE) | ||
692 | uint itemtype = (uint)GridItemType.LandForSale; | ||
693 | |||
694 | if (response.ContainsKey(itemtype.ToString())) | ||
695 | { | 767 | { |
696 | List<mapItemReply> returnitems = new List<mapItemReply>(); | 768 | if(av!=null) |
697 | OSDArray itemarray = (OSDArray)response[itemtype.ToString()]; | ||
698 | for (int i = 0; i < itemarray.Count; i++) | ||
699 | { | 769 | { |
700 | OSDMap mapitem = (OSDMap)itemarray[i]; | 770 | // this will mainly only send green dots now |
701 | mapItemReply mi = new mapItemReply(); | 771 | if (responseMap.ContainsKey(st.itemtype.ToString())) |
702 | mi.FromOSD(mapitem); | 772 | { |
703 | returnitems.Add(mi); | 773 | List<mapItemReply> returnitems = new List<mapItemReply>(); |
774 | OSDArray itemarray = (OSDArray)responseMap[st.itemtype.ToString()]; | ||
775 | for (int i = 0; i < itemarray.Count; i++) | ||
776 | { | ||
777 | OSDMap mapitem = (OSDMap)itemarray[i]; | ||
778 | mapItemReply mi = new mapItemReply(); | ||
779 | mi.x = (uint)mapitem["X"].AsInteger(); | ||
780 | mi.y = (uint)mapitem["Y"].AsInteger(); | ||
781 | mi.id = mapitem["ID"].AsUUID(); | ||
782 | mi.Extra = mapitem["Extra"].AsInteger(); | ||
783 | mi.Extra2 = mapitem["Extra2"].AsInteger(); | ||
784 | mi.name = mapitem["Name"].AsString(); | ||
785 | returnitems.Add(mi); | ||
786 | } | ||
787 | av.ControllingClient.SendMapItemReply(returnitems.ToArray(), st.itemtype, st.flags & 0xffff); | ||
788 | } | ||
704 | } | 789 | } |
705 | av.ControllingClient.SendMapItemReply(returnitems.ToArray(), itemtype, mrs.flags); | ||
706 | } | 790 | } |
707 | 791 | else | |
708 | // Service 1 (MAP_ITEM_TELEHUB) | ||
709 | itemtype = (uint)GridItemType.Telehub; | ||
710 | |||
711 | if (response.ContainsKey(itemtype.ToString())) | ||
712 | { | 792 | { |
713 | List<mapItemReply> returnitems = new List<mapItemReply>(); | 793 | // request still beeing processed, enqueue it back |
714 | OSDArray itemarray = (OSDArray)response[itemtype.ToString()]; | 794 | requests.Enqueue(st); |
715 | for (int i = 0; i < itemarray.Count; i++) | 795 | if (requests.Count() < 3) |
716 | { | 796 | Thread.Sleep(100); |
717 | OSDMap mapitem = (OSDMap)itemarray[i]; | ||
718 | mapItemReply mi = new mapItemReply(); | ||
719 | mi.FromOSD(mapitem); | ||
720 | returnitems.Add(mi); | ||
721 | } | ||
722 | av.ControllingClient.SendMapItemReply(returnitems.ToArray(), itemtype, mrs.flags); | ||
723 | } | 797 | } |
724 | } | 798 | } |
799 | |||
800 | while (nAsyncRequests >= MAX_ASYNC_REQUESTS) // hit the break | ||
801 | { | ||
802 | Thread.Sleep(100); | ||
803 | Watchdog.UpdateThread(); | ||
804 | } | ||
725 | } | 805 | } |
726 | } | 806 | } |
807 | |||
808 | catch (Exception e) | ||
809 | { | ||
810 | m_log.ErrorFormat("[WORLD MAP]: Map item request thread terminated abnormally with exception {0}", e); | ||
811 | } | ||
812 | |||
813 | threadrunning = false; | ||
814 | Watchdog.RemoveThread(); | ||
727 | } | 815 | } |
728 | 816 | ||
729 | /// <summary> | 817 | /// <summary> |
@@ -746,11 +834,12 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
746 | st.godlike = godlike; | 834 | st.godlike = godlike; |
747 | st.itemtype = itemtype; | 835 | st.itemtype = itemtype; |
748 | st.regionhandle = regionhandle; | 836 | st.regionhandle = regionhandle; |
749 | EnqueueMapItemRequest(st); | 837 | |
838 | requests.Enqueue(st); | ||
750 | } | 839 | } |
751 | 840 | ||
752 | private delegate OSDMap RequestMapItemsDelegate(UUID id, uint flags, | 841 | uint[] itemTypesForcedSend = new uint[] { 6, 1, 7, 10 }; // green dots, infohub, land sells |
753 | uint EstateID, bool godlike, uint itemtype, ulong regionhandle); | 842 | |
754 | /// <summary> | 843 | /// <summary> |
755 | /// Does the actual remote mapitem request | 844 | /// Does the actual remote mapitem request |
756 | /// This should be called from an asynchronous thread | 845 | /// This should be called from an asynchronous thread |
@@ -765,94 +854,55 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
765 | /// <param name="itemtype">passed in from packet</param> | 854 | /// <param name="itemtype">passed in from packet</param> |
766 | /// <param name="regionhandle">Region we're looking up</param> | 855 | /// <param name="regionhandle">Region we're looking up</param> |
767 | /// <returns></returns> | 856 | /// <returns></returns> |
768 | private OSDMap RequestMapItemsAsync(UUID id, uint flags, | 857 | private void RequestMapItemsAsync(UUID id, uint flags, |
769 | uint EstateID, bool godlike, uint itemtype, ulong regionhandle) | 858 | uint EstateID, bool godlike, uint itemtype, ulong regionhandle) |
770 | { | 859 | { |
771 | // m_log.DebugFormat("[WORLDMAP]: RequestMapItemsAsync; region handle: {0} {1}", regionhandle, itemtype); | 860 | // m_log.DebugFormat("[WORLDMAP]: RequestMapItemsAsync; region handle: {0} {1}", regionhandle, itemtype); |
772 | 861 | ||
773 | string httpserver = ""; | 862 | string httpserver = ""; |
774 | bool blacklisted = false; | 863 | bool blacklisted = false; |
775 | lock (m_blacklistedregions) | ||
776 | { | ||
777 | if (m_blacklistedregions.ContainsKey(regionhandle)) | ||
778 | { | ||
779 | if (Environment.TickCount > (m_blacklistedregions[regionhandle] + blacklistTimeout)) | ||
780 | { | ||
781 | m_log.DebugFormat("[WORLD MAP]: Unblock blacklisted region {0}", regionhandle); | ||
782 | 864 | ||
783 | m_blacklistedregions.Remove(regionhandle); | 865 | lock (m_blacklistedregions) |
784 | } | 866 | blacklisted = m_blacklistedregions.Contains(regionhandle); |
785 | else | ||
786 | blacklisted = true; | ||
787 | } | ||
788 | } | ||
789 | 867 | ||
790 | if (blacklisted) | 868 | if (blacklisted) |
791 | return new OSDMap(); | 869 | { |
870 | Interlocked.Decrement(ref nAsyncRequests); | ||
871 | return; | ||
872 | } | ||
792 | 873 | ||
793 | UUID requestID = UUID.Random(); | 874 | UUID requestID = UUID.Random(); |
794 | lock (m_cachedRegionMapItemsAddress) | 875 | lock (m_cachedRegionMapItemsAddress) |
795 | { | 876 | m_cachedRegionMapItemsAddress.TryGetValue(regionhandle, out httpserver); |
796 | if (m_cachedRegionMapItemsAddress.ContainsKey(regionhandle)) | 877 | |
797 | httpserver = m_cachedRegionMapItemsAddress[regionhandle]; | 878 | if (httpserver == null || httpserver.Length == 0) |
798 | } | ||
799 | if (httpserver.Length == 0) | ||
800 | { | 879 | { |
801 | uint x = 0, y = 0; | 880 | uint x = 0, y = 0; |
802 | Util.RegionHandleToWorldLoc(regionhandle, out x, out y); | 881 | Util.RegionHandleToWorldLoc(regionhandle, out x, out y); |
882 | |||
803 | GridRegion mreg = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, (int)x, (int)y); | 883 | GridRegion mreg = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, (int)x, (int)y); |
804 | 884 | ||
805 | if (mreg != null) | 885 | if (mreg != null) |
806 | { | 886 | { |
807 | httpserver = mreg.ServerURI + "MAP/MapItems/" + regionhandle.ToString(); | 887 | httpserver = mreg.ServerURI + "MAP/MapItems/" + regionhandle.ToString(); |
808 | lock (m_cachedRegionMapItemsAddress) | 888 | lock (m_cachedRegionMapItemsAddress) |
809 | { | 889 | m_cachedRegionMapItemsAddress.AddOrUpdate(regionhandle, httpserver, 2.0 * expireBlackListTime); |
810 | if (!m_cachedRegionMapItemsAddress.ContainsKey(regionhandle)) | ||
811 | m_cachedRegionMapItemsAddress.Add(regionhandle, httpserver); | ||
812 | } | ||
813 | } | ||
814 | else | ||
815 | { | ||
816 | lock (m_blacklistedregions) | ||
817 | { | ||
818 | if (!m_blacklistedregions.ContainsKey(regionhandle)) | ||
819 | m_blacklistedregions.Add(regionhandle, Environment.TickCount); | ||
820 | } | ||
821 | //m_log.InfoFormat("[WORLD MAP]: Blacklisted region {0}", regionhandle.ToString()); | ||
822 | } | 890 | } |
823 | } | 891 | } |
824 | 892 | ||
825 | blacklisted = false; | ||
826 | lock (m_blacklistedurls) | 893 | lock (m_blacklistedurls) |
827 | { | 894 | { |
828 | if (m_blacklistedurls.ContainsKey(httpserver)) | 895 | if (httpserver == null || httpserver.Length == 0 || m_blacklistedurls.Contains(httpserver)) |
829 | { | 896 | { |
830 | if (Environment.TickCount > (m_blacklistedurls[httpserver] + blacklistTimeout)) | 897 | // Can't find the http server or its blocked |
831 | { | 898 | lock (m_blacklistedregions) |
832 | m_log.DebugFormat("[WORLD MAP]: Unblock blacklisted URL {0}", httpserver); | 899 | m_blacklistedregions.AddOrUpdate(regionhandle, 0, expireBlackListTime); |
833 | 900 | ||
834 | m_blacklistedurls.Remove(httpserver); | 901 | Interlocked.Decrement(ref nAsyncRequests); |
835 | } | 902 | return; |
836 | else | ||
837 | blacklisted = true; | ||
838 | } | 903 | } |
839 | } | 904 | } |
840 | 905 | ||
841 | // Can't find the http server | ||
842 | if (httpserver.Length == 0 || blacklisted) | ||
843 | return new OSDMap(); | ||
844 | |||
845 | MapRequestState mrs = new MapRequestState(); | ||
846 | mrs.agentID = id; | ||
847 | mrs.EstateID = EstateID; | ||
848 | mrs.flags = flags; | ||
849 | mrs.godlike = godlike; | ||
850 | mrs.itemtype=itemtype; | ||
851 | mrs.regionhandle = regionhandle; | ||
852 | |||
853 | lock (m_openRequests) | ||
854 | m_openRequests.Add(requestID, mrs); | ||
855 | |||
856 | WebRequest mapitemsrequest = null; | 906 | WebRequest mapitemsrequest = null; |
857 | try | 907 | try |
858 | { | 908 | { |
@@ -861,132 +911,144 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
861 | catch (Exception e) | 911 | catch (Exception e) |
862 | { | 912 | { |
863 | m_log.DebugFormat("[WORLD MAP]: Access to {0} failed with {1}", httpserver, e); | 913 | m_log.DebugFormat("[WORLD MAP]: Access to {0} failed with {1}", httpserver, e); |
864 | return new OSDMap(); | 914 | Interlocked.Decrement(ref nAsyncRequests); |
915 | return; | ||
865 | } | 916 | } |
866 | 917 | ||
867 | mapitemsrequest.Method = "POST"; | 918 | mapitemsrequest.Method = "POST"; |
868 | mapitemsrequest.ContentType = "application/xml+llsd"; | 919 | mapitemsrequest.ContentType = "application/xml+llsd"; |
869 | OSDMap RAMap = new OSDMap(); | ||
870 | 920 | ||
921 | OSDMap RAMap = new OSDMap(); | ||
871 | // string RAMapString = RAMap.ToString(); | 922 | // string RAMapString = RAMap.ToString(); |
872 | OSD LLSDofRAMap = RAMap; // RENAME if this works | 923 | OSD LLSDofRAMap = RAMap; // RENAME if this works |
873 | 924 | ||
874 | byte[] buffer = OSDParser.SerializeLLSDXmlBytes(LLSDofRAMap); | 925 | byte[] buffer = OSDParser.SerializeLLSDXmlBytes(LLSDofRAMap); |
926 | |||
875 | OSDMap responseMap = new OSDMap(); | 927 | OSDMap responseMap = new OSDMap(); |
876 | responseMap["requestID"] = OSD.FromUUID(requestID); | ||
877 | 928 | ||
878 | Stream os = null; | ||
879 | try | 929 | try |
880 | { // send the Post | 930 | { // send the Post |
881 | mapitemsrequest.ContentLength = buffer.Length; //Count bytes to send | 931 | mapitemsrequest.ContentLength = buffer.Length; //Count bytes to send |
882 | os = mapitemsrequest.GetRequestStream(); | 932 | using (Stream os = mapitemsrequest.GetRequestStream()) |
883 | os.Write(buffer, 0, buffer.Length); //Send it | 933 | os.Write(buffer, 0, buffer.Length); //Send it |
884 | //m_log.DebugFormat("[WORLD MAP]: Getting MapItems from {0}", httpserver); | 934 | //m_log.DebugFormat("[WORLD MAP]: Getting MapItems from {0}", httpserver); |
885 | } | 935 | } |
886 | catch (WebException ex) | 936 | catch (WebException ex) |
887 | { | 937 | { |
888 | m_log.WarnFormat("[WORLD MAP]: Bad send on GetMapItems {0}", ex.Message); | 938 | m_log.WarnFormat("[WORLD MAP]: Bad send on GetMapItems {0}", ex.Message); |
889 | responseMap["connect"] = OSD.FromBoolean(false); | 939 | m_log.WarnFormat("[WORLD MAP]: Blacklisted url {0}", httpserver); |
890 | lock (m_blacklistedurls) | 940 | lock (m_blacklistedurls) |
891 | { | 941 | m_blacklistedurls.AddOrUpdate(httpserver, 0, expireBlackListTime); |
892 | if (!m_blacklistedurls.ContainsKey(httpserver)) | 942 | lock (m_blacklistedregions) |
893 | m_blacklistedurls.Add(httpserver, Environment.TickCount); | 943 | m_blacklistedregions.AddOrUpdate(regionhandle, 0, expireBlackListTime); |
894 | } | ||
895 | |||
896 | m_log.WarnFormat("[WORLD MAP]: Blacklisted {0}", httpserver); | ||
897 | 944 | ||
898 | return responseMap; | 945 | Interlocked.Decrement(ref nAsyncRequests); |
946 | return; | ||
899 | } | 947 | } |
900 | catch | 948 | catch |
901 | { | 949 | { |
902 | m_log.DebugFormat("[WORLD MAP]: RequestMapItems failed for {0}", httpserver); | 950 | m_log.DebugFormat("[WORLD MAP]: RequestMapItems failed for {0}", httpserver); |
903 | responseMap["connect"] = OSD.FromBoolean(false); | 951 | Interlocked.Decrement(ref nAsyncRequests); |
904 | return responseMap; | 952 | return; |
905 | } | ||
906 | finally | ||
907 | { | ||
908 | if (os != null) | ||
909 | os.Dispose(); | ||
910 | } | 953 | } |
911 | 954 | ||
912 | string response_mapItems_reply = null; | 955 | string response_mapItems_reply = null; |
913 | { | 956 | { // get the response |
914 | try | 957 | try |
915 | { | 958 | { |
916 | using (WebResponse webResponse = mapitemsrequest.GetResponse()) | 959 | using (WebResponse webResponse = mapitemsrequest.GetResponse()) |
917 | { | 960 | { |
918 | if (webResponse != null) | 961 | if (webResponse != null) |
919 | { | 962 | { |
920 | using (Stream s = webResponse.GetResponseStream()) | 963 | using (StreamReader sr = new StreamReader(webResponse.GetResponseStream())) |
921 | using (StreamReader sr = new StreamReader(s)) | 964 | { |
922 | response_mapItems_reply = sr.ReadToEnd().Trim(); | 965 | response_mapItems_reply = sr.ReadToEnd().Trim(); |
966 | } | ||
923 | } | 967 | } |
924 | else | 968 | else |
925 | { | 969 | { |
926 | return new OSDMap(); | 970 | Interlocked.Decrement(ref nAsyncRequests); |
927 | } | 971 | return; |
928 | } | 972 | } |
973 | } | ||
929 | } | 974 | } |
930 | catch (WebException) | 975 | catch (WebException) |
931 | { | 976 | { |
932 | responseMap["connect"] = OSD.FromBoolean(false); | ||
933 | lock (m_blacklistedurls) | 977 | lock (m_blacklistedurls) |
934 | { | 978 | m_blacklistedurls.AddOrUpdate(httpserver, 0, expireBlackListTime); |
935 | if (!m_blacklistedurls.ContainsKey(httpserver)) | 979 | lock (m_blacklistedregions) |
936 | m_blacklistedurls.Add(httpserver, Environment.TickCount); | 980 | m_blacklistedregions.AddOrUpdate(regionhandle, 0, expireBlackListTime); |
937 | } | ||
938 | 981 | ||
939 | m_log.WarnFormat("[WORLD MAP]: Blacklisted {0}", httpserver); | 982 | m_log.WarnFormat("[WORLD MAP]: Blacklisted url {0}", httpserver); |
940 | 983 | ||
941 | return responseMap; | 984 | Interlocked.Decrement(ref nAsyncRequests); |
985 | return; | ||
942 | } | 986 | } |
943 | catch | 987 | catch |
944 | { | 988 | { |
945 | m_log.DebugFormat("[WORLD MAP]: RequestMapItems failed for {0}", httpserver); | 989 | m_log.DebugFormat("[WORLD MAP]: RequestMapItems failed for {0}", httpserver); |
946 | responseMap["connect"] = OSD.FromBoolean(false); | ||
947 | lock (m_blacklistedregions) | 990 | lock (m_blacklistedregions) |
948 | { | 991 | m_blacklistedregions.AddOrUpdate(regionhandle, 0, expireBlackListTime); |
949 | if (!m_blacklistedregions.ContainsKey(regionhandle)) | ||
950 | m_blacklistedregions.Add(regionhandle, Environment.TickCount); | ||
951 | } | ||
952 | 992 | ||
953 | return responseMap; | 993 | Interlocked.Decrement(ref nAsyncRequests); |
994 | return; | ||
954 | } | 995 | } |
955 | 996 | ||
956 | OSD rezResponse = null; | ||
957 | try | 997 | try |
958 | { | 998 | { |
959 | rezResponse = OSDParser.DeserializeLLSDXml(response_mapItems_reply); | 999 | responseMap = (OSDMap)OSDParser.DeserializeLLSDXml(response_mapItems_reply); |
960 | |||
961 | responseMap = (OSDMap)rezResponse; | ||
962 | responseMap["requestID"] = OSD.FromUUID(requestID); | ||
963 | } | 1000 | } |
964 | catch (Exception ex) | 1001 | catch (Exception ex) |
965 | { | 1002 | { |
966 | m_log.InfoFormat("[WORLD MAP]: exception on parse of RequestMapItems reply from {0}: {1}", httpserver, ex.Message); | 1003 | m_log.InfoFormat("[WORLD MAP]: exception on parse of RequestMapItems reply from {0}: {1}", httpserver, ex.Message); |
967 | responseMap["connect"] = OSD.FromBoolean(false); | ||
968 | |||
969 | lock (m_blacklistedregions) | 1004 | lock (m_blacklistedregions) |
970 | { | 1005 | m_blacklistedregions.AddOrUpdate(regionhandle, 0, expireBlackListTime); |
971 | if (!m_blacklistedregions.ContainsKey(regionhandle)) | ||
972 | m_blacklistedregions.Add(regionhandle, Environment.TickCount); | ||
973 | } | ||
974 | 1006 | ||
975 | return responseMap; | 1007 | Interlocked.Decrement(ref nAsyncRequests); |
1008 | return; | ||
976 | } | 1009 | } |
977 | } | 1010 | } |
978 | 1011 | ||
979 | if (!responseMap.ContainsKey(itemtype.ToString())) // remote sim doesnt have the stated region handle | 1012 | // cache the response that may include other valid items |
1013 | lock (m_cachedRegionMapItemsResponses) | ||
1014 | m_cachedRegionMapItemsResponses.AddOrUpdate(regionhandle, responseMap, expireResponsesTime); | ||
1015 | |||
1016 | flags &= 0xffff; | ||
1017 | |||
1018 | if (id != UUID.Zero) | ||
980 | { | 1019 | { |
981 | m_log.DebugFormat("[WORLD MAP]: Remote sim does not have the stated region. Blacklisting."); | 1020 | ScenePresence av = null; |
982 | lock (m_blacklistedregions) | 1021 | m_scene.TryGetScenePresence(id, out av); |
1022 | if (av != null && !av.IsChildAgent && !av.IsDeleted && !av.IsInTransit) | ||
983 | { | 1023 | { |
984 | if (!m_blacklistedregions.ContainsKey(regionhandle)) | 1024 | // send all the items or viewers will never ask for them, except green dots |
985 | m_blacklistedregions.Add(regionhandle, Environment.TickCount); | 1025 | foreach (uint itfs in itemTypesForcedSend) |
1026 | { | ||
1027 | if (responseMap.ContainsKey(itfs.ToString())) | ||
1028 | { | ||
1029 | List<mapItemReply> returnitems = new List<mapItemReply>(); | ||
1030 | // OSDArray itemarray = (OSDArray)responseMap[itemtype.ToString()]; | ||
1031 | OSDArray itemarray = (OSDArray)responseMap[itfs.ToString()]; | ||
1032 | for (int i = 0; i < itemarray.Count; i++) | ||
1033 | { | ||
1034 | OSDMap mapitem = (OSDMap)itemarray[i]; | ||
1035 | mapItemReply mi = new mapItemReply(); | ||
1036 | mi.x = (uint)mapitem["X"].AsInteger(); | ||
1037 | mi.y = (uint)mapitem["Y"].AsInteger(); | ||
1038 | mi.id = mapitem["ID"].AsUUID(); | ||
1039 | mi.Extra = mapitem["Extra"].AsInteger(); | ||
1040 | mi.Extra2 = mapitem["Extra2"].AsInteger(); | ||
1041 | mi.name = mapitem["Name"].AsString(); | ||
1042 | returnitems.Add(mi); | ||
1043 | } | ||
1044 | // av.ControllingClient.SendMapItemReply(returnitems.ToArray(), itemtype, flags); | ||
1045 | av.ControllingClient.SendMapItemReply(returnitems.ToArray(), itfs, flags); | ||
1046 | } | ||
1047 | } | ||
986 | } | 1048 | } |
987 | } | 1049 | } |
988 | 1050 | ||
989 | return responseMap; | 1051 | Interlocked.Decrement(ref nAsyncRequests); |
990 | } | 1052 | } |
991 | 1053 | ||
992 | /// <summary> | 1054 | /// <summary> |
@@ -996,87 +1058,133 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
996 | /// <param name="minY"></param> | 1058 | /// <param name="minY"></param> |
997 | /// <param name="maxX"></param> | 1059 | /// <param name="maxX"></param> |
998 | /// <param name="maxY"></param> | 1060 | /// <param name="maxY"></param> |
999 | public virtual void RequestMapBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag) | 1061 | public void RequestMapBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag) |
1062 | { | ||
1063 | m_log.DebugFormat("[WoldMapModule] RequestMapBlocks {0}={1}={2}={3} {4}", minX, minY, maxX, maxY, flag); | ||
1064 | |||
1065 | GetAndSendBlocks(remoteClient, minX, minY, maxX, maxY, flag); | ||
1066 | } | ||
1067 | |||
1068 | protected virtual List<MapBlockData> GetAndSendBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag) | ||
1069 | { | ||
1070 | MapBlockRequestData req = new MapBlockRequestData(); | ||
1071 | |||
1072 | req.client = remoteClient; | ||
1073 | req.minX = minX; | ||
1074 | req.maxX = maxX; | ||
1075 | req.minY = minY; | ||
1076 | req.maxY = maxY; | ||
1077 | req.flags = flag; | ||
1078 | |||
1079 | lock (m_mapBlockRequestEvent) | ||
1080 | { | ||
1081 | if (!m_mapBlockRequests.ContainsKey(remoteClient.AgentId)) | ||
1082 | m_mapBlockRequests[remoteClient.AgentId] = new Queue<MapBlockRequestData>(); | ||
1083 | m_mapBlockRequests[remoteClient.AgentId].Enqueue(req); | ||
1084 | m_mapBlockRequestEvent.Set(); | ||
1085 | } | ||
1086 | |||
1087 | return new List<MapBlockData>(); | ||
1088 | } | ||
1089 | |||
1090 | protected void MapBlockSendThread() | ||
1000 | { | 1091 | { |
1001 | if ((flag & 0x10000) != 0) // user clicked on qthe map a tile that isn't visible | 1092 | while (true) |
1002 | { | 1093 | { |
1003 | List<MapBlockData> response = new List<MapBlockData>(); | 1094 | List<MapBlockRequestData> thisRunData = new List<MapBlockRequestData>(); |
1004 | 1095 | ||
1005 | // this should return one mapblock at most. It is triggered by a click | 1096 | m_mapBlockRequestEvent.WaitOne(); |
1006 | // on an unloaded square. | 1097 | lock (m_mapBlockRequestEvent) |
1007 | // But make sure: Look whether the one we requested is in there | ||
1008 | List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID, | ||
1009 | (int)Util.RegionToWorldLoc((uint)minX), (int)Util.RegionToWorldLoc((uint)maxX), | ||
1010 | (int)Util.RegionToWorldLoc((uint)minY), (int)Util.RegionToWorldLoc((uint)maxY) ); | ||
1011 | |||
1012 | m_log.DebugFormat("[WORLD MAP MODULE] RequestMapBlocks min=<{0},{1}>, max=<{2},{3}>, flag={4}, cntFound={5}", | ||
1013 | minX, minY, maxX, maxY, flag.ToString("X"), regions.Count); | ||
1014 | if (regions != null) | ||
1015 | { | 1098 | { |
1016 | foreach (GridRegion r in regions) | 1099 | int total = 0; |
1100 | foreach (Queue<MapBlockRequestData> q in m_mapBlockRequests.Values) | ||
1017 | { | 1101 | { |
1018 | if (r.RegionLocX == Util.RegionToWorldLoc((uint)minX) | 1102 | if (q.Count > 0) |
1019 | && r.RegionLocY == Util.RegionToWorldLoc((uint)minY) ) | 1103 | thisRunData.Add(q.Dequeue()); |
1020 | { | 1104 | |
1021 | // found it => add it to response | 1105 | total += q.Count; |
1022 | // Version 2 viewers can handle the larger regions | ||
1023 | if ((flag & 2) == 2) | ||
1024 | response.AddRange(Map2BlockFromGridRegion(r, flag)); | ||
1025 | else | ||
1026 | response.Add(MapBlockFromGridRegion(r, flag)); | ||
1027 | break; | ||
1028 | } | ||
1029 | } | 1106 | } |
1107 | |||
1108 | if (total == 0) | ||
1109 | m_mapBlockRequestEvent.Reset(); | ||
1030 | } | 1110 | } |
1031 | 1111 | ||
1032 | if (response.Count == 0) | 1112 | foreach (MapBlockRequestData req in thisRunData) |
1033 | { | 1113 | { |
1034 | // response still empty => couldn't find the map-tile the user clicked on => tell the client | 1114 | // Null client stops thread |
1035 | MapBlockData block = new MapBlockData(); | 1115 | if (req.client == null) |
1036 | block.X = (ushort)minX; | 1116 | return; |
1037 | block.Y = (ushort)minY; | 1117 | |
1038 | block.Access = (byte)SimAccess.Down; // means 'simulator is offline' | 1118 | GetAndSendBlocksInternal(req.client, req.minX, req.minY, req.maxX, req.maxY, req.flags); |
1039 | // block.Access = (byte)SimAccess.NonExistent; | ||
1040 | response.Add(block); | ||
1041 | } | 1119 | } |
1042 | // The lower 16 bits are an unsigned int16 | 1120 | |
1043 | remoteClient.SendMapBlock(response, flag & 0xffff); | 1121 | Thread.Sleep(50); |
1044 | } | ||
1045 | else | ||
1046 | { | ||
1047 | // normal mapblock request. Use the provided values | ||
1048 | GetAndSendBlocks(remoteClient, minX, minY, maxX, maxY, flag); | ||
1049 | } | 1122 | } |
1050 | } | 1123 | } |
1051 | 1124 | ||
1052 | protected virtual List<MapBlockData> GetAndSendBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag) | 1125 | protected virtual List<MapBlockData> GetAndSendBlocksInternal(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag) |
1053 | { | 1126 | { |
1127 | List<MapBlockData> allBlocks = new List<MapBlockData>(); | ||
1054 | List<MapBlockData> mapBlocks = new List<MapBlockData>(); | 1128 | List<MapBlockData> mapBlocks = new List<MapBlockData>(); |
1055 | List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID, | 1129 | List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID, |
1056 | (int)Util.RegionToWorldLoc((uint)(minX - 4)), (int)Util.RegionToWorldLoc((uint)(maxX + 4)), | 1130 | minX * (int)Constants.RegionSize, |
1057 | (int)Util.RegionToWorldLoc((uint)(minY - 4)), (int)Util.RegionToWorldLoc((uint)(maxY + 4)) ); | 1131 | maxX * (int)Constants.RegionSize, |
1058 | //m_log.DebugFormat("{0} GetAndSendBlocks. min=<{1},{2}>, max=<{3},{4}>, cntFound={5}", | 1132 | minY * (int)Constants.RegionSize, |
1059 | // LogHeader, minX, minY, maxX, maxY, regions.Count); | 1133 | maxY * (int)Constants.RegionSize); |
1134 | |||
1135 | // only send a negative answer for a single region request | ||
1136 | // corresponding to a click on the map. Current viewers | ||
1137 | // keep displaying "loading.." without this | ||
1138 | if (regions.Count == 0 && (flag & 0x10000) != 0 && minX == maxX && minY == maxY) | ||
1139 | { | ||
1140 | MapBlockData block = new MapBlockData(); | ||
1141 | block.X = (ushort)minX; | ||
1142 | block.Y = (ushort)minY; | ||
1143 | block.MapImageId = UUID.Zero; | ||
1144 | block.Access = (byte)SimAccess.NonExistent; | ||
1145 | allBlocks.Add(block); | ||
1146 | mapBlocks.Add(block); | ||
1147 | remoteClient.SendMapBlock(mapBlocks, flag & 0xffff); | ||
1148 | return allBlocks; | ||
1149 | } | ||
1150 | |||
1151 | flag &= 0xffff; | ||
1152 | |||
1060 | foreach (GridRegion r in regions) | 1153 | foreach (GridRegion r in regions) |
1061 | { | 1154 | { |
1062 | // Version 2 viewers can handle the larger regions | 1155 | if (r == null) |
1063 | if ((flag & 2) == 2) | 1156 | continue; |
1064 | mapBlocks.AddRange(Map2BlockFromGridRegion(r, flag)); | 1157 | MapBlockData block = new MapBlockData(); |
1065 | else | 1158 | MapBlockFromGridRegion(block, r, flag); |
1066 | mapBlocks.Add(MapBlockFromGridRegion(r, flag)); | 1159 | mapBlocks.Add(block); |
1160 | allBlocks.Add(block); | ||
1161 | |||
1162 | if (mapBlocks.Count >= 10) | ||
1163 | { | ||
1164 | remoteClient.SendMapBlock(mapBlocks, flag); | ||
1165 | mapBlocks.Clear(); | ||
1166 | Thread.Sleep(50); | ||
1167 | } | ||
1067 | } | 1168 | } |
1068 | remoteClient.SendMapBlock(mapBlocks, flag & 0xffff); | 1169 | if (mapBlocks.Count > 0) |
1170 | remoteClient.SendMapBlock(mapBlocks, flag); | ||
1069 | 1171 | ||
1070 | return mapBlocks; | 1172 | return allBlocks; |
1071 | } | 1173 | } |
1072 | 1174 | ||
1073 | // Fill a passed MapBlockData from a GridRegion | 1175 | public void MapBlockFromGridRegion(MapBlockData block, GridRegion r, uint flag) |
1074 | public MapBlockData MapBlockFromGridRegion(GridRegion r, uint flag) | ||
1075 | { | 1176 | { |
1076 | MapBlockData block = new MapBlockData(); | 1177 | if (r == null) |
1178 | { | ||
1179 | // we should not get here ?? | ||
1180 | // block.Access = (byte)SimAccess.Down; this is for a grid reply on r | ||
1181 | block.Access = (byte)SimAccess.NonExistent; | ||
1182 | block.MapImageId = UUID.Zero; | ||
1183 | return; | ||
1184 | } | ||
1077 | 1185 | ||
1078 | block.Access = r.Access; | 1186 | block.Access = r.Access; |
1079 | switch (flag & 0xffff) | 1187 | switch (flag) |
1080 | { | 1188 | { |
1081 | case 0: | 1189 | case 0: |
1082 | block.MapImageId = r.TerrainImage; | 1190 | block.MapImageId = r.TerrainImage; |
@@ -1089,50 +1197,13 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1089 | break; | 1197 | break; |
1090 | } | 1198 | } |
1091 | block.Name = r.RegionName; | 1199 | block.Name = r.RegionName; |
1092 | block.X = (ushort)Util.WorldToRegionLoc((uint)r.RegionLocX); | 1200 | block.X = (ushort)(r.RegionLocX / Constants.RegionSize); |
1093 | block.Y = (ushort)Util.WorldToRegionLoc((uint)r.RegionLocY); | 1201 | block.Y = (ushort)(r.RegionLocY / Constants.RegionSize); |
1094 | block.SizeX = (ushort) r.RegionSizeX; | 1202 | block.SizeX = (ushort)r.RegionSizeX; |
1095 | block.SizeY = (ushort) r.RegionSizeY; | 1203 | block.SizeY = (ushort)r.RegionSizeY; |
1096 | 1204 | ||
1097 | return block; | ||
1098 | } | 1205 | } |
1099 | 1206 | ||
1100 | public List<MapBlockData> Map2BlockFromGridRegion(GridRegion r, uint flag) | ||
1101 | { | ||
1102 | List<MapBlockData> blocks = new List<MapBlockData>(); | ||
1103 | MapBlockData block = new MapBlockData(); | ||
1104 | if (r == null) | ||
1105 | { | ||
1106 | block.Access = (byte)SimAccess.Down; | ||
1107 | block.MapImageId = UUID.Zero; | ||
1108 | blocks.Add(block); | ||
1109 | } | ||
1110 | else | ||
1111 | { | ||
1112 | block.Access = r.Access; | ||
1113 | switch (flag & 0xffff) | ||
1114 | { | ||
1115 | case 0: | ||
1116 | block.MapImageId = r.TerrainImage; | ||
1117 | break; | ||
1118 | case 2: | ||
1119 | block.MapImageId = r.ParcelImage; | ||
1120 | break; | ||
1121 | default: | ||
1122 | block.MapImageId = UUID.Zero; | ||
1123 | break; | ||
1124 | } | ||
1125 | block.Name = r.RegionName; | ||
1126 | block.X = (ushort)(r.RegionLocX / Constants.RegionSize); | ||
1127 | block.Y = (ushort)(r.RegionLocY / Constants.RegionSize); | ||
1128 | block.SizeX = (ushort)r.RegionSizeX; | ||
1129 | block.SizeY = (ushort)r.RegionSizeY; | ||
1130 | blocks.Add(block); | ||
1131 | } | ||
1132 | return blocks; | ||
1133 | } | ||
1134 | |||
1135 | |||
1136 | public Hashtable OnHTTPThrottled(Hashtable keysvals) | 1207 | public Hashtable OnHTTPThrottled(Hashtable keysvals) |
1137 | { | 1208 | { |
1138 | Hashtable reply = new Hashtable(); | 1209 | Hashtable reply = new Hashtable(); |
@@ -1145,67 +1216,71 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1145 | 1216 | ||
1146 | public Hashtable OnHTTPGetMapImage(Hashtable keysvals) | 1217 | public Hashtable OnHTTPGetMapImage(Hashtable keysvals) |
1147 | { | 1218 | { |
1148 | m_log.Debug("[WORLD MAP]: Sending map image jpeg"); | ||
1149 | Hashtable reply = new Hashtable(); | 1219 | Hashtable reply = new Hashtable(); |
1150 | int statuscode = 200; | 1220 | int statuscode = 200; |
1151 | byte[] jpeg = new byte[0]; | 1221 | byte[] jpeg = new byte[0]; |
1152 | 1222 | ||
1153 | if (myMapImageJPEG.Length == 0) | 1223 | if (m_scene.RegionInfo.RegionSettings.TerrainImageID != UUID.Zero) |
1154 | { | 1224 | { |
1155 | MemoryStream imgstream = null; | 1225 | m_log.Debug("[WORLD MAP]: Sending map image jpeg"); |
1156 | Bitmap mapTexture = new Bitmap(1,1); | ||
1157 | ManagedImage managedImage; | ||
1158 | Image image = (Image)mapTexture; | ||
1159 | 1226 | ||
1160 | try | 1227 | if (myMapImageJPEG.Length == 0) |
1161 | { | 1228 | { |
1162 | // Taking our jpeg2000 data, decoding it, then saving it to a byte array with regular jpeg data | 1229 | MemoryStream imgstream = null; |
1230 | Bitmap mapTexture = new Bitmap(1, 1); | ||
1231 | ManagedImage managedImage; | ||
1232 | Image image = (Image)mapTexture; | ||
1233 | |||
1234 | try | ||
1235 | { | ||
1236 | // Taking our jpeg2000 data, decoding it, then saving it to a byte array with regular jpeg data | ||
1163 | 1237 | ||
1164 | imgstream = new MemoryStream(); | 1238 | imgstream = new MemoryStream(); |
1165 | 1239 | ||
1166 | // non-async because we know we have the asset immediately. | 1240 | // non-async because we know we have the asset immediately. |
1167 | AssetBase mapasset = m_scene.AssetService.Get(m_scene.RegionInfo.RegionSettings.TerrainImageID.ToString()); | 1241 | AssetBase mapasset = m_scene.AssetService.Get(m_scene.RegionInfo.RegionSettings.TerrainImageID.ToString()); |
1168 | 1242 | ||
1169 | // Decode image to System.Drawing.Image | 1243 | // Decode image to System.Drawing.Image |
1170 | if (OpenJPEG.DecodeToImage(mapasset.Data, out managedImage, out image)) | 1244 | if (OpenJPEG.DecodeToImage(mapasset.Data, out managedImage, out image)) |
1171 | { | 1245 | { |
1172 | // Save to bitmap | 1246 | // Save to bitmap |
1173 | mapTexture = new Bitmap(image); | 1247 | mapTexture = new Bitmap(image); |
1174 | 1248 | ||
1175 | EncoderParameters myEncoderParameters = new EncoderParameters(); | 1249 | EncoderParameters myEncoderParameters = new EncoderParameters(); |
1176 | myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 95L); | 1250 | myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 95L); |
1177 | 1251 | ||
1178 | // Save bitmap to stream | 1252 | // Save bitmap to stream |
1179 | mapTexture.Save(imgstream, GetEncoderInfo("image/jpeg"), myEncoderParameters); | 1253 | mapTexture.Save(imgstream, GetEncoderInfo("image/jpeg"), myEncoderParameters); |
1180 | 1254 | ||
1181 | // Write the stream to a byte array for output | 1255 | // Write the stream to a byte array for output |
1182 | jpeg = imgstream.ToArray(); | 1256 | jpeg = imgstream.ToArray(); |
1183 | myMapImageJPEG = jpeg; | 1257 | myMapImageJPEG = jpeg; |
1258 | } | ||
1184 | } | 1259 | } |
1185 | } | 1260 | catch (Exception) |
1186 | catch (Exception) | 1261 | { |
1187 | { | 1262 | // Dummy! |
1188 | // Dummy! | 1263 | m_log.Warn("[WORLD MAP]: Unable to generate Map image"); |
1189 | m_log.Warn("[WORLD MAP]: Unable to generate Map image"); | 1264 | } |
1190 | } | 1265 | finally |
1191 | finally | 1266 | { |
1192 | { | 1267 | // Reclaim memory, these are unmanaged resources |
1193 | // Reclaim memory, these are unmanaged resources | 1268 | // If we encountered an exception, one or more of these will be null |
1194 | // If we encountered an exception, one or more of these will be null | 1269 | if (mapTexture != null) |
1195 | if (mapTexture != null) | 1270 | mapTexture.Dispose(); |
1196 | mapTexture.Dispose(); | ||
1197 | 1271 | ||
1198 | if (image != null) | 1272 | if (image != null) |
1199 | image.Dispose(); | 1273 | image.Dispose(); |
1200 | 1274 | ||
1201 | if (imgstream != null) | 1275 | if (imgstream != null) |
1202 | imgstream.Dispose(); | 1276 | imgstream.Dispose(); |
1277 | } | ||
1278 | } | ||
1279 | else | ||
1280 | { | ||
1281 | // Use cached version so we don't have to loose our mind | ||
1282 | jpeg = myMapImageJPEG; | ||
1203 | } | 1283 | } |
1204 | } | ||
1205 | else | ||
1206 | { | ||
1207 | // Use cached version so we don't have to loose our mind | ||
1208 | jpeg = myMapImageJPEG; | ||
1209 | } | 1284 | } |
1210 | 1285 | ||
1211 | reply["str_response_string"] = Convert.ToBase64String(jpeg); | 1286 | reply["str_response_string"] = Convert.ToBase64String(jpeg); |
@@ -1267,22 +1342,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1267 | 1342 | ||
1268 | foreach (GridRegion r in regions) | 1343 | foreach (GridRegion r in regions) |
1269 | { | 1344 | { |
1270 | MapBlockData mapBlock = MapBlockFromGridRegion(r, 0); | 1345 | MapBlockData mapBlock = new MapBlockData(); |
1346 | MapBlockFromGridRegion(mapBlock, r, 0); | ||
1271 | AssetBase texAsset = m_scene.AssetService.Get(mapBlock.MapImageId.ToString()); | 1347 | AssetBase texAsset = m_scene.AssetService.Get(mapBlock.MapImageId.ToString()); |
1272 | 1348 | ||
1273 | if (texAsset != null) | 1349 | if (texAsset != null) |
1274 | { | 1350 | { |
1275 | textures.Add(texAsset); | 1351 | textures.Add(texAsset); |
1276 | } | 1352 | } |
1277 | //else | ||
1278 | //{ | ||
1279 | // // WHAT?!? This doesn't seem right. Commenting (diva) | ||
1280 | // texAsset = m_scene.AssetService.Get(mapBlock.MapImageId.ToString()); | ||
1281 | // if (texAsset != null) | ||
1282 | // { | ||
1283 | // textures.Add(texAsset); | ||
1284 | // } | ||
1285 | //} | ||
1286 | } | 1353 | } |
1287 | 1354 | ||
1288 | foreach (AssetBase asset in textures) | 1355 | foreach (AssetBase asset in textures) |
@@ -1320,17 +1387,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1320 | if (consoleScene != null && consoleScene != m_scene) | 1387 | if (consoleScene != null && consoleScene != m_scene) |
1321 | return; | 1388 | return; |
1322 | 1389 | ||
1323 | if (m_mapImageGenerator == null) | 1390 | GenerateMaptile(); |
1324 | { | ||
1325 | Console.WriteLine("No map image generator available for {0}", m_scene.Name); | ||
1326 | return; | ||
1327 | } | ||
1328 | |||
1329 | using (Bitmap mapbmp = m_mapImageGenerator.CreateMapTile()) | ||
1330 | { | ||
1331 | GenerateMaptile(mapbmp); | ||
1332 | m_mapImageServiceModule.UploadMapTile(m_scene, mapbmp); | ||
1333 | } | ||
1334 | } | 1391 | } |
1335 | 1392 | ||
1336 | public OSD HandleRemoteMapItemRequest(string path, OSD request, string endpoint) | 1393 | public OSD HandleRemoteMapItemRequest(string path, OSD request, string endpoint) |
@@ -1338,9 +1395,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1338 | uint xstart = 0; | 1395 | uint xstart = 0; |
1339 | uint ystart = 0; | 1396 | uint ystart = 0; |
1340 | 1397 | ||
1341 | Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle,out xstart,out ystart); | 1398 | Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle, out xstart, out ystart); |
1342 | // m_log.DebugFormat("{0} HandleRemoteMapItemRequest. loc=<{1},{2}>", | ||
1343 | // LogHeader, Util.WorldToRegionLoc(xstart), Util.WorldToRegionLoc(ystart)); | ||
1344 | 1399 | ||
1345 | // Service 6 (MAP_ITEM_AGENTS_LOCATION; green dots) | 1400 | // Service 6 (MAP_ITEM_AGENTS_LOCATION; green dots) |
1346 | 1401 | ||
@@ -1362,8 +1417,8 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1362 | } | 1417 | } |
1363 | else | 1418 | else |
1364 | { | 1419 | { |
1365 | OSDArray responsearr = new OSDArray(m_scene.GetRootAgentCount()); | 1420 | OSDArray responsearr = new OSDArray(); // Don't preallocate. MT (m_scene.GetRootAgentCount()); |
1366 | m_scene.ForEachRootScenePresence(delegate(ScenePresence sp) | 1421 | m_scene.ForEachRootScenePresence(delegate (ScenePresence sp) |
1367 | { | 1422 | { |
1368 | OSDMap responsemapdata = new OSDMap(); | 1423 | OSDMap responsemapdata = new OSDMap(); |
1369 | responsemapdata["X"] = OSD.FromInteger((int)(xstart + sp.AbsolutePosition.X)); | 1424 | responsemapdata["X"] = OSD.FromInteger((int)(xstart + sp.AbsolutePosition.X)); |
@@ -1377,28 +1432,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1377 | responsemap["6"] = responsearr; | 1432 | responsemap["6"] = responsearr; |
1378 | } | 1433 | } |
1379 | 1434 | ||
1380 | // Service 7 (MAP_ITEM_LAND_FOR_SALE) | 1435 | // Service 7/10 (MAP_ITEM_LAND_FOR_SALE/ADULT) |
1381 | 1436 | ||
1382 | ILandChannel landChannel = m_scene.LandChannel; | 1437 | ILandChannel landChannel = m_scene.LandChannel; |
1383 | List<ILandObject> parcels = landChannel.AllParcels(); | 1438 | List<ILandObject> parcels = landChannel.AllParcels(); |
1384 | 1439 | ||
1385 | if ((parcels == null) || (parcels.Count == 0)) | 1440 | if ((parcels != null) && (parcels.Count >= 0)) |
1386 | { | 1441 | { |
1387 | OSDMap responsemapdata = new OSDMap(); | 1442 | OSDArray responsearr = new OSDArray(parcels.Count); |
1388 | responsemapdata["X"] = OSD.FromInteger((int)(xstart + 1)); | ||
1389 | responsemapdata["Y"] = OSD.FromInteger((int)(ystart + 1)); | ||
1390 | responsemapdata["ID"] = OSD.FromUUID(UUID.Zero); | ||
1391 | responsemapdata["Name"] = OSD.FromString(""); | ||
1392 | responsemapdata["Extra"] = OSD.FromInteger(0); | ||
1393 | responsemapdata["Extra2"] = OSD.FromInteger(0); | ||
1394 | OSDArray responsearr = new OSDArray(); | ||
1395 | responsearr.Add(responsemapdata); | ||
1396 | |||
1397 | responsemap["7"] = responsearr; | ||
1398 | } | ||
1399 | else | ||
1400 | { | ||
1401 | OSDArray responsearr = new OSDArray(m_scene.GetRootAgentCount()); | ||
1402 | foreach (ILandObject parcel_interface in parcels) | 1443 | foreach (ILandObject parcel_interface in parcels) |
1403 | { | 1444 | { |
1404 | // Play it safe | 1445 | // Play it safe |
@@ -1413,8 +1454,8 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1413 | { | 1454 | { |
1414 | Vector3 min = parcel.AABBMin; | 1455 | Vector3 min = parcel.AABBMin; |
1415 | Vector3 max = parcel.AABBMax; | 1456 | Vector3 max = parcel.AABBMax; |
1416 | float x = (min.X+max.X)/2; | 1457 | float x = (min.X + max.X) / 2; |
1417 | float y = (min.Y+max.Y)/2; | 1458 | float y = (min.Y + max.Y) / 2; |
1418 | 1459 | ||
1419 | OSDMap responsemapdata = new OSDMap(); | 1460 | OSDMap responsemapdata = new OSDMap(); |
1420 | responsemapdata["X"] = OSD.FromInteger((int)(xstart + x)); | 1461 | responsemapdata["X"] = OSD.FromInteger((int)(xstart + x)); |
@@ -1427,7 +1468,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1427 | responsearr.Add(responsemapdata); | 1468 | responsearr.Add(responsemapdata); |
1428 | } | 1469 | } |
1429 | } | 1470 | } |
1430 | responsemap["7"] = responsearr; | 1471 | |
1472 | if(responsearr.Count > 0) | ||
1473 | { | ||
1474 | if(m_scene.RegionInfo.RegionSettings.Maturity == 2) | ||
1475 | responsemap["10"] = responsearr; | ||
1476 | else | ||
1477 | responsemap["7"] = responsearr; | ||
1478 | } | ||
1431 | } | 1479 | } |
1432 | 1480 | ||
1433 | if (m_scene.RegionInfo.RegionSettings.TelehubObject != UUID.Zero) | 1481 | if (m_scene.RegionInfo.RegionSettings.TelehubObject != UUID.Zero) |
@@ -1459,55 +1507,88 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1459 | if (m_scene.Heightmap == null) | 1507 | if (m_scene.Heightmap == null) |
1460 | return; | 1508 | return; |
1461 | 1509 | ||
1510 | if (m_mapImageGenerator == null) | ||
1511 | { | ||
1512 | Console.WriteLine("No map image generator available for {0}", m_scene.Name); | ||
1513 | return; | ||
1514 | } | ||
1462 | m_log.DebugFormat("[WORLD MAP]: Generating map image for {0}", m_scene.Name); | 1515 | m_log.DebugFormat("[WORLD MAP]: Generating map image for {0}", m_scene.Name); |
1463 | 1516 | ||
1464 | using (Bitmap mapbmp = m_mapImageGenerator.CreateMapTile()) | 1517 | using (Bitmap mapbmp = m_mapImageGenerator.CreateMapTile()) |
1465 | { | 1518 | { |
1466 | // V1 (This Module) | ||
1467 | GenerateMaptile(mapbmp); | 1519 | GenerateMaptile(mapbmp); |
1468 | 1520 | ||
1469 | // v2/3 (MapImageServiceModule) | 1521 | if (m_mapImageServiceModule != null) |
1470 | m_mapImageServiceModule.UploadMapTile(m_scene, mapbmp); | 1522 | m_mapImageServiceModule.UploadMapTile(m_scene, mapbmp); |
1471 | } | 1523 | } |
1472 | } | 1524 | } |
1473 | 1525 | ||
1474 | private void GenerateMaptile(Bitmap mapbmp) | 1526 | private void GenerateMaptile(Bitmap mapbmp) |
1475 | { | 1527 | { |
1476 | byte[] data; | 1528 | bool needRegionSave = false; |
1477 | 1529 | ||
1478 | try | 1530 | // remove old assets |
1531 | UUID lastID = m_scene.RegionInfo.RegionSettings.TerrainImageID; | ||
1532 | if (lastID != UUID.Zero) | ||
1479 | { | 1533 | { |
1480 | data = OpenJPEG.EncodeFromImage(mapbmp, true); | 1534 | m_scene.AssetService.Delete(lastID.ToString()); |
1535 | m_scene.RegionInfo.RegionSettings.TerrainImageID = UUID.Zero; | ||
1536 | needRegionSave = true; | ||
1481 | } | 1537 | } |
1482 | catch (Exception e) // LEGIT: Catching problems caused by OpenJPEG p/invoke | 1538 | |
1539 | lastID = m_scene.RegionInfo.RegionSettings.ParcelImageID; | ||
1540 | if (lastID != UUID.Zero) | ||
1483 | { | 1541 | { |
1484 | m_log.Error("[WORLD MAP]: Failed generating terrain map: " + e); | 1542 | m_scene.AssetService.Delete(lastID.ToString()); |
1485 | return; | 1543 | m_scene.RegionInfo.RegionSettings.ParcelImageID = UUID.Zero; |
1544 | needRegionSave = true; | ||
1486 | } | 1545 | } |
1487 | 1546 | ||
1488 | byte[] overlay = GenerateOverlay(); | 1547 | // bypass terrain image for large regions |
1548 | if (m_scene.RegionInfo.RegionSizeX <= Constants.RegionSize && | ||
1549 | m_scene.RegionInfo.RegionSizeY <= Constants.RegionSize | ||
1550 | && mapbmp != null) | ||
1551 | { | ||
1552 | try | ||
1553 | { | ||
1554 | byte[] data; | ||
1555 | |||
1556 | data = OpenJPEG.EncodeFromImage(mapbmp, true); | ||
1557 | |||
1558 | if (data != null && data.Length > 0) | ||
1559 | { | ||
1560 | UUID terrainImageID = UUID.Random(); | ||
1489 | 1561 | ||
1490 | UUID terrainImageID = UUID.Random(); | 1562 | AssetBase asset = new AssetBase( |
1491 | UUID parcelImageID = UUID.Zero; | 1563 | terrainImageID, |
1564 | "terrainImage_" + m_scene.RegionInfo.RegionID.ToString(), | ||
1565 | (sbyte)AssetType.Texture, | ||
1566 | m_scene.RegionInfo.RegionID.ToString()); | ||
1567 | asset.Data = data; | ||
1568 | asset.Description = m_scene.RegionInfo.RegionName; | ||
1569 | asset.Temporary = false; | ||
1570 | asset.Flags = AssetFlags.Maptile; | ||
1492 | 1571 | ||
1493 | AssetBase asset = new AssetBase( | 1572 | // Store the new one |
1494 | terrainImageID, | 1573 | m_log.DebugFormat("[WORLD MAP]: Storing map tile {0} for {1}", asset.ID, m_scene.RegionInfo.RegionName); |
1495 | "terrainImage_" + m_scene.RegionInfo.RegionID.ToString(), | ||
1496 | (sbyte)AssetType.Texture, | ||
1497 | m_scene.RegionInfo.RegionID.ToString()); | ||
1498 | asset.Data = data; | ||
1499 | asset.Description = m_scene.RegionInfo.RegionName; | ||
1500 | asset.Temporary = false; | ||
1501 | asset.Flags = AssetFlags.Maptile; | ||
1502 | 1574 | ||
1503 | // Store the new one | 1575 | m_scene.AssetService.Store(asset); |
1504 | m_log.DebugFormat("[WORLD MAP]: Storing map tile {0} for {1}", asset.ID, m_scene.RegionInfo.RegionName); | ||
1505 | |||
1506 | m_scene.AssetService.Store(asset); | ||
1507 | 1576 | ||
1577 | m_scene.RegionInfo.RegionSettings.TerrainImageID = terrainImageID; | ||
1578 | needRegionSave = true; | ||
1579 | } | ||
1580 | } | ||
1581 | catch (Exception e) | ||
1582 | { | ||
1583 | m_log.Error("[WORLD MAP]: Failed generating terrain map: " + e); | ||
1584 | } | ||
1585 | } | ||
1586 | |||
1587 | // V2/3 still seem to need this, or we are missing something somewhere | ||
1588 | byte[] overlay = GenerateOverlay(); | ||
1508 | if (overlay != null) | 1589 | if (overlay != null) |
1509 | { | 1590 | { |
1510 | parcelImageID = UUID.Random(); | 1591 | UUID parcelImageID = UUID.Random(); |
1511 | 1592 | ||
1512 | AssetBase parcels = new AssetBase( | 1593 | AssetBase parcels = new AssetBase( |
1513 | parcelImageID, | 1594 | parcelImageID, |
@@ -1520,20 +1601,13 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1520 | parcels.Flags = AssetFlags.Maptile; | 1601 | parcels.Flags = AssetFlags.Maptile; |
1521 | 1602 | ||
1522 | m_scene.AssetService.Store(parcels); | 1603 | m_scene.AssetService.Store(parcels); |
1604 | |||
1605 | m_scene.RegionInfo.RegionSettings.ParcelImageID = parcelImageID; | ||
1606 | needRegionSave = true; | ||
1523 | } | 1607 | } |
1524 | 1608 | ||
1525 | // Switch to the new one | 1609 | if (needRegionSave) |
1526 | UUID lastTerrainImageID = m_scene.RegionInfo.RegionSettings.TerrainImageID; | 1610 | m_scene.RegionInfo.RegionSettings.Save(); |
1527 | UUID lastParcelImageID = m_scene.RegionInfo.RegionSettings.ParcelImageID; | ||
1528 | m_scene.RegionInfo.RegionSettings.TerrainImageID = terrainImageID; | ||
1529 | m_scene.RegionInfo.RegionSettings.ParcelImageID = parcelImageID; | ||
1530 | m_scene.RegionInfo.RegionSettings.Save(); | ||
1531 | |||
1532 | // Delete the old one | ||
1533 | // m_log.DebugFormat("[WORLDMAP]: Deleting old map tile {0}", lastTerrainImageID); | ||
1534 | m_scene.AssetService.Delete(lastTerrainImageID.ToString()); | ||
1535 | if (lastParcelImageID != UUID.Zero) | ||
1536 | m_scene.AssetService.Delete(lastParcelImageID.ToString()); | ||
1537 | } | 1611 | } |
1538 | 1612 | ||
1539 | private void MakeRootAgent(ScenePresence avatar) | 1613 | private void MakeRootAgent(ScenePresence avatar) |
@@ -1553,6 +1627,12 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1553 | { | 1627 | { |
1554 | m_rootAgents.Remove(avatar.UUID); | 1628 | m_rootAgents.Remove(avatar.UUID); |
1555 | } | 1629 | } |
1630 | |||
1631 | lock (m_mapBlockRequestEvent) | ||
1632 | { | ||
1633 | if (m_mapBlockRequests.ContainsKey(avatar.UUID)) | ||
1634 | m_mapBlockRequests.Remove(avatar.UUID); | ||
1635 | } | ||
1556 | } | 1636 | } |
1557 | 1637 | ||
1558 | public void OnRegionUp(GridRegion otherRegion) | 1638 | public void OnRegionUp(GridRegion otherRegion) |
@@ -1562,46 +1642,67 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1562 | 1642 | ||
1563 | lock (m_blacklistedregions) | 1643 | lock (m_blacklistedregions) |
1564 | { | 1644 | { |
1565 | if (!m_blacklistedregions.ContainsKey(regionhandle)) | 1645 | if (m_blacklistedregions.Contains(regionhandle)) |
1566 | m_blacklistedregions.Remove(regionhandle); | 1646 | m_blacklistedregions.Remove(regionhandle); |
1567 | } | 1647 | } |
1568 | 1648 | ||
1569 | lock (m_blacklistedurls) | 1649 | lock (m_blacklistedurls) |
1570 | { | 1650 | { |
1571 | if (m_blacklistedurls.ContainsKey(httpserver)) | 1651 | if (m_blacklistedurls.Contains(httpserver)) |
1572 | m_blacklistedurls.Remove(httpserver); | 1652 | m_blacklistedurls.Remove(httpserver); |
1573 | } | 1653 | } |
1574 | 1654 | ||
1575 | lock (m_cachedRegionMapItemsAddress) | 1655 | lock (m_cachedRegionMapItemsAddress) |
1576 | { | 1656 | { |
1577 | if (!m_cachedRegionMapItemsAddress.ContainsKey(regionhandle)) | 1657 | m_cachedRegionMapItemsAddress.AddOrUpdate(regionhandle, |
1578 | m_cachedRegionMapItemsAddress.Remove(regionhandle); | 1658 | httpserver, 5.0 * expireBlackListTime); |
1579 | } | 1659 | } |
1580 | } | 1660 | } |
1581 | 1661 | ||
1582 | private Byte[] GenerateOverlay() | 1662 | private Byte[] GenerateOverlay() |
1583 | { | 1663 | { |
1664 | int landTileSize = LandManagementModule.LandUnit; | ||
1665 | |||
1584 | // These need to be ints for bitmap generation | 1666 | // These need to be ints for bitmap generation |
1585 | int regionSizeX = (int)m_scene.RegionInfo.RegionSizeX; | 1667 | int regionSizeX = (int)m_scene.RegionInfo.RegionSizeX; |
1586 | int regionSizeY = (int)m_scene.RegionInfo.RegionSizeY; | ||
1587 | |||
1588 | int landTileSize = LandManagementModule.LandUnit; | ||
1589 | int regionLandTilesX = regionSizeX / landTileSize; | 1668 | int regionLandTilesX = regionSizeX / landTileSize; |
1669 | |||
1670 | int regionSizeY = (int)m_scene.RegionInfo.RegionSizeY; | ||
1590 | int regionLandTilesY = regionSizeY / landTileSize; | 1671 | int regionLandTilesY = regionSizeY / landTileSize; |
1591 | 1672 | ||
1592 | using (Bitmap overlay = new Bitmap(regionSizeX, regionSizeY)) | 1673 | bool landForSale = false; |
1674 | ILandObject land; | ||
1675 | |||
1676 | // scan terrain avoiding potencial merges of large bitmaps | ||
1677 | //TODO create the sell bitmap at landchannel / landmaster ? | ||
1678 | // and auction also, still not suported | ||
1679 | |||
1680 | bool[,] saleBitmap = new bool[regionLandTilesX, regionLandTilesY]; | ||
1681 | for (int x = 0, xx = 0; x < regionLandTilesX; x++ ,xx += landTileSize) | ||
1593 | { | 1682 | { |
1594 | bool[,] saleBitmap = new bool[regionLandTilesX, regionLandTilesY]; | 1683 | for (int y = 0, yy = 0; y < regionLandTilesY; y++, yy += landTileSize) |
1595 | for (int x = 0; x < regionLandTilesX; x++) | ||
1596 | { | 1684 | { |
1597 | for (int y = 0; y < regionLandTilesY; y++) | 1685 | land = m_scene.LandChannel.GetLandObject(xx, yy); |
1686 | if (land != null && (land.LandData.Flags & (uint)ParcelFlags.ForSale) != 0) | ||
1687 | { | ||
1688 | saleBitmap[x, y] = true; | ||
1689 | landForSale = true; | ||
1690 | } | ||
1691 | else | ||
1598 | saleBitmap[x, y] = false; | 1692 | saleBitmap[x, y] = false; |
1599 | } | 1693 | } |
1694 | } | ||
1600 | 1695 | ||
1601 | bool landForSale = false; | 1696 | if (!landForSale) |
1697 | { | ||
1698 | m_log.DebugFormat("[WORLD MAP]: Region {0} has no parcels for sale, not generating overlay", m_scene.RegionInfo.RegionName); | ||
1699 | return null; | ||
1700 | } | ||
1602 | 1701 | ||
1603 | List<ILandObject> parcels = m_scene.LandChannel.AllParcels(); | 1702 | m_log.DebugFormat("[WORLD MAP]: Region {0} has parcels for sale, generating overlay", m_scene.RegionInfo.RegionName); |
1604 | 1703 | ||
1704 | using (Bitmap overlay = new Bitmap(regionSizeX, regionSizeY)) | ||
1705 | { | ||
1605 | Color background = Color.FromArgb(0, 0, 0, 0); | 1706 | Color background = Color.FromArgb(0, 0, 0, 0); |
1606 | 1707 | ||
1607 | using (Graphics g = Graphics.FromImage(overlay)) | 1708 | using (Graphics g = Graphics.FromImage(overlay)) |
@@ -1609,36 +1710,19 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1609 | using (SolidBrush transparent = new SolidBrush(background)) | 1710 | using (SolidBrush transparent = new SolidBrush(background)) |
1610 | g.FillRectangle(transparent, 0, 0, regionSizeX, regionSizeY); | 1711 | g.FillRectangle(transparent, 0, 0, regionSizeX, regionSizeY); |
1611 | 1712 | ||
1612 | foreach (ILandObject land in parcels) | 1713 | // make it a bit transparent |
1714 | using (SolidBrush yellow = new SolidBrush(Color.FromArgb(192, 249, 223, 9))) | ||
1613 | { | 1715 | { |
1614 | // m_log.DebugFormat("[WORLD MAP]: Parcel {0} flags {1}", land.LandData.Name, land.LandData.Flags); | 1716 | for (int x = 0; x < regionLandTilesX; x++) |
1615 | if ((land.LandData.Flags & (uint)ParcelFlags.ForSale) != 0) | ||
1616 | { | 1717 | { |
1617 | landForSale = true; | 1718 | for (int y = 0; y < regionLandTilesY; y++) |
1618 | |||
1619 | saleBitmap = land.MergeLandBitmaps(saleBitmap, land.GetLandBitmap()); | ||
1620 | } | ||
1621 | } | ||
1622 | |||
1623 | if (!landForSale) | ||
1624 | { | ||
1625 | m_log.DebugFormat("[WORLD MAP]: Region {0} has no parcels for sale, not generating overlay", m_scene.RegionInfo.RegionName); | ||
1626 | return null; | ||
1627 | } | ||
1628 | |||
1629 | m_log.DebugFormat("[WORLD MAP]: Region {0} has parcels for sale, generating overlay", m_scene.RegionInfo.RegionName); | ||
1630 | |||
1631 | using (SolidBrush yellow = new SolidBrush(Color.FromArgb(255, 249, 223, 9))) | ||
1632 | { | ||
1633 | for (int x = 0 ; x < regionLandTilesX ; x++) | ||
1634 | { | ||
1635 | for (int y = 0 ; y < regionLandTilesY ; y++) | ||
1636 | { | 1719 | { |
1637 | if (saleBitmap[x, y]) | 1720 | if (saleBitmap[x, y]) |
1638 | g.FillRectangle( | 1721 | g.FillRectangle( |
1639 | yellow, x * landTileSize, | 1722 | yellow, |
1640 | regionSizeX - landTileSize - (y * landTileSize), | 1723 | x * landTileSize, |
1641 | landTileSize, | 1724 | regionSizeX - landTileSize - (y * landTileSize), |
1725 | landTileSize, | ||
1642 | landTileSize); | 1726 | landTileSize); |
1643 | } | 1727 | } |
1644 | } | 1728 | } |
@@ -1659,7 +1743,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1659 | } | 1743 | } |
1660 | } | 1744 | } |
1661 | 1745 | ||
1662 | public struct MapRequestState | 1746 | public class MapRequestState |
1663 | { | 1747 | { |
1664 | public UUID agentID; | 1748 | public UUID agentID; |
1665 | public uint flags; | 1749 | public uint flags; |
@@ -1668,4 +1752,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1668 | public uint itemtype; | 1752 | public uint itemtype; |
1669 | public ulong regionhandle; | 1753 | public ulong regionhandle; |
1670 | } | 1754 | } |
1755 | |||
1756 | public struct MapBlockRequestData | ||
1757 | { | ||
1758 | public IClientAPI client; | ||
1759 | public int minX; | ||
1760 | public int minY; | ||
1761 | public int maxX; | ||
1762 | public int maxY; | ||
1763 | public uint flags; | ||
1764 | } | ||
1671 | } | 1765 | } |