WorldMapModule.cs 72 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813
  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. using System;
  28. using System.Collections;
  29. using System.Collections.Concurrent;
  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.Threading;
  37. using log4net;
  38. using Nini.Config;
  39. using OpenMetaverse;
  40. using OpenMetaverse.Imaging;
  41. using OpenMetaverse.StructuredData;
  42. using Mono.Addins;
  43. using OpenSim.Framework;
  44. using OpenSim.Framework.Capabilities;
  45. using OpenSim.Framework.Monitoring;
  46. using OpenSim.Framework.Servers;
  47. using OpenSim.Framework.Servers.HttpServer;
  48. using OpenSim.Region.Framework.Interfaces;
  49. using OpenSim.Region.Framework.Scenes;
  50. using OpenSim.Region.CoreModules.World.Land;
  51. using Caps=OpenSim.Framework.Capabilities.Caps;
  52. using OSDArray=OpenMetaverse.StructuredData.OSDArray;
  53. using OSDMap=OpenMetaverse.StructuredData.OSDMap;
  54. using GridRegion = OpenSim.Services.Interfaces.GridRegion;
  55. namespace OpenSim.Region.CoreModules.World.WorldMap
  56. {
  57. [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WorldMapModule")]
  58. public class WorldMapModule : INonSharedRegionModule, IWorldMapModule, IDisposable
  59. {
  60. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  61. private const string LogHeader = "[WORLD MAP]";
  62. private static readonly string DEFAULT_WORLD_MAP_EXPORT_PATH = "exportmap.jpg";
  63. private IMapImageGenerator m_mapImageGenerator;
  64. private IMapImageUploadModule m_mapImageServiceModule;
  65. protected Scene m_scene;
  66. private ulong m_regionHandle;
  67. private uint m_regionGlobalX;
  68. private uint m_regionGlobalY;
  69. private uint m_regionSizeX;
  70. private uint m_regionSizeY;
  71. private string m_regionName;
  72. private byte[] myMapImageJPEG;
  73. protected volatile bool m_Enabled = false;
  74. private ManualResetEvent m_mapBlockRequestEvent = new ManualResetEvent(false);
  75. private ObjectJobEngine m_mapItemsRequests;
  76. private readonly Dictionary<UUID, Queue<MapBlockRequestData>> m_mapBlockRequests = new Dictionary<UUID, Queue<MapBlockRequestData>>();
  77. private readonly List<MapBlockData> cachedMapBlocks = new List<MapBlockData>();
  78. private ExpiringKey<string> m_blacklistedurls = new ExpiringKey<string>(60000);
  79. private ExpiringKey<ulong> m_blacklistedregions = new ExpiringKey<ulong>(60000);
  80. private ExpiringCacheOS<ulong, OSDMap> m_cachedRegionMapItemsResponses = new ExpiringCacheOS<ulong, OSDMap>(1000);
  81. private readonly HashSet<UUID> m_rootAgents = new HashSet<UUID>();
  82. private volatile bool m_threadsRunning = false;
  83. // expire time for the blacklists in seconds
  84. protected int expireBlackListTime = 300; // 5 minutes
  85. // expire mapItems responses time in seconds. Throttles requests to regions that do answer
  86. private const double expireResponsesTime = 120.0; // 2 minutes ?
  87. //private int CacheRegionsDistance = 256;
  88. protected bool m_exportPrintScale = false; // prints the scale of map in meters on exported map
  89. protected bool m_exportPrintRegionName = false; // prints the region name exported map
  90. protected bool m_localV1MapAssets = false; // keep V1 map assets only on local cache
  91. public WorldMapModule()
  92. {
  93. }
  94. ~WorldMapModule()
  95. {
  96. Dispose(false);
  97. }
  98. public void Dispose()
  99. {
  100. if (!disposed)
  101. {
  102. Dispose(true);
  103. GC.SuppressFinalize(this);
  104. }
  105. }
  106. bool disposed;
  107. public virtual void Dispose(bool disposing)
  108. {
  109. if (!disposed)
  110. {
  111. disposed = true;
  112. m_mapBlockRequestEvent?.Dispose();
  113. m_blacklistedurls?.Dispose();
  114. m_blacklistedregions?.Dispose();
  115. m_mapItemsRequests?.Dispose();
  116. m_cachedRegionMapItemsResponses?.Dispose();
  117. m_mapBlockRequestEvent = null;
  118. m_blacklistedurls = null;
  119. m_blacklistedregions = null;
  120. m_mapItemsRequests = null;
  121. m_cachedRegionMapItemsResponses = null;
  122. }
  123. }
  124. #region INonSharedRegionModule Members
  125. public virtual void Initialise(IConfigSource config)
  126. {
  127. string[] configSections = new string[] { "Map", "Startup" };
  128. if (Util.GetConfigVarFromSections<string>(
  129. config, "WorldMapModule", configSections, "WorldMap") == "WorldMap")
  130. m_Enabled = true;
  131. expireBlackListTime = (int)Util.GetConfigVarFromSections<int>(config, "BlacklistTimeout", configSections, 10 * 60);
  132. expireBlackListTime *= 1000;
  133. m_exportPrintScale =
  134. Util.GetConfigVarFromSections<bool>(config, "ExportMapAddScale", configSections, m_exportPrintScale);
  135. m_exportPrintRegionName =
  136. Util.GetConfigVarFromSections<bool>(config, "ExportMapAddRegionName", configSections, m_exportPrintRegionName);
  137. m_localV1MapAssets =
  138. Util.GetConfigVarFromSections<bool>(config, "LocalV1MapAssets", configSections, m_localV1MapAssets);
  139. }
  140. public virtual void AddRegion(Scene scene)
  141. {
  142. if (!m_Enabled)
  143. return;
  144. lock (scene)
  145. {
  146. m_scene = scene;
  147. m_regionHandle = scene.RegionInfo.RegionHandle;
  148. m_regionGlobalX = scene.RegionInfo.WorldLocX;
  149. m_regionGlobalY = scene.RegionInfo.WorldLocY;
  150. m_regionSizeX = scene.RegionInfo.RegionSizeX;
  151. m_regionSizeY = scene.RegionInfo.RegionSizeX;
  152. m_regionName = scene.RegionInfo.RegionName;
  153. m_scene.RegisterModuleInterface<IWorldMapModule>(this);
  154. m_scene.AddCommand(
  155. "Regions", this, "export-map",
  156. "export-map [<path>]",
  157. "Save an image of the world map", HandleExportWorldMapConsoleCommand);
  158. m_scene.AddCommand(
  159. "Regions", this, "generate map",
  160. "generate map",
  161. "Generates and stores a new maptile.", HandleGenerateMapConsoleCommand);
  162. AddHandlers();
  163. }
  164. }
  165. public virtual void RemoveRegion(Scene scene)
  166. {
  167. if (!m_Enabled)
  168. return;
  169. lock (m_scene)
  170. {
  171. m_Enabled = false;
  172. RemoveHandlers();
  173. m_scene = null;
  174. }
  175. }
  176. public virtual void RegionLoaded(Scene scene)
  177. {
  178. if (!m_Enabled)
  179. return;
  180. m_mapImageGenerator = m_scene.RequestModuleInterface<IMapImageGenerator>();
  181. m_mapImageServiceModule = m_scene.RequestModuleInterface<IMapImageUploadModule>();
  182. }
  183. public virtual void Close()
  184. {
  185. Dispose();
  186. }
  187. public Type ReplaceableInterface
  188. {
  189. get { return null; }
  190. }
  191. public virtual string Name
  192. {
  193. get { return "WorldMapModule"; }
  194. }
  195. #endregion
  196. // this has to be called with a lock on m_scene
  197. protected virtual void AddHandlers()
  198. {
  199. myMapImageJPEG = new byte[0];
  200. string regionimage = "regionImage" + m_scene.RegionInfo.RegionID.ToString();
  201. regionimage = regionimage.Replace("-", "");
  202. m_log.Info("[WORLD MAP]: JPEG Map location: " + m_scene.RegionInfo.ServerURI + "index.php?method=" + regionimage);
  203. MainServer.Instance.AddIndexPHPMethodHandler(regionimage, OnHTTPGetMapImage);
  204. MainServer.Instance.AddSimpleStreamHandler(new SimpleStreamHandler(
  205. "/MAP/MapItems/" + m_regionHandle.ToString(), HandleRemoteMapItemRequest));
  206. m_scene.EventManager.OnRegisterCaps += OnRegisterCaps;
  207. m_scene.EventManager.OnNewClient += OnNewClient;
  208. m_scene.EventManager.OnClientClosed += ClientLoggedOut;
  209. m_scene.EventManager.OnMakeChildAgent += MakeChildAgent;
  210. m_scene.EventManager.OnMakeRootAgent += MakeRootAgent;
  211. m_scene.EventManager.OnRegionUp += OnRegionUp;
  212. StartThreads();
  213. }
  214. // this has to be called with a lock on m_scene
  215. protected virtual void RemoveHandlers()
  216. {
  217. StopThreads();
  218. m_scene.EventManager.OnRegionUp -= OnRegionUp;
  219. m_scene.EventManager.OnMakeRootAgent -= MakeRootAgent;
  220. m_scene.EventManager.OnMakeChildAgent -= MakeChildAgent;
  221. m_scene.EventManager.OnClientClosed -= ClientLoggedOut;
  222. m_scene.EventManager.OnNewClient -= OnNewClient;
  223. m_scene.EventManager.OnRegisterCaps -= OnRegisterCaps;
  224. m_scene.UnregisterModuleInterface<IWorldMapModule>(this);
  225. MainServer.Instance.RemoveSimpleStreamHandler("/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString());
  226. string regionimage = "regionImage" + m_scene.RegionInfo.RegionID.ToString();
  227. regionimage = regionimage.Replace("-", "");
  228. MainServer.Instance.RemoveIndexPHPMethodHandler(regionimage);
  229. }
  230. public void OnRegisterCaps(UUID agentID, Caps caps)
  231. {
  232. //m_log.DebugFormat("[WORLD MAP]: OnRegisterCaps: agentID {0} caps {1}", agentID, caps);
  233. caps.RegisterSimpleHandler("MapLayer", new SimpleStreamHandler("/" + UUID.Random(), MapLayerRequest));
  234. }
  235. /// <summary>
  236. /// Callback for a map layer request
  237. /// </summary>
  238. /// <param name="request"></param>
  239. /// <param name="path"></param>
  240. /// <param name="param"></param>
  241. /// <param name="agentID"></param>
  242. /// <param name="caps"></param>
  243. /// <returns></returns>
  244. public void MapLayerRequest(IOSHttpRequest request, IOSHttpResponse response)
  245. {
  246. if(request.HttpMethod != "POST")
  247. {
  248. response.StatusCode = (int)HttpStatusCode.NotFound;
  249. return;
  250. }
  251. LLSDMapLayerResponse mapResponse = new LLSDMapLayerResponse();
  252. mapResponse.LayerData.Array.Add(GetOSDMapLayerResponse());
  253. response.RawBuffer = System.Text.Encoding.UTF8.GetBytes(LLSDHelpers.SerialiseLLSDReply(mapResponse));
  254. response.StatusCode = (int)HttpStatusCode.OK;
  255. }
  256. /// <summary>
  257. ///
  258. /// </summary>
  259. /// <returns></returns>
  260. protected static OSDMapLayer GetOSDMapLayerResponse()
  261. {
  262. // not sure about this.... 2048 or master 5000 and hack above?
  263. OSDMapLayer mapLayer = new OSDMapLayer();
  264. mapLayer.Right = 30000;
  265. mapLayer.Top = 30000;
  266. mapLayer.ImageID = new UUID("00000000-0000-1111-9999-000000000006");
  267. return mapLayer;
  268. }
  269. #region EventHandlers
  270. /// <summary>
  271. /// Registered for event
  272. /// </summary>
  273. /// <param name="client"></param>
  274. private void OnNewClient(IClientAPI client)
  275. {
  276. client.OnRequestMapBlocks += RequestMapBlocks;
  277. client.OnMapItemRequest += HandleMapItemRequest;
  278. }
  279. /// <summary>
  280. /// Client logged out, check to see if there are any more root agents in the simulator
  281. /// If not, stop the mapItemRequest Thread
  282. /// Event handler
  283. /// </summary>
  284. /// <param name="AgentId">AgentID that logged out</param>
  285. private void ClientLoggedOut(UUID AgentId, Scene scene)
  286. {
  287. lock (m_rootAgents)
  288. {
  289. m_rootAgents.Remove(AgentId);
  290. }
  291. lock (m_mapBlockRequestEvent)
  292. {
  293. m_mapBlockRequests.Remove(AgentId);
  294. }
  295. }
  296. #endregion
  297. /// <summary>
  298. /// Starts the MapItemRequest Thread
  299. /// Note that this only gets started when there are actually agents in the region
  300. /// Additionally, it gets stopped when there are none.
  301. /// </summary>
  302. /// <param name="o"></param>
  303. private void StartThreads()
  304. {
  305. if (!m_threadsRunning)
  306. {
  307. m_threadsRunning = true;
  308. m_mapItemsRequests = new ObjectJobEngine(MapItemsprocess,string.Format("MapItems ({0})", m_regionName));
  309. WorkManager.StartThread(MapBlocksProcess, string.Format("MapBlocks ({0})", m_regionName));
  310. }
  311. }
  312. /// <summary>
  313. /// Enqueues a 'stop thread' MapRequestState. Causes the MapItemRequest thread to end
  314. /// </summary>
  315. private void StopThreads()
  316. {
  317. m_threadsRunning = false;
  318. m_mapBlockRequestEvent.Set();
  319. m_mapItemsRequests.Dispose();
  320. }
  321. public virtual void HandleMapItemRequest(IClientAPI remoteClient, uint flags,
  322. uint EstateID, bool godlike, uint itemtype, ulong regionhandle)
  323. {
  324. // m_log.DebugFormat("[WORLD MAP]: Handle MapItem request {0} {1}", regionhandle, itemtype);
  325. lock (m_rootAgents)
  326. {
  327. if (!m_rootAgents.Contains(remoteClient.AgentId))
  328. return;
  329. }
  330. // local or remote request?
  331. if (regionhandle != 0 && regionhandle != m_regionHandle)
  332. {
  333. Util.RegionHandleToWorldLoc(regionhandle, out uint x, out uint y);
  334. if( x < m_regionGlobalX || y < m_regionGlobalY ||
  335. x >= (m_regionGlobalX + m_regionSizeX) || y >= (m_regionGlobalY + m_regionSizeY))
  336. {
  337. RequestMapItems(remoteClient.AgentId, flags, EstateID, godlike, itemtype, regionhandle);
  338. return;
  339. }
  340. }
  341. // its about this region...
  342. List<mapItemReply> mapitems = new List<mapItemReply>();
  343. mapItemReply mapitem = new mapItemReply();
  344. // viewers only ask for green dots to each region now
  345. // except at login with regionhandle 0
  346. // possible on some other rare ocasions
  347. // use previous hack of sending all items with the green dots
  348. bool adultRegion;
  349. int tc = Environment.TickCount;
  350. string hash = Util.Md5Hash(m_regionName + tc.ToString());
  351. if (regionhandle == 0)
  352. {
  353. switch (itemtype)
  354. {
  355. case (int)GridItemType.AgentLocations:
  356. // Service 6 right now (MAP_ITEM_AGENTS_LOCATION; green dots)
  357. if (m_scene.GetRootAgentCount() <= 1) //own position is not sent
  358. {
  359. mapitem = new mapItemReply(
  360. m_regionGlobalX + 1,
  361. m_regionGlobalY + 1,
  362. UUID.Zero,
  363. hash,
  364. 0, 0);
  365. mapitems.Add(mapitem);
  366. }
  367. else
  368. {
  369. m_scene.ForEachRootScenePresence(delegate (ScenePresence sp)
  370. {
  371. // Don't send a green dot for yourself
  372. if (sp.UUID != remoteClient.AgentId)
  373. {
  374. if (sp.IsNPC || sp.IsDeleted || sp.IsInTransit)
  375. return;
  376. mapitem = new mapItemReply(
  377. m_regionGlobalX + (uint)sp.AbsolutePosition.X,
  378. m_regionGlobalY + (uint)sp.AbsolutePosition.Y,
  379. UUID.Zero,
  380. hash,
  381. 1, 0);
  382. mapitems.Add(mapitem);
  383. }
  384. });
  385. }
  386. remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags);
  387. break;
  388. case (int)GridItemType.Telehub:
  389. // Service 1 (MAP_ITEM_TELEHUB)
  390. SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject);
  391. if (sog != null)
  392. {
  393. mapitem = new mapItemReply(
  394. m_regionGlobalX + (uint)sog.AbsolutePosition.X,
  395. m_regionGlobalY + (uint)sog.AbsolutePosition.Y,
  396. UUID.Zero,
  397. sog.Name,
  398. 0, // color (not used)
  399. 0 // 0 = telehub / 1 = infohub
  400. );
  401. mapitems.Add(mapitem);
  402. remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags);
  403. }
  404. break;
  405. case (int)GridItemType.AdultLandForSale:
  406. case (int)GridItemType.LandForSale:
  407. // Service 7 (MAP_ITEM_LAND_FOR_SALE)
  408. adultRegion = m_scene.RegionInfo.RegionSettings.Maturity == 2;
  409. if (adultRegion)
  410. {
  411. if (itemtype == (int)GridItemType.LandForSale)
  412. break;
  413. }
  414. else
  415. {
  416. if (itemtype == (int)GridItemType.AdultLandForSale)
  417. break;
  418. }
  419. // Parcels
  420. ILandChannel landChannel = m_scene.LandChannel;
  421. List<ILandObject> parcels = landChannel.AllParcels();
  422. if ((parcels != null) && (parcels.Count >= 1))
  423. {
  424. foreach (ILandObject parcel_interface in parcels)
  425. {
  426. // Play it safe
  427. if (!(parcel_interface is LandObject))
  428. continue;
  429. LandObject land = (LandObject)parcel_interface;
  430. LandData parcel = land.LandData;
  431. // Show land for sale
  432. if ((parcel.Flags & (uint)ParcelFlags.ForSale) == (uint)ParcelFlags.ForSale)
  433. {
  434. float x = land.CenterPoint.X + m_regionGlobalX;
  435. float y = land.CenterPoint.Y + m_regionGlobalY;
  436. mapitem = new mapItemReply(
  437. (uint)x, (uint)y,
  438. parcel.GlobalID,
  439. parcel.Name,
  440. parcel.Area,
  441. parcel.SalePrice
  442. );
  443. mapitems.Add(mapitem);
  444. }
  445. }
  446. }
  447. remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags);
  448. break;
  449. case (uint)GridItemType.PgEvent:
  450. case (uint)GridItemType.MatureEvent:
  451. case (uint)GridItemType.AdultEvent:
  452. case (uint)GridItemType.Classified:
  453. case (uint)GridItemType.Popular:
  454. // TODO
  455. // just dont not cry about them
  456. break;
  457. default:
  458. // unkown map item type
  459. m_log.DebugFormat("[WORLD MAP]: Unknown MapItem type {0}", itemtype);
  460. break;
  461. }
  462. }
  463. else
  464. {
  465. // send all items till we get a better fix
  466. // Service 6 right now (MAP_ITEM_AGENTS_LOCATION; green dots)
  467. if (m_scene.GetRootAgentCount() <= 1) // own is not sent
  468. {
  469. mapitem = new mapItemReply(
  470. m_regionGlobalX + 1,
  471. m_regionGlobalY + 1,
  472. UUID.Zero,
  473. hash,
  474. 0, 0);
  475. mapitems.Add(mapitem);
  476. }
  477. else
  478. {
  479. m_scene.ForEachRootScenePresence(delegate (ScenePresence sp)
  480. {
  481. // Don't send a green dot for yourself
  482. if (sp.UUID != remoteClient.AgentId)
  483. {
  484. if (sp.IsNPC || sp.IsDeleted || sp.IsInTransit)
  485. return;
  486. mapitem = new mapItemReply(
  487. m_regionGlobalX + (uint)sp.AbsolutePosition.X,
  488. m_regionGlobalY + (uint)sp.AbsolutePosition.Y,
  489. UUID.Zero,
  490. hash,
  491. 1, 0);
  492. mapitems.Add(mapitem);
  493. }
  494. });
  495. }
  496. remoteClient.SendMapItemReply(mapitems.ToArray(), 6, flags);
  497. mapitems.Clear();
  498. // Service 1 (MAP_ITEM_TELEHUB)
  499. SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject);
  500. if (sog != null)
  501. {
  502. mapitem = new mapItemReply(
  503. m_regionGlobalX + (uint)sog.AbsolutePosition.X,
  504. m_regionGlobalY + (uint)sog.AbsolutePosition.Y,
  505. UUID.Zero,
  506. sog.Name,
  507. 0, // color (not used)
  508. 0 // 0 = telehub / 1 = infohub
  509. );
  510. mapitems.Add(mapitem);
  511. remoteClient.SendMapItemReply(mapitems.ToArray(), 1, flags);
  512. mapitems.Clear();
  513. }
  514. // Service 7 (MAP_ITEM_LAND_FOR_SALE)
  515. uint its = 7;
  516. if (m_scene.RegionInfo.RegionSettings.Maturity == 2)
  517. its = 10;
  518. // Parcels
  519. ILandChannel landChannel = m_scene.LandChannel;
  520. List<ILandObject> parcels = landChannel.AllParcels();
  521. if ((parcels != null) && (parcels.Count >= 1))
  522. {
  523. foreach (ILandObject parcel_interface in parcels)
  524. {
  525. // Play it safe
  526. if (!(parcel_interface is LandObject))
  527. continue;
  528. LandObject land = (LandObject)parcel_interface;
  529. LandData parcel = land.LandData;
  530. // Show land for sale
  531. if ((parcel.Flags & (uint)ParcelFlags.ForSale) == (uint)ParcelFlags.ForSale)
  532. {
  533. float x = land.CenterPoint.X + m_regionGlobalX;
  534. float y = land.CenterPoint.Y + m_regionGlobalY;
  535. mapitem = new mapItemReply(
  536. (uint)x, (uint)y,
  537. parcel.GlobalID,
  538. parcel.Name,
  539. parcel.Area,
  540. parcel.SalePrice
  541. );
  542. mapitems.Add(mapitem);
  543. }
  544. }
  545. if(mapitems.Count >0)
  546. remoteClient.SendMapItemReply(mapitems.ToArray(), its, flags);
  547. mapitems.Clear();
  548. }
  549. }
  550. }
  551. private int nAsyncRequests = 0;
  552. /// <summary>
  553. /// Processing thread main() loop for doing remote mapitem requests
  554. /// </summary>
  555. public void MapItemsprocess(object o)
  556. {
  557. if (m_scene == null || !m_threadsRunning)
  558. return;
  559. const int MAX_ASYNC_REQUESTS = 5;
  560. ScenePresence av = null;
  561. MapRequestState st = o as MapRequestState;
  562. if (st == null || st.agentID == UUID.Zero)
  563. return;
  564. if (m_blacklistedregions.ContainsKey(st.regionhandle))
  565. return;
  566. if (!m_scene.TryGetScenePresence(st.agentID, out av))
  567. return;
  568. if (av == null || av.IsChildAgent || av.IsDeleted || av.IsInTransit)
  569. return;
  570. try
  571. {
  572. if (m_cachedRegionMapItemsResponses.TryGetValue(st.regionhandle, out OSDMap responseMap))
  573. {
  574. if (responseMap != null)
  575. {
  576. if (responseMap.ContainsKey(st.itemtype.ToString()))
  577. {
  578. List<mapItemReply> returnitems = new List<mapItemReply>();
  579. OSDArray itemarray = (OSDArray)responseMap[st.itemtype.ToString()];
  580. for (int i = 0; i < itemarray.Count; i++)
  581. {
  582. OSDMap mapitem = (OSDMap)itemarray[i];
  583. mapItemReply mi = new mapItemReply();
  584. mi.x = (uint)mapitem["X"].AsInteger();
  585. mi.y = (uint)mapitem["Y"].AsInteger();
  586. mi.id = mapitem["ID"].AsUUID();
  587. mi.Extra = mapitem["Extra"].AsInteger();
  588. mi.Extra2 = mapitem["Extra2"].AsInteger();
  589. mi.name = mapitem["Name"].AsString();
  590. returnitems.Add(mi);
  591. }
  592. av.ControllingClient.SendMapItemReply(returnitems.ToArray(), st.itemtype, st.flags & 0xffff);
  593. }
  594. }
  595. else
  596. {
  597. m_mapItemsRequests.Enqueue(st);
  598. if (m_mapItemsRequests.Count < 3)
  599. Thread.Sleep(100);
  600. }
  601. }
  602. else
  603. {
  604. m_cachedRegionMapItemsResponses.AddOrUpdate(st.regionhandle, null, expireResponsesTime); // a bit more time for the access
  605. // nothig for region, fire a request
  606. Interlocked.Increment(ref nAsyncRequests);
  607. MapRequestState rst = st;
  608. Util.FireAndForget(x =>
  609. {
  610. RequestMapItemsAsync(rst);
  611. });
  612. }
  613. while (nAsyncRequests >= MAX_ASYNC_REQUESTS) // hit the break
  614. {
  615. Thread.Sleep(100);
  616. if (m_scene == null || !m_threadsRunning)
  617. break;
  618. }
  619. }
  620. catch { }
  621. }
  622. /// <summary>
  623. /// Enqueue the MapItem request for remote processing
  624. /// </summary>
  625. /// <param name="id">Agent ID that we are making this request on behalf</param>
  626. /// <param name="flags">passed in from packet</param>
  627. /// <param name="EstateID">passed in from packet</param>
  628. /// <param name="godlike">passed in from packet</param>
  629. /// <param name="itemtype">passed in from packet</param>
  630. /// <param name="regionhandle">Region we're looking up</param>
  631. public void RequestMapItems(UUID id, uint flags, uint EstateID, bool godlike, uint itemtype, ulong regionhandle)
  632. {
  633. if(!m_threadsRunning)
  634. return;
  635. MapRequestState st = new MapRequestState();
  636. st.agentID = id;
  637. st.flags = flags;
  638. st.EstateID = EstateID;
  639. st.godlike = godlike;
  640. st.itemtype = itemtype;
  641. st.regionhandle = regionhandle;
  642. m_mapItemsRequests.Enqueue(st);
  643. }
  644. private static readonly uint[] itemTypesForcedSend = new uint[] { 6, 1, 7, 10 }; // green dots, infohub, land sells
  645. /// <summary>
  646. /// Does the actual remote mapitem request
  647. /// This should be called from an asynchronous thread
  648. /// Request failures get blacklisted until region restart so we don't
  649. /// continue to spend resources trying to contact regions that are down.
  650. /// </summary>
  651. /// <param name="httpserver">blank string, we discover this in the process</param>
  652. /// <param name="id">Agent ID that we are making this request on behalf</param>
  653. /// <param name="flags">passed in from packet</param>
  654. /// <param name="EstateID">passed in from packet</param>
  655. /// <param name="godlike">passed in from packet</param>
  656. /// <param name="itemtype">passed in from packet</param>
  657. /// <param name="regionhandle">Region we're looking up</param>
  658. /// <returns></returns>
  659. private void RequestMapItemsAsync(MapRequestState requestState)
  660. {
  661. // m_log.DebugFormat("[WORLDMAP]: RequestMapItemsAsync; region handle: {0} {1}", regionhandle, itemtype);
  662. ulong regionhandle = requestState.regionhandle;
  663. if (m_blacklistedregions.ContainsKey(regionhandle))
  664. {
  665. m_cachedRegionMapItemsResponses.Remove(regionhandle);
  666. Interlocked.Decrement(ref nAsyncRequests);
  667. return;
  668. }
  669. UUID agentID = requestState.agentID;
  670. if (agentID == UUID.Zero || !m_scene.TryGetScenePresence(agentID, out ScenePresence sp))
  671. {
  672. m_cachedRegionMapItemsResponses.Remove(regionhandle);
  673. Interlocked.Decrement(ref nAsyncRequests);
  674. return;
  675. }
  676. GridRegion mreg = m_scene.GridService.GetRegionByHandle(m_scene.RegionInfo.ScopeID, regionhandle);
  677. if (mreg == null)
  678. {
  679. // Can't find the http server or its blocked
  680. m_blacklistedregions.Add(regionhandle, expireBlackListTime);
  681. m_cachedRegionMapItemsResponses.Remove(regionhandle);
  682. Interlocked.Decrement(ref nAsyncRequests);
  683. return;
  684. }
  685. if (!m_threadsRunning)
  686. return;
  687. string serverURI = mreg.ServerURI;
  688. if(WebUtil.GlobalExpiringBadURLs.ContainsKey(serverURI))
  689. {
  690. m_blacklistedregions.Add(regionhandle, expireBlackListTime);
  691. m_cachedRegionMapItemsResponses.Remove(regionhandle);
  692. Interlocked.Decrement(ref nAsyncRequests);
  693. return;
  694. }
  695. string httpserver = serverURI + "MAP/MapItems/" + regionhandle.ToString();
  696. if (m_blacklistedurls.ContainsKey(httpserver))
  697. {
  698. m_blacklistedregions.Add(regionhandle, expireBlackListTime);
  699. m_cachedRegionMapItemsResponses.Remove(regionhandle);
  700. Interlocked.Decrement(ref nAsyncRequests);
  701. return;
  702. }
  703. if (!m_threadsRunning)
  704. return;
  705. WebRequest mapitemsrequest = null;
  706. try
  707. {
  708. mapitemsrequest = WebRequest.Create(httpserver);
  709. }
  710. catch (Exception e)
  711. {
  712. WebUtil.GlobalExpiringBadURLs.Add(serverURI, 120000);
  713. m_blacklistedregions.Add(regionhandle, expireBlackListTime);
  714. m_cachedRegionMapItemsResponses.Remove(regionhandle);
  715. m_log.DebugFormat("[WORLD MAP]: Access to {0} failed with {1}", httpserver, e);
  716. Interlocked.Decrement(ref nAsyncRequests);
  717. return;
  718. }
  719. UUID requestID = UUID.Random();
  720. mapitemsrequest.Method = "GET";
  721. mapitemsrequest.ContentType = "application/xml+llsd";
  722. string response_mapItems_reply = null;
  723. // get the response
  724. try
  725. {
  726. using (WebResponse webResponse = mapitemsrequest.GetResponse())
  727. {
  728. using (StreamReader sr = new StreamReader(webResponse.GetResponseStream()))
  729. response_mapItems_reply = sr.ReadToEnd().Trim();
  730. }
  731. }
  732. catch (WebException)
  733. {
  734. WebUtil.GlobalExpiringBadURLs.Add(serverURI, 60000);
  735. m_blacklistedurls.Add(httpserver, expireBlackListTime);
  736. m_blacklistedregions.Add(regionhandle, expireBlackListTime);
  737. m_cachedRegionMapItemsResponses.Remove(regionhandle);
  738. m_log.WarnFormat("[WORLD MAP]: Blacklisted url {0}", httpserver);
  739. Interlocked.Decrement(ref nAsyncRequests);
  740. return;
  741. }
  742. catch
  743. {
  744. m_log.DebugFormat("[WORLD MAP]: RequestMapItems failed for {0}", httpserver);
  745. m_blacklistedregions.Add(regionhandle, expireBlackListTime);
  746. m_cachedRegionMapItemsResponses.Remove(regionhandle);
  747. Interlocked.Decrement(ref nAsyncRequests);
  748. return;
  749. }
  750. if (!m_threadsRunning)
  751. return;
  752. OSDMap responseMap = null;
  753. try
  754. {
  755. responseMap = (OSDMap)OSDParser.DeserializeLLSDXml(response_mapItems_reply);
  756. }
  757. catch (Exception ex)
  758. {
  759. m_log.InfoFormat("[WORLD MAP]: exception on parse of RequestMapItems reply from {0}: {1}", httpserver, ex.Message);
  760. m_blacklistedregions.Add(regionhandle, expireBlackListTime);
  761. m_cachedRegionMapItemsResponses.Remove(regionhandle);
  762. Interlocked.Decrement(ref nAsyncRequests);
  763. return;
  764. }
  765. if (!m_threadsRunning)
  766. return;
  767. m_cachedRegionMapItemsResponses.AddOrUpdate(regionhandle, responseMap, expireResponsesTime);
  768. uint flags = requestState.flags & 0xffff;
  769. if(m_scene.TryGetScenePresence(agentID, out ScenePresence av) &&
  770. av != null && !av.IsChildAgent && !av.IsDeleted && !av.IsInTransit)
  771. {
  772. // send all the items or viewers will never ask for them, except green dots
  773. foreach (uint itfs in itemTypesForcedSend)
  774. {
  775. if (responseMap.ContainsKey(itfs.ToString()))
  776. {
  777. List<mapItemReply> returnitems = new List<mapItemReply>();
  778. OSDArray itemarray = (OSDArray)responseMap[itfs.ToString()];
  779. for (int i = 0; i < itemarray.Count; i++)
  780. {
  781. if (!m_threadsRunning)
  782. return;
  783. OSDMap mapitem = (OSDMap)itemarray[i];
  784. mapItemReply mi = new mapItemReply();
  785. mi.x = (uint)mapitem["X"].AsInteger();
  786. mi.y = (uint)mapitem["Y"].AsInteger();
  787. mi.id = mapitem["ID"].AsUUID();
  788. mi.Extra = mapitem["Extra"].AsInteger();
  789. mi.Extra2 = mapitem["Extra2"].AsInteger();
  790. mi.name = mapitem["Name"].AsString();
  791. returnitems.Add(mi);
  792. }
  793. av.ControllingClient.SendMapItemReply(returnitems.ToArray(), itfs, flags);
  794. }
  795. }
  796. }
  797. Interlocked.Decrement(ref nAsyncRequests);
  798. }
  799. private const double SPAMBLOCKTIMEms = 300000; // 5 minutes
  800. private Dictionary<UUID,double> spamBlocked = new Dictionary<UUID,double>();
  801. /// <summary>
  802. /// Requests map blocks in area of minX, maxX, minY, MaxY in world cordinates
  803. /// </summary>
  804. /// <param name="minX"></param>
  805. /// <param name="minY"></param>
  806. /// <param name="maxX"></param>
  807. /// <param name="maxY"></param>
  808. public void RequestMapBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag)
  809. {
  810. // anti spam because of FireStorm 4.7.7 absurd request repeat rates
  811. // possible others
  812. double now = Util.GetTimeStampMS();
  813. UUID agentID = remoteClient.AgentId;
  814. lock (m_mapBlockRequestEvent)
  815. {
  816. if(spamBlocked.ContainsKey(agentID))
  817. {
  818. if(spamBlocked[agentID] < now &&
  819. (!m_mapBlockRequests.ContainsKey(agentID) ||
  820. m_mapBlockRequests[agentID].Count == 0 ))
  821. {
  822. spamBlocked.Remove(agentID);
  823. m_log.DebugFormat("[WoldMapModule] RequestMapBlocks release spammer {0}", agentID);
  824. }
  825. else
  826. return;
  827. }
  828. else
  829. {
  830. // ugly slow expire spammers
  831. if(spamBlocked.Count > 0)
  832. {
  833. UUID k = UUID.Zero;
  834. bool expireone = false;
  835. foreach(UUID k2 in spamBlocked.Keys)
  836. {
  837. if(spamBlocked[k2] < now &&
  838. (!m_mapBlockRequests.ContainsKey(k2) ||
  839. m_mapBlockRequests[k2].Count == 0 ))
  840. {
  841. m_log.DebugFormat("[WoldMapModule] RequestMapBlocks release spammer {0}", k2);
  842. k = k2;
  843. expireone = true;
  844. }
  845. break; // doing one at a time
  846. }
  847. if(expireone)
  848. spamBlocked.Remove(k);
  849. }
  850. }
  851. // m_log.DebugFormat("[WoldMapModule] RequestMapBlocks {0}={1}={2}={3} {4}", minX, minY, maxX, maxY, flag);
  852. MapBlockRequestData req = new MapBlockRequestData()
  853. {
  854. client = remoteClient,
  855. minX = minX,
  856. maxX = maxX,
  857. minY = minY,
  858. maxY = maxY,
  859. flags = flag
  860. };
  861. Queue<MapBlockRequestData> agentq;
  862. if(!m_mapBlockRequests.TryGetValue(agentID, out agentq))
  863. {
  864. agentq = new Queue<MapBlockRequestData>();
  865. m_mapBlockRequests[agentID] = agentq;
  866. }
  867. if(agentq.Count < 150 )
  868. agentq.Enqueue(req);
  869. else
  870. {
  871. spamBlocked[agentID] = now + SPAMBLOCKTIMEms;
  872. m_log.DebugFormat("[WoldMapModule] RequestMapBlocks blocking spammer {0} for {1} s",agentID, SPAMBLOCKTIMEms/1000.0);
  873. }
  874. m_mapBlockRequestEvent.Set();
  875. }
  876. }
  877. protected void MapBlocksProcess()
  878. {
  879. List<MapBlockRequestData> thisRunData = new List<MapBlockRequestData>();
  880. List<UUID> toRemove = new List<UUID>();
  881. try
  882. {
  883. while (true)
  884. {
  885. while(!m_mapBlockRequestEvent.WaitOne(4900))
  886. {
  887. Watchdog.UpdateThread();
  888. if (m_scene == null || !m_threadsRunning)
  889. {
  890. Watchdog.RemoveThread();
  891. return;
  892. }
  893. }
  894. Watchdog.UpdateThread();
  895. if (m_scene == null || !m_threadsRunning)
  896. break;
  897. lock (m_mapBlockRequestEvent)
  898. {
  899. int total = 0;
  900. foreach (KeyValuePair<UUID, Queue<MapBlockRequestData>> kvp in m_mapBlockRequests)
  901. {
  902. if (kvp.Value.Count > 0)
  903. {
  904. thisRunData.Add(kvp.Value.Dequeue());
  905. total += kvp.Value.Count;
  906. }
  907. else
  908. toRemove.Add(kvp.Key);
  909. }
  910. if (m_scene == null || !m_threadsRunning)
  911. break;
  912. if (total == 0)
  913. m_mapBlockRequestEvent.Reset();
  914. }
  915. if (toRemove.Count > 0)
  916. {
  917. foreach (UUID u in toRemove)
  918. m_mapBlockRequests.Remove(u);
  919. toRemove.Clear();
  920. }
  921. if (thisRunData.Count > 0)
  922. {
  923. foreach (MapBlockRequestData req in thisRunData)
  924. {
  925. GetAndSendBlocksInternal(req.client, req.minX, req.minY, req.maxX, req.maxY, req.flags);
  926. if (m_scene == null || !m_threadsRunning)
  927. break;
  928. Watchdog.UpdateThread();
  929. }
  930. thisRunData.Clear();
  931. }
  932. if (m_scene == null || !m_threadsRunning)
  933. break;
  934. Thread.Sleep(50);
  935. }
  936. }
  937. catch { }
  938. Watchdog.RemoveThread();
  939. }
  940. protected virtual List<MapBlockData> GetAndSendBlocksInternal(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag)
  941. {
  942. List<MapBlockData> mapBlocks = new List<MapBlockData>();
  943. List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID,
  944. minX * (int)Constants.RegionSize,
  945. maxX * (int)Constants.RegionSize,
  946. minY * (int)Constants.RegionSize,
  947. maxY * (int)Constants.RegionSize);
  948. // only send a negative answer for a single region request
  949. // corresponding to a click on the map. Current viewers
  950. // keep displaying "loading.." without this
  951. if (regions.Count == 0)
  952. {
  953. if((flag & 0x10000) != 0 && minX == maxX && minY == maxY)
  954. {
  955. MapBlockData block = new MapBlockData();
  956. block.X = (ushort)minX;
  957. block.Y = (ushort)minY;
  958. block.MapImageId = UUID.Zero;
  959. block.Access = (byte)SimAccess.NonExistent;
  960. mapBlocks.Add(block);
  961. remoteClient.SendMapBlock(mapBlocks, flag & 0xffff);
  962. }
  963. return mapBlocks;
  964. }
  965. List<MapBlockData> allBlocks = new List<MapBlockData>();
  966. flag &= 0xffff;
  967. foreach (GridRegion r in regions)
  968. {
  969. if (r == null)
  970. continue;
  971. MapBlockData block = new MapBlockData();
  972. MapBlockFromGridRegion(block, r, flag);
  973. mapBlocks.Add(block);
  974. allBlocks.Add(block);
  975. if (mapBlocks.Count >= 10)
  976. {
  977. remoteClient.SendMapBlock(mapBlocks, flag);
  978. mapBlocks.Clear();
  979. Thread.Sleep(50);
  980. }
  981. if (m_scene == null || !m_threadsRunning)
  982. return allBlocks;
  983. }
  984. if (mapBlocks.Count > 0)
  985. remoteClient.SendMapBlock(mapBlocks, flag);
  986. return allBlocks;
  987. }
  988. public void MapBlockFromGridRegion(MapBlockData block, GridRegion r, uint flag)
  989. {
  990. if (r == null)
  991. {
  992. // we should not get here ??
  993. // block.Access = (byte)SimAccess.Down; this is for a grid reply on r
  994. block.Access = (byte)SimAccess.NonExistent;
  995. block.MapImageId = UUID.Zero;
  996. return;
  997. }
  998. block.Access = r.Access;
  999. switch (flag)
  1000. {
  1001. case 0:
  1002. block.MapImageId = r.TerrainImage;
  1003. break;
  1004. case 2:
  1005. block.MapImageId = r.ParcelImage;
  1006. break;
  1007. default:
  1008. block.MapImageId = UUID.Zero;
  1009. break;
  1010. }
  1011. block.Name = r.RegionName;
  1012. block.X = (ushort)(r.RegionLocX / Constants.RegionSize);
  1013. block.Y = (ushort)(r.RegionLocY / Constants.RegionSize);
  1014. block.SizeX = (ushort)r.RegionSizeX;
  1015. block.SizeY = (ushort)r.RegionSizeY;
  1016. }
  1017. public Hashtable OnHTTPThrottled(Hashtable keysvals)
  1018. {
  1019. Hashtable reply = new Hashtable();
  1020. int statuscode = 500;
  1021. reply["str_response_string"] = "";
  1022. reply["int_response_code"] = statuscode;
  1023. reply["content_type"] = "text/plain";
  1024. return reply;
  1025. }
  1026. public void OnHTTPGetMapImage(IOSHttpRequest request, IOSHttpResponse response)
  1027. {
  1028. response.KeepAlive = false;
  1029. if (request.HttpMethod != "GET" || m_scene.RegionInfo.RegionSettings.TerrainImageID == UUID.Zero)
  1030. {
  1031. response.StatusCode = (int)HttpStatusCode.NotFound;
  1032. return;
  1033. }
  1034. byte[] jpeg = null;
  1035. m_log.Debug("[WORLD MAP]: Sending map image jpeg");
  1036. if (myMapImageJPEG.Length == 0)
  1037. {
  1038. MemoryStream imgstream = null;
  1039. Bitmap mapTexture = new Bitmap(1, 1);
  1040. ManagedImage managedImage;
  1041. Image image = (Image)mapTexture;
  1042. try
  1043. {
  1044. // Taking our jpeg2000 data, decoding it, then saving it to a byte array with regular jpeg data
  1045. imgstream = new MemoryStream();
  1046. // non-async because we know we have the asset immediately.
  1047. AssetBase mapasset = m_scene.AssetService.Get(m_scene.RegionInfo.RegionSettings.TerrainImageID.ToString());
  1048. if(mapasset == null || mapasset.Data == null || mapasset.Data.Length == 0)
  1049. {
  1050. response.StatusCode = (int)HttpStatusCode.NotFound;
  1051. return;
  1052. }
  1053. // Decode image to System.Drawing.Image
  1054. if (OpenJPEG.DecodeToImage(mapasset.Data, out managedImage, out image))
  1055. {
  1056. // Save to bitmap
  1057. mapTexture = new Bitmap(image);
  1058. EncoderParameters myEncoderParameters = new EncoderParameters();
  1059. myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 95L);
  1060. // Save bitmap to stream
  1061. mapTexture.Save(imgstream, GetEncoderInfo("image/jpeg"), myEncoderParameters);
  1062. // Write the stream to a byte array for output
  1063. jpeg = imgstream.ToArray();
  1064. myMapImageJPEG = jpeg;
  1065. }
  1066. }
  1067. catch (Exception e)
  1068. {
  1069. // Dummy!
  1070. m_log.Warn("[WORLD MAP]: Unable to generate Map image" + e.Message);
  1071. response.StatusCode = (int)HttpStatusCode.NotFound;
  1072. return;
  1073. }
  1074. finally
  1075. {
  1076. // Reclaim memory, these are unmanaged resources
  1077. // If we encountered an exception, one or more of these will be null
  1078. if (mapTexture != null)
  1079. mapTexture.Dispose();
  1080. if (image != null)
  1081. image.Dispose();
  1082. if (imgstream != null)
  1083. imgstream.Dispose();
  1084. }
  1085. }
  1086. else
  1087. {
  1088. // Use cached version so we don't have to loose our mind
  1089. jpeg = myMapImageJPEG;
  1090. }
  1091. if(jpeg == null)
  1092. {
  1093. response.StatusCode = (int)HttpStatusCode.NotFound;
  1094. return;
  1095. }
  1096. response.RawBuffer = jpeg;
  1097. //response.RawBuffer = Convert.ToBase64String(jpeg);
  1098. response.ContentType = "image/jpeg";
  1099. response.StatusCode = (int)HttpStatusCode.OK;
  1100. }
  1101. // From msdn
  1102. private static ImageCodecInfo GetEncoderInfo(String mimeType)
  1103. {
  1104. ImageCodecInfo[] encoders;
  1105. encoders = ImageCodecInfo.GetImageEncoders();
  1106. for (int j = 0; j < encoders.Length; ++j)
  1107. {
  1108. if (encoders[j].MimeType == mimeType)
  1109. return encoders[j];
  1110. }
  1111. return null;
  1112. }
  1113. /// <summary>
  1114. /// Export the world map
  1115. /// </summary>
  1116. /// <param name="fileName"></param>
  1117. public void HandleExportWorldMapConsoleCommand(string module, string[] cmdparams)
  1118. {
  1119. if (m_scene.ConsoleScene() == null)
  1120. {
  1121. // FIXME: If console region is root then this will be printed by every module. Currently, there is no
  1122. // way to prevent this, short of making the entire module shared (which is complete overkill).
  1123. // One possibility is to return a bool to signal whether the module has completely handled the command
  1124. m_log.InfoFormat("[WORLD MAP]: Please change to a specific region in order to export its world map");
  1125. return;
  1126. }
  1127. if (m_scene.ConsoleScene() != m_scene)
  1128. return;
  1129. string exportPath;
  1130. if (cmdparams.Length > 1)
  1131. exportPath = cmdparams[1];
  1132. else
  1133. exportPath = DEFAULT_WORLD_MAP_EXPORT_PATH;
  1134. m_log.InfoFormat(
  1135. "[WORLD MAP]: Exporting world map for {0} to {1}", m_regionName, exportPath);
  1136. // assumed this is 1m less than next grid line
  1137. int regionsView = (int)m_scene.MaxRegionViewDistance;
  1138. int regionSizeX = (int)m_regionSizeX;
  1139. int regionSizeY = (int)m_regionSizeY;
  1140. int regionX = (int)m_regionGlobalX;
  1141. int regionY = (int)m_regionGlobalY;
  1142. int startX = regionX - regionsView;
  1143. int startY = regionY - regionsView;
  1144. int endX = regionX + regionSizeX + regionsView;
  1145. int endY = regionY + regionSizeY + regionsView;
  1146. int spanX = endX - startX + 2;
  1147. int spanY = endY - startY + 2;
  1148. Bitmap mapTexture = new Bitmap(spanX, spanY);
  1149. ImageAttributes gatrib = new ImageAttributes();
  1150. gatrib.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipXY);
  1151. Graphics g = Graphics.FromImage(mapTexture);
  1152. g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
  1153. g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
  1154. g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
  1155. g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.None;
  1156. SolidBrush sea = new SolidBrush(Color.DarkBlue);
  1157. g.FillRectangle(sea, 0, 0, spanX, spanY);
  1158. sea.Dispose();
  1159. Font drawFont = new Font("Arial", 32);
  1160. SolidBrush drawBrush = new SolidBrush(Color.White);
  1161. List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID,
  1162. startX, startY, endX, endY);
  1163. startX--;
  1164. startY--;
  1165. bool doneLocal = false;
  1166. string filename = "MAP-" + m_scene.RegionInfo.RegionID.ToString() + ".png";
  1167. try
  1168. {
  1169. using(Image localMap = Bitmap.FromFile(filename))
  1170. {
  1171. int x = regionX - startX;
  1172. int y = regionY - startY;
  1173. int sx = regionSizeX;
  1174. int sy = regionSizeY;
  1175. // y origin is top
  1176. g.DrawImage(localMap,new Rectangle(x, spanY - y - sy, sx, sy),
  1177. 0, 0, localMap.Width, localMap.Height, GraphicsUnit.Pixel, gatrib);
  1178. if(m_exportPrintRegionName)
  1179. {
  1180. SizeF stringSize = g.MeasureString(m_regionName, drawFont);
  1181. g.DrawString(m_regionName, drawFont, drawBrush, x + 30, spanY - y - 30 - stringSize.Height);
  1182. }
  1183. }
  1184. doneLocal = true;
  1185. }
  1186. catch {}
  1187. if(regions.Count > 0)
  1188. {
  1189. ManagedImage managedImage = null;
  1190. Image image = null;
  1191. foreach(GridRegion r in regions)
  1192. {
  1193. if(r.TerrainImage == UUID.Zero)
  1194. continue;
  1195. if(doneLocal && r.RegionHandle == m_regionHandle)
  1196. continue;
  1197. AssetBase texAsset = m_scene.AssetService.Get(r.TerrainImage.ToString());
  1198. if(texAsset == null)
  1199. continue;
  1200. if(OpenJPEG.DecodeToImage(texAsset.Data, out managedImage, out image))
  1201. {
  1202. int x = r.RegionLocX - startX;
  1203. int y = r.RegionLocY - startY;
  1204. int sx = r.RegionSizeX;
  1205. int sy = r.RegionSizeY;
  1206. // y origin is top
  1207. g.DrawImage(image,new Rectangle(x, spanY - y - sy, sx, sy),
  1208. 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, gatrib);
  1209. if(m_exportPrintRegionName && r.RegionHandle == m_regionHandle)
  1210. {
  1211. SizeF stringSize = g.MeasureString(r.RegionName, drawFont);
  1212. g.DrawString(r.RegionName, drawFont, drawBrush, x + 30, spanY - y - 30 - stringSize.Height);
  1213. }
  1214. }
  1215. }
  1216. if(image != null)
  1217. image.Dispose();
  1218. }
  1219. if(m_exportPrintScale)
  1220. {
  1221. String drawString = string.Format("{0}m x {1}m", spanX, spanY);
  1222. g.DrawString(drawString, drawFont, drawBrush, 30, 30);
  1223. }
  1224. drawBrush.Dispose();
  1225. drawFont.Dispose();
  1226. gatrib.Dispose();
  1227. g.Dispose();
  1228. mapTexture.Save(exportPath, ImageFormat.Jpeg);
  1229. mapTexture.Dispose();
  1230. m_log.InfoFormat(
  1231. "[WORLD MAP]: Successfully exported world map for {0} to {1}",
  1232. m_regionName, exportPath);
  1233. }
  1234. public void HandleGenerateMapConsoleCommand(string module, string[] cmdparams)
  1235. {
  1236. Scene consoleScene = m_scene.ConsoleScene();
  1237. if (consoleScene != null && consoleScene != m_scene)
  1238. return;
  1239. m_scene.RegenerateMaptileAndReregister(this, null);
  1240. }
  1241. public void HandleRemoteMapItemRequest(IOSHttpRequest request, IOSHttpResponse response)
  1242. {
  1243. // Service 6 (MAP_ITEM_AGENTS_LOCATION; green dots)
  1244. OSDMap responsemap = new OSDMap();
  1245. int tc = Environment.TickCount;
  1246. OSD osdhash = OSD.FromString(Util.Md5Hash(m_regionName + tc.ToString()));
  1247. if (m_scene.GetRootAgentCount() == 0)
  1248. {
  1249. OSDMap responsemapdata = new OSDMap();
  1250. responsemapdata["X"] = OSD.FromInteger((int)(m_regionGlobalX + 1));
  1251. responsemapdata["Y"] = OSD.FromInteger((int)(m_regionGlobalY + 1));
  1252. responsemapdata["ID"] = OSD.FromUUID(UUID.Zero);
  1253. responsemapdata["Name"] = osdhash;
  1254. responsemapdata["Extra"] = OSD.FromInteger(0);
  1255. responsemapdata["Extra2"] = OSD.FromInteger(0);
  1256. OSDArray responsearr = new OSDArray();
  1257. responsearr.Add(responsemapdata);
  1258. responsemap["6"] = responsearr;
  1259. }
  1260. else
  1261. {
  1262. OSDArray responsearr = new OSDArray(); // Don't preallocate. MT (m_scene.GetRootAgentCount());
  1263. m_scene.ForEachRootScenePresence(delegate (ScenePresence sp)
  1264. {
  1265. if (sp.IsNPC || sp.IsDeleted || sp.IsInTransit)
  1266. return;
  1267. OSDMap responsemapdata = new OSDMap();
  1268. responsemapdata["X"] = OSD.FromInteger((int)(m_regionGlobalX + sp.AbsolutePosition.X));
  1269. responsemapdata["Y"] = OSD.FromInteger((int)(m_regionGlobalY + sp.AbsolutePosition.Y));
  1270. responsemapdata["ID"] = OSD.FromUUID(UUID.Zero);
  1271. responsemapdata["Name"] = osdhash;
  1272. responsemapdata["Extra"] = OSD.FromInteger(1);
  1273. responsemapdata["Extra2"] = OSD.FromInteger(0);
  1274. responsearr.Add(responsemapdata);
  1275. });
  1276. responsemap["6"] = responsearr;
  1277. }
  1278. // Service 7/10 (MAP_ITEM_LAND_FOR_SALE/ADULT)
  1279. ILandChannel landChannel = m_scene.LandChannel;
  1280. List<ILandObject> parcels = landChannel.AllParcels();
  1281. if ((parcels != null) && (parcels.Count >= 0))
  1282. {
  1283. OSDArray responsearr = new OSDArray(parcels.Count);
  1284. foreach (ILandObject parcel_interface in parcels)
  1285. {
  1286. // Play it safe
  1287. if (!(parcel_interface is LandObject))
  1288. continue;
  1289. LandObject land = (LandObject)parcel_interface;
  1290. LandData parcel = land.LandData;
  1291. // Show land for sale
  1292. if ((parcel.Flags & (uint)ParcelFlags.ForSale) == (uint)ParcelFlags.ForSale)
  1293. {
  1294. float x = m_regionGlobalX + land.CenterPoint.X;
  1295. float y = m_regionGlobalY + land.CenterPoint.Y;
  1296. OSDMap responsemapdata = new OSDMap();
  1297. responsemapdata["X"] = OSD.FromInteger((int)x);
  1298. responsemapdata["Y"] = OSD.FromInteger((int)y);
  1299. // responsemapdata["Z"] = OSD.FromInteger((int)m_scene.GetGroundHeight(x,y));
  1300. responsemapdata["ID"] = OSD.FromUUID(land.FakeID);
  1301. responsemapdata["Name"] = OSD.FromString(parcel.Name);
  1302. responsemapdata["Extra"] = OSD.FromInteger(parcel.Area);
  1303. responsemapdata["Extra2"] = OSD.FromInteger(parcel.SalePrice);
  1304. responsearr.Add(responsemapdata);
  1305. }
  1306. }
  1307. if(responsearr.Count > 0)
  1308. {
  1309. if(m_scene.RegionInfo.RegionSettings.Maturity == 2)
  1310. responsemap["10"] = responsearr;
  1311. else
  1312. responsemap["7"] = responsearr;
  1313. }
  1314. }
  1315. if (m_scene.RegionInfo.RegionSettings.TelehubObject != UUID.Zero)
  1316. {
  1317. SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject);
  1318. if (sog != null)
  1319. {
  1320. OSDArray responsearr = new OSDArray();
  1321. OSDMap responsemapdata = new OSDMap();
  1322. responsemapdata["X"] = OSD.FromInteger((int)(m_regionGlobalX + sog.AbsolutePosition.X));
  1323. responsemapdata["Y"] = OSD.FromInteger((int)(m_regionGlobalY + sog.AbsolutePosition.Y));
  1324. // responsemapdata["Z"] = OSD.FromInteger((int)m_scene.GetGroundHeight(x,y));
  1325. responsemapdata["ID"] = OSD.FromUUID(sog.UUID);
  1326. responsemapdata["Name"] = OSD.FromString(sog.Name);
  1327. responsemapdata["Extra"] = OSD.FromInteger(0); // color (unused)
  1328. responsemapdata["Extra2"] = OSD.FromInteger(0); // 0 = telehub / 1 = infohub
  1329. responsearr.Add(responsemapdata);
  1330. responsemap["1"] = responsearr;
  1331. }
  1332. }
  1333. response.RawBuffer = OSDParser.SerializeLLSDXmlBytes(responsemap);
  1334. response.StatusCode = (int)HttpStatusCode.OK;
  1335. }
  1336. public void GenerateMaptile()
  1337. {
  1338. // Cannot create a map for a nonexistent heightmap
  1339. if (m_scene.Heightmap == null)
  1340. return;
  1341. if (m_mapImageGenerator == null)
  1342. {
  1343. Console.WriteLine("No map image generator available for {0}", m_scene.Name);
  1344. return;
  1345. }
  1346. m_log.DebugFormat("[WORLD MAP]: Generating map image for {0}", m_scene.Name);
  1347. using (Bitmap mapbmp = m_mapImageGenerator.CreateMapTile())
  1348. {
  1349. GenerateMaptile(mapbmp);
  1350. if (m_mapImageServiceModule != null)
  1351. m_mapImageServiceModule.UploadMapTile(m_scene, mapbmp);
  1352. }
  1353. }
  1354. public void DeregisterMap()
  1355. {
  1356. //if (m_mapImageServiceModule != null)
  1357. // m_mapImageServiceModule.RemoveMapTiles(m_scene);
  1358. }
  1359. private void GenerateMaptile(Bitmap mapbmp)
  1360. {
  1361. bool needRegionSave = false;
  1362. // remove old assets
  1363. UUID lastID = m_scene.RegionInfo.RegionSettings.TerrainImageID;
  1364. if (lastID != UUID.Zero)
  1365. {
  1366. m_scene.AssetService.Delete(lastID.ToString());
  1367. m_scene.RegionInfo.RegionSettings.TerrainImageID = UUID.Zero;
  1368. myMapImageJPEG = new byte[0];
  1369. needRegionSave = true;
  1370. }
  1371. lastID = m_scene.RegionInfo.RegionSettings.ParcelImageID;
  1372. if (lastID != UUID.Zero)
  1373. {
  1374. m_scene.AssetService.Delete(lastID.ToString());
  1375. m_scene.RegionInfo.RegionSettings.ParcelImageID = UUID.Zero;
  1376. needRegionSave = true;
  1377. }
  1378. if(mapbmp != null)
  1379. {
  1380. try
  1381. {
  1382. byte[] data;
  1383. // if large region limit its size since new viewers will not use it
  1384. // but it is still usable for ossl
  1385. if(m_scene.RegionInfo.RegionSizeX > Constants.RegionSize ||
  1386. m_scene.RegionInfo.RegionSizeY > Constants.RegionSize)
  1387. {
  1388. int bx = mapbmp.Width;
  1389. int by = mapbmp.Height;
  1390. int mb = bx;
  1391. if(mb < by)
  1392. mb = by;
  1393. if(mb > Constants.RegionSize && mb > 0)
  1394. {
  1395. float scale = (float)Constants.RegionSize/(float)mb;
  1396. using(Bitmap scaledbmp = Util.ResizeImageSolid(mapbmp, (int)(bx * scale), (int)(by * scale)))
  1397. data = OpenJPEG.EncodeFromImage(scaledbmp, true);
  1398. }
  1399. else
  1400. data = OpenJPEG.EncodeFromImage(mapbmp, true);
  1401. }
  1402. else
  1403. data = OpenJPEG.EncodeFromImage(mapbmp, true);
  1404. if (data != null && data.Length > 0)
  1405. {
  1406. UUID terrainImageID = UUID.Random();
  1407. AssetBase asset = new AssetBase(
  1408. terrainImageID,
  1409. "terrainImage_" + m_scene.RegionInfo.RegionID.ToString(),
  1410. (sbyte)AssetType.Texture,
  1411. m_scene.RegionInfo.RegionID.ToString());
  1412. asset.Data = data;
  1413. asset.Description = m_regionName;
  1414. asset.Local = m_localV1MapAssets;
  1415. asset.Temporary = false;
  1416. asset.Flags = AssetFlags.Maptile;
  1417. // Store the new one
  1418. m_log.DebugFormat("[WORLD MAP]: Storing map image {0} for {1}", asset.ID, m_regionName);
  1419. m_scene.AssetService.Store(asset);
  1420. m_scene.RegionInfo.RegionSettings.TerrainImageID = terrainImageID;
  1421. needRegionSave = true;
  1422. }
  1423. }
  1424. catch (Exception e)
  1425. {
  1426. m_log.Error("[WORLD MAP]: Failed generating terrain map: " + e);
  1427. }
  1428. }
  1429. // V2/3 still seem to need this, or we are missing something somewhere
  1430. byte[] overlay = GenerateOverlay();
  1431. if (overlay != null)
  1432. {
  1433. UUID parcelImageID = UUID.Random();
  1434. AssetBase parcels = new AssetBase(
  1435. parcelImageID,
  1436. "parcelImage_" + m_scene.RegionInfo.RegionID.ToString(),
  1437. (sbyte)AssetType.Texture,
  1438. m_scene.RegionInfo.RegionID.ToString());
  1439. parcels.Data = overlay;
  1440. parcels.Description = m_regionName;
  1441. parcels.Temporary = false;
  1442. parcels.Local = m_localV1MapAssets;
  1443. parcels.Flags = AssetFlags.Maptile;
  1444. m_scene.AssetService.Store(parcels);
  1445. m_scene.RegionInfo.RegionSettings.ParcelImageID = parcelImageID;
  1446. needRegionSave = true;
  1447. }
  1448. if (needRegionSave)
  1449. m_scene.RegionInfo.RegionSettings.Save();
  1450. }
  1451. private void MakeRootAgent(ScenePresence avatar)
  1452. {
  1453. lock (m_rootAgents)
  1454. {
  1455. if (!m_rootAgents.Contains(avatar.UUID))
  1456. {
  1457. m_rootAgents.Add(avatar.UUID);
  1458. }
  1459. }
  1460. }
  1461. private void MakeChildAgent(ScenePresence avatar)
  1462. {
  1463. lock (m_rootAgents)
  1464. {
  1465. m_rootAgents.Remove(avatar.UUID);
  1466. }
  1467. lock (m_mapBlockRequestEvent)
  1468. {
  1469. if (m_mapBlockRequests.ContainsKey(avatar.UUID))
  1470. m_mapBlockRequests.Remove(avatar.UUID);
  1471. }
  1472. }
  1473. public void OnRegionUp(GridRegion otherRegion)
  1474. {
  1475. ulong regionhandle = otherRegion.RegionHandle;
  1476. string httpserver = otherRegion.ServerURI + "MAP/MapItems/" + regionhandle.ToString();
  1477. m_blacklistedregions.Remove(regionhandle);
  1478. m_blacklistedurls.Remove(httpserver);
  1479. }
  1480. private Byte[] GenerateOverlay()
  1481. {
  1482. int landTileSize = LandManagementModule.LandUnit;
  1483. // These need to be ints for bitmap generation
  1484. int regionSizeX = (int)m_scene.RegionInfo.RegionSizeX;
  1485. int regionLandTilesX = regionSizeX / landTileSize;
  1486. int regionSizeY = (int)m_scene.RegionInfo.RegionSizeY;
  1487. int regionLandTilesY = regionSizeY / landTileSize;
  1488. bool landForSale = false;
  1489. ILandObject land;
  1490. // scan terrain avoiding potencial merges of large bitmaps
  1491. //TODO create the sell bitmap at landchannel / landmaster ?
  1492. // and auction also, still not suported
  1493. bool[,] saleBitmap = new bool[regionLandTilesX, regionLandTilesY];
  1494. for (int x = 0, xx = 0; x < regionLandTilesX; x++ ,xx += landTileSize)
  1495. {
  1496. for (int y = 0, yy = 0; y < regionLandTilesY; y++, yy += landTileSize)
  1497. {
  1498. land = m_scene.LandChannel.GetLandObject(xx, yy);
  1499. if (land != null && (land.LandData.Flags & (uint)ParcelFlags.ForSale) != 0)
  1500. {
  1501. saleBitmap[x, y] = true;
  1502. landForSale = true;
  1503. }
  1504. else
  1505. saleBitmap[x, y] = false;
  1506. }
  1507. }
  1508. if (!landForSale)
  1509. {
  1510. m_log.DebugFormat("[WORLD MAP]: Region {0} has no parcels for sale, not generating overlay", m_regionName);
  1511. return null;
  1512. }
  1513. m_log.DebugFormat("[WORLD MAP]: Region {0} has parcels for sale, generating overlay", m_regionName);
  1514. using (Bitmap overlay = new Bitmap(regionSizeX, regionSizeY))
  1515. {
  1516. Color background = Color.FromArgb(0, 0, 0, 0);
  1517. using (Graphics g = Graphics.FromImage(overlay))
  1518. {
  1519. using (SolidBrush transparent = new SolidBrush(background))
  1520. g.FillRectangle(transparent, 0, 0, regionSizeX, regionSizeY);
  1521. // make it a bit transparent
  1522. using (SolidBrush yellow = new SolidBrush(Color.FromArgb(192, 249, 223, 9)))
  1523. {
  1524. for (int x = 0; x < regionLandTilesX; x++)
  1525. {
  1526. for (int y = 0; y < regionLandTilesY; y++)
  1527. {
  1528. if (saleBitmap[x, y])
  1529. g.FillRectangle(
  1530. yellow,
  1531. x * landTileSize,
  1532. regionSizeX - landTileSize - (y * landTileSize),
  1533. landTileSize,
  1534. landTileSize);
  1535. }
  1536. }
  1537. }
  1538. }
  1539. try
  1540. {
  1541. return OpenJPEG.EncodeFromImage(overlay, false);
  1542. }
  1543. catch (Exception e)
  1544. {
  1545. m_log.DebugFormat("[WORLD MAP]: Error creating parcel overlay: " + e.ToString());
  1546. }
  1547. }
  1548. return null;
  1549. }
  1550. }
  1551. public class MapRequestState
  1552. {
  1553. public UUID agentID;
  1554. public uint flags;
  1555. public uint EstateID;
  1556. public bool godlike;
  1557. public uint itemtype;
  1558. public ulong regionhandle;
  1559. }
  1560. public struct MapBlockRequestData
  1561. {
  1562. public IClientAPI client;
  1563. public int minX;
  1564. public int minY;
  1565. public int maxX;
  1566. public int maxY;
  1567. public uint flags;
  1568. }
  1569. }