WorldMapModule.cs 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082
  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.Generic;
  30. using System.Drawing;
  31. using System.Drawing.Imaging;
  32. using System.IO;
  33. using System.Net;
  34. using System.Reflection;
  35. using System.Threading;
  36. using log4net;
  37. using Nini.Config;
  38. using OpenMetaverse;
  39. using OpenMetaverse.Imaging;
  40. using OpenMetaverse.StructuredData;
  41. using OpenSim.Framework;
  42. using OpenSim.Framework.Capabilities;
  43. using OpenSim.Framework.Servers;
  44. using OpenSim.Framework.Servers.HttpServer;
  45. using OpenSim.Region.Framework.Interfaces;
  46. using OpenSim.Region.Framework.Scenes;
  47. using Caps=OpenSim.Framework.Capabilities.Caps;
  48. using OSDArray=OpenMetaverse.StructuredData.OSDArray;
  49. using OSDMap=OpenMetaverse.StructuredData.OSDMap;
  50. using GridRegion = OpenSim.Services.Interfaces.GridRegion;
  51. namespace OpenSim.Region.CoreModules.World.WorldMap
  52. {
  53. public class WorldMapModule : INonSharedRegionModule, IWorldMapModule
  54. {
  55. private static readonly ILog m_log =
  56. LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  57. private static readonly string DEFAULT_WORLD_MAP_EXPORT_PATH = "exportmap.jpg";
  58. private static readonly UUID STOP_UUID = UUID.Random();
  59. private static readonly string m_mapLayerPath = "0001/";
  60. private OpenSim.Framework.BlockingQueue<MapRequestState> requests = new OpenSim.Framework.BlockingQueue<MapRequestState>();
  61. //private IConfig m_config;
  62. protected Scene m_scene;
  63. private List<MapBlockData> cachedMapBlocks = new List<MapBlockData>();
  64. private int cachedTime = 0;
  65. private byte[] myMapImageJPEG;
  66. protected volatile bool m_Enabled = false;
  67. private Dictionary<UUID, MapRequestState> m_openRequests = new Dictionary<UUID, MapRequestState>();
  68. private Dictionary<string, int> m_blacklistedurls = new Dictionary<string, int>();
  69. private Dictionary<ulong, int> m_blacklistedregions = new Dictionary<ulong, int>();
  70. private Dictionary<ulong, string> m_cachedRegionMapItemsAddress = new Dictionary<ulong, string>();
  71. private List<UUID> m_rootAgents = new List<UUID>();
  72. private volatile bool threadrunning = false;
  73. //private int CacheRegionsDistance = 256;
  74. #region INonSharedRegionModule Members
  75. public virtual void Initialise (IConfigSource config)
  76. {
  77. IConfig startupConfig = config.Configs["Startup"];
  78. if (startupConfig.GetString("WorldMapModule", "WorldMap") == "WorldMap")
  79. m_Enabled = true;
  80. }
  81. public virtual void AddRegion (Scene scene)
  82. {
  83. if (!m_Enabled)
  84. return;
  85. lock (scene)
  86. {
  87. m_scene = scene;
  88. m_scene.RegisterModuleInterface<IWorldMapModule>(this);
  89. m_scene.AddCommand(
  90. this, "export-map",
  91. "export-map [<path>]",
  92. "Save an image of the world map", HandleExportWorldMapConsoleCommand);
  93. AddHandlers();
  94. }
  95. }
  96. public virtual void RemoveRegion (Scene scene)
  97. {
  98. if (!m_Enabled)
  99. return;
  100. lock (m_scene)
  101. {
  102. m_Enabled = false;
  103. RemoveHandlers();
  104. m_scene = null;
  105. }
  106. }
  107. public virtual void RegionLoaded (Scene scene)
  108. {
  109. }
  110. public virtual void Close()
  111. {
  112. }
  113. public Type ReplaceableInterface
  114. {
  115. get { return null; }
  116. }
  117. public virtual string Name
  118. {
  119. get { return "WorldMapModule"; }
  120. }
  121. #endregion
  122. // this has to be called with a lock on m_scene
  123. protected virtual void AddHandlers()
  124. {
  125. myMapImageJPEG = new byte[0];
  126. string regionimage = "regionImage" + m_scene.RegionInfo.RegionID.ToString();
  127. regionimage = regionimage.Replace("-", "");
  128. m_log.Info("[WORLD MAP]: JPEG Map location: http://" + m_scene.RegionInfo.ExternalEndPoint.Address.ToString() + ":" + m_scene.RegionInfo.HttpPort.ToString() + "/index.php?method=" + regionimage);
  129. MainServer.Instance.AddHTTPHandler(regionimage, OnHTTPGetMapImage);
  130. MainServer.Instance.AddLLSDHandler(
  131. "/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString(), HandleRemoteMapItemRequest);
  132. m_scene.EventManager.OnRegisterCaps += OnRegisterCaps;
  133. m_scene.EventManager.OnNewClient += OnNewClient;
  134. m_scene.EventManager.OnClientClosed += ClientLoggedOut;
  135. m_scene.EventManager.OnMakeChildAgent += MakeChildAgent;
  136. m_scene.EventManager.OnMakeRootAgent += MakeRootAgent;
  137. }
  138. // this has to be called with a lock on m_scene
  139. protected virtual void RemoveHandlers()
  140. {
  141. m_scene.EventManager.OnMakeRootAgent -= MakeRootAgent;
  142. m_scene.EventManager.OnMakeChildAgent -= MakeChildAgent;
  143. m_scene.EventManager.OnClientClosed -= ClientLoggedOut;
  144. m_scene.EventManager.OnNewClient -= OnNewClient;
  145. m_scene.EventManager.OnRegisterCaps -= OnRegisterCaps;
  146. string regionimage = "regionImage" + m_scene.RegionInfo.RegionID.ToString();
  147. regionimage = regionimage.Replace("-", "");
  148. MainServer.Instance.RemoveLLSDHandler("/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString(),
  149. HandleRemoteMapItemRequest);
  150. MainServer.Instance.RemoveHTTPHandler("", regionimage);
  151. }
  152. public void OnRegisterCaps(UUID agentID, Caps caps)
  153. {
  154. //m_log.DebugFormat("[WORLD MAP]: OnRegisterCaps: agentID {0} caps {1}", agentID, caps);
  155. string capsBase = "/CAPS/" + caps.CapsObjectPath;
  156. caps.RegisterHandler("MapLayer",
  157. new RestStreamHandler("POST", capsBase + m_mapLayerPath,
  158. delegate(string request, string path, string param,
  159. OSHttpRequest httpRequest, OSHttpResponse httpResponse)
  160. {
  161. return MapLayerRequest(request, path, param,
  162. agentID, caps);
  163. }));
  164. }
  165. /// <summary>
  166. /// Callback for a map layer request
  167. /// </summary>
  168. /// <param name="request"></param>
  169. /// <param name="path"></param>
  170. /// <param name="param"></param>
  171. /// <param name="agentID"></param>
  172. /// <param name="caps"></param>
  173. /// <returns></returns>
  174. public string MapLayerRequest(string request, string path, string param,
  175. UUID agentID, Caps caps)
  176. {
  177. //try
  178. //{
  179. //m_log.DebugFormat("[MAPLAYER]: request: {0}, path: {1}, param: {2}, agent:{3}",
  180. //request, path, param,agentID.ToString());
  181. // this is here because CAPS map requests work even beyond the 10,000 limit.
  182. ScenePresence avatarPresence = null;
  183. m_scene.TryGetScenePresence(agentID, out avatarPresence);
  184. if (avatarPresence != null)
  185. {
  186. bool lookup = false;
  187. lock (cachedMapBlocks)
  188. {
  189. if (cachedMapBlocks.Count > 0 && ((cachedTime + 1800) > Util.UnixTimeSinceEpoch()))
  190. {
  191. List<MapBlockData> mapBlocks;
  192. mapBlocks = cachedMapBlocks;
  193. avatarPresence.ControllingClient.SendMapBlock(mapBlocks, 0);
  194. }
  195. else
  196. {
  197. lookup = true;
  198. }
  199. }
  200. if (lookup)
  201. {
  202. List<MapBlockData> mapBlocks = new List<MapBlockData>(); ;
  203. List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID,
  204. (int)(m_scene.RegionInfo.RegionLocX - 8) * (int)Constants.RegionSize,
  205. (int)(m_scene.RegionInfo.RegionLocX + 8) * (int)Constants.RegionSize,
  206. (int)(m_scene.RegionInfo.RegionLocY - 8) * (int)Constants.RegionSize,
  207. (int)(m_scene.RegionInfo.RegionLocY + 8) * (int)Constants.RegionSize);
  208. foreach (GridRegion r in regions)
  209. {
  210. MapBlockData block = new MapBlockData();
  211. MapBlockFromGridRegion(block, r);
  212. mapBlocks.Add(block);
  213. }
  214. avatarPresence.ControllingClient.SendMapBlock(mapBlocks, 0);
  215. lock (cachedMapBlocks)
  216. cachedMapBlocks = mapBlocks;
  217. cachedTime = Util.UnixTimeSinceEpoch();
  218. }
  219. }
  220. LLSDMapLayerResponse mapResponse = new LLSDMapLayerResponse();
  221. mapResponse.LayerData.Array.Add(GetOSDMapLayerResponse());
  222. return mapResponse.ToString();
  223. }
  224. /// <summary>
  225. ///
  226. /// </summary>
  227. /// <param name="mapReq"></param>
  228. /// <returns></returns>
  229. public LLSDMapLayerResponse GetMapLayer(LLSDMapRequest mapReq)
  230. {
  231. m_log.Debug("[WORLD MAP]: MapLayer Request in region: " + m_scene.RegionInfo.RegionName);
  232. LLSDMapLayerResponse mapResponse = new LLSDMapLayerResponse();
  233. mapResponse.LayerData.Array.Add(GetOSDMapLayerResponse());
  234. return mapResponse;
  235. }
  236. /// <summary>
  237. ///
  238. /// </summary>
  239. /// <returns></returns>
  240. protected static OSDMapLayer GetOSDMapLayerResponse()
  241. {
  242. OSDMapLayer mapLayer = new OSDMapLayer();
  243. mapLayer.Right = 5000;
  244. mapLayer.Top = 5000;
  245. mapLayer.ImageID = new UUID("00000000-0000-1111-9999-000000000006");
  246. return mapLayer;
  247. }
  248. #region EventHandlers
  249. /// <summary>
  250. /// Registered for event
  251. /// </summary>
  252. /// <param name="client"></param>
  253. private void OnNewClient(IClientAPI client)
  254. {
  255. client.OnRequestMapBlocks += RequestMapBlocks;
  256. client.OnMapItemRequest += HandleMapItemRequest;
  257. }
  258. /// <summary>
  259. /// Client logged out, check to see if there are any more root agents in the simulator
  260. /// If not, stop the mapItemRequest Thread
  261. /// Event handler
  262. /// </summary>
  263. /// <param name="AgentId">AgentID that logged out</param>
  264. private void ClientLoggedOut(UUID AgentId, Scene scene)
  265. {
  266. lock (m_rootAgents)
  267. {
  268. m_rootAgents.Remove(AgentId);
  269. if(m_rootAgents.Count == 0)
  270. StopThread();
  271. }
  272. }
  273. #endregion
  274. /// <summary>
  275. /// Starts the MapItemRequest Thread
  276. /// Note that this only gets started when there are actually agents in the region
  277. /// Additionally, it gets stopped when there are none.
  278. /// </summary>
  279. /// <param name="o"></param>
  280. private void StartThread(object o)
  281. {
  282. if (threadrunning) return;
  283. threadrunning = true;
  284. m_log.Debug("[WORLD MAP]: Starting remote MapItem request thread");
  285. Watchdog.StartThread(process, "MapItemRequestThread", ThreadPriority.BelowNormal, true);
  286. }
  287. /// <summary>
  288. /// Enqueues a 'stop thread' MapRequestState. Causes the MapItemRequest thread to end
  289. /// </summary>
  290. private void StopThread()
  291. {
  292. MapRequestState st = new MapRequestState();
  293. st.agentID=STOP_UUID;
  294. st.EstateID=0;
  295. st.flags=0;
  296. st.godlike=false;
  297. st.itemtype=0;
  298. st.regionhandle=0;
  299. requests.Enqueue(st);
  300. }
  301. public virtual void HandleMapItemRequest(IClientAPI remoteClient, uint flags,
  302. uint EstateID, bool godlike, uint itemtype, ulong regionhandle)
  303. {
  304. lock (m_rootAgents)
  305. {
  306. if (!m_rootAgents.Contains(remoteClient.AgentId))
  307. return;
  308. }
  309. uint xstart = 0;
  310. uint ystart = 0;
  311. Utils.LongToUInts(m_scene.RegionInfo.RegionHandle, out xstart, out ystart);
  312. if (itemtype == 6) // we only sevice 6 right now (avatar green dots)
  313. {
  314. if (regionhandle == 0 || regionhandle == m_scene.RegionInfo.RegionHandle)
  315. {
  316. // Local Map Item Request
  317. int tc = Environment.TickCount;
  318. List<mapItemReply> mapitems = new List<mapItemReply>();
  319. mapItemReply mapitem = new mapItemReply();
  320. if (m_scene.GetRootAgentCount() <= 1)
  321. {
  322. mapitem = new mapItemReply();
  323. mapitem.x = (uint)(xstart + 1);
  324. mapitem.y = (uint)(ystart + 1);
  325. mapitem.id = UUID.Zero;
  326. mapitem.name = Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString());
  327. mapitem.Extra = 0;
  328. mapitem.Extra2 = 0;
  329. mapitems.Add(mapitem);
  330. }
  331. else
  332. {
  333. m_scene.ForEachScenePresence(delegate(ScenePresence sp)
  334. {
  335. // Don't send a green dot for yourself
  336. if (!sp.IsChildAgent && sp.UUID != remoteClient.AgentId)
  337. {
  338. mapitem = new mapItemReply();
  339. mapitem.x = (uint)(xstart + sp.AbsolutePosition.X);
  340. mapitem.y = (uint)(ystart + sp.AbsolutePosition.Y);
  341. mapitem.id = UUID.Zero;
  342. mapitem.name = Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString());
  343. mapitem.Extra = 1;
  344. mapitem.Extra2 = 0;
  345. mapitems.Add(mapitem);
  346. }
  347. });
  348. }
  349. remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags);
  350. }
  351. else
  352. {
  353. // Remote Map Item Request
  354. // ensures that the blockingqueue doesn't get borked if the GetAgents() timing changes.
  355. // Note that we only start up a remote mapItem Request thread if there's users who could
  356. // be making requests
  357. if (!threadrunning)
  358. {
  359. m_log.Warn("[WORLD MAP]: Starting new remote request thread manually. This means that AvatarEnteringParcel never fired! This needs to be fixed! Don't Mantis this, as the developers can see it in this message");
  360. StartThread(new object());
  361. }
  362. RequestMapItems("",remoteClient.AgentId,flags,EstateID,godlike,itemtype,regionhandle);
  363. }
  364. }
  365. }
  366. /// <summary>
  367. /// Processing thread main() loop for doing remote mapitem requests
  368. /// </summary>
  369. public void process()
  370. {
  371. try
  372. {
  373. while (true)
  374. {
  375. MapRequestState st = requests.Dequeue(1000);
  376. // end gracefully
  377. if (st.agentID == STOP_UUID)
  378. break;
  379. if (st.agentID != UUID.Zero)
  380. {
  381. bool dorequest = true;
  382. lock (m_rootAgents)
  383. {
  384. if (!m_rootAgents.Contains(st.agentID))
  385. dorequest = false;
  386. }
  387. if (dorequest)
  388. {
  389. OSDMap response = RequestMapItemsAsync("", st.agentID, st.flags, st.EstateID, st.godlike, st.itemtype, st.regionhandle);
  390. RequestMapItemsCompleted(response);
  391. }
  392. }
  393. Watchdog.UpdateThread();
  394. }
  395. }
  396. catch (Exception e)
  397. {
  398. m_log.ErrorFormat("[WORLD MAP]: Map item request thread terminated abnormally with exception {0}", e);
  399. }
  400. threadrunning = false;
  401. Watchdog.RemoveThread();
  402. }
  403. /// <summary>
  404. /// Enqueues the map item request into the processing thread
  405. /// </summary>
  406. /// <param name="state"></param>
  407. public void EnqueueMapItemRequest(MapRequestState state)
  408. {
  409. requests.Enqueue(state);
  410. }
  411. /// <summary>
  412. /// Sends the mapitem response to the IClientAPI
  413. /// </summary>
  414. /// <param name="response">The OSDMap Response for the mapitem</param>
  415. private void RequestMapItemsCompleted(OSDMap response)
  416. {
  417. UUID requestID = response["requestID"].AsUUID();
  418. if (requestID != UUID.Zero)
  419. {
  420. MapRequestState mrs = new MapRequestState();
  421. mrs.agentID = UUID.Zero;
  422. lock (m_openRequests)
  423. {
  424. if (m_openRequests.ContainsKey(requestID))
  425. {
  426. mrs = m_openRequests[requestID];
  427. m_openRequests.Remove(requestID);
  428. }
  429. }
  430. if (mrs.agentID != UUID.Zero)
  431. {
  432. ScenePresence av = null;
  433. m_scene.TryGetScenePresence(mrs.agentID, out av);
  434. if (av != null)
  435. {
  436. if (response.ContainsKey(mrs.itemtype.ToString()))
  437. {
  438. List<mapItemReply> returnitems = new List<mapItemReply>();
  439. OSDArray itemarray = (OSDArray)response[mrs.itemtype.ToString()];
  440. for (int i = 0; i < itemarray.Count; i++)
  441. {
  442. OSDMap mapitem = (OSDMap)itemarray[i];
  443. mapItemReply mi = new mapItemReply();
  444. mi.x = (uint)mapitem["X"].AsInteger();
  445. mi.y = (uint)mapitem["Y"].AsInteger();
  446. mi.id = mapitem["ID"].AsUUID();
  447. mi.Extra = mapitem["Extra"].AsInteger();
  448. mi.Extra2 = mapitem["Extra2"].AsInteger();
  449. mi.name = mapitem["Name"].AsString();
  450. returnitems.Add(mi);
  451. }
  452. av.ControllingClient.SendMapItemReply(returnitems.ToArray(), mrs.itemtype, mrs.flags);
  453. }
  454. }
  455. }
  456. }
  457. }
  458. /// <summary>
  459. /// Enqueue the MapItem request for remote processing
  460. /// </summary>
  461. /// <param name="httpserver">blank string, we discover this in the process</param>
  462. /// <param name="id">Agent ID that we are making this request on behalf</param>
  463. /// <param name="flags">passed in from packet</param>
  464. /// <param name="EstateID">passed in from packet</param>
  465. /// <param name="godlike">passed in from packet</param>
  466. /// <param name="itemtype">passed in from packet</param>
  467. /// <param name="regionhandle">Region we're looking up</param>
  468. public void RequestMapItems(string httpserver, UUID id, uint flags,
  469. uint EstateID, bool godlike, uint itemtype, ulong regionhandle)
  470. {
  471. MapRequestState st = new MapRequestState();
  472. st.agentID = id;
  473. st.flags = flags;
  474. st.EstateID = EstateID;
  475. st.godlike = godlike;
  476. st.itemtype = itemtype;
  477. st.regionhandle = regionhandle;
  478. EnqueueMapItemRequest(st);
  479. }
  480. /// <summary>
  481. /// Does the actual remote mapitem request
  482. /// This should be called from an asynchronous thread
  483. /// Request failures get blacklisted until region restart so we don't
  484. /// continue to spend resources trying to contact regions that are down.
  485. /// </summary>
  486. /// <param name="httpserver">blank string, we discover this in the process</param>
  487. /// <param name="id">Agent ID that we are making this request on behalf</param>
  488. /// <param name="flags">passed in from packet</param>
  489. /// <param name="EstateID">passed in from packet</param>
  490. /// <param name="godlike">passed in from packet</param>
  491. /// <param name="itemtype">passed in from packet</param>
  492. /// <param name="regionhandle">Region we're looking up</param>
  493. /// <returns></returns>
  494. private OSDMap RequestMapItemsAsync(string httpserver, UUID id, uint flags,
  495. uint EstateID, bool godlike, uint itemtype, ulong regionhandle)
  496. {
  497. bool blacklisted = false;
  498. lock (m_blacklistedregions)
  499. {
  500. if (m_blacklistedregions.ContainsKey(regionhandle))
  501. blacklisted = true;
  502. }
  503. if (blacklisted)
  504. return new OSDMap();
  505. UUID requestID = UUID.Random();
  506. lock (m_cachedRegionMapItemsAddress)
  507. {
  508. if (m_cachedRegionMapItemsAddress.ContainsKey(regionhandle))
  509. httpserver = m_cachedRegionMapItemsAddress[regionhandle];
  510. }
  511. if (httpserver.Length == 0)
  512. {
  513. uint x = 0, y = 0;
  514. Utils.LongToUInts(regionhandle, out x, out y);
  515. GridRegion mreg = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, (int)x, (int)y);
  516. if (mreg != null)
  517. {
  518. httpserver = "http://" + mreg.ExternalEndPoint.Address.ToString() + ":" + mreg.HttpPort + "/MAP/MapItems/" + regionhandle.ToString();
  519. lock (m_cachedRegionMapItemsAddress)
  520. {
  521. if (!m_cachedRegionMapItemsAddress.ContainsKey(regionhandle))
  522. m_cachedRegionMapItemsAddress.Add(regionhandle, httpserver);
  523. }
  524. }
  525. else
  526. {
  527. lock (m_blacklistedregions)
  528. {
  529. if (!m_blacklistedregions.ContainsKey(regionhandle))
  530. m_blacklistedregions.Add(regionhandle, Environment.TickCount);
  531. }
  532. m_log.InfoFormat("[WORLD MAP]: Blacklisted region {0}", regionhandle.ToString());
  533. }
  534. }
  535. blacklisted = false;
  536. lock (m_blacklistedurls)
  537. {
  538. if (m_blacklistedurls.ContainsKey(httpserver))
  539. blacklisted = true;
  540. }
  541. // Can't find the http server
  542. if (httpserver.Length == 0 || blacklisted)
  543. return new OSDMap();
  544. MapRequestState mrs = new MapRequestState();
  545. mrs.agentID = id;
  546. mrs.EstateID = EstateID;
  547. mrs.flags = flags;
  548. mrs.godlike = godlike;
  549. mrs.itemtype=itemtype;
  550. mrs.regionhandle = regionhandle;
  551. lock (m_openRequests)
  552. m_openRequests.Add(requestID, mrs);
  553. WebRequest mapitemsrequest = WebRequest.Create(httpserver);
  554. mapitemsrequest.Method = "POST";
  555. mapitemsrequest.ContentType = "application/xml+llsd";
  556. OSDMap RAMap = new OSDMap();
  557. // string RAMapString = RAMap.ToString();
  558. OSD LLSDofRAMap = RAMap; // RENAME if this works
  559. byte[] buffer = OSDParser.SerializeLLSDXmlBytes(LLSDofRAMap);
  560. OSDMap responseMap = new OSDMap();
  561. responseMap["requestID"] = OSD.FromUUID(requestID);
  562. Stream os = null;
  563. try
  564. { // send the Post
  565. mapitemsrequest.ContentLength = buffer.Length; //Count bytes to send
  566. os = mapitemsrequest.GetRequestStream();
  567. os.Write(buffer, 0, buffer.Length); //Send it
  568. os.Close();
  569. //m_log.DebugFormat("[WORLD MAP]: Getting MapItems from Sim {0}", httpserver);
  570. }
  571. catch (WebException ex)
  572. {
  573. m_log.WarnFormat("[WORLD MAP]: Bad send on GetMapItems {0}", ex.Message);
  574. responseMap["connect"] = OSD.FromBoolean(false);
  575. lock (m_blacklistedurls)
  576. {
  577. if (!m_blacklistedurls.ContainsKey(httpserver))
  578. m_blacklistedurls.Add(httpserver, Environment.TickCount);
  579. }
  580. m_log.WarnFormat("[WORLD MAP]: Blacklisted {0}", httpserver);
  581. return responseMap;
  582. }
  583. string response_mapItems_reply = null;
  584. { // get the response
  585. try
  586. {
  587. WebResponse webResponse = mapitemsrequest.GetResponse();
  588. if (webResponse != null)
  589. {
  590. StreamReader sr = new StreamReader(webResponse.GetResponseStream());
  591. response_mapItems_reply = sr.ReadToEnd().Trim();
  592. }
  593. else
  594. {
  595. return new OSDMap();
  596. }
  597. }
  598. catch (WebException)
  599. {
  600. responseMap["connect"] = OSD.FromBoolean(false);
  601. lock (m_blacklistedurls)
  602. {
  603. if (!m_blacklistedurls.ContainsKey(httpserver))
  604. m_blacklistedurls.Add(httpserver, Environment.TickCount);
  605. }
  606. m_log.WarnFormat("[WORLD MAP]: Blacklisted {0}", httpserver);
  607. return responseMap;
  608. }
  609. OSD rezResponse = null;
  610. try
  611. {
  612. rezResponse = OSDParser.DeserializeLLSDXml(response_mapItems_reply);
  613. responseMap = (OSDMap)rezResponse;
  614. responseMap["requestID"] = OSD.FromUUID(requestID);
  615. }
  616. catch (Exception)
  617. {
  618. //m_log.InfoFormat("[OGP]: exception on parse of rez reply {0}", ex.Message);
  619. responseMap["connect"] = OSD.FromBoolean(false);
  620. return responseMap;
  621. }
  622. }
  623. return responseMap;
  624. }
  625. /// <summary>
  626. /// Requests map blocks in area of minX, maxX, minY, MaxY in world cordinates
  627. /// </summary>
  628. /// <param name="minX"></param>
  629. /// <param name="minY"></param>
  630. /// <param name="maxX"></param>
  631. /// <param name="maxY"></param>
  632. public virtual void RequestMapBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag)
  633. {
  634. if ((flag & 0x10000) != 0) // user clicked on the map a tile that isn't visible
  635. {
  636. List<MapBlockData> response = new List<MapBlockData>();
  637. // this should return one mapblock at most.
  638. // (diva note: why?? in that case we should GetRegionByPosition)
  639. // But make sure: Look whether the one we requested is in there
  640. List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID,
  641. minX * (int)Constants.RegionSize,
  642. maxX * (int)Constants.RegionSize,
  643. minY * (int)Constants.RegionSize,
  644. maxY * (int)Constants.RegionSize);
  645. if (regions != null)
  646. {
  647. foreach (GridRegion r in regions)
  648. {
  649. if ((r.RegionLocX == minX * (int)Constants.RegionSize) &&
  650. (r.RegionLocY == minY * (int)Constants.RegionSize))
  651. {
  652. // found it => add it to response
  653. MapBlockData block = new MapBlockData();
  654. MapBlockFromGridRegion(block, r);
  655. response.Add(block);
  656. break;
  657. }
  658. }
  659. }
  660. if (response.Count == 0)
  661. {
  662. // response still empty => couldn't find the map-tile the user clicked on => tell the client
  663. MapBlockData block = new MapBlockData();
  664. block.X = (ushort)minX;
  665. block.Y = (ushort)minY;
  666. block.Access = 254; // == not there
  667. response.Add(block);
  668. }
  669. remoteClient.SendMapBlock(response, 0);
  670. }
  671. else
  672. {
  673. // normal mapblock request. Use the provided values
  674. GetAndSendBlocks(remoteClient, minX, minY, maxX, maxY, flag);
  675. }
  676. }
  677. protected virtual void GetAndSendBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag)
  678. {
  679. List<MapBlockData> mapBlocks = new List<MapBlockData>();
  680. List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID,
  681. (minX - 4) * (int)Constants.RegionSize,
  682. (maxX + 4) * (int)Constants.RegionSize,
  683. (minY - 4) * (int)Constants.RegionSize,
  684. (maxY + 4) * (int)Constants.RegionSize);
  685. foreach (GridRegion r in regions)
  686. {
  687. MapBlockData block = new MapBlockData();
  688. MapBlockFromGridRegion(block, r);
  689. mapBlocks.Add(block);
  690. }
  691. remoteClient.SendMapBlock(mapBlocks, flag);
  692. }
  693. protected void MapBlockFromGridRegion(MapBlockData block, GridRegion r)
  694. {
  695. block.Access = r.Access;
  696. block.MapImageId = r.TerrainImage;
  697. block.Name = r.RegionName;
  698. block.X = (ushort)(r.RegionLocX / Constants.RegionSize);
  699. block.Y = (ushort)(r.RegionLocY / Constants.RegionSize);
  700. }
  701. public Hashtable OnHTTPGetMapImage(Hashtable keysvals)
  702. {
  703. m_log.Debug("[WORLD MAP]: Sending map image jpeg");
  704. Hashtable reply = new Hashtable();
  705. int statuscode = 200;
  706. byte[] jpeg = new byte[0];
  707. if (myMapImageJPEG.Length == 0)
  708. {
  709. MemoryStream imgstream = new MemoryStream();
  710. Bitmap mapTexture = new Bitmap(1,1);
  711. ManagedImage managedImage;
  712. Image image = (Image)mapTexture;
  713. try
  714. {
  715. // Taking our jpeg2000 data, decoding it, then saving it to a byte array with regular jpeg data
  716. imgstream = new MemoryStream();
  717. // non-async because we know we have the asset immediately.
  718. AssetBase mapasset = m_scene.AssetService.Get(m_scene.RegionInfo.RegionSettings.TerrainImageID.ToString());
  719. // Decode image to System.Drawing.Image
  720. if (OpenJPEG.DecodeToImage(mapasset.Data, out managedImage, out image))
  721. {
  722. // Save to bitmap
  723. mapTexture = new Bitmap(image);
  724. EncoderParameters myEncoderParameters = new EncoderParameters();
  725. myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 95L);
  726. // Save bitmap to stream
  727. mapTexture.Save(imgstream, GetEncoderInfo("image/jpeg"), myEncoderParameters);
  728. // Write the stream to a byte array for output
  729. jpeg = imgstream.ToArray();
  730. myMapImageJPEG = jpeg;
  731. }
  732. }
  733. catch (Exception)
  734. {
  735. // Dummy!
  736. m_log.Warn("[WORLD MAP]: Unable to generate Map image");
  737. }
  738. finally
  739. {
  740. // Reclaim memory, these are unmanaged resources
  741. // If we encountered an exception, one or more of these will be null
  742. if (mapTexture != null)
  743. mapTexture.Dispose();
  744. if (image != null)
  745. image.Dispose();
  746. if (imgstream != null)
  747. {
  748. imgstream.Close();
  749. imgstream.Dispose();
  750. }
  751. }
  752. }
  753. else
  754. {
  755. // Use cached version so we don't have to loose our mind
  756. jpeg = myMapImageJPEG;
  757. }
  758. reply["str_response_string"] = Convert.ToBase64String(jpeg);
  759. reply["int_response_code"] = statuscode;
  760. reply["content_type"] = "image/jpeg";
  761. return reply;
  762. }
  763. // From msdn
  764. private static ImageCodecInfo GetEncoderInfo(String mimeType)
  765. {
  766. ImageCodecInfo[] encoders;
  767. encoders = ImageCodecInfo.GetImageEncoders();
  768. for (int j = 0; j < encoders.Length; ++j)
  769. {
  770. if (encoders[j].MimeType == mimeType)
  771. return encoders[j];
  772. }
  773. return null;
  774. }
  775. /// <summary>
  776. /// Export the world map
  777. /// </summary>
  778. /// <param name="fileName"></param>
  779. public void HandleExportWorldMapConsoleCommand(string module, string[] cmdparams)
  780. {
  781. if (m_scene.ConsoleScene() == null)
  782. {
  783. // FIXME: If console region is root then this will be printed by every module. Currently, there is no
  784. // way to prevent this, short of making the entire module shared (which is complete overkill).
  785. // One possibility is to return a bool to signal whether the module has completely handled the command
  786. m_log.InfoFormat("[WORLD MAP]: Please change to a specific region in order to export its world map");
  787. return;
  788. }
  789. if (m_scene.ConsoleScene() != m_scene)
  790. return;
  791. string exportPath;
  792. if (cmdparams.Length > 1)
  793. exportPath = cmdparams[1];
  794. else
  795. exportPath = DEFAULT_WORLD_MAP_EXPORT_PATH;
  796. m_log.InfoFormat(
  797. "[WORLD MAP]: Exporting world map for {0} to {1}", m_scene.RegionInfo.RegionName, exportPath);
  798. List<MapBlockData> mapBlocks = new List<MapBlockData>();
  799. List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID,
  800. (int)(m_scene.RegionInfo.RegionLocX - 9) * (int)Constants.RegionSize,
  801. (int)(m_scene.RegionInfo.RegionLocX + 9) * (int)Constants.RegionSize,
  802. (int)(m_scene.RegionInfo.RegionLocY - 9) * (int)Constants.RegionSize,
  803. (int)(m_scene.RegionInfo.RegionLocY + 9) * (int)Constants.RegionSize);
  804. List<AssetBase> textures = new List<AssetBase>();
  805. List<Image> bitImages = new List<Image>();
  806. foreach (GridRegion r in regions)
  807. {
  808. MapBlockData mapBlock = new MapBlockData();
  809. MapBlockFromGridRegion(mapBlock, r);
  810. AssetBase texAsset = m_scene.AssetService.Get(mapBlock.MapImageId.ToString());
  811. if (texAsset != null)
  812. {
  813. textures.Add(texAsset);
  814. }
  815. //else
  816. //{
  817. // // WHAT?!? This doesn't seem right. Commenting (diva)
  818. // texAsset = m_scene.AssetService.Get(mapBlock.MapImageId.ToString());
  819. // if (texAsset != null)
  820. // {
  821. // textures.Add(texAsset);
  822. // }
  823. //}
  824. }
  825. foreach (AssetBase asset in textures)
  826. {
  827. ManagedImage managedImage;
  828. Image image;
  829. if (OpenJPEG.DecodeToImage(asset.Data, out managedImage, out image))
  830. bitImages.Add(image);
  831. }
  832. Bitmap mapTexture = new Bitmap(2560, 2560);
  833. Graphics g = Graphics.FromImage(mapTexture);
  834. SolidBrush sea = new SolidBrush(Color.DarkBlue);
  835. g.FillRectangle(sea, 0, 0, 2560, 2560);
  836. for (int i = 0; i < mapBlocks.Count; i++)
  837. {
  838. ushort x = (ushort)((mapBlocks[i].X - m_scene.RegionInfo.RegionLocX) + 10);
  839. ushort y = (ushort)((mapBlocks[i].Y - m_scene.RegionInfo.RegionLocY) + 10);
  840. g.DrawImage(bitImages[i], (x * 128), 2560 - (y * 128), 128, 128); // y origin is top
  841. }
  842. mapTexture.Save(exportPath, ImageFormat.Jpeg);
  843. m_log.InfoFormat(
  844. "[WORLD MAP]: Successfully exported world map for {0} to {1}",
  845. m_scene.RegionInfo.RegionName, exportPath);
  846. }
  847. public OSD HandleRemoteMapItemRequest(string path, OSD request, string endpoint)
  848. {
  849. uint xstart = 0;
  850. uint ystart = 0;
  851. Utils.LongToUInts(m_scene.RegionInfo.RegionHandle,out xstart,out ystart);
  852. OSDMap responsemap = new OSDMap();
  853. int tc = Environment.TickCount;
  854. if (m_scene.GetRootAgentCount() == 0)
  855. {
  856. OSDMap responsemapdata = new OSDMap();
  857. responsemapdata["X"] = OSD.FromInteger((int)(xstart + 1));
  858. responsemapdata["Y"] = OSD.FromInteger((int)(ystart + 1));
  859. responsemapdata["ID"] = OSD.FromUUID(UUID.Zero);
  860. responsemapdata["Name"] = OSD.FromString(Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()));
  861. responsemapdata["Extra"] = OSD.FromInteger(0);
  862. responsemapdata["Extra2"] = OSD.FromInteger(0);
  863. OSDArray responsearr = new OSDArray();
  864. responsearr.Add(responsemapdata);
  865. responsemap["6"] = responsearr;
  866. }
  867. else
  868. {
  869. OSDArray responsearr = new OSDArray(m_scene.GetRootAgentCount());
  870. m_scene.ForEachScenePresence(delegate(ScenePresence sp)
  871. {
  872. OSDMap responsemapdata = new OSDMap();
  873. responsemapdata["X"] = OSD.FromInteger((int)(xstart + sp.AbsolutePosition.X));
  874. responsemapdata["Y"] = OSD.FromInteger((int)(ystart + sp.AbsolutePosition.Y));
  875. responsemapdata["ID"] = OSD.FromUUID(UUID.Zero);
  876. responsemapdata["Name"] = OSD.FromString(Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()));
  877. responsemapdata["Extra"] = OSD.FromInteger(1);
  878. responsemapdata["Extra2"] = OSD.FromInteger(0);
  879. responsearr.Add(responsemapdata);
  880. });
  881. responsemap["6"] = responsearr;
  882. }
  883. return responsemap;
  884. }
  885. public void GenerateMaptile()
  886. {
  887. // Cannot create a map for a nonexistant heightmap
  888. if (m_scene.Heightmap == null)
  889. return;
  890. //create a texture asset of the terrain
  891. IMapImageGenerator terrain = m_scene.RequestModuleInterface<IMapImageGenerator>();
  892. if (terrain == null)
  893. return;
  894. byte[] data = terrain.WriteJpeg2000Image("defaultstripe.png");
  895. if (data == null)
  896. return;
  897. UUID lastMapRegionUUID = m_scene.RegionInfo.RegionSettings.TerrainImageID;
  898. m_log.Debug("[WORLDMAP]: STORING MAPTILE IMAGE");
  899. m_scene.RegionInfo.RegionSettings.TerrainImageID = UUID.Random();
  900. AssetBase asset = new AssetBase(
  901. m_scene.RegionInfo.RegionSettings.TerrainImageID,
  902. "terrainImage_" + m_scene.RegionInfo.RegionID.ToString(),
  903. (sbyte)AssetType.Texture,
  904. m_scene.RegionInfo.RegionID.ToString());
  905. asset.Data = data;
  906. asset.Description = m_scene.RegionInfo.RegionName;
  907. asset.Temporary = false;
  908. asset.Flags = AssetFlags.Maptile;
  909. // Store the new one
  910. m_log.DebugFormat("[WORLDMAP]: Storing map tile {0}", asset.ID);
  911. m_scene.AssetService.Store(asset);
  912. m_scene.RegionInfo.RegionSettings.Save();
  913. // Delete the old one
  914. m_log.DebugFormat("[WORLDMAP]: Deleting old map tile {0}", lastMapRegionUUID);
  915. m_scene.AssetService.Delete(lastMapRegionUUID.ToString());
  916. }
  917. private void MakeRootAgent(ScenePresence avatar)
  918. {
  919. // You may ask, why this is in a threadpool to start with..
  920. // The reason is so we don't cause the thread to freeze waiting
  921. // for the 1 second it costs to start a thread manually.
  922. if (!threadrunning)
  923. Util.FireAndForget(this.StartThread);
  924. lock (m_rootAgents)
  925. {
  926. if (!m_rootAgents.Contains(avatar.UUID))
  927. {
  928. m_rootAgents.Add(avatar.UUID);
  929. }
  930. }
  931. }
  932. private void MakeChildAgent(ScenePresence avatar)
  933. {
  934. lock (m_rootAgents)
  935. {
  936. m_rootAgents.Remove(avatar.UUID);
  937. if (m_rootAgents.Count == 0)
  938. StopThread();
  939. }
  940. }
  941. }
  942. public struct MapRequestState
  943. {
  944. public UUID agentID;
  945. public uint flags;
  946. public uint EstateID;
  947. public bool godlike;
  948. public uint itemtype;
  949. public ulong regionhandle;
  950. }
  951. }