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