clockwerk-opensim – Blame information for rev 1

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