opensim-development – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 1 /*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27  
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Drawing;
32 using System.Drawing.Imaging;
33 using System.IO;
34 using System.Net;
35 using System.Reflection;
36 using System.Runtime.Remoting.Messaging;
37 using System.Threading;
38 using log4net;
39 using Nini.Config;
40 using OpenMetaverse;
41 using OpenMetaverse.Imaging;
42 using OpenMetaverse.StructuredData;
43 using Mono.Addins;
44 using OpenSim.Framework;
45 using OpenSim.Framework.Capabilities;
46 using OpenSim.Framework.Monitoring;
47 using OpenSim.Framework.Servers;
48 using OpenSim.Framework.Servers.HttpServer;
49 using OpenSim.Region.Framework.Interfaces;
50 using OpenSim.Region.Framework.Scenes;
51 using OpenSim.Region.CoreModules.World.Land;
52 using Caps=OpenSim.Framework.Capabilities.Caps;
53 using OSDArray=OpenMetaverse.StructuredData.OSDArray;
54 using OSDMap=OpenMetaverse.StructuredData.OSDMap;
55 using GridRegion = OpenSim.Services.Interfaces.GridRegion;
56  
57 namespace OpenSim.Region.CoreModules.World.WorldMap
58 {
59 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WorldMapModule")]
60 public class WorldMapModule : INonSharedRegionModule, IWorldMapModule
61 {
62 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
63 private static string LogHeader = "[WORLD MAP]";
64  
65 private static readonly string DEFAULT_WORLD_MAP_EXPORT_PATH = "exportmap.jpg";
66 private static readonly UUID STOP_UUID = UUID.Random();
67 private static readonly string m_mapLayerPath = "0001/";
68  
69 private OpenSim.Framework.BlockingQueue<MapRequestState> requests = new OpenSim.Framework.BlockingQueue<MapRequestState>();
70  
71 protected Scene m_scene;
72 private List<MapBlockData> cachedMapBlocks = new List<MapBlockData>();
73 private int cachedTime = 0;
74 private int blacklistTimeout = 10*60*1000; // 10 minutes
75 private byte[] myMapImageJPEG;
76 protected volatile bool m_Enabled = false;
77 private Dictionary<UUID, MapRequestState> m_openRequests = new Dictionary<UUID, MapRequestState>();
78 private Dictionary<string, int> m_blacklistedurls = new Dictionary<string, int>();
79 private Dictionary<ulong, int> m_blacklistedregions = new Dictionary<ulong, int>();
80 private Dictionary<ulong, string> m_cachedRegionMapItemsAddress = new Dictionary<ulong, string>();
81 private List<UUID> m_rootAgents = new List<UUID>();
82 private volatile bool threadrunning = false;
83  
84 private IServiceThrottleModule m_ServiceThrottle;
85  
86 //private int CacheRegionsDistance = 256;
87  
88 #region INonSharedRegionModule Members
89 public virtual void Initialise (IConfigSource config)
90 {
91 string[] configSections = new string[] { "Map", "Startup" };
92  
93 if (Util.GetConfigVarFromSections<string>(
94 config, "WorldMapModule", configSections, "WorldMap") == "WorldMap")
95 m_Enabled = true;
96  
97 blacklistTimeout
98 = Util.GetConfigVarFromSections<int>(config, "BlacklistTimeout", configSections, 10 * 60) * 1000;
99 }
100  
101 public virtual void AddRegion (Scene scene)
102 {
103 if (!m_Enabled)
104 return;
105  
106 lock (scene)
107 {
108 m_scene = scene;
109  
110 m_scene.RegisterModuleInterface<IWorldMapModule>(this);
111  
112 m_scene.AddCommand(
113 "Regions", this, "export-map",
114 "export-map [<path>]",
115 "Save an image of the world map", HandleExportWorldMapConsoleCommand);
116  
117 m_scene.AddCommand(
118 "Regions", this, "generate map",
119 "generate map",
120 "Generates and stores a new maptile.", HandleGenerateMapConsoleCommand);
121  
122 AddHandlers();
123 }
124 }
125  
126 public virtual void RemoveRegion (Scene scene)
127 {
128 if (!m_Enabled)
129 return;
130  
131 lock (m_scene)
132 {
133 m_Enabled = false;
134 RemoveHandlers();
135 m_scene = null;
136 }
137 }
138  
139 public virtual void RegionLoaded (Scene scene)
140 {
141 if (!m_Enabled)
142 return;
143  
144 m_ServiceThrottle = scene.RequestModuleInterface<IServiceThrottleModule>();
145 }
146  
147  
148 public virtual void Close()
149 {
150 }
151  
152 public Type ReplaceableInterface
153 {
154 get { return null; }
155 }
156  
157 public virtual string Name
158 {
159 get { return "WorldMapModule"; }
160 }
161  
162 #endregion
163  
164 // this has to be called with a lock on m_scene
165 protected virtual void AddHandlers()
166 {
167 myMapImageJPEG = new byte[0];
168  
169 string regionimage = "regionImage" + m_scene.RegionInfo.RegionID.ToString();
170 regionimage = regionimage.Replace("-", "");
171 m_log.Info("[WORLD MAP]: JPEG Map location: " + m_scene.RegionInfo.ServerURI + "index.php?method=" + regionimage);
172  
173 MainServer.Instance.AddHTTPHandler(regionimage,
174 new GenericHTTPDOSProtector(OnHTTPGetMapImage, OnHTTPThrottled, new BasicDosProtectorOptions()
175 {
176 AllowXForwardedFor = false,
177 ForgetTimeSpan = TimeSpan.FromMinutes(2),
178 MaxRequestsInTimeframe = 4,
179 ReportingName = "MAPDOSPROTECTOR",
180 RequestTimeSpan = TimeSpan.FromSeconds(10),
181 ThrottledAction = BasicDOSProtector.ThrottleAction.DoThrottledMethod
182 }).Process);
183 MainServer.Instance.AddLLSDHandler(
184 "/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString(), HandleRemoteMapItemRequest);
185  
186 m_scene.EventManager.OnRegisterCaps += OnRegisterCaps;
187 m_scene.EventManager.OnNewClient += OnNewClient;
188 m_scene.EventManager.OnClientClosed += ClientLoggedOut;
189 m_scene.EventManager.OnMakeChildAgent += MakeChildAgent;
190 m_scene.EventManager.OnMakeRootAgent += MakeRootAgent;
191 m_scene.EventManager.OnRegionUp += OnRegionUp;
192  
193 // StartThread(new object());
194 }
195  
196 // this has to be called with a lock on m_scene
197 protected virtual void RemoveHandlers()
198 {
199 // StopThread();
200  
201 m_scene.EventManager.OnRegionUp -= OnRegionUp;
202 m_scene.EventManager.OnMakeRootAgent -= MakeRootAgent;
203 m_scene.EventManager.OnMakeChildAgent -= MakeChildAgent;
204 m_scene.EventManager.OnClientClosed -= ClientLoggedOut;
205 m_scene.EventManager.OnNewClient -= OnNewClient;
206 m_scene.EventManager.OnRegisterCaps -= OnRegisterCaps;
207  
208 string regionimage = "regionImage" + m_scene.RegionInfo.RegionID.ToString();
209 regionimage = regionimage.Replace("-", "");
210 MainServer.Instance.RemoveLLSDHandler("/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString(),
211 HandleRemoteMapItemRequest);
212 MainServer.Instance.RemoveHTTPHandler("", regionimage);
213 }
214  
215 public void OnRegisterCaps(UUID agentID, Caps caps)
216 {
217 //m_log.DebugFormat("[WORLD MAP]: OnRegisterCaps: agentID {0} caps {1}", agentID, caps);
218 string capsBase = "/CAPS/" + caps.CapsObjectPath;
219 caps.RegisterHandler(
220 "MapLayer",
221 new RestStreamHandler(
222 "POST",
223 capsBase + m_mapLayerPath,
224 (request, path, param, httpRequest, httpResponse)
225 => MapLayerRequest(request, path, param, agentID, caps),
226 "MapLayer",
227 agentID.ToString()));
228 }
229  
230 /// <summary>
231 /// Callback for a map layer request
232 /// </summary>
233 /// <param name="request"></param>
234 /// <param name="path"></param>
235 /// <param name="param"></param>
236 /// <param name="agentID"></param>
237 /// <param name="caps"></param>
238 /// <returns></returns>
239 public string MapLayerRequest(string request, string path, string param,
240 UUID agentID, Caps caps)
241 {
242 //try
243 //
244 //m_log.DebugFormat("[MAPLAYER]: path: {0}, param: {1}, agent:{2}",
245 // path, param, agentID.ToString());
246  
247 // There is a major hack going on in this method. The viewer doesn't request
248 // map blocks (RequestMapBlocks) above 2048. That means that if we don't hack,
249 // grids above that cell don't have a map at all. So, here's the hack: we wait
250 // for this CAP request to come, and we inject the map blocks at this point.
251 // In a normal scenario, this request simply sends back the MapLayer (the blue color).
252 // In the hacked scenario, it also sends the map blocks via UDP.
253 //
254 // 6/8/2011 -- I'm adding an explicit 2048 check, so that we never forget that there is
255 // a hack here, and so that regions below 4096 don't get spammed with unnecessary map blocks.
256  
257 if (m_scene.RegionInfo.RegionLocX >= 2048 || m_scene.RegionInfo.RegionLocY >= 2048)
258 {
259 ScenePresence avatarPresence = null;
260  
261 m_scene.TryGetScenePresence(agentID, out avatarPresence);
262  
263 if (avatarPresence != null)
264 {
265 bool lookup = false;
266  
267 lock (cachedMapBlocks)
268 {
269 if (cachedMapBlocks.Count > 0 && ((cachedTime + 1800) > Util.UnixTimeSinceEpoch()))
270 {
271 List<MapBlockData> mapBlocks;
272  
273 mapBlocks = cachedMapBlocks;
274 avatarPresence.ControllingClient.SendMapBlock(mapBlocks, 0);
275 }
276 else
277 {
278 lookup = true;
279 }
280 }
281 if (lookup)
282 {
283 List<MapBlockData> mapBlocks = new List<MapBlockData>(); ;
284  
285 // Get regions that are within 8 regions of here
286 List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID,
287 (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocX - 8),
288 (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocX + 8),
289 (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocY - 8),
290 (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocY + 8) );
291 foreach (GridRegion r in regions)
292 {
293 MapBlockData block = MapBlockFromGridRegion(r, 0);
294 mapBlocks.Add(block);
295 }
296 avatarPresence.ControllingClient.SendMapBlock(mapBlocks, 0);
297  
298 lock (cachedMapBlocks)
299 cachedMapBlocks = mapBlocks;
300  
301 cachedTime = Util.UnixTimeSinceEpoch();
302 }
303 }
304 }
305  
306 LLSDMapLayerResponse mapResponse = new LLSDMapLayerResponse();
307 mapResponse.LayerData.Array.Add(GetOSDMapLayerResponse());
308 return mapResponse.ToString();
309 }
310  
311 /// <summary>
312 ///
313 /// </summary>
314 /// <param name="mapReq"></param>
315 /// <returns></returns>
316 public LLSDMapLayerResponse GetMapLayer(LLSDMapRequest mapReq)
317 {
318 // m_log.DebugFormat("[WORLD MAP]: MapLayer Request in region: {0}", m_scene.RegionInfo.RegionName);
319 LLSDMapLayerResponse mapResponse = new LLSDMapLayerResponse();
320 mapResponse.LayerData.Array.Add(GetOSDMapLayerResponse());
321 return mapResponse;
322 }
323  
324 /// <summary>
325 ///
326 /// </summary>
327 /// <returns></returns>
328 protected static OSDMapLayer GetOSDMapLayerResponse()
329 {
330 OSDMapLayer mapLayer = new OSDMapLayer();
331 mapLayer.Right = 5000;
332 mapLayer.Top = 5000;
333 mapLayer.ImageID = new UUID("00000000-0000-1111-9999-000000000006");
334  
335 return mapLayer;
336 }
337 #region EventHandlers
338  
339 /// <summary>
340 /// Registered for event
341 /// </summary>
342 /// <param name="client"></param>
343 private void OnNewClient(IClientAPI client)
344 {
345 client.OnRequestMapBlocks += RequestMapBlocks;
346 client.OnMapItemRequest += HandleMapItemRequest;
347 }
348  
349 /// <summary>
350 /// Client logged out, check to see if there are any more root agents in the simulator
351 /// If not, stop the mapItemRequest Thread
352 /// Event handler
353 /// </summary>
354 /// <param name="AgentId">AgentID that logged out</param>
355 private void ClientLoggedOut(UUID AgentId, Scene scene)
356 {
357 lock (m_rootAgents)
358 {
359 m_rootAgents.Remove(AgentId);
360 }
361 }
362 #endregion
363  
364 /// <summary>
365 /// Starts the MapItemRequest Thread
366 /// Note that this only gets started when there are actually agents in the region
367 /// Additionally, it gets stopped when there are none.
368 /// </summary>
369 /// <param name="o"></param>
370 private void StartThread(object o)
371 {
372 if (threadrunning) return;
373 threadrunning = true;
374  
375 // m_log.Debug("[WORLD MAP]: Starting remote MapItem request thread");
376  
377 Watchdog.StartThread(
378 process,
379 string.Format("MapItemRequestThread ({0})", m_scene.RegionInfo.RegionName),
380 ThreadPriority.BelowNormal,
381 true,
382 true);
383 }
384  
385 /// <summary>
386 /// Enqueues a 'stop thread' MapRequestState. Causes the MapItemRequest thread to end
387 /// </summary>
388 private void StopThread()
389 {
390 MapRequestState st = new MapRequestState();
391 st.agentID = STOP_UUID;
392 st.EstateID=0;
393 st.flags=0;
394 st.godlike=false;
395 st.itemtype=0;
396 st.regionhandle=0;
397  
398 requests.Enqueue(st);
399 }
400  
401 public virtual void HandleMapItemRequest(IClientAPI remoteClient, uint flags,
402 uint EstateID, bool godlike, uint itemtype, ulong regionhandle)
403 {
404 // m_log.DebugFormat("[WORLD MAP]: Handle MapItem request {0} {1}", regionhandle, itemtype);
405  
406 lock (m_rootAgents)
407 {
408 if (!m_rootAgents.Contains(remoteClient.AgentId))
409 return;
410 }
411 uint xstart = 0;
412 uint ystart = 0;
413 Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle, out xstart, out ystart);
414 if (itemtype == (int)GridItemType.AgentLocations)
415 {
416 if (regionhandle == 0 || regionhandle == m_scene.RegionInfo.RegionHandle)
417 {
418 // Just requesting map info about the current, local region
419 int tc = Environment.TickCount;
420 List<mapItemReply> mapitems = new List<mapItemReply>();
421 mapItemReply mapitem = new mapItemReply();
422 if (m_scene.GetRootAgentCount() <= 1)
423 {
424 mapitem = new mapItemReply(
425 xstart + 1,
426 ystart + 1,
427 UUID.Zero,
428 Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()),
429 0, 0);
430 mapitems.Add(mapitem);
431 }
432 else
433 {
434 m_scene.ForEachRootScenePresence(delegate(ScenePresence sp)
435 {
436 // Don't send a green dot for yourself
437 if (sp.UUID != remoteClient.AgentId)
438 {
439 mapitem = new mapItemReply(
440 xstart + (uint)sp.AbsolutePosition.X,
441 ystart + (uint)sp.AbsolutePosition.Y,
442 UUID.Zero,
443 Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()),
444 1, 0);
445 mapitems.Add(mapitem);
446 }
447 });
448 }
449 remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags);
450 }
451 else
452 {
453 // Remote Map Item Request
454  
455 // ensures that the blockingqueue doesn't get borked if the GetAgents() timing changes.
456 RequestMapItems("",remoteClient.AgentId,flags,EstateID,godlike,itemtype,regionhandle);
457 }
458 }
459 else if (itemtype == (int)GridItemType.LandForSale) // Service 7 (MAP_ITEM_LAND_FOR_SALE)
460 {
461 if (regionhandle == 0 || regionhandle == m_scene.RegionInfo.RegionHandle)
462 {
463 // Parcels
464 ILandChannel landChannel = m_scene.LandChannel;
465 List<ILandObject> parcels = landChannel.AllParcels();
466  
467 // Local Map Item Request
468 List<mapItemReply> mapitems = new List<mapItemReply>();
469 mapItemReply mapitem = new mapItemReply();
470 if ((parcels != null) && (parcels.Count >= 1))
471 {
472 foreach (ILandObject parcel_interface in parcels)
473 {
474 // Play it safe
475 if (!(parcel_interface is LandObject))
476 continue;
477  
478 LandObject land = (LandObject)parcel_interface;
479 LandData parcel = land.LandData;
480  
481 // Show land for sale
482 if ((parcel.Flags & (uint)ParcelFlags.ForSale) == (uint)ParcelFlags.ForSale)
483 {
484 Vector3 min = parcel.AABBMin;
485 Vector3 max = parcel.AABBMax;
486 float x = (min.X+max.X)/2;
487 float y = (min.Y+max.Y)/2;
488  
489 mapitem = new mapItemReply(
490 xstart + (uint)x,
491 ystart + (uint)y,
492 parcel.GlobalID,
493 parcel.Name,
494 parcel.Area,
495 parcel.SalePrice
496 );
497 mapitems.Add(mapitem);
498 }
499 }
500 }
501 remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags);
502 }
503 else
504 {
505 // Remote Map Item Request
506  
507 // ensures that the blockingqueue doesn't get borked if the GetAgents() timing changes.
508 RequestMapItems("",remoteClient.AgentId,flags,EstateID,godlike,itemtype,regionhandle);
509 }
510 }
511 else if (itemtype == (int)GridItemType.Telehub) // Service 1 (MAP_ITEM_TELEHUB)
512 {
513 if (regionhandle == 0 || regionhandle == m_scene.RegionInfo.RegionHandle)
514 {
515 List<mapItemReply> mapitems = new List<mapItemReply>();
516 mapItemReply mapitem = new mapItemReply();
517  
518 SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject);
519 if (sog != null)
520 {
521 mapitem = new mapItemReply(
522 xstart + (uint)sog.AbsolutePosition.X,
523 ystart + (uint)sog.AbsolutePosition.Y,
524 UUID.Zero,
525 sog.Name,
526 0, // color (not used)
527  
528 );
529 mapitems.Add(mapitem);
530  
531 remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags);
532 }
533 }
534 else
535 {
536 // Remote Map Item Request
537 RequestMapItems("",remoteClient.AgentId,flags,EstateID,godlike,itemtype,regionhandle);
538 }
539 }
540 }
541  
542 private int nAsyncRequests = 0;
543 /// <summary>
544 /// Processing thread main() loop for doing remote mapitem requests
545 /// </summary>
546 public void process()
547 {
548 //const int MAX_ASYNC_REQUESTS = 20;
549 try
550 {
551 while (true)
552 {
553 MapRequestState st = requests.Dequeue(1000);
554  
555 // end gracefully
556 if (st.agentID == STOP_UUID)
557 break;
558  
559 if (st.agentID != UUID.Zero)
560 {
561 bool dorequest = true;
562 lock (m_rootAgents)
563 {
564 if (!m_rootAgents.Contains(st.agentID))
565 dorequest = false;
566 }
567  
568 if (dorequest && !m_blacklistedregions.ContainsKey(st.regionhandle))
569 {
570 while (nAsyncRequests >= MAX_ASYNC_REQUESTS) // hit the break
571 Thread.Sleep(80);
572  
573 RequestMapItemsDelegate d = RequestMapItemsAsync;
574 d.BeginInvoke(st.agentID, st.flags, st.EstateID, st.godlike, st.itemtype, st.regionhandle, RequestMapItemsCompleted, null);
575 //OSDMap response = RequestMapItemsAsync(st.agentID, st.flags, st.EstateID, st.godlike, st.itemtype, st.regionhandle);
576 //RequestMapItemsCompleted(response);
577 Interlocked.Increment(ref nAsyncRequests);
578 }
579 }
580  
581 Watchdog.UpdateThread();
582 }
583 }
584 catch (Exception e)
585 {
586 m_log.ErrorFormat("[WORLD MAP]: Map item request thread terminated abnormally with exception {0}", e);
587 }
588  
589 threadrunning = false;
590 Watchdog.RemoveThread();
591 }
592  
593 const int MAX_ASYNC_REQUESTS = 20;
594  
595 /// <summary>
596 /// Enqueues the map item request into the services throttle processing thread
597 /// </summary>
598 /// <param name="state"></param>
599 public void EnqueueMapItemRequest(MapRequestState st)
600 {
601  
602 m_ServiceThrottle.Enqueue("map-item", st.regionhandle.ToString() + st.agentID.ToString(), delegate
603 {
604 if (st.agentID != UUID.Zero)
605 {
606 bool dorequest = true;
607 lock (m_rootAgents)
608 {
609 if (!m_rootAgents.Contains(st.agentID))
610 dorequest = false;
611 }
612  
613 if (dorequest && !m_blacklistedregions.ContainsKey(st.regionhandle))
614 {
615 if (nAsyncRequests >= MAX_ASYNC_REQUESTS) // hit the break
616 {
617 // AH!!! Recursive !
618 // Put this request back in the queue and return
619 EnqueueMapItemRequest(st);
620 return;
621 }
622  
623 RequestMapItemsDelegate d = RequestMapItemsAsync;
624 d.BeginInvoke(st.agentID, st.flags, st.EstateID, st.godlike, st.itemtype, st.regionhandle, RequestMapItemsCompleted, null);
625 //OSDMap response = RequestMapItemsAsync(st.agentID, st.flags, st.EstateID, st.godlike, st.itemtype, st.regionhandle);
626 //RequestMapItemsCompleted(response);
627 Interlocked.Increment(ref nAsyncRequests);
628 }
629 }
630 });
631 }
632  
633 /// <summary>
634 /// Sends the mapitem response to the IClientAPI
635 /// </summary>
636 /// <param name="response">The OSDMap Response for the mapitem</param>
637 private void RequestMapItemsCompleted(IAsyncResult iar)
638 {
639 AsyncResult result = (AsyncResult)iar;
640 RequestMapItemsDelegate icon = (RequestMapItemsDelegate)result.AsyncDelegate;
641  
642 OSDMap response = (OSDMap)icon.EndInvoke(iar);
643  
644 Interlocked.Decrement(ref nAsyncRequests);
645  
646 if (!response.ContainsKey("requestID"))
647 return;
648  
649 UUID requestID = response["requestID"].AsUUID();
650  
651 if (requestID != UUID.Zero)
652 {
653 MapRequestState mrs = new MapRequestState();
654 mrs.agentID = UUID.Zero;
655 lock (m_openRequests)
656 {
657 if (m_openRequests.ContainsKey(requestID))
658 {
659 mrs = m_openRequests[requestID];
660 m_openRequests.Remove(requestID);
661 }
662 }
663  
664 if (mrs.agentID != UUID.Zero)
665 {
666 ScenePresence av = null;
667 m_scene.TryGetScenePresence(mrs.agentID, out av);
668 if (av != null)
669 {
670 if (response.ContainsKey(mrs.itemtype.ToString()))
671 {
672 List<mapItemReply> returnitems = new List<mapItemReply>();
673 OSDArray itemarray = (OSDArray)response[mrs.itemtype.ToString()];
674 for (int i = 0; i < itemarray.Count; i++)
675 {
676 OSDMap mapitem = (OSDMap)itemarray[i];
677 mapItemReply mi = new mapItemReply();
678 mi.FromOSD(mapitem);
679 returnitems.Add(mi);
680 }
681 av.ControllingClient.SendMapItemReply(returnitems.ToArray(), mrs.itemtype, mrs.flags);
682 }
683  
684 // Service 7 (MAP_ITEM_LAND_FOR_SALE)
685 uint itemtype = (uint)GridItemType.LandForSale;
686  
687 if (response.ContainsKey(itemtype.ToString()))
688 {
689 List<mapItemReply> returnitems = new List<mapItemReply>();
690 OSDArray itemarray = (OSDArray)response[itemtype.ToString()];
691 for (int i = 0; i < itemarray.Count; i++)
692 {
693 OSDMap mapitem = (OSDMap)itemarray[i];
694 mapItemReply mi = new mapItemReply();
695 mi.FromOSD(mapitem);
696 returnitems.Add(mi);
697 }
698 av.ControllingClient.SendMapItemReply(returnitems.ToArray(), itemtype, mrs.flags);
699 }
700  
701 // Service 1 (MAP_ITEM_TELEHUB)
702 itemtype = (uint)GridItemType.Telehub;
703  
704 if (response.ContainsKey(itemtype.ToString()))
705 {
706 List<mapItemReply> returnitems = new List<mapItemReply>();
707 OSDArray itemarray = (OSDArray)response[itemtype.ToString()];
708 for (int i = 0; i < itemarray.Count; i++)
709 {
710 OSDMap mapitem = (OSDMap)itemarray[i];
711 mapItemReply mi = new mapItemReply();
712 mi.FromOSD(mapitem);
713 returnitems.Add(mi);
714 }
715 av.ControllingClient.SendMapItemReply(returnitems.ToArray(), itemtype, mrs.flags);
716 }
717 }
718 }
719 }
720 }
721  
722 /// <summary>
723 /// Enqueue the MapItem request for remote processing
724 /// </summary>
725 /// <param name="httpserver">blank string, we discover this in the process</param>
726 /// <param name="id">Agent ID that we are making this request on behalf</param>
727 /// <param name="flags">passed in from packet</param>
728 /// <param name="EstateID">passed in from packet</param>
729 /// <param name="godlike">passed in from packet</param>
730 /// <param name="itemtype">passed in from packet</param>
731 /// <param name="regionhandle">Region we're looking up</param>
732 public void RequestMapItems(string httpserver, UUID id, uint flags,
733 uint EstateID, bool godlike, uint itemtype, ulong regionhandle)
734 {
735 MapRequestState st = new MapRequestState();
736 st.agentID = id;
737 st.flags = flags;
738 st.EstateID = EstateID;
739 st.godlike = godlike;
740 st.itemtype = itemtype;
741 st.regionhandle = regionhandle;
742 EnqueueMapItemRequest(st);
743 }
744  
745 private delegate OSDMap RequestMapItemsDelegate(UUID id, uint flags,
746 uint EstateID, bool godlike, uint itemtype, ulong regionhandle);
747 /// <summary>
748 /// Does the actual remote mapitem request
749 /// This should be called from an asynchronous thread
750 /// Request failures get blacklisted until region restart so we don't
751 /// continue to spend resources trying to contact regions that are down.
752 /// </summary>
753 /// <param name="httpserver">blank string, we discover this in the process</param>
754 /// <param name="id">Agent ID that we are making this request on behalf</param>
755 /// <param name="flags">passed in from packet</param>
756 /// <param name="EstateID">passed in from packet</param>
757 /// <param name="godlike">passed in from packet</param>
758 /// <param name="itemtype">passed in from packet</param>
759 /// <param name="regionhandle">Region we're looking up</param>
760 /// <returns></returns>
761 private OSDMap RequestMapItemsAsync(UUID id, uint flags,
762 uint EstateID, bool godlike, uint itemtype, ulong regionhandle)
763 {
764 // m_log.DebugFormat("[WORLDMAP]: RequestMapItemsAsync; region handle: {0} {1}", regionhandle, itemtype);
765  
766 string httpserver = "";
767 bool blacklisted = false;
768 lock (m_blacklistedregions)
769 {
770 if (m_blacklistedregions.ContainsKey(regionhandle))
771 {
772 if (Environment.TickCount > (m_blacklistedregions[regionhandle] + blacklistTimeout))
773 {
774 m_log.DebugFormat("[WORLD MAP]: Unblock blacklisted region {0}", regionhandle);
775  
776 m_blacklistedregions.Remove(regionhandle);
777 }
778 else
779 blacklisted = true;
780 }
781 }
782  
783 if (blacklisted)
784 return new OSDMap();
785  
786 UUID requestID = UUID.Random();
787 lock (m_cachedRegionMapItemsAddress)
788 {
789 if (m_cachedRegionMapItemsAddress.ContainsKey(regionhandle))
790 httpserver = m_cachedRegionMapItemsAddress[regionhandle];
791 }
792 if (httpserver.Length == 0)
793 {
794 uint x = 0, y = 0;
795 Util.RegionHandleToWorldLoc(regionhandle, out x, out y);
796 GridRegion mreg = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, (int)x, (int)y);
797  
798 if (mreg != null)
799 {
800 httpserver = mreg.ServerURI + "MAP/MapItems/" + regionhandle.ToString();
801 lock (m_cachedRegionMapItemsAddress)
802 {
803 if (!m_cachedRegionMapItemsAddress.ContainsKey(regionhandle))
804 m_cachedRegionMapItemsAddress.Add(regionhandle, httpserver);
805 }
806 }
807 else
808 {
809 lock (m_blacklistedregions)
810 {
811 if (!m_blacklistedregions.ContainsKey(regionhandle))
812 m_blacklistedregions.Add(regionhandle, Environment.TickCount);
813 }
814 //m_log.InfoFormat("[WORLD MAP]: Blacklisted region {0}", regionhandle.ToString());
815 }
816 }
817  
818 blacklisted = false;
819 lock (m_blacklistedurls)
820 {
821 if (m_blacklistedurls.ContainsKey(httpserver))
822 {
823 if (Environment.TickCount > (m_blacklistedurls[httpserver] + blacklistTimeout))
824 {
825 m_log.DebugFormat("[WORLD MAP]: Unblock blacklisted URL {0}", httpserver);
826  
827 m_blacklistedurls.Remove(httpserver);
828 }
829 else
830 blacklisted = true;
831 }
832 }
833  
834 // Can't find the http server
835 if (httpserver.Length == 0 || blacklisted)
836 return new OSDMap();
837  
838 MapRequestState mrs = new MapRequestState();
839 mrs.agentID = id;
840 mrs.EstateID = EstateID;
841 mrs.flags = flags;
842 mrs.godlike = godlike;
843 mrs.itemtype=itemtype;
844 mrs.regionhandle = regionhandle;
845  
846 lock (m_openRequests)
847 m_openRequests.Add(requestID, mrs);
848  
849 WebRequest mapitemsrequest = null;
850 try
851 {
852 mapitemsrequest = WebRequest.Create(httpserver);
853 }
854 catch (Exception e)
855 {
856 m_log.DebugFormat("[WORLD MAP]: Access to {0} failed with {1}", httpserver, e);
857 return new OSDMap();
858 }
859  
860 mapitemsrequest.Method = "POST";
861 mapitemsrequest.ContentType = "application/xml+llsd";
862 OSDMap RAMap = new OSDMap();
863  
864 // string RAMapString = RAMap.ToString();
865 OSD LLSDofRAMap = RAMap; // RENAME if this works
866  
867 byte[] buffer = OSDParser.SerializeLLSDXmlBytes(LLSDofRAMap);
868 OSDMap responseMap = new OSDMap();
869 responseMap["requestID"] = OSD.FromUUID(requestID);
870  
871 Stream os = null;
872 try
873 { // send the Post
874 mapitemsrequest.ContentLength = buffer.Length; //Count bytes to send
875 os = mapitemsrequest.GetRequestStream();
876 os.Write(buffer, 0, buffer.Length); //Send it
877 //m_log.DebugFormat("[WORLD MAP]: Getting MapItems from {0}", httpserver);
878 }
879 catch (WebException ex)
880 {
881 m_log.WarnFormat("[WORLD MAP]: Bad send on GetMapItems {0}", ex.Message);
882 responseMap["connect"] = OSD.FromBoolean(false);
883 lock (m_blacklistedurls)
884 {
885 if (!m_blacklistedurls.ContainsKey(httpserver))
886 m_blacklistedurls.Add(httpserver, Environment.TickCount);
887 }
888  
889 m_log.WarnFormat("[WORLD MAP]: Blacklisted {0}", httpserver);
890  
891 return responseMap;
892 }
893 catch
894 {
895 m_log.DebugFormat("[WORLD MAP]: RequestMapItems failed for {0}", httpserver);
896 responseMap["connect"] = OSD.FromBoolean(false);
897 return responseMap;
898 }
899 finally
900 {
901 if (os != null)
902 os.Dispose();
903 }
904  
905 string response_mapItems_reply = null;
906 {
907 try
908 {
909 using (WebResponse webResponse = mapitemsrequest.GetResponse())
910 {
911 if (webResponse != null)
912 {
913 using (Stream s = webResponse.GetResponseStream())
914 using (StreamReader sr = new StreamReader(s))
915 response_mapItems_reply = sr.ReadToEnd().Trim();
916 }
917 else
918 {
919 return new OSDMap();
920 }
921 }
922 }
923 catch (WebException)
924 {
925 responseMap["connect"] = OSD.FromBoolean(false);
926 lock (m_blacklistedurls)
927 {
928 if (!m_blacklistedurls.ContainsKey(httpserver))
929 m_blacklistedurls.Add(httpserver, Environment.TickCount);
930 }
931  
932 m_log.WarnFormat("[WORLD MAP]: Blacklisted {0}", httpserver);
933  
934 return responseMap;
935 }
936 catch
937 {
938 m_log.DebugFormat("[WORLD MAP]: RequestMapItems failed for {0}", httpserver);
939 responseMap["connect"] = OSD.FromBoolean(false);
940 lock (m_blacklistedregions)
941 {
942 if (!m_blacklistedregions.ContainsKey(regionhandle))
943 m_blacklistedregions.Add(regionhandle, Environment.TickCount);
944 }
945  
946 return responseMap;
947 }
948  
949 OSD rezResponse = null;
950 try
951 {
952 rezResponse = OSDParser.DeserializeLLSDXml(response_mapItems_reply);
953  
954 responseMap = (OSDMap)rezResponse;
955 responseMap["requestID"] = OSD.FromUUID(requestID);
956 }
957 catch (Exception ex)
958 {
959 m_log.InfoFormat("[WORLD MAP]: exception on parse of RequestMapItems reply from {0}: {1}", httpserver, ex.Message);
960 responseMap["connect"] = OSD.FromBoolean(false);
961  
962 lock (m_blacklistedregions)
963 {
964 if (!m_blacklistedregions.ContainsKey(regionhandle))
965 m_blacklistedregions.Add(regionhandle, Environment.TickCount);
966 }
967  
968 return responseMap;
969 }
970 }
971  
972 if (!responseMap.ContainsKey(itemtype.ToString())) // remote sim doesnt have the stated region handle
973 {
974 m_log.DebugFormat("[WORLD MAP]: Remote sim does not have the stated region. Blacklisting.");
975 lock (m_blacklistedregions)
976 {
977 if (!m_blacklistedregions.ContainsKey(regionhandle))
978 m_blacklistedregions.Add(regionhandle, Environment.TickCount);
979 }
980 }
981  
982 return responseMap;
983 }
984  
985 /// <summary>
986 /// Requests map blocks in area of minX, maxX, minY, MaxY in world cordinates
987 /// </summary>
988 /// <param name="minX"></param>
989 /// <param name="minY"></param>
990 /// <param name="maxX"></param>
991 /// <param name="maxY"></param>
992 public virtual void RequestMapBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag)
993 {
994 if ((flag & 0x10000) != 0) // user clicked on qthe map a tile that isn't visible
995 {
996 List<MapBlockData> response = new List<MapBlockData>();
997  
998 // this should return one mapblock at most. It is triggered by a click
999 // on an unloaded square.
1000 // But make sure: Look whether the one we requested is in there
1001 List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID,
1002 (int)Util.RegionToWorldLoc((uint)minX), (int)Util.RegionToWorldLoc((uint)maxX),
1003 (int)Util.RegionToWorldLoc((uint)minY), (int)Util.RegionToWorldLoc((uint)maxY) );
1004  
1005 m_log.DebugFormat("[WORLD MAP MODULE] RequestMapBlocks min=<{0},{1}>, max=<{2},{3}>, flag={4}, cntFound={5}",
1006 minX, minY, maxX, maxY, flag.ToString("X"), regions.Count);
1007 if (regions != null)
1008 {
1009 foreach (GridRegion r in regions)
1010 {
1011 if (r.RegionLocX == Util.RegionToWorldLoc((uint)minX)
1012 && r.RegionLocY == Util.RegionToWorldLoc((uint)minY) )
1013 {
1014 // found it => add it to response
1015 // Version 2 viewers can handle the larger regions
1016 if ((flag & 2) == 2)
1017 response.AddRange(Map2BlockFromGridRegion(r, flag));
1018 else
1019 response.Add(MapBlockFromGridRegion(r, flag));
1020 break;
1021 }
1022 }
1023 }
1024  
1025 if (response.Count == 0)
1026 {
1027 // response still empty => couldn't find the map-tile the user clicked on => tell the client
1028 MapBlockData block = new MapBlockData();
1029 block.X = (ushort)minX;
1030 block.Y = (ushort)minY;
1031 block.Access = (byte)SimAccess.Down; // means 'simulator is offline'
1032 // block.Access = (byte)SimAccess.NonExistant;
1033 response.Add(block);
1034 }
1035 // The lower 16 bits are an unsigned int16
1036 remoteClient.SendMapBlock(response, flag & 0xffff);
1037 }
1038 else
1039 {
1040 // normal mapblock request. Use the provided values
1041 GetAndSendBlocks(remoteClient, minX, minY, maxX, maxY, flag);
1042 }
1043 }
1044  
1045 protected virtual List<MapBlockData> GetAndSendBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag)
1046 {
1047 List<MapBlockData> mapBlocks = new List<MapBlockData>();
1048 List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID,
1049 (int)Util.RegionToWorldLoc((uint)(minX - 4)), (int)Util.RegionToWorldLoc((uint)(maxX + 4)),
1050 (int)Util.RegionToWorldLoc((uint)(minY - 4)), (int)Util.RegionToWorldLoc((uint)(maxY + 4)) );
1051 m_log.DebugFormat("{0} GetAndSendBlocks. min=<{1},{2}>, max=<{3},{4}>, cntFound={5}",
1052 LogHeader, minX, minY, maxX, maxY, regions.Count);
1053 foreach (GridRegion r in regions)
1054 {
1055 // Version 2 viewers can handle the larger regions
1056 if ((flag & 2) == 2)
1057 mapBlocks.AddRange(Map2BlockFromGridRegion(r, flag));
1058 else
1059 mapBlocks.Add(MapBlockFromGridRegion(r, flag));
1060 }
1061 remoteClient.SendMapBlock(mapBlocks, flag & 0xffff);
1062  
1063 return mapBlocks;
1064 }
1065  
1066 // Fill a passed MapBlockData from a GridRegion
1067 protected MapBlockData MapBlockFromGridRegion(GridRegion r, uint flag)
1068 {
1069 MapBlockData block = new MapBlockData();
1070  
1071 block.Access = r.Access;
1072 switch (flag & 0xffff)
1073 {
1074 case 0:
1075 block.MapImageId = r.TerrainImage;
1076 break;
1077 case 2:
1078 block.MapImageId = r.ParcelImage;
1079 break;
1080 default:
1081 block.MapImageId = UUID.Zero;
1082 break;
1083 }
1084 block.Name = r.RegionName;
1085 block.X = (ushort)Util.WorldToRegionLoc((uint)r.RegionLocX);
1086 block.Y = (ushort)Util.WorldToRegionLoc((uint)r.RegionLocY);
1087 block.SizeX = (ushort) r.RegionSizeX;
1088 block.SizeY = (ushort) r.RegionSizeY;
1089  
1090 return block;
1091 }
1092  
1093 protected List<MapBlockData> Map2BlockFromGridRegion(GridRegion r, uint flag)
1094 {
1095 List<MapBlockData> blocks = new List<MapBlockData>();
1096 MapBlockData block = new MapBlockData();
1097 if (r == null)
1098 {
1099 block.Access = (byte)SimAccess.Down;
1100 block.MapImageId = UUID.Zero;
1101 blocks.Add(block);
1102 }
1103 else
1104 {
1105 block.Access = r.Access;
1106 switch (flag & 0xffff)
1107 {
1108 case 0:
1109 block.MapImageId = r.TerrainImage;
1110 break;
1111 case 2:
1112 block.MapImageId = r.ParcelImage;
1113 break;
1114 default:
1115 block.MapImageId = UUID.Zero;
1116 break;
1117 }
1118 block.Name = r.RegionName;
1119 block.X = (ushort)(r.RegionLocX / Constants.RegionSize);
1120 block.Y = (ushort)(r.RegionLocY / Constants.RegionSize);
1121 block.SizeX = (ushort)r.RegionSizeX;
1122 block.SizeY = (ushort)r.RegionSizeY;
1123 blocks.Add(block);
1124 // If these are larger than legacy regions, create fake map entries for the covered
1125 // regions. The map system only does legacy sized regions so we have to fake map
1126 // entries for all the covered regions.
1127 if (r.RegionSizeX > Constants.RegionSize || r.RegionSizeY > Constants.RegionSize)
1128 {
1129 for (int x = 0; x < r.RegionSizeX / Constants.RegionSize; x++)
1130 {
1131 for (int y = 0; y < r.RegionSizeY / Constants.RegionSize; y++)
1132 {
1133 if (x == 0 && y == 0)
1134 continue;
1135 block = new MapBlockData
1136 {
1137 Access = r.Access,
1138 MapImageId = r.TerrainImage,
1139 Name = r.RegionName,
1140 X = (ushort)((r.RegionLocX / Constants.RegionSize) + x),
1141 Y = (ushort)((r.RegionLocY / Constants.RegionSize) + y),
1142 SizeX = (ushort)r.RegionSizeX,
1143 SizeY = (ushort)r.RegionSizeY
1144 };
1145 //Child piece, so ignore it
1146 blocks.Add(block);
1147 }
1148 }
1149 }
1150 }
1151 return blocks;
1152 }
1153  
1154  
1155 public Hashtable OnHTTPThrottled(Hashtable keysvals)
1156 {
1157 Hashtable reply = new Hashtable();
1158 int statuscode = 500;
1159 reply["str_response_string"] = "";
1160 reply["int_response_code"] = statuscode;
1161 reply["content_type"] = "text/plain";
1162 return reply;
1163 }
1164  
1165 public Hashtable OnHTTPGetMapImage(Hashtable keysvals)
1166 {
1167 m_log.Debug("[WORLD MAP]: Sending map image jpeg");
1168 Hashtable reply = new Hashtable();
1169 int statuscode = 200;
1170 byte[] jpeg = new byte[0];
1171  
1172 if (myMapImageJPEG.Length == 0)
1173 {
1174 MemoryStream imgstream = new MemoryStream();
1175 Bitmap mapTexture = new Bitmap(1,1);
1176 ManagedImage managedImage;
1177 Image image = (Image)mapTexture;
1178  
1179 try
1180 {
1181 // Taking our jpeg2000 data, decoding it, then saving it to a byte array with regular jpeg data
1182  
1183 imgstream = new MemoryStream();
1184  
1185 // non-async because we know we have the asset immediately.
1186 AssetBase mapasset = m_scene.AssetService.Get(m_scene.RegionInfo.RegionSettings.TerrainImageID.ToString());
1187  
1188 // Decode image to System.Drawing.Image
1189 if (OpenJPEG.DecodeToImage(mapasset.Data, out managedImage, out image))
1190 {
1191 // Save to bitmap
1192 mapTexture = new Bitmap(image);
1193  
1194 EncoderParameters myEncoderParameters = new EncoderParameters();
1195 myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 95L);
1196  
1197 // Save bitmap to stream
1198 mapTexture.Save(imgstream, GetEncoderInfo("image/jpeg"), myEncoderParameters);
1199  
1200 // Write the stream to a byte array for output
1201 jpeg = imgstream.ToArray();
1202 myMapImageJPEG = jpeg;
1203 }
1204 }
1205 catch (Exception)
1206 {
1207 // Dummy!
1208 m_log.Warn("[WORLD MAP]: Unable to generate Map image");
1209 }
1210 finally
1211 {
1212 // Reclaim memory, these are unmanaged resources
1213 // If we encountered an exception, one or more of these will be null
1214 if (mapTexture != null)
1215 mapTexture.Dispose();
1216  
1217 if (image != null)
1218 image.Dispose();
1219  
1220 if (imgstream != null)
1221 {
1222 imgstream.Close();
1223 imgstream.Dispose();
1224 }
1225 }
1226 }
1227 else
1228 {
1229 // Use cached version so we don't have to loose our mind
1230 jpeg = myMapImageJPEG;
1231 }
1232  
1233 reply["str_response_string"] = Convert.ToBase64String(jpeg);
1234 reply["int_response_code"] = statuscode;
1235 reply["content_type"] = "image/jpeg";
1236  
1237 return reply;
1238 }
1239  
1240 // From msdn
1241 private static ImageCodecInfo GetEncoderInfo(String mimeType)
1242 {
1243 ImageCodecInfo[] encoders;
1244 encoders = ImageCodecInfo.GetImageEncoders();
1245 for (int j = 0; j < encoders.Length; ++j)
1246 {
1247 if (encoders[j].MimeType == mimeType)
1248 return encoders[j];
1249 }
1250 return null;
1251 }
1252  
1253 /// <summary>
1254 /// Export the world map
1255 /// </summary>
1256 /// <param name="fileName"></param>
1257 public void HandleExportWorldMapConsoleCommand(string module, string[] cmdparams)
1258 {
1259 if (m_scene.ConsoleScene() == null)
1260 {
1261 // FIXME: If console region is root then this will be printed by every module. Currently, there is no
1262 // way to prevent this, short of making the entire module shared (which is complete overkill).
1263 // One possibility is to return a bool to signal whether the module has completely handled the command
1264 m_log.InfoFormat("[WORLD MAP]: Please change to a specific region in order to export its world map");
1265 return;
1266 }
1267  
1268 if (m_scene.ConsoleScene() != m_scene)
1269 return;
1270  
1271 string exportPath;
1272  
1273 if (cmdparams.Length > 1)
1274 exportPath = cmdparams[1];
1275 else
1276 exportPath = DEFAULT_WORLD_MAP_EXPORT_PATH;
1277  
1278 m_log.InfoFormat(
1279 "[WORLD MAP]: Exporting world map for {0} to {1}", m_scene.RegionInfo.RegionName, exportPath);
1280  
1281 List<MapBlockData> mapBlocks = new List<MapBlockData>();
1282 List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID,
1283 (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocX - 9),
1284 (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocX + 9),
1285 (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocY - 9),
1286 (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocY + 9));
1287 List<AssetBase> textures = new List<AssetBase>();
1288 List<Image> bitImages = new List<Image>();
1289  
1290 foreach (GridRegion r in regions)
1291 {
1292 MapBlockData mapBlock = MapBlockFromGridRegion(r, 0);
1293 AssetBase texAsset = m_scene.AssetService.Get(mapBlock.MapImageId.ToString());
1294  
1295 if (texAsset != null)
1296 {
1297 textures.Add(texAsset);
1298 }
1299 //else
1300 //{
1301 // // WHAT?!? This doesn't seem right. Commenting (diva)
1302 // texAsset = m_scene.AssetService.Get(mapBlock.MapImageId.ToString());
1303 // if (texAsset != null)
1304 // {
1305 // textures.Add(texAsset);
1306 // }
1307 //}
1308 }
1309  
1310 foreach (AssetBase asset in textures)
1311 {
1312 ManagedImage managedImage;
1313 Image image;
1314  
1315 if (OpenJPEG.DecodeToImage(asset.Data, out managedImage, out image))
1316 bitImages.Add(image);
1317 }
1318  
1319 Bitmap mapTexture = new Bitmap(2560, 2560);
1320 Graphics g = Graphics.FromImage(mapTexture);
1321 SolidBrush sea = new SolidBrush(Color.DarkBlue);
1322 g.FillRectangle(sea, 0, 0, 2560, 2560);
1323  
1324 for (int i = 0; i < mapBlocks.Count; i++)
1325 {
1326 ushort x = (ushort)((mapBlocks[i].X - m_scene.RegionInfo.RegionLocX) + 10);
1327 ushort y = (ushort)((mapBlocks[i].Y - m_scene.RegionInfo.RegionLocY) + 10);
1328 g.DrawImage(bitImages[i], (x * 128), 2560 - (y * 128), 128, 128); // y origin is top
1329 }
1330  
1331 mapTexture.Save(exportPath, ImageFormat.Jpeg);
1332  
1333 m_log.InfoFormat(
1334 "[WORLD MAP]: Successfully exported world map for {0} to {1}",
1335 m_scene.RegionInfo.RegionName, exportPath);
1336 }
1337  
1338 public void HandleGenerateMapConsoleCommand(string module, string[] cmdparams)
1339 {
1340 Scene consoleScene = m_scene.ConsoleScene();
1341  
1342 if (consoleScene != null && consoleScene != m_scene)
1343 return;
1344  
1345 GenerateMaptile();
1346 }
1347  
1348 public OSD HandleRemoteMapItemRequest(string path, OSD request, string endpoint)
1349 {
1350 uint xstart = 0;
1351 uint ystart = 0;
1352  
1353 Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle,out xstart,out ystart);
1354 // m_log.DebugFormat("{0} HandleRemoteMapItemRequest. loc=<{1},{2}>",
1355 // LogHeader, Util.WorldToRegionLoc(xstart), Util.WorldToRegionLoc(ystart));
1356  
1357 // Service 6 (MAP_ITEM_AGENTS_LOCATION; green dots)
1358  
1359 OSDMap responsemap = new OSDMap();
1360 int tc = Environment.TickCount;
1361 if (m_scene.GetRootAgentCount() == 0)
1362 {
1363 OSDMap responsemapdata = new OSDMap();
1364 responsemapdata["X"] = OSD.FromInteger((int)(xstart + 1));
1365 responsemapdata["Y"] = OSD.FromInteger((int)(ystart + 1));
1366 responsemapdata["ID"] = OSD.FromUUID(UUID.Zero);
1367 responsemapdata["Name"] = OSD.FromString(Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()));
1368 responsemapdata["Extra"] = OSD.FromInteger(0);
1369 responsemapdata["Extra2"] = OSD.FromInteger(0);
1370 OSDArray responsearr = new OSDArray();
1371 responsearr.Add(responsemapdata);
1372  
1373 responsemap["6"] = responsearr;
1374 }
1375 else
1376 {
1377 OSDArray responsearr = new OSDArray(m_scene.GetRootAgentCount());
1378 m_scene.ForEachRootScenePresence(delegate(ScenePresence sp)
1379 {
1380 OSDMap responsemapdata = new OSDMap();
1381 responsemapdata["X"] = OSD.FromInteger((int)(xstart + sp.AbsolutePosition.X));
1382 responsemapdata["Y"] = OSD.FromInteger((int)(ystart + sp.AbsolutePosition.Y));
1383 responsemapdata["ID"] = OSD.FromUUID(UUID.Zero);
1384 responsemapdata["Name"] = OSD.FromString(Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()));
1385 responsemapdata["Extra"] = OSD.FromInteger(1);
1386 responsemapdata["Extra2"] = OSD.FromInteger(0);
1387 responsearr.Add(responsemapdata);
1388 });
1389 responsemap["6"] = responsearr;
1390 }
1391  
1392 // Service 7 (MAP_ITEM_LAND_FOR_SALE)
1393  
1394 ILandChannel landChannel = m_scene.LandChannel;
1395 List<ILandObject> parcels = landChannel.AllParcels();
1396  
1397 if ((parcels == null) || (parcels.Count == 0))
1398 {
1399 OSDMap responsemapdata = new OSDMap();
1400 responsemapdata["X"] = OSD.FromInteger((int)(xstart + 1));
1401 responsemapdata["Y"] = OSD.FromInteger((int)(ystart + 1));
1402 responsemapdata["ID"] = OSD.FromUUID(UUID.Zero);
1403 responsemapdata["Name"] = OSD.FromString("");
1404 responsemapdata["Extra"] = OSD.FromInteger(0);
1405 responsemapdata["Extra2"] = OSD.FromInteger(0);
1406 OSDArray responsearr = new OSDArray();
1407 responsearr.Add(responsemapdata);
1408  
1409 responsemap["7"] = responsearr;
1410 }
1411 else
1412 {
1413 OSDArray responsearr = new OSDArray(m_scene.GetRootAgentCount());
1414 foreach (ILandObject parcel_interface in parcels)
1415 {
1416 // Play it safe
1417 if (!(parcel_interface is LandObject))
1418 continue;
1419  
1420 LandObject land = (LandObject)parcel_interface;
1421 LandData parcel = land.LandData;
1422  
1423 // Show land for sale
1424 if ((parcel.Flags & (uint)ParcelFlags.ForSale) == (uint)ParcelFlags.ForSale)
1425 {
1426 Vector3 min = parcel.AABBMin;
1427 Vector3 max = parcel.AABBMax;
1428 float x = (min.X+max.X)/2;
1429 float y = (min.Y+max.Y)/2;
1430  
1431 OSDMap responsemapdata = new OSDMap();
1432 responsemapdata["X"] = OSD.FromInteger((int)(xstart + x));
1433 responsemapdata["Y"] = OSD.FromInteger((int)(ystart + y));
1434 // responsemapdata["Z"] = OSD.FromInteger((int)m_scene.GetGroundHeight(x,y));
1435 responsemapdata["ID"] = OSD.FromUUID(parcel.GlobalID);
1436 responsemapdata["Name"] = OSD.FromString(parcel.Name);
1437 responsemapdata["Extra"] = OSD.FromInteger(parcel.Area);
1438 responsemapdata["Extra2"] = OSD.FromInteger(parcel.SalePrice);
1439 responsearr.Add(responsemapdata);
1440 }
1441 }
1442 responsemap["7"] = responsearr;
1443 }
1444  
1445 if (m_scene.RegionInfo.RegionSettings.TelehubObject != UUID.Zero)
1446 {
1447 SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject);
1448 if (sog != null)
1449 {
1450 OSDArray responsearr = new OSDArray();
1451 OSDMap responsemapdata = new OSDMap();
1452 responsemapdata["X"] = OSD.FromInteger((int)(xstart + sog.AbsolutePosition.X));
1453 responsemapdata["Y"] = OSD.FromInteger((int)(ystart + sog.AbsolutePosition.Y));
1454 // responsemapdata["Z"] = OSD.FromInteger((int)m_scene.GetGroundHeight(x,y));
1455 responsemapdata["ID"] = OSD.FromUUID(sog.UUID);
1456 responsemapdata["Name"] = OSD.FromString(sog.Name);
1457 responsemapdata["Extra"] = OSD.FromInteger(0); // color (unused)
1458 responsemapdata["Extra2"] = OSD.FromInteger(0); // 0 = telehub / 1 = infohub
1459 responsearr.Add(responsemapdata);
1460  
1461 responsemap["1"] = responsearr;
1462 }
1463 }
1464  
1465 return responsemap;
1466 }
1467  
1468 public void GenerateMaptile()
1469 {
1470 // Cannot create a map for a nonexistant heightmap
1471 if (m_scene.Heightmap == null)
1472 return;
1473  
1474 //create a texture asset of the terrain
1475 IMapImageGenerator terrain = m_scene.RequestModuleInterface<IMapImageGenerator>();
1476 if (terrain == null)
1477 return;
1478  
1479 m_log.DebugFormat("[WORLD MAP]: Generating map image for {0}", m_scene.RegionInfo.RegionName);
1480  
1481 byte[] data = terrain.WriteJpeg2000Image();
1482 if (data == null)
1483 return;
1484  
1485 byte[] overlay = GenerateOverlay();
1486  
1487 UUID terrainImageID = UUID.Random();
1488 UUID parcelImageID = UUID.Zero;
1489  
1490 AssetBase asset = new AssetBase(
1491 terrainImageID,
1492 "terrainImage_" + m_scene.RegionInfo.RegionID.ToString(),
1493 (sbyte)AssetType.Texture,
1494 m_scene.RegionInfo.RegionID.ToString());
1495 asset.Data = data;
1496 asset.Description = m_scene.RegionInfo.RegionName;
1497 asset.Temporary = false;
1498 asset.Flags = AssetFlags.Maptile;
1499  
1500 // Store the new one
1501 m_log.DebugFormat("[WORLD MAP]: Storing map tile {0} for {1}", asset.ID, m_scene.RegionInfo.RegionName);
1502  
1503 m_scene.AssetService.Store(asset);
1504  
1505 if (overlay != null)
1506 {
1507 parcelImageID = UUID.Random();
1508  
1509 AssetBase parcels = new AssetBase(
1510 parcelImageID,
1511 "parcelImage_" + m_scene.RegionInfo.RegionID.ToString(),
1512 (sbyte)AssetType.Texture,
1513 m_scene.RegionInfo.RegionID.ToString());
1514 parcels.Data = overlay;
1515 parcels.Description = m_scene.RegionInfo.RegionName;
1516 parcels.Temporary = false;
1517 parcels.Flags = AssetFlags.Maptile;
1518  
1519 m_scene.AssetService.Store(parcels);
1520 }
1521  
1522 // Switch to the new one
1523 UUID lastTerrainImageID = m_scene.RegionInfo.RegionSettings.TerrainImageID;
1524 UUID lastParcelImageID = m_scene.RegionInfo.RegionSettings.ParcelImageID;
1525 m_scene.RegionInfo.RegionSettings.TerrainImageID = terrainImageID;
1526 m_scene.RegionInfo.RegionSettings.ParcelImageID = parcelImageID;
1527 m_scene.RegionInfo.RegionSettings.Save();
1528  
1529 // Delete the old one
1530 // m_log.DebugFormat("[WORLDMAP]: Deleting old map tile {0}", lastTerrainImageID);
1531 m_scene.AssetService.Delete(lastTerrainImageID.ToString());
1532 if (lastParcelImageID != UUID.Zero)
1533 m_scene.AssetService.Delete(lastParcelImageID.ToString());
1534 }
1535  
1536 private void MakeRootAgent(ScenePresence avatar)
1537 {
1538 lock (m_rootAgents)
1539 {
1540 if (!m_rootAgents.Contains(avatar.UUID))
1541 {
1542 m_rootAgents.Add(avatar.UUID);
1543 }
1544 }
1545 }
1546  
1547 private void MakeChildAgent(ScenePresence avatar)
1548 {
1549 lock (m_rootAgents)
1550 {
1551 m_rootAgents.Remove(avatar.UUID);
1552 }
1553 }
1554  
1555 public void OnRegionUp(GridRegion otherRegion)
1556 {
1557 ulong regionhandle = otherRegion.RegionHandle;
1558 string httpserver = otherRegion.ServerURI + "MAP/MapItems/" + regionhandle.ToString();
1559  
1560 lock (m_blacklistedregions)
1561 {
1562 if (!m_blacklistedregions.ContainsKey(regionhandle))
1563 m_blacklistedregions.Remove(regionhandle);
1564 }
1565  
1566 lock (m_blacklistedurls)
1567 {
1568 if (m_blacklistedurls.ContainsKey(httpserver))
1569 m_blacklistedurls.Remove(httpserver);
1570 }
1571  
1572 lock (m_cachedRegionMapItemsAddress)
1573 {
1574 if (!m_cachedRegionMapItemsAddress.ContainsKey(regionhandle))
1575 m_cachedRegionMapItemsAddress.Remove(regionhandle);
1576 }
1577 }
1578  
1579 private Byte[] GenerateOverlay()
1580 {
1581 using (Bitmap overlay = new Bitmap(256, 256))
1582 {
1583 bool[,] saleBitmap = new bool[64, 64];
1584 for (int x = 0 ; x < 64 ; x++)
1585 {
1586 for (int y = 0 ; y < 64 ; y++)
1587 saleBitmap[x, y] = false;
1588 }
1589  
1590 bool landForSale = false;
1591  
1592 List<ILandObject> parcels = m_scene.LandChannel.AllParcels();
1593  
1594 Color background = Color.FromArgb(0, 0, 0, 0);
1595  
1596 using (Graphics g = Graphics.FromImage(overlay))
1597 {
1598 using (SolidBrush transparent = new SolidBrush(background))
1599 g.FillRectangle(transparent, 0, 0, 256, 256);
1600  
1601  
1602 foreach (ILandObject land in parcels)
1603 {
1604 // m_log.DebugFormat("[WORLD MAP]: Parcel {0} flags {1}", land.LandData.Name, land.LandData.Flags);
1605 if ((land.LandData.Flags & (uint)ParcelFlags.ForSale) != 0)
1606 {
1607 landForSale = true;
1608  
1609 saleBitmap = land.MergeLandBitmaps(saleBitmap, land.GetLandBitmap());
1610 }
1611 }
1612  
1613 if (!landForSale)
1614 {
1615 m_log.DebugFormat("[WORLD MAP]: Region {0} has no parcels for sale, not generating overlay", m_scene.RegionInfo.RegionName);
1616 return null;
1617 }
1618  
1619 m_log.DebugFormat("[WORLD MAP]: Region {0} has parcels for sale, generating overlay", m_scene.RegionInfo.RegionName);
1620  
1621 using (SolidBrush yellow = new SolidBrush(Color.FromArgb(255, 249, 223, 9)))
1622 {
1623 for (int x = 0 ; x < 64 ; x++)
1624 {
1625 for (int y = 0 ; y < 64 ; y++)
1626 {
1627 if (saleBitmap[x, y])
1628 g.FillRectangle(yellow, x * 4, 252 - (y * 4), 4, 4);
1629 }
1630 }
1631 }
1632 }
1633  
1634 try
1635 {
1636 return OpenJPEG.EncodeFromImage(overlay, true);
1637 }
1638 catch (Exception e)
1639 {
1640 m_log.DebugFormat("[WORLD MAP]: Error creating parcel overlay: " + e.ToString());
1641 }
1642 }
1643  
1644 return null;
1645 }
1646 }
1647  
1648 public struct MapRequestState
1649 {
1650 public UUID agentID;
1651 public uint flags;
1652 public uint EstateID;
1653 public bool godlike;
1654 public uint itemtype;
1655 public ulong regionhandle;
1656 }
1657 }