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