OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
WorldMapModule.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.org/
3  * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the OpenSimulator Project nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Drawing;
32 using System.Drawing.Imaging;
33 using System.IO;
34 using System.Net;
35 using System.Reflection;
36 using System.Runtime.Remoting.Messaging;
37 using System.Threading;
38 using log4net;
39 using Nini.Config;
40 using OpenMetaverse;
41 using OpenMetaverse.Imaging;
42 using OpenMetaverse.StructuredData;
43 using Mono.Addins;
44 using OpenSim.Framework;
45 using OpenSim.Framework.Capabilities;
46 using OpenSim.Framework.Monitoring;
47 using OpenSim.Framework.Servers;
48 using OpenSim.Framework.Servers.HttpServer;
49 using OpenSim.Region.Framework.Interfaces;
50 using OpenSim.Region.Framework.Scenes;
51 using OpenSim.Region.CoreModules.World.Land;
56 
57 namespace OpenSim.Region.CoreModules.World.WorldMap
58 {
59  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WorldMapModule")]
61  {
62  private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
63 #pragma warning disable 414
64  private static string LogHeader = "[WORLD MAP]";
65 #pragma warning restore 414
66 
67  private static readonly string DEFAULT_WORLD_MAP_EXPORT_PATH = "exportmap.jpg";
68  private static readonly UUID STOP_UUID = UUID.Random();
69  private static readonly string m_mapLayerPath = "0001/";
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 
76  private IMapImageGenerator m_mapImageGenerator;
77  private IMapImageUploadModule m_mapImageServiceModule;
78 
79  protected Scene m_scene;
80  private List<MapBlockData> cachedMapBlocks = new List<MapBlockData>();
81  private byte[] myMapImageJPEG;
82  protected volatile bool m_Enabled = false;
83  private ExpiringCache<string, int> m_blacklistedurls = new ExpiringCache<string, int>();
84  private ExpiringCache<ulong, int> m_blacklistedregions = new ExpiringCache<ulong, int>();
85  private ExpiringCache<ulong, string> m_cachedRegionMapItemsAddress = new ExpiringCache<ulong, string>();
86  private ExpiringCache<ulong, OSDMap> m_cachedRegionMapItemsResponses =
87  new ExpiringCache<ulong, OSDMap>();
88  private List<UUID> m_rootAgents = new List<UUID>();
89  private volatile bool threadrunning = false;
90  // expire time for the blacklists in seconds
91  private double expireBlackListTime = 600.0; // 10 minutes
92  // expire mapItems responses time in seconds. Throttles requests to regions that do answer
93  private const double expireResponsesTime = 120.0; // 2 minutes ?
94  //private int CacheRegionsDistance = 256;
95 
96  #region INonSharedRegionModule Members
97  public virtual void Initialise(IConfigSource config)
98  {
99  string[] configSections = new string[] { "Map", "Startup" };
100 
101  if (Util.GetConfigVarFromSections<string>(
102  config, "WorldMapModule", configSections, "WorldMap") == "WorldMap")
103  m_Enabled = true;
104 
105  expireBlackListTime = (double)Util.GetConfigVarFromSections<int>(config, "BlacklistTimeout", configSections, 10 * 60);
106  }
107 
108  public virtual void AddRegion(Scene scene)
109  {
110  if (!m_Enabled)
111  return;
112 
113  lock (scene)
114  {
115  m_scene = scene;
116 
117  m_scene.RegisterModuleInterface<IWorldMapModule>(this);
118 
119  m_scene.AddCommand(
120  "Regions", this, "export-map",
121  "export-map [<path>]",
122  "Save an image of the world map", HandleExportWorldMapConsoleCommand);
123 
124  m_scene.AddCommand(
125  "Regions", this, "generate map",
126  "generate map",
127  "Generates and stores a new maptile.", HandleGenerateMapConsoleCommand);
128 
129  AddHandlers();
130  }
131  }
132 
133  public virtual void RemoveRegion(Scene scene)
134  {
135  if (!m_Enabled)
136  return;
137 
138  lock (m_scene)
139  {
140  m_Enabled = false;
141  RemoveHandlers();
142  m_scene = null;
143  }
144  }
145 
146  public virtual void RegionLoaded(Scene scene)
147  {
148  if (!m_Enabled)
149  return;
150 
151  m_mapImageGenerator = m_scene.RequestModuleInterface<IMapImageGenerator>();
152  m_mapImageServiceModule = m_scene.RequestModuleInterface<IMapImageUploadModule>();
153  }
154 
155  public virtual void Close()
156  {
157  }
158 
159  public Type ReplaceableInterface
160  {
161  get { return null; }
162  }
163 
164  public virtual string Name
165  {
166  get { return "WorldMapModule"; }
167  }
168 
169  #endregion
170 
171  // this has to be called with a lock on m_scene
172  protected virtual void AddHandlers()
173  {
174  myMapImageJPEG = new byte[0];
175 
176  string regionimage = "regionImage" + m_scene.RegionInfo.RegionID.ToString();
177  regionimage = regionimage.Replace("-", "");
178  m_log.Info("[WORLD MAP]: JPEG Map location: " + m_scene.RegionInfo.ServerURI + "index.php?method=" + regionimage);
179 
180  MainServer.Instance.AddHTTPHandler(regionimage,
181  new GenericHTTPDOSProtector(OnHTTPGetMapImage, OnHTTPThrottled, new BasicDosProtectorOptions()
182  {
183  AllowXForwardedFor = false,
184  ForgetTimeSpan = TimeSpan.FromMinutes(2),
185  MaxRequestsInTimeframe = 4,
186  ReportingName = "MAPDOSPROTECTOR",
187  RequestTimeSpan = TimeSpan.FromSeconds(10),
188  ThrottledAction = BasicDOSProtector.ThrottleAction.DoThrottledMethod
189  }).Process);
190  MainServer.Instance.AddLLSDHandler(
191  "/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString(), HandleRemoteMapItemRequest);
192 
193  m_scene.EventManager.OnRegisterCaps += OnRegisterCaps;
194  m_scene.EventManager.OnNewClient += OnNewClient;
195  m_scene.EventManager.OnClientClosed += ClientLoggedOut;
196  m_scene.EventManager.OnMakeChildAgent += MakeChildAgent;
197  m_scene.EventManager.OnMakeRootAgent += MakeRootAgent;
198  m_scene.EventManager.OnRegionUp += OnRegionUp;
199 
200  StartThread(new object());
201  }
202 
203  // this has to be called with a lock on m_scene
204  protected virtual void RemoveHandlers()
205  {
206  StopThread();
207 
208  m_scene.EventManager.OnRegionUp -= OnRegionUp;
209  m_scene.EventManager.OnMakeRootAgent -= MakeRootAgent;
210  m_scene.EventManager.OnMakeChildAgent -= MakeChildAgent;
211  m_scene.EventManager.OnClientClosed -= ClientLoggedOut;
212  m_scene.EventManager.OnNewClient -= OnNewClient;
213  m_scene.EventManager.OnRegisterCaps -= OnRegisterCaps;
214 
215  string regionimage = "regionImage" + m_scene.RegionInfo.RegionID.ToString();
216  regionimage = regionimage.Replace("-", "");
217  MainServer.Instance.RemoveLLSDHandler("/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString(),
218  HandleRemoteMapItemRequest);
219  MainServer.Instance.RemoveHTTPHandler("", regionimage);
220  }
221 
222  public void OnRegisterCaps(UUID agentID, Caps caps)
223  {
224  //m_log.DebugFormat("[WORLD MAP]: OnRegisterCaps: agentID {0} caps {1}", agentID, caps);
225  string capsBase = "/CAPS/" + caps.CapsObjectPath;
226  caps.RegisterHandler(
227  "MapLayer",
228  new RestStreamHandler(
229  "POST",
230  capsBase + m_mapLayerPath,
231  (request, path, param, httpRequest, httpResponse)
232  => MapLayerRequest(request, path, param, agentID, caps),
233  "MapLayer",
234  agentID.ToString()));
235  }
236 
246  public string MapLayerRequest(string request, string path, string param,
247  UUID agentID, Caps caps)
248  {
249  // not sure about this....
250 
251  //try
252  //
253  //m_log.DebugFormat("[MAPLAYER]: path: {0}, param: {1}, agent:{2}",
254  // path, param, agentID.ToString());
255 
256  // There is a major hack going on in this method. The viewer doesn't request
257  // map blocks (RequestMapBlocks) above 2048. That means that if we don't hack,
258  // grids above that cell don't have a map at all. So, here's the hack: we wait
259  // for this CAP request to come, and we inject the map blocks at this point.
260  // In a normal scenario, this request simply sends back the MapLayer (the blue color).
261  // In the hacked scenario, it also sends the map blocks via UDP.
262  //
263  // 6/8/2011 -- I'm adding an explicit 2048 check, so that we never forget that there is
264  // a hack here, and so that regions below 4096 don't get spammed with unnecessary map blocks.
265 
266  //if (m_scene.RegionInfo.RegionLocX >= 2048 || m_scene.RegionInfo.RegionLocY >= 2048)
267  //{
268  // ScenePresence avatarPresence = null;
269 
270  // m_scene.TryGetScenePresence(agentID, out avatarPresence);
271 
272  // if (avatarPresence != null)
273  // {
274  // bool lookup = false;
275 
276  // lock (cachedMapBlocks)
277  // {
278  // if (cachedMapBlocks.Count > 0 && ((cachedTime + 1800) > Util.UnixTimeSinceEpoch()))
279  // {
280  // List<MapBlockData> mapBlocks;
281 
282  // mapBlocks = cachedMapBlocks;
283  // avatarPresence.ControllingClient.SendMapBlock(mapBlocks, 0);
284  // }
285  // else
286  // {
287  // lookup = true;
288  // }
289  // }
290  // if (lookup)
291  // {
292  // List<MapBlockData> mapBlocks = new List<MapBlockData>(); ;
293 
294  // List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID,
295  // (int)(m_scene.RegionInfo.RegionLocX - 8) * (int)Constants.RegionSize,
296  // (int)(m_scene.RegionInfo.RegionLocX + 8) * (int)Constants.RegionSize,
297  // (int)(m_scene.RegionInfo.RegionLocY - 8) * (int)Constants.RegionSize,
298  // (int)(m_scene.RegionInfo.RegionLocY + 8) * (int)Constants.RegionSize);
299  // foreach (GridRegion r in regions)
300  // {
301  // MapBlockData block = new MapBlockData();
302  // MapBlockFromGridRegion(block, r, 0);
303  // mapBlocks.Add(block);
304  // }
305  // avatarPresence.ControllingClient.SendMapBlock(mapBlocks, 0);
306 
307  // lock (cachedMapBlocks)
308  // cachedMapBlocks = mapBlocks;
309 
310  // cachedTime = Util.UnixTimeSinceEpoch();
311  // }
312  // }
313  //}
314 
315  LLSDMapLayerResponse mapResponse = new LLSDMapLayerResponse();
316  mapResponse.LayerData.Array.Add(GetOSDMapLayerResponse());
317  return mapResponse.ToString();
318  }
319 
326  {
327  // m_log.DebugFormat("[WORLD MAP]: MapLayer Request in region: {0}", m_scene.RegionInfo.RegionName);
328  LLSDMapLayerResponse mapResponse = new LLSDMapLayerResponse();
329  mapResponse.LayerData.Array.Add(GetOSDMapLayerResponse());
330  return mapResponse;
331  }
332 
338  {
339  // not sure about this.... 2048 or master 5000 and hack above?
340 
341  OSDMapLayer mapLayer = new OSDMapLayer();
342  mapLayer.Right = 2048;
343  mapLayer.Top = 2048;
344  mapLayer.ImageID = new UUID("00000000-0000-1111-9999-000000000006");
345 
346  return mapLayer;
347  }
348  #region EventHandlers
349 
354  private void OnNewClient(IClientAPI client)
355  {
356  client.OnRequestMapBlocks += RequestMapBlocks;
357  client.OnMapItemRequest += HandleMapItemRequest;
358  }
359 
366  private void ClientLoggedOut(UUID AgentId, Scene scene)
367  {
368  lock (m_rootAgents)
369  {
370  m_rootAgents.Remove(AgentId);
371  }
372  lock (m_mapBlockRequestEvent)
373  {
374  if (m_mapBlockRequests.ContainsKey(AgentId))
375  m_mapBlockRequests.Remove(AgentId);
376  }
377  }
378  #endregion
379 
386  private void StartThread(object o)
387  {
388  if (threadrunning) return;
389  threadrunning = true;
390 
391  // m_log.Debug("[WORLD MAP]: Starting remote MapItem request thread");
392 
393  WorkManager.StartThread(
394  process,
395  string.Format("MapItemRequestThread ({0})", m_scene.RegionInfo.RegionName),
396  ThreadPriority.BelowNormal,
397  true,
398  false);
399  WorkManager.StartThread(
400  MapBlockSendThread,
401  string.Format("MapBlockSendThread ({0})", m_scene.RegionInfo.RegionName),
402  ThreadPriority.BelowNormal,
403  true,
404  false);
405  }
406 
410  private void StopThread()
411  {
412  MapRequestState st = new MapRequestState();
413  st.agentID = STOP_UUID;
414  st.EstateID = 0;
415  st.flags = 0;
416  st.godlike = false;
417  st.itemtype = 0;
418  st.regionhandle = 0;
419 
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  }
437  }
438 
439  public virtual void HandleMapItemRequest(IClientAPI remoteClient, uint flags,
440  uint EstateID, bool godlike, uint itemtype, ulong regionhandle)
441  {
442  // m_log.DebugFormat("[WORLD MAP]: Handle MapItem request {0} {1}", regionhandle, itemtype);
443 
444  lock (m_rootAgents)
445  {
446  if (!m_rootAgents.Contains(remoteClient.AgentId))
447  return;
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 
459  uint xstart = 0;
460  uint ystart = 0;
461  Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle, out xstart, out ystart);
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)
475  {
476  switch (itemtype)
477  {
478  case (int)GridItemType.AgentLocations:
479  // Service 6 right now (MAP_ITEM_AGENTS_LOCATION; green dots)
480 
481  int tc = Environment.TickCount;
482  if (m_scene.GetRootAgentCount() <= 1)
483  {
484  mapitem = new mapItemReply(
485  xstart + 1,
486  ystart + 1,
487  UUID.Zero,
488  Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()),
489  0, 0);
490  mapitems.Add(mapitem);
491  }
492  else
493  {
494  m_scene.ForEachRootScenePresence(delegate (ScenePresence sp)
495  {
496  // Don't send a green dot for yourself
497  if (sp.UUID != remoteClient.AgentId)
498  {
499  mapitem = new mapItemReply(
500  xstart + (uint)sp.AbsolutePosition.X,
501  ystart + (uint)sp.AbsolutePosition.Y,
502  UUID.Zero,
503  Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()),
504  1, 0);
505  mapitems.Add(mapitem);
506  }
507  });
508  }
509  remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags);
510  break;
511 
512  case (int)GridItemType.Telehub:
513  // Service 1 (MAP_ITEM_TELEHUB)
514 
515  SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject);
516  if (sog != null)
517  {
518  mapitem = new mapItemReply(
519  xstart + (uint)sog.AbsolutePosition.X,
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;
530 
531  case (int)GridItemType.AdultLandForSale:
532  case (int)GridItemType.LandForSale:
533 
534  // Service 7 (MAP_ITEM_LAND_FOR_SALE)
535  adultRegion = m_scene.RegionInfo.RegionSettings.Maturity == 2;
536  if (adultRegion)
537  {
538  if (itemtype == (int)GridItemType.LandForSale)
539  break;
540  }
541  else
542  {
543  if (itemtype == (int)GridItemType.AdultLandForSale)
544  break;
545  }
546 
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(
570  xstart + (uint)x,
571  ystart + (uint)y,
572  parcel.GlobalID,
573  parcel.Name,
574  parcel.Area,
575  parcel.SalePrice
576  );
577  mapitems.Add(mapitem);
578  }
579  }
580  }
581  remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags);
582  break;
583 
584  case (uint)GridItemType.PgEvent:
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;
597  }
598  }
599  else
600  {
601  // send all items till we get a better fix
602 
603  // Service 6 right now (MAP_ITEM_AGENTS_LOCATION; green dots)
604 
605  int tc = Environment.TickCount;
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);
615  }
616  else
617  {
618  m_scene.ForEachRootScenePresence(delegate (ScenePresence sp)
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  });
632  }
633  remoteClient.SendMapItemReply(mapitems.ToArray(), 6, flags);
634  mapitems.Clear();
635 
636  // Service 1 (MAP_ITEM_TELEHUB)
637 
638  SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject);
639  if (sog != null)
640  {
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  }
653 
654  // Service 7 (MAP_ITEM_LAND_FOR_SALE)
655 
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)
667  {
668  // Play it safe
669  if (!(parcel_interface is LandObject))
670  continue;
671 
672  LandObject land = (LandObject)parcel_interface;
673  LandData parcel = land.LandData;
674 
675  // Show land for sale
676  if ((parcel.Flags & (uint)ParcelFlags.ForSale) == (uint)ParcelFlags.ForSale)
677  {
678  Vector3 min = parcel.AABBMin;
679  Vector3 max = parcel.AABBMax;
680  float x = (min.X + max.X) / 2;
681  float y = (min.Y + max.Y) / 2;
682  mapitem = new mapItemReply(
683  xstart + (uint)x,
684  ystart + (uint)y,
685  parcel.GlobalID,
686  parcel.Name,
687  parcel.Area,
688  parcel.SalePrice
689  );
690  mapitems.Add(mapitem);
691  }
692  }
693  if(mapitems.Count >0)
694  remoteClient.SendMapItemReply(mapitems.ToArray(), its, flags);
695  mapitems.Clear();
696  }
697  }
698  }
699 
700  private int nAsyncRequests = 0;
704  public void process()
705  {
706  const int MAX_ASYNC_REQUESTS = 20;
707  ScenePresence av = null;
708  MapRequestState st = null;
709 
710  try
711  {
712  while (true)
713  {
714  Watchdog.UpdateThread();
715 
716  av = null;
717  st = null;
718 
719  st = requests.Dequeue(4900); // timeout to make watchdog happy
720 
721  if (st == null || st.agentID == UUID.Zero)
722  continue;
723 
724  // end gracefully
725  if (st.agentID == STOP_UUID)
726  break;
727 
728  // agent gone?
729 
730  m_scene.TryGetScenePresence(st.agentID, out av);
731  if (av == null || av.IsChildAgent || av.IsDeleted || av.IsInTransit)
732  continue;
733 
734  // region unreachable?
735  if (m_blacklistedregions.Contains(st.regionhandle))
736  continue;
737 
738  bool dorequest = true;
739  OSDMap responseMap = null;
740 
741  // check if we are already serving this region
742  lock (m_cachedRegionMapItemsResponses)
743  {
744  if (m_cachedRegionMapItemsResponses.Contains(st.regionhandle))
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
751  }
752 
753  if (dorequest)
754  {
755  // nothig for region, fire a request
756  Interlocked.Increment(ref nAsyncRequests);
757  MapRequestState rst = st;
758  Util.FireAndForget(x =>
759  {
760  RequestMapItemsAsync(rst.agentID, rst.flags, rst.EstateID, rst.godlike, rst.itemtype, rst.regionhandle);
761  });
762  }
763  else
764  {
765  // do we have the response?
766  if (responseMap != null)
767  {
768  if(av!=null)
769  {
770  // this will mainly only send green dots now
771  if (responseMap.ContainsKey(st.itemtype.ToString()))
772  {
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  }
789  }
790  }
791  else
792  {
793  // request still beeing processed, enqueue it back
794  requests.Enqueue(st);
795  if (requests.Count() < 3)
796  Thread.Sleep(100);
797  }
798  }
799 
800  while (nAsyncRequests >= MAX_ASYNC_REQUESTS) // hit the break
801  {
802  Thread.Sleep(100);
803  Watchdog.UpdateThread();
804  }
805  }
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();
815  }
816 
827  public void RequestMapItems(string httpserver, UUID id, uint flags,
828  uint EstateID, bool godlike, uint itemtype, ulong regionhandle)
829  {
830  MapRequestState st = new MapRequestState();
831  st.agentID = id;
832  st.flags = flags;
833  st.EstateID = EstateID;
834  st.godlike = godlike;
835  st.itemtype = itemtype;
836  st.regionhandle = regionhandle;
837 
838  requests.Enqueue(st);
839  }
840 
841  uint[] itemTypesForcedSend = new uint[] { 6, 1, 7, 10 }; // green dots, infohub, land sells
842 
857  private void RequestMapItemsAsync(UUID id, uint flags,
858  uint EstateID, bool godlike, uint itemtype, ulong regionhandle)
859  {
860  // m_log.DebugFormat("[WORLDMAP]: RequestMapItemsAsync; region handle: {0} {1}", regionhandle, itemtype);
861 
862  string httpserver = "";
863  bool blacklisted = false;
864 
865  lock (m_blacklistedregions)
866  blacklisted = m_blacklistedregions.Contains(regionhandle);
867 
868  if (blacklisted)
869  {
870  Interlocked.Decrement(ref nAsyncRequests);
871  return;
872  }
873 
874  UUID requestID = UUID.Random();
875  lock (m_cachedRegionMapItemsAddress)
876  m_cachedRegionMapItemsAddress.TryGetValue(regionhandle, out httpserver);
877 
878  if (httpserver == null || httpserver.Length == 0)
879  {
880  uint x = 0, y = 0;
881  Util.RegionHandleToWorldLoc(regionhandle, out x, out y);
882 
883  GridRegion mreg = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, (int)x, (int)y);
884 
885  if (mreg != null)
886  {
887  httpserver = mreg.ServerURI + "MAP/MapItems/" + regionhandle.ToString();
888  lock (m_cachedRegionMapItemsAddress)
889  m_cachedRegionMapItemsAddress.AddOrUpdate(regionhandle, httpserver, 2.0 * expireBlackListTime);
890  }
891  }
892 
893  lock (m_blacklistedurls)
894  {
895  if (httpserver == null || httpserver.Length == 0 || m_blacklistedurls.Contains(httpserver))
896  {
897  // Can't find the http server or its blocked
898  lock (m_blacklistedregions)
899  m_blacklistedregions.AddOrUpdate(regionhandle, 0, expireBlackListTime);
900 
901  Interlocked.Decrement(ref nAsyncRequests);
902  return;
903  }
904  }
905 
906  WebRequest mapitemsrequest = null;
907  try
908  {
909  mapitemsrequest = WebRequest.Create(httpserver);
910  }
911  catch (Exception e)
912  {
913  m_log.DebugFormat("[WORLD MAP]: Access to {0} failed with {1}", httpserver, e);
914  Interlocked.Decrement(ref nAsyncRequests);
915  return;
916  }
917 
918  mapitemsrequest.Method = "POST";
919  mapitemsrequest.ContentType = "application/xml+llsd";
920 
921  OSDMap RAMap = new OSDMap();
922  // string RAMapString = RAMap.ToString();
923  OSD LLSDofRAMap = RAMap; // RENAME if this works
924 
925  byte[] buffer = OSDParser.SerializeLLSDXmlBytes(LLSDofRAMap);
926 
927  OSDMap responseMap = new OSDMap();
928 
929  try
930  { // send the Post
931  mapitemsrequest.ContentLength = buffer.Length; //Count bytes to send
932  using (Stream os = mapitemsrequest.GetRequestStream())
933  os.Write(buffer, 0, buffer.Length); //Send it
934  //m_log.DebugFormat("[WORLD MAP]: Getting MapItems from {0}", httpserver);
935  }
936  catch (WebException ex)
937  {
938  m_log.WarnFormat("[WORLD MAP]: Bad send on GetMapItems {0}", ex.Message);
939  m_log.WarnFormat("[WORLD MAP]: Blacklisted url {0}", httpserver);
940  lock (m_blacklistedurls)
941  m_blacklistedurls.AddOrUpdate(httpserver, 0, expireBlackListTime);
942  lock (m_blacklistedregions)
943  m_blacklistedregions.AddOrUpdate(regionhandle, 0, expireBlackListTime);
944 
945  Interlocked.Decrement(ref nAsyncRequests);
946  return;
947  }
948  catch
949  {
950  m_log.DebugFormat("[WORLD MAP]: RequestMapItems failed for {0}", httpserver);
951  Interlocked.Decrement(ref nAsyncRequests);
952  return;
953  }
954 
955  string response_mapItems_reply = null;
956  { // get the response
957  try
958  {
959  using (WebResponse webResponse = mapitemsrequest.GetResponse())
960  {
961  if (webResponse != null)
962  {
963  using (StreamReader sr = new StreamReader(webResponse.GetResponseStream()))
964  {
965  response_mapItems_reply = sr.ReadToEnd().Trim();
966  }
967  }
968  else
969  {
970  Interlocked.Decrement(ref nAsyncRequests);
971  return;
972  }
973  }
974  }
975  catch (WebException)
976  {
977  lock (m_blacklistedurls)
978  m_blacklistedurls.AddOrUpdate(httpserver, 0, expireBlackListTime);
979  lock (m_blacklistedregions)
980  m_blacklistedregions.AddOrUpdate(regionhandle, 0, expireBlackListTime);
981 
982  m_log.WarnFormat("[WORLD MAP]: Blacklisted url {0}", httpserver);
983 
984  Interlocked.Decrement(ref nAsyncRequests);
985  return;
986  }
987  catch
988  {
989  m_log.DebugFormat("[WORLD MAP]: RequestMapItems failed for {0}", httpserver);
990  lock (m_blacklistedregions)
991  m_blacklistedregions.AddOrUpdate(regionhandle, 0, expireBlackListTime);
992 
993  Interlocked.Decrement(ref nAsyncRequests);
994  return;
995  }
996 
997  try
998  {
999  responseMap = (OSDMap)OSDParser.DeserializeLLSDXml(response_mapItems_reply);
1000  }
1001  catch (Exception ex)
1002  {
1003  m_log.InfoFormat("[WORLD MAP]: exception on parse of RequestMapItems reply from {0}: {1}", httpserver, ex.Message);
1004  lock (m_blacklistedregions)
1005  m_blacklistedregions.AddOrUpdate(regionhandle, 0, expireBlackListTime);
1006 
1007  Interlocked.Decrement(ref nAsyncRequests);
1008  return;
1009  }
1010  }
1011 
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)
1019  {
1020  ScenePresence av = null;
1021  m_scene.TryGetScenePresence(id, out av);
1022  if (av != null && !av.IsChildAgent && !av.IsDeleted && !av.IsInTransit)
1023  {
1024  // send all the items or viewers will never ask for them, except green dots
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  }
1048  }
1049  }
1050 
1051  Interlocked.Decrement(ref nAsyncRequests);
1052  }
1053 
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()
1091  {
1092  while (true)
1093  {
1094  List<MapBlockRequestData> thisRunData = new List<MapBlockRequestData>();
1095 
1096  m_mapBlockRequestEvent.WaitOne();
1097  lock (m_mapBlockRequestEvent)
1098  {
1099  int total = 0;
1100  foreach (Queue<MapBlockRequestData> q in m_mapBlockRequests.Values)
1101  {
1102  if (q.Count > 0)
1103  thisRunData.Add(q.Dequeue());
1104 
1105  total += q.Count;
1106  }
1107 
1108  if (total == 0)
1109  m_mapBlockRequestEvent.Reset();
1110  }
1111 
1112  foreach (MapBlockRequestData req in thisRunData)
1113  {
1114  // Null client stops thread
1115  if (req.client == null)
1116  return;
1117 
1118  GetAndSendBlocksInternal(req.client, req.minX, req.minY, req.maxX, req.maxY, req.flags);
1119  }
1120 
1121  Thread.Sleep(50);
1122  }
1123  }
1124 
1125  protected virtual List<MapBlockData> GetAndSendBlocksInternal(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag)
1126  {
1127  List<MapBlockData> allBlocks = new List<MapBlockData>();
1128  List<MapBlockData> mapBlocks = new List<MapBlockData>();
1129  List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID,
1130  minX * (int)Constants.RegionSize,
1131  maxX * (int)Constants.RegionSize,
1132  minY * (int)Constants.RegionSize,
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 
1153  foreach (GridRegion r in regions)
1154  {
1155  if (r == null)
1156  continue;
1157  MapBlockData block = new MapBlockData();
1158  MapBlockFromGridRegion(block, 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  }
1168  }
1169  if (mapBlocks.Count > 0)
1170  remoteClient.SendMapBlock(mapBlocks, flag);
1171 
1172  return allBlocks;
1173  }
1174 
1175  public void MapBlockFromGridRegion(MapBlockData block, GridRegion r, uint flag)
1176  {
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  }
1185 
1186  block.Access = r.Access;
1187  switch (flag)
1188  {
1189  case 0:
1190  block.MapImageId = r.TerrainImage;
1191  break;
1192  case 2:
1193  block.MapImageId = r.ParcelImage;
1194  break;
1195  default:
1196  block.MapImageId = UUID.Zero;
1197  break;
1198  }
1199  block.Name = r.RegionName;
1200  block.X = (ushort)(r.RegionLocX / Constants.RegionSize);
1201  block.Y = (ushort)(r.RegionLocY / Constants.RegionSize);
1202  block.SizeX = (ushort)r.RegionSizeX;
1203  block.SizeY = (ushort)r.RegionSizeY;
1204 
1205  }
1206 
1207  public Hashtable OnHTTPThrottled(Hashtable keysvals)
1208  {
1209  Hashtable reply = new Hashtable();
1210  int statuscode = 500;
1211  reply["str_response_string"] = "";
1212  reply["int_response_code"] = statuscode;
1213  reply["content_type"] = "text/plain";
1214  return reply;
1215  }
1216 
1217  public Hashtable OnHTTPGetMapImage(Hashtable keysvals)
1218  {
1219  Hashtable reply = new Hashtable();
1220  int statuscode = 200;
1221  byte[] jpeg = new byte[0];
1222 
1223  if (m_scene.RegionInfo.RegionSettings.TerrainImageID != UUID.Zero)
1224  {
1225  m_log.Debug("[WORLD MAP]: Sending map image jpeg");
1226 
1227  if (myMapImageJPEG.Length == 0)
1228  {
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
1237 
1238  imgstream = new MemoryStream();
1239 
1240  // non-async because we know we have the asset immediately.
1241  AssetBase mapasset = m_scene.AssetService.Get(m_scene.RegionInfo.RegionSettings.TerrainImageID.ToString());
1242 
1243  // Decode image to System.Drawing.Image
1244  if (OpenJPEG.DecodeToImage(mapasset.Data, out managedImage, out image))
1245  {
1246  // Save to bitmap
1247  mapTexture = new Bitmap(image);
1248 
1249  EncoderParameters myEncoderParameters = new EncoderParameters();
1250  myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 95L);
1251 
1252  // Save bitmap to stream
1253  mapTexture.Save(imgstream, GetEncoderInfo("image/jpeg"), myEncoderParameters);
1254 
1255  // Write the stream to a byte array for output
1256  jpeg = imgstream.ToArray();
1257  myMapImageJPEG = jpeg;
1258  }
1259  }
1260  catch (Exception)
1261  {
1262  // Dummy!
1263  m_log.Warn("[WORLD MAP]: Unable to generate Map image");
1264  }
1265  finally
1266  {
1267  // Reclaim memory, these are unmanaged resources
1268  // If we encountered an exception, one or more of these will be null
1269  if (mapTexture != null)
1270  mapTexture.Dispose();
1271 
1272  if (image != null)
1273  image.Dispose();
1274 
1275  if (imgstream != null)
1276  imgstream.Dispose();
1277  }
1278  }
1279  else
1280  {
1281  // Use cached version so we don't have to loose our mind
1282  jpeg = myMapImageJPEG;
1283  }
1284  }
1285 
1286  reply["str_response_string"] = Convert.ToBase64String(jpeg);
1287  reply["int_response_code"] = statuscode;
1288  reply["content_type"] = "image/jpeg";
1289 
1290  return reply;
1291  }
1292 
1293  // From msdn
1294  private static ImageCodecInfo GetEncoderInfo(String mimeType)
1295  {
1296  ImageCodecInfo[] encoders;
1297  encoders = ImageCodecInfo.GetImageEncoders();
1298  for (int j = 0; j < encoders.Length; ++j)
1299  {
1300  if (encoders[j].MimeType == mimeType)
1301  return encoders[j];
1302  }
1303  return null;
1304  }
1305 
1310  public void HandleExportWorldMapConsoleCommand(string module, string[] cmdparams)
1311  {
1312  if (m_scene.ConsoleScene() == null)
1313  {
1314  // FIXME: If console region is root then this will be printed by every module. Currently, there is no
1315  // way to prevent this, short of making the entire module shared (which is complete overkill).
1316  // One possibility is to return a bool to signal whether the module has completely handled the command
1317  m_log.InfoFormat("[WORLD MAP]: Please change to a specific region in order to export its world map");
1318  return;
1319  }
1320 
1321  if (m_scene.ConsoleScene() != m_scene)
1322  return;
1323 
1324  string exportPath;
1325 
1326  if (cmdparams.Length > 1)
1327  exportPath = cmdparams[1];
1328  else
1329  exportPath = DEFAULT_WORLD_MAP_EXPORT_PATH;
1330 
1331  m_log.InfoFormat(
1332  "[WORLD MAP]: Exporting world map for {0} to {1}", m_scene.RegionInfo.RegionName, exportPath);
1333 
1334  List<MapBlockData> mapBlocks = new List<MapBlockData>();
1335  List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID,
1336  (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocX - 9),
1337  (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocX + 9),
1338  (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocY - 9),
1339  (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocY + 9));
1340  List<AssetBase> textures = new List<AssetBase>();
1341  List<Image> bitImages = new List<Image>();
1342 
1343  foreach (GridRegion r in regions)
1344  {
1345  MapBlockData mapBlock = new MapBlockData();
1346  MapBlockFromGridRegion(mapBlock, r, 0);
1347  AssetBase texAsset = m_scene.AssetService.Get(mapBlock.MapImageId.ToString());
1348 
1349  if (texAsset != null)
1350  {
1351  textures.Add(texAsset);
1352  }
1353  }
1354 
1355  foreach (AssetBase asset in textures)
1356  {
1357  ManagedImage managedImage;
1358  Image image;
1359 
1360  if (OpenJPEG.DecodeToImage(asset.Data, out managedImage, out image))
1361  bitImages.Add(image);
1362  }
1363 
1364  Bitmap mapTexture = new Bitmap(2560, 2560);
1365  Graphics g = Graphics.FromImage(mapTexture);
1366  SolidBrush sea = new SolidBrush(Color.DarkBlue);
1367  g.FillRectangle(sea, 0, 0, 2560, 2560);
1368 
1369  for (int i = 0; i < mapBlocks.Count; i++)
1370  {
1371  ushort x = (ushort)((mapBlocks[i].X - m_scene.RegionInfo.RegionLocX) + 10);
1372  ushort y = (ushort)((mapBlocks[i].Y - m_scene.RegionInfo.RegionLocY) + 10);
1373  g.DrawImage(bitImages[i], (x * 128), 2560 - (y * 128), 128, 128); // y origin is top
1374  }
1375 
1376  mapTexture.Save(exportPath, ImageFormat.Jpeg);
1377 
1378  m_log.InfoFormat(
1379  "[WORLD MAP]: Successfully exported world map for {0} to {1}",
1380  m_scene.RegionInfo.RegionName, exportPath);
1381  }
1382 
1383  public void HandleGenerateMapConsoleCommand(string module, string[] cmdparams)
1384  {
1385  Scene consoleScene = m_scene.ConsoleScene();
1386 
1387  if (consoleScene != null && consoleScene != m_scene)
1388  return;
1389 
1390  GenerateMaptile();
1391  }
1392 
1393  public OSD HandleRemoteMapItemRequest(string path, OSD request, string endpoint)
1394  {
1395  uint xstart = 0;
1396  uint ystart = 0;
1397 
1398  Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle, out xstart, out ystart);
1399 
1400  // Service 6 (MAP_ITEM_AGENTS_LOCATION; green dots)
1401 
1402  OSDMap responsemap = new OSDMap();
1403  int tc = Environment.TickCount;
1404  if (m_scene.GetRootAgentCount() == 0)
1405  {
1406  OSDMap responsemapdata = new OSDMap();
1407  responsemapdata["X"] = OSD.FromInteger((int)(xstart + 1));
1408  responsemapdata["Y"] = OSD.FromInteger((int)(ystart + 1));
1409  responsemapdata["ID"] = OSD.FromUUID(UUID.Zero);
1410  responsemapdata["Name"] = OSD.FromString(Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()));
1411  responsemapdata["Extra"] = OSD.FromInteger(0);
1412  responsemapdata["Extra2"] = OSD.FromInteger(0);
1413  OSDArray responsearr = new OSDArray();
1414  responsearr.Add(responsemapdata);
1415 
1416  responsemap["6"] = responsearr;
1417  }
1418  else
1419  {
1420  OSDArray responsearr = new OSDArray(); // Don't preallocate. MT (m_scene.GetRootAgentCount());
1421  m_scene.ForEachRootScenePresence(delegate (ScenePresence sp)
1422  {
1423  OSDMap responsemapdata = new OSDMap();
1424  responsemapdata["X"] = OSD.FromInteger((int)(xstart + sp.AbsolutePosition.X));
1425  responsemapdata["Y"] = OSD.FromInteger((int)(ystart + sp.AbsolutePosition.Y));
1426  responsemapdata["ID"] = OSD.FromUUID(UUID.Zero);
1427  responsemapdata["Name"] = OSD.FromString(Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()));
1428  responsemapdata["Extra"] = OSD.FromInteger(1);
1429  responsemapdata["Extra2"] = OSD.FromInteger(0);
1430  responsearr.Add(responsemapdata);
1431  });
1432  responsemap["6"] = responsearr;
1433  }
1434 
1435  // Service 7/10 (MAP_ITEM_LAND_FOR_SALE/ADULT)
1436 
1437  ILandChannel landChannel = m_scene.LandChannel;
1438  List<ILandObject> parcels = landChannel.AllParcels();
1439 
1440  if ((parcels != null) && (parcels.Count >= 0))
1441  {
1442  OSDArray responsearr = new OSDArray(parcels.Count);
1443  foreach (ILandObject parcel_interface in parcels)
1444  {
1445  // Play it safe
1446  if (!(parcel_interface is LandObject))
1447  continue;
1448 
1449  LandObject land = (LandObject)parcel_interface;
1450  LandData parcel = land.LandData;
1451 
1452  // Show land for sale
1453  if ((parcel.Flags & (uint)ParcelFlags.ForSale) == (uint)ParcelFlags.ForSale)
1454  {
1455  Vector3 min = parcel.AABBMin;
1456  Vector3 max = parcel.AABBMax;
1457  float x = (min.X + max.X) / 2;
1458  float y = (min.Y + max.Y) / 2;
1459 
1460  OSDMap responsemapdata = new OSDMap();
1461  responsemapdata["X"] = OSD.FromInteger((int)(xstart + x));
1462  responsemapdata["Y"] = OSD.FromInteger((int)(ystart + y));
1463  // responsemapdata["Z"] = OSD.FromInteger((int)m_scene.GetGroundHeight(x,y));
1464  responsemapdata["ID"] = OSD.FromUUID(parcel.GlobalID);
1465  responsemapdata["Name"] = OSD.FromString(parcel.Name);
1466  responsemapdata["Extra"] = OSD.FromInteger(parcel.Area);
1467  responsemapdata["Extra2"] = OSD.FromInteger(parcel.SalePrice);
1468  responsearr.Add(responsemapdata);
1469  }
1470  }
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  }
1479  }
1480 
1481  if (m_scene.RegionInfo.RegionSettings.TelehubObject != UUID.Zero)
1482  {
1483  SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject);
1484  if (sog != null)
1485  {
1486  OSDArray responsearr = new OSDArray();
1487  OSDMap responsemapdata = new OSDMap();
1488  responsemapdata["X"] = OSD.FromInteger((int)(xstart + sog.AbsolutePosition.X));
1489  responsemapdata["Y"] = OSD.FromInteger((int)(ystart + sog.AbsolutePosition.Y));
1490  // responsemapdata["Z"] = OSD.FromInteger((int)m_scene.GetGroundHeight(x,y));
1491  responsemapdata["ID"] = OSD.FromUUID(sog.UUID);
1492  responsemapdata["Name"] = OSD.FromString(sog.Name);
1493  responsemapdata["Extra"] = OSD.FromInteger(0); // color (unused)
1494  responsemapdata["Extra2"] = OSD.FromInteger(0); // 0 = telehub / 1 = infohub
1495  responsearr.Add(responsemapdata);
1496 
1497  responsemap["1"] = responsearr;
1498  }
1499  }
1500 
1501  return responsemap;
1502  }
1503 
1504  public void GenerateMaptile()
1505  {
1506  // Cannot create a map for a nonexistent heightmap
1507  if (m_scene.Heightmap == null)
1508  return;
1509 
1510  if (m_mapImageGenerator == null)
1511  {
1512  Console.WriteLine("No map image generator available for {0}", m_scene.Name);
1513  return;
1514  }
1515  m_log.DebugFormat("[WORLD MAP]: Generating map image for {0}", m_scene.Name);
1516 
1517  using (Bitmap mapbmp = m_mapImageGenerator.CreateMapTile())
1518  {
1519  GenerateMaptile(mapbmp);
1520 
1521  if (m_mapImageServiceModule != null)
1522  m_mapImageServiceModule.UploadMapTile(m_scene, mapbmp);
1523  }
1524  }
1525 
1526  private void GenerateMaptile(Bitmap mapbmp)
1527  {
1528  bool needRegionSave = false;
1529 
1530  // remove old assets
1531  UUID lastID = m_scene.RegionInfo.RegionSettings.TerrainImageID;
1532  if (lastID != UUID.Zero)
1533  {
1534  m_scene.AssetService.Delete(lastID.ToString());
1535  m_scene.RegionInfo.RegionSettings.TerrainImageID = UUID.Zero;
1536  needRegionSave = true;
1537  }
1538 
1539  lastID = m_scene.RegionInfo.RegionSettings.ParcelImageID;
1540  if (lastID != UUID.Zero)
1541  {
1542  m_scene.AssetService.Delete(lastID.ToString());
1543  m_scene.RegionInfo.RegionSettings.ParcelImageID = UUID.Zero;
1544  needRegionSave = true;
1545  }
1546 
1547  if(mapbmp != null)
1548  {
1549  try
1550  {
1551  byte[] data;
1552 
1553  // if large region limit its size since new viewers will not use it
1554  // but it is still usable for ossl
1555  if(m_scene.RegionInfo.RegionSizeX > Constants.RegionSize ||
1556  m_scene.RegionInfo.RegionSizeY > Constants.RegionSize)
1557  {
1558  int bx = mapbmp.Width;
1559  int by = mapbmp.Height;
1560  int mb = bx;
1561  if(mb < by)
1562  mb = by;
1563  if(mb > 2 * Constants.RegionSize && mb > 0)
1564  {
1565  float scale = 2.0f * (float)Constants.RegionSize/(float)mb;
1566  Size newsize = new Size();
1567  newsize.Width = (int)(bx * scale);
1568  newsize.Height = (int)(by * scale);
1569 
1570  using(Bitmap scaledbmp = new Bitmap(mapbmp,newsize))
1571  data = OpenJPEG.EncodeFromImage(scaledbmp, true);
1572  }
1573  else
1574  data = OpenJPEG.EncodeFromImage(mapbmp, true);
1575  }
1576  else
1577  data = OpenJPEG.EncodeFromImage(mapbmp, true);
1578 
1579  if (data != null && data.Length > 0)
1580  {
1581  UUID terrainImageID = UUID.Random();
1582 
1583  AssetBase asset = new AssetBase(
1584  terrainImageID,
1585  "terrainImage_" + m_scene.RegionInfo.RegionID.ToString(),
1586  (sbyte)AssetType.Texture,
1587  m_scene.RegionInfo.RegionID.ToString());
1588  asset.Data = data;
1589  asset.Description = m_scene.RegionInfo.RegionName;
1590  asset.Temporary = false;
1591  asset.Flags = AssetFlags.Maptile;
1592 
1593  // Store the new one
1594  m_log.DebugFormat("[WORLD MAP]: Storing map tile {0} for {1}", asset.ID, m_scene.RegionInfo.RegionName);
1595 
1596  m_scene.AssetService.Store(asset);
1597 
1598  m_scene.RegionInfo.RegionSettings.TerrainImageID = terrainImageID;
1599  needRegionSave = true;
1600  }
1601  }
1602  catch (Exception e)
1603  {
1604  m_log.Error("[WORLD MAP]: Failed generating terrain map: " + e);
1605  }
1606  }
1607 
1608  // V2/3 still seem to need this, or we are missing something somewhere
1609  byte[] overlay = GenerateOverlay();
1610  if (overlay != null)
1611  {
1612  UUID parcelImageID = UUID.Random();
1613 
1614  AssetBase parcels = new AssetBase(
1615  parcelImageID,
1616  "parcelImage_" + m_scene.RegionInfo.RegionID.ToString(),
1617  (sbyte)AssetType.Texture,
1618  m_scene.RegionInfo.RegionID.ToString());
1619  parcels.Data = overlay;
1620  parcels.Description = m_scene.RegionInfo.RegionName;
1621  parcels.Temporary = false;
1622  parcels.Flags = AssetFlags.Maptile;
1623 
1624  m_scene.AssetService.Store(parcels);
1625 
1626  m_scene.RegionInfo.RegionSettings.ParcelImageID = parcelImageID;
1627  needRegionSave = true;
1628  }
1629 
1630  if (needRegionSave)
1631  m_scene.RegionInfo.RegionSettings.Save();
1632  }
1633 
1634  private void MakeRootAgent(ScenePresence avatar)
1635  {
1636  lock (m_rootAgents)
1637  {
1638  if (!m_rootAgents.Contains(avatar.UUID))
1639  {
1640  m_rootAgents.Add(avatar.UUID);
1641  }
1642  }
1643  }
1644 
1645  private void MakeChildAgent(ScenePresence avatar)
1646  {
1647  lock (m_rootAgents)
1648  {
1649  m_rootAgents.Remove(avatar.UUID);
1650  }
1651 
1652  lock (m_mapBlockRequestEvent)
1653  {
1654  if (m_mapBlockRequests.ContainsKey(avatar.UUID))
1655  m_mapBlockRequests.Remove(avatar.UUID);
1656  }
1657  }
1658 
1659  public void OnRegionUp(GridRegion otherRegion)
1660  {
1661  ulong regionhandle = otherRegion.RegionHandle;
1662  string httpserver = otherRegion.ServerURI + "MAP/MapItems/" + regionhandle.ToString();
1663 
1664  lock (m_blacklistedregions)
1665  {
1666  if (m_blacklistedregions.Contains(regionhandle))
1667  m_blacklistedregions.Remove(regionhandle);
1668  }
1669 
1670  lock (m_blacklistedurls)
1671  {
1672  if (m_blacklistedurls.Contains(httpserver))
1673  m_blacklistedurls.Remove(httpserver);
1674  }
1675 
1676  lock (m_cachedRegionMapItemsAddress)
1677  {
1678  m_cachedRegionMapItemsAddress.AddOrUpdate(regionhandle,
1679  httpserver, 5.0 * expireBlackListTime);
1680  }
1681  }
1682 
1683  private Byte[] GenerateOverlay()
1684  {
1685  int landTileSize = LandManagementModule.LandUnit;
1686 
1687  // These need to be ints for bitmap generation
1688  int regionSizeX = (int)m_scene.RegionInfo.RegionSizeX;
1689  int regionLandTilesX = regionSizeX / landTileSize;
1690 
1691  int regionSizeY = (int)m_scene.RegionInfo.RegionSizeY;
1692  int regionLandTilesY = regionSizeY / landTileSize;
1693 
1694  bool landForSale = false;
1695  ILandObject land;
1696 
1697  // scan terrain avoiding potencial merges of large bitmaps
1698  //TODO create the sell bitmap at landchannel / landmaster ?
1699  // and auction also, still not suported
1700 
1701  bool[,] saleBitmap = new bool[regionLandTilesX, regionLandTilesY];
1702  for (int x = 0, xx = 0; x < regionLandTilesX; x++ ,xx += landTileSize)
1703  {
1704  for (int y = 0, yy = 0; y < regionLandTilesY; y++, yy += landTileSize)
1705  {
1706  land = m_scene.LandChannel.GetLandObject(xx, yy);
1707  if (land != null && (land.LandData.Flags & (uint)ParcelFlags.ForSale) != 0)
1708  {
1709  saleBitmap[x, y] = true;
1710  landForSale = true;
1711  }
1712  else
1713  saleBitmap[x, y] = false;
1714  }
1715  }
1716 
1717  if (!landForSale)
1718  {
1719  m_log.DebugFormat("[WORLD MAP]: Region {0} has no parcels for sale, not generating overlay", m_scene.RegionInfo.RegionName);
1720  return null;
1721  }
1722 
1723  m_log.DebugFormat("[WORLD MAP]: Region {0} has parcels for sale, generating overlay", m_scene.RegionInfo.RegionName);
1724 
1725  using (Bitmap overlay = new Bitmap(regionSizeX, regionSizeY))
1726  {
1727  Color background = Color.FromArgb(0, 0, 0, 0);
1728 
1729  using (Graphics g = Graphics.FromImage(overlay))
1730  {
1731  using (SolidBrush transparent = new SolidBrush(background))
1732  g.FillRectangle(transparent, 0, 0, regionSizeX, regionSizeY);
1733 
1734  // make it a bit transparent
1735  using (SolidBrush yellow = new SolidBrush(Color.FromArgb(192, 249, 223, 9)))
1736  {
1737  for (int x = 0; x < regionLandTilesX; x++)
1738  {
1739  for (int y = 0; y < regionLandTilesY; y++)
1740  {
1741  if (saleBitmap[x, y])
1742  g.FillRectangle(
1743  yellow,
1744  x * landTileSize,
1745  regionSizeX - landTileSize - (y * landTileSize),
1746  landTileSize,
1747  landTileSize);
1748  }
1749  }
1750  }
1751  }
1752 
1753  try
1754  {
1755  return OpenJPEG.EncodeFromImage(overlay, true);
1756  }
1757  catch (Exception e)
1758  {
1759  m_log.DebugFormat("[WORLD MAP]: Error creating parcel overlay: " + e.ToString());
1760  }
1761  }
1762 
1763  return null;
1764  }
1765  }
1766 
1767  public class MapRequestState
1768  {
1769  public UUID agentID;
1770  public uint flags;
1771  public uint EstateID;
1772  public bool godlike;
1773  public uint itemtype;
1774  public ulong regionhandle;
1775  }
1776 
1777  public struct MapBlockRequestData
1778  {
1779  public IClientAPI client;
1780  public int minX;
1781  public int minY;
1782  public int maxX;
1783  public int maxY;
1784  public uint flags;
1785  }
1786 }
delegate void RequestMapBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag)
OpenSim.Framework.Capabilities.Caps Caps
virtual void HandleMapItemRequest(IClientAPI remoteClient, uint flags, uint EstateID, bool godlike, uint itemtype, ulong regionhandle)
void RequestMapItems(string httpserver, UUID id, uint flags, uint EstateID, bool godlike, uint itemtype, ulong regionhandle)
Enqueue the MapItem request for remote processing
virtual void Initialise(IConfigSource config)
This is called to initialize the region module. For shared modules, this is called exactly once...
A scene object group is conceptually an object in the scene. The object is constituted of SceneObject...
LLSDMapLayerResponse GetMapLayer(LLSDMapRequest mapReq)
OpenMetaverse.StructuredData.OSDArray OSDArray
virtual void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
UUID GlobalID
Global ID for the parcel. (3rd Party Integration)
Definition: LandData.cs:327
string MapLayerRequest(string request, string path, string param, UUID agentID, Caps caps)
Callback for a map layer request
Keeps track of a specific piece of land's information
Definition: LandObject.cs:43
OpenMetaverse.StructuredData.OSDMap OSDMap
void process()
Processing thread main() loop for doing remote mapitem requests
virtual void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
override Vector3 AbsolutePosition
Position of this avatar relative to the region the avatar is in
Details of a Parcel of land
Definition: LandData.cs:47
OpenSim.Services.Interfaces.GridRegion GridRegion
OpenMetaverse.StructuredData.OSD OSD
string Name
Parcel Name
Definition: LandData.cs:433
virtual void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
override Vector3 AbsolutePosition
The absolute position of this scene object in the scene
uint Flags
Parcel settings. Access flags, Fly, NoPush, Voice, Scripts allowed, etc. ParcelFlags ...
Definition: LandData.cs:402
int SalePrice
When the parcel is being sold, this is the price to purchase the parcel
Definition: LandData.cs:611
OpenSim.Services.Interfaces.GridRegion GridRegion
override string Name
The name of an object grouping is always the same as its root part
int Area
Area in meters^2 the parcel contains
Definition: LandData.cs:237
virtual void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...