clockwerk-opensim-stable – Blame information for rev 1
?pathlinks?
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 | } |