aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs')
-rw-r--r--OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs1488
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}