diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs')
-rw-r--r-- | OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs | 1579 |
1 files changed, 883 insertions, 696 deletions
diff --git a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs index db1187e..03a4d34 100644 --- a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs +++ b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs | |||
@@ -66,32 +66,34 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
66 | 66 | ||
67 | private static readonly string DEFAULT_WORLD_MAP_EXPORT_PATH = "exportmap.jpg"; | 67 | private static readonly string DEFAULT_WORLD_MAP_EXPORT_PATH = "exportmap.jpg"; |
68 | private static readonly UUID STOP_UUID = UUID.Random(); | 68 | private static readonly UUID STOP_UUID = UUID.Random(); |
69 | private static readonly string m_mapLayerPath = "0001/"; | 69 | |
70 | private OpenSim.Framework.BlockingQueue<MapRequestState> requests = new OpenSim.Framework.BlockingQueue<MapRequestState>(); | ||
71 | |||
72 | private ManualResetEvent m_mapBlockRequestEvent = new ManualResetEvent(false); | ||
73 | private Dictionary<UUID, Queue<MapBlockRequestData>> m_mapBlockRequests = new Dictionary<UUID, Queue<MapBlockRequestData>>(); | ||
70 | 74 | ||
71 | private IMapImageGenerator m_mapImageGenerator; | 75 | private IMapImageGenerator m_mapImageGenerator; |
72 | private IMapImageUploadModule m_mapImageServiceModule; | 76 | private IMapImageUploadModule m_mapImageServiceModule; |
73 | 77 | ||
74 | private OpenSim.Framework.BlockingQueue<MapRequestState> requests = new OpenSim.Framework.BlockingQueue<MapRequestState>(); | ||
75 | |||
76 | protected Scene m_scene; | 78 | protected Scene m_scene; |
77 | private List<MapBlockData> cachedMapBlocks = new List<MapBlockData>(); | 79 | private List<MapBlockData> cachedMapBlocks = new List<MapBlockData>(); |
78 | private int cachedTime = 0; | ||
79 | private int blacklistTimeout = 10*60*1000; // 10 minutes | ||
80 | private byte[] myMapImageJPEG; | 80 | private byte[] myMapImageJPEG; |
81 | protected volatile bool m_Enabled = false; | 81 | protected volatile bool m_Enabled = false; |
82 | private Dictionary<UUID, MapRequestState> m_openRequests = new Dictionary<UUID, MapRequestState>(); | 82 | private ExpiringCache<string, int> m_blacklistedurls = new ExpiringCache<string, int>(); |
83 | private Dictionary<string, int> m_blacklistedurls = new Dictionary<string, int>(); | 83 | private ExpiringCache<ulong, int> m_blacklistedregions = new ExpiringCache<ulong, int>(); |
84 | private Dictionary<ulong, int> m_blacklistedregions = new Dictionary<ulong, int>(); | 84 | private ExpiringCache<ulong, string> m_cachedRegionMapItemsAddress = new ExpiringCache<ulong, string>(); |
85 | private Dictionary<ulong, string> m_cachedRegionMapItemsAddress = new Dictionary<ulong, string>(); | 85 | private ExpiringCache<ulong, OSDMap> m_cachedRegionMapItemsResponses = |
86 | new ExpiringCache<ulong, OSDMap>(); | ||
86 | private List<UUID> m_rootAgents = new List<UUID>(); | 87 | private List<UUID> m_rootAgents = new List<UUID>(); |
87 | private volatile bool threadrunning = false; | 88 | private volatile bool threadrunning = false; |
88 | 89 | // expire time for the blacklists in seconds | |
89 | private IServiceThrottleModule m_ServiceThrottle; | 90 | private double expireBlackListTime = 600.0; // 10 minutes |
90 | 91 | // expire mapItems responses time in seconds. Throttles requests to regions that do answer | |
92 | private const double expireResponsesTime = 120.0; // 2 minutes ? | ||
91 | //private int CacheRegionsDistance = 256; | 93 | //private int CacheRegionsDistance = 256; |
92 | 94 | ||
93 | #region INonSharedRegionModule Members | 95 | #region INonSharedRegionModule Members |
94 | public virtual void Initialise (IConfigSource config) | 96 | public virtual void Initialise(IConfigSource config) |
95 | { | 97 | { |
96 | string[] configSections = new string[] { "Map", "Startup" }; | 98 | string[] configSections = new string[] { "Map", "Startup" }; |
97 | 99 | ||
@@ -99,8 +101,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
99 | config, "WorldMapModule", configSections, "WorldMap") == "WorldMap") | 101 | config, "WorldMapModule", configSections, "WorldMap") == "WorldMap") |
100 | m_Enabled = true; | 102 | m_Enabled = true; |
101 | 103 | ||
102 | blacklistTimeout | 104 | expireBlackListTime = (double)Util.GetConfigVarFromSections<int>(config, "BlacklistTimeout", configSections, 10 * 60); |
103 | = Util.GetConfigVarFromSections<int>(config, "BlacklistTimeout", configSections, 10 * 60) * 1000; | ||
104 | } | 105 | } |
105 | 106 | ||
106 | public virtual void AddRegion(Scene scene) | 107 | public virtual void AddRegion(Scene scene) |
@@ -128,7 +129,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
128 | } | 129 | } |
129 | } | 130 | } |
130 | 131 | ||
131 | public virtual void RemoveRegion (Scene scene) | 132 | public virtual void RemoveRegion(Scene scene) |
132 | { | 133 | { |
133 | if (!m_Enabled) | 134 | if (!m_Enabled) |
134 | return; | 135 | return; |
@@ -141,13 +142,11 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
141 | } | 142 | } |
142 | } | 143 | } |
143 | 144 | ||
144 | public virtual void RegionLoaded (Scene scene) | 145 | public virtual void RegionLoaded(Scene scene) |
145 | { | 146 | { |
146 | if (!m_Enabled) | 147 | if (!m_Enabled) |
147 | return; | 148 | return; |
148 | 149 | ||
149 | m_ServiceThrottle = scene.RequestModuleInterface<IServiceThrottleModule>(); | ||
150 | |||
151 | m_mapImageGenerator = m_scene.RequestModuleInterface<IMapImageGenerator>(); | 150 | m_mapImageGenerator = m_scene.RequestModuleInterface<IMapImageGenerator>(); |
152 | m_mapImageServiceModule = m_scene.RequestModuleInterface<IMapImageUploadModule>(); | 151 | m_mapImageServiceModule = m_scene.RequestModuleInterface<IMapImageUploadModule>(); |
153 | } | 152 | } |
@@ -177,16 +176,20 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
177 | regionimage = regionimage.Replace("-", ""); | 176 | regionimage = regionimage.Replace("-", ""); |
178 | m_log.Info("[WORLD MAP]: JPEG Map location: " + m_scene.RegionInfo.ServerURI + "index.php?method=" + regionimage); | 177 | m_log.Info("[WORLD MAP]: JPEG Map location: " + m_scene.RegionInfo.ServerURI + "index.php?method=" + regionimage); |
179 | 178 | ||
179 | /* | ||
180 | MainServer.Instance.AddHTTPHandler(regionimage, | 180 | MainServer.Instance.AddHTTPHandler(regionimage, |
181 | new GenericHTTPDOSProtector(OnHTTPGetMapImage, OnHTTPThrottled, new BasicDosProtectorOptions() | 181 | new GenericHTTPDOSProtector(OnHTTPGetMapImage, OnHTTPThrottled, new BasicDosProtectorOptions() |
182 | { | 182 | { |
183 | AllowXForwardedFor = false, | 183 | AllowXForwardedFor = false, |
184 | ForgetTimeSpan = TimeSpan.FromMinutes(2), | 184 | ForgetTimeSpan = TimeSpan.FromMinutes(2), |
185 | MaxRequestsInTimeframe = 4, | 185 | MaxRequestsInTimeframe = 4, |
186 | ReportingName = "MAPDOSPROTECTOR", | 186 | ReportingName = "MAPDOSPROTECTOR", |
187 | RequestTimeSpan = TimeSpan.FromSeconds(10), | 187 | RequestTimeSpan = TimeSpan.FromSeconds(10), |
188 | ThrottledAction = BasicDOSProtector.ThrottleAction.DoThrottledMethod | 188 | ThrottledAction = BasicDOSProtector.ThrottleAction.DoThrottledMethod |
189 | }).Process); | 189 | }).Process); |
190 | */ | ||
191 | |||
192 | MainServer.Instance.AddHTTPHandler(regionimage, OnHTTPGetMapImage); | ||
190 | MainServer.Instance.AddLLSDHandler( | 193 | MainServer.Instance.AddLLSDHandler( |
191 | "/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString(), HandleRemoteMapItemRequest); | 194 | "/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString(), HandleRemoteMapItemRequest); |
192 | 195 | ||
@@ -197,13 +200,13 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
197 | m_scene.EventManager.OnMakeRootAgent += MakeRootAgent; | 200 | m_scene.EventManager.OnMakeRootAgent += MakeRootAgent; |
198 | m_scene.EventManager.OnRegionUp += OnRegionUp; | 201 | m_scene.EventManager.OnRegionUp += OnRegionUp; |
199 | 202 | ||
200 | // StartThread(new object()); | 203 | StartThread(new object()); |
201 | } | 204 | } |
202 | 205 | ||
203 | // this has to be called with a lock on m_scene | 206 | // this has to be called with a lock on m_scene |
204 | protected virtual void RemoveHandlers() | 207 | protected virtual void RemoveHandlers() |
205 | { | 208 | { |
206 | // StopThread(); | 209 | StopThread(); |
207 | 210 | ||
208 | m_scene.EventManager.OnRegionUp -= OnRegionUp; | 211 | m_scene.EventManager.OnRegionUp -= OnRegionUp; |
209 | m_scene.EventManager.OnMakeRootAgent -= MakeRootAgent; | 212 | m_scene.EventManager.OnMakeRootAgent -= MakeRootAgent; |
@@ -212,6 +215,8 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
212 | m_scene.EventManager.OnNewClient -= OnNewClient; | 215 | m_scene.EventManager.OnNewClient -= OnNewClient; |
213 | m_scene.EventManager.OnRegisterCaps -= OnRegisterCaps; | 216 | m_scene.EventManager.OnRegisterCaps -= OnRegisterCaps; |
214 | 217 | ||
218 | m_scene.UnregisterModuleInterface<IWorldMapModule>(this); | ||
219 | |||
215 | string regionimage = "regionImage" + m_scene.RegionInfo.RegionID.ToString(); | 220 | string regionimage = "regionImage" + m_scene.RegionInfo.RegionID.ToString(); |
216 | regionimage = regionimage.Replace("-", ""); | 221 | regionimage = regionimage.Replace("-", ""); |
217 | MainServer.Instance.RemoveLLSDHandler("/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString(), | 222 | MainServer.Instance.RemoveLLSDHandler("/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString(), |
@@ -222,12 +227,12 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
222 | public void OnRegisterCaps(UUID agentID, Caps caps) | 227 | public void OnRegisterCaps(UUID agentID, Caps caps) |
223 | { | 228 | { |
224 | //m_log.DebugFormat("[WORLD MAP]: OnRegisterCaps: agentID {0} caps {1}", agentID, caps); | 229 | //m_log.DebugFormat("[WORLD MAP]: OnRegisterCaps: agentID {0} caps {1}", agentID, caps); |
225 | string capsBase = "/CAPS/" + caps.CapsObjectPath; | 230 | string capspath = "/CAPS/" + UUID.Random(); |
226 | caps.RegisterHandler( | 231 | caps.RegisterHandler( |
227 | "MapLayer", | 232 | "MapLayer", |
228 | new RestStreamHandler( | 233 | new RestStreamHandler( |
229 | "POST", | 234 | "POST", |
230 | capsBase + m_mapLayerPath, | 235 | capspath, |
231 | (request, path, param, httpRequest, httpResponse) | 236 | (request, path, param, httpRequest, httpResponse) |
232 | => MapLayerRequest(request, path, param, agentID, caps), | 237 | => MapLayerRequest(request, path, param, agentID, caps), |
233 | "MapLayer", | 238 | "MapLayer", |
@@ -246,6 +251,8 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
246 | public string MapLayerRequest(string request, string path, string param, | 251 | public string MapLayerRequest(string request, string path, string param, |
247 | UUID agentID, Caps caps) | 252 | UUID agentID, Caps caps) |
248 | { | 253 | { |
254 | // not sure about this.... | ||
255 | |||
249 | //try | 256 | //try |
250 | // | 257 | // |
251 | //m_log.DebugFormat("[MAPLAYER]: path: {0}, param: {1}, agent:{2}", | 258 | //m_log.DebugFormat("[MAPLAYER]: path: {0}, param: {1}, agent:{2}", |
@@ -261,54 +268,54 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
261 | // 6/8/2011 -- I'm adding an explicit 2048 check, so that we never forget that there is | 268 | // 6/8/2011 -- I'm adding an explicit 2048 check, so that we never forget that there is |
262 | // a hack here, and so that regions below 4096 don't get spammed with unnecessary map blocks. | 269 | // a hack here, and so that regions below 4096 don't get spammed with unnecessary map blocks. |
263 | 270 | ||
264 | if (m_scene.RegionInfo.RegionLocX >= 2048 || m_scene.RegionInfo.RegionLocY >= 2048) | 271 | //if (m_scene.RegionInfo.RegionLocX >= 2048 || m_scene.RegionInfo.RegionLocY >= 2048) |
265 | { | 272 | //{ |
266 | ScenePresence avatarPresence = null; | 273 | // ScenePresence avatarPresence = null; |
267 | 274 | ||
268 | m_scene.TryGetScenePresence(agentID, out avatarPresence); | 275 | // m_scene.TryGetScenePresence(agentID, out avatarPresence); |
269 | 276 | ||
270 | if (avatarPresence != null) | 277 | // if (avatarPresence != null) |
271 | { | 278 | // { |
272 | bool lookup = false; | 279 | // bool lookup = false; |
273 | 280 | ||
274 | lock (cachedMapBlocks) | 281 | // lock (cachedMapBlocks) |
275 | { | 282 | // { |
276 | if (cachedMapBlocks.Count > 0 && ((cachedTime + 1800) > Util.UnixTimeSinceEpoch())) | 283 | // if (cachedMapBlocks.Count > 0 && ((cachedTime + 1800) > Util.UnixTimeSinceEpoch())) |
277 | { | 284 | // { |
278 | List<MapBlockData> mapBlocks; | 285 | // List<MapBlockData> mapBlocks; |
279 | 286 | ||
280 | mapBlocks = cachedMapBlocks; | 287 | // mapBlocks = cachedMapBlocks; |
281 | avatarPresence.ControllingClient.SendMapBlock(mapBlocks, 0); | 288 | // avatarPresence.ControllingClient.SendMapBlock(mapBlocks, 0); |
282 | } | 289 | // } |
283 | else | 290 | // else |
284 | { | 291 | // { |
285 | lookup = true; | 292 | // lookup = true; |
286 | } | 293 | // } |
287 | } | 294 | // } |
288 | if (lookup) | 295 | // if (lookup) |
289 | { | 296 | // { |
290 | List<MapBlockData> mapBlocks = new List<MapBlockData>(); ; | 297 | // List<MapBlockData> mapBlocks = new List<MapBlockData>(); ; |
291 | 298 | ||
292 | // Get regions that are within 8 regions of here | 299 | // List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID, |
293 | List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID, | 300 | // (int)(m_scene.RegionInfo.RegionLocX - 8) * (int)Constants.RegionSize, |
294 | (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocX - 8), | 301 | // (int)(m_scene.RegionInfo.RegionLocX + 8) * (int)Constants.RegionSize, |
295 | (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocX + 8), | 302 | // (int)(m_scene.RegionInfo.RegionLocY - 8) * (int)Constants.RegionSize, |
296 | (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocY - 8), | 303 | // (int)(m_scene.RegionInfo.RegionLocY + 8) * (int)Constants.RegionSize); |
297 | (int)Util.RegionToWorldLoc(m_scene.RegionInfo.RegionLocY + 8) ); | 304 | // foreach (GridRegion r in regions) |
298 | foreach (GridRegion r in regions) | 305 | // { |
299 | { | 306 | // MapBlockData block = new MapBlockData(); |
300 | MapBlockData block = MapBlockFromGridRegion(r, 0); | 307 | // MapBlockFromGridRegion(block, r, 0); |
301 | mapBlocks.Add(block); | 308 | // mapBlocks.Add(block); |
302 | } | 309 | // } |
303 | avatarPresence.ControllingClient.SendMapBlock(mapBlocks, 0); | 310 | // avatarPresence.ControllingClient.SendMapBlock(mapBlocks, 0); |
304 | 311 | ||
305 | lock (cachedMapBlocks) | 312 | // lock (cachedMapBlocks) |
306 | cachedMapBlocks = mapBlocks; | 313 | // cachedMapBlocks = mapBlocks; |
307 | 314 | ||
308 | cachedTime = Util.UnixTimeSinceEpoch(); | 315 | // cachedTime = Util.UnixTimeSinceEpoch(); |
309 | } | 316 | // } |
310 | } | 317 | // } |
311 | } | 318 | //} |
312 | 319 | ||
313 | LLSDMapLayerResponse mapResponse = new LLSDMapLayerResponse(); | 320 | LLSDMapLayerResponse mapResponse = new LLSDMapLayerResponse(); |
314 | mapResponse.LayerData.Array.Add(GetOSDMapLayerResponse()); | 321 | mapResponse.LayerData.Array.Add(GetOSDMapLayerResponse()); |
@@ -334,9 +341,11 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
334 | /// <returns></returns> | 341 | /// <returns></returns> |
335 | protected static OSDMapLayer GetOSDMapLayerResponse() | 342 | protected static OSDMapLayer GetOSDMapLayerResponse() |
336 | { | 343 | { |
344 | // not sure about this.... 2048 or master 5000 and hack above? | ||
345 | |||
337 | OSDMapLayer mapLayer = new OSDMapLayer(); | 346 | OSDMapLayer mapLayer = new OSDMapLayer(); |
338 | mapLayer.Right = 5000; | 347 | mapLayer.Right = 2048; |
339 | mapLayer.Top = 5000; | 348 | mapLayer.Top = 2048; |
340 | mapLayer.ImageID = new UUID("00000000-0000-1111-9999-000000000006"); | 349 | mapLayer.ImageID = new UUID("00000000-0000-1111-9999-000000000006"); |
341 | 350 | ||
342 | return mapLayer; | 351 | return mapLayer; |
@@ -365,6 +374,11 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
365 | { | 374 | { |
366 | m_rootAgents.Remove(AgentId); | 375 | m_rootAgents.Remove(AgentId); |
367 | } | 376 | } |
377 | lock (m_mapBlockRequestEvent) | ||
378 | { | ||
379 | if (m_mapBlockRequests.ContainsKey(AgentId)) | ||
380 | m_mapBlockRequests.Remove(AgentId); | ||
381 | } | ||
368 | } | 382 | } |
369 | #endregion | 383 | #endregion |
370 | 384 | ||
@@ -379,14 +393,20 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
379 | if (threadrunning) return; | 393 | if (threadrunning) return; |
380 | threadrunning = true; | 394 | threadrunning = true; |
381 | 395 | ||
382 | // m_log.Debug("[WORLD MAP]: Starting remote MapItem request thread"); | 396 | // m_log.Debug("[WORLD MAP]: Starting remote MapItem request thread"); |
383 | 397 | ||
384 | WorkManager.StartThread( | 398 | WorkManager.StartThread( |
385 | process, | 399 | process, |
386 | string.Format("MapItemRequestThread ({0})", m_scene.RegionInfo.RegionName), | 400 | string.Format("MapItemRequestThread ({0})", m_scene.RegionInfo.RegionName), |
387 | ThreadPriority.BelowNormal, | 401 | ThreadPriority.BelowNormal, |
388 | true, | 402 | true, |
389 | true); | 403 | false); |
404 | WorkManager.StartThread( | ||
405 | MapBlockSendThread, | ||
406 | string.Format("MapBlockSendThread ({0})", m_scene.RegionInfo.RegionName), | ||
407 | ThreadPriority.BelowNormal, | ||
408 | true, | ||
409 | false); | ||
390 | } | 410 | } |
391 | 411 | ||
392 | /// <summary> | 412 | /// <summary> |
@@ -396,13 +416,29 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
396 | { | 416 | { |
397 | MapRequestState st = new MapRequestState(); | 417 | MapRequestState st = new MapRequestState(); |
398 | st.agentID = STOP_UUID; | 418 | st.agentID = STOP_UUID; |
399 | st.EstateID=0; | 419 | st.EstateID = 0; |
400 | st.flags=0; | 420 | st.flags = 0; |
401 | st.godlike=false; | 421 | st.godlike = false; |
402 | st.itemtype=0; | 422 | st.itemtype = 0; |
403 | st.regionhandle=0; | 423 | st.regionhandle = 0; |
404 | 424 | ||
405 | requests.Enqueue(st); | 425 | requests.Enqueue(st); |
426 | |||
427 | MapBlockRequestData req = new MapBlockRequestData(); | ||
428 | |||
429 | req.client = null; | ||
430 | req.minX = 0; | ||
431 | req.maxX = 0; | ||
432 | req.minY = 0; | ||
433 | req.maxY = 0; | ||
434 | req.flags = 0; | ||
435 | |||
436 | lock (m_mapBlockRequestEvent) | ||
437 | { | ||
438 | m_mapBlockRequests[UUID.Zero] = new Queue<MapBlockRequestData>(); | ||
439 | m_mapBlockRequests[UUID.Zero].Enqueue(req); | ||
440 | m_mapBlockRequestEvent.Set(); | ||
441 | } | ||
406 | } | 442 | } |
407 | 443 | ||
408 | public virtual void HandleMapItemRequest(IClientAPI remoteClient, uint flags, | 444 | public virtual void HandleMapItemRequest(IClientAPI remoteClient, uint flags, |
@@ -415,315 +451,371 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
415 | if (!m_rootAgents.Contains(remoteClient.AgentId)) | 451 | if (!m_rootAgents.Contains(remoteClient.AgentId)) |
416 | return; | 452 | return; |
417 | } | 453 | } |
454 | |||
455 | // local or remote request? | ||
456 | if (regionhandle != 0 && regionhandle != m_scene.RegionInfo.RegionHandle) | ||
457 | { | ||
458 | // its Remote Map Item Request | ||
459 | // ensures that the blockingqueue doesn't get borked if the GetAgents() timing changes. | ||
460 | RequestMapItems("", remoteClient.AgentId, flags, EstateID, godlike, itemtype, regionhandle); | ||
461 | return; | ||
462 | } | ||
463 | |||
418 | uint xstart = 0; | 464 | uint xstart = 0; |
419 | uint ystart = 0; | 465 | uint ystart = 0; |
420 | Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle, out xstart, out ystart); | 466 | Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle, out xstart, out ystart); |
421 | if (itemtype == (int)GridItemType.AgentLocations) | 467 | |
468 | // its about this region... | ||
469 | |||
470 | List<mapItemReply> mapitems = new List<mapItemReply>(); | ||
471 | mapItemReply mapitem = new mapItemReply(); | ||
472 | |||
473 | // viewers only ask for green dots to each region now | ||
474 | // except at login with regionhandle 0 | ||
475 | // possible on some other rare ocasions | ||
476 | // use previus hack of sending all items with the green dots | ||
477 | |||
478 | bool adultRegion; | ||
479 | if (regionhandle == 0) | ||
422 | { | 480 | { |
423 | if (regionhandle == 0 || regionhandle == m_scene.RegionInfo.RegionHandle) | 481 | switch (itemtype) |
424 | { | 482 | { |
425 | // Just requesting map info about the current, local region | 483 | case (int)GridItemType.AgentLocations: |
426 | int tc = Environment.TickCount; | 484 | // Service 6 right now (MAP_ITEM_AGENTS_LOCATION; green dots) |
427 | List<mapItemReply> mapitems = new List<mapItemReply>(); | 485 | |
428 | mapItemReply mapitem = new mapItemReply(); | 486 | int tc = Environment.TickCount; |
429 | if (m_scene.GetRootAgentCount() <= 1) | 487 | if (m_scene.GetRootAgentCount() <= 1) |
430 | { | 488 | { |
431 | mapitem = new mapItemReply( | 489 | mapitem = new mapItemReply( |
432 | xstart + 1, | 490 | xstart + 1, |
433 | ystart + 1, | 491 | ystart + 1, |
434 | UUID.Zero, | 492 | UUID.Zero, |
435 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), | 493 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), |
436 | 0, 0); | 494 | 0, 0); |
437 | mapitems.Add(mapitem); | 495 | mapitems.Add(mapitem); |
438 | } | 496 | } |
439 | else | 497 | else |
440 | { | ||
441 | m_scene.ForEachRootScenePresence(delegate(ScenePresence sp) | ||
442 | { | 498 | { |
499 | m_scene.ForEachRootScenePresence(delegate (ScenePresence sp) | ||
500 | { | ||
443 | // Don't send a green dot for yourself | 501 | // Don't send a green dot for yourself |
444 | if (sp.UUID != remoteClient.AgentId) | 502 | if (sp.UUID != remoteClient.AgentId) |
445 | { | 503 | { |
446 | mapitem = new mapItemReply( | 504 | mapitem = new mapItemReply( |
447 | xstart + (uint)sp.AbsolutePosition.X, | 505 | xstart + (uint)sp.AbsolutePosition.X, |
448 | ystart + (uint)sp.AbsolutePosition.Y, | 506 | ystart + (uint)sp.AbsolutePosition.Y, |
449 | UUID.Zero, | 507 | UUID.Zero, |
450 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), | 508 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), |
451 | 1, 0); | 509 | 1, 0); |
452 | mapitems.Add(mapitem); | 510 | mapitems.Add(mapitem); |
453 | } | 511 | } |
454 | }); | 512 | }); |
455 | } | 513 | } |
456 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); | 514 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); |
457 | } | 515 | break; |
458 | else | ||
459 | { | ||
460 | // Remote Map Item Request | ||
461 | 516 | ||
462 | // ensures that the blockingqueue doesn't get borked if the GetAgents() timing changes. | 517 | case (int)GridItemType.Telehub: |
463 | RequestMapItems("",remoteClient.AgentId,flags,EstateID,godlike,itemtype,regionhandle); | 518 | // Service 1 (MAP_ITEM_TELEHUB) |
464 | } | 519 | |
465 | } | 520 | SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject); |
466 | else if (itemtype == (int)GridItemType.LandForSale) // Service 7 (MAP_ITEM_LAND_FOR_SALE) | 521 | if (sog != null) |
467 | { | ||
468 | if (regionhandle == 0 || regionhandle == m_scene.RegionInfo.RegionHandle) | ||
469 | { | ||
470 | // Parcels | ||
471 | ILandChannel landChannel = m_scene.LandChannel; | ||
472 | List<ILandObject> parcels = landChannel.AllParcels(); | ||
473 | |||
474 | // Local Map Item Request | ||
475 | List<mapItemReply> mapitems = new List<mapItemReply>(); | ||
476 | mapItemReply mapitem = new mapItemReply(); | ||
477 | if ((parcels != null) && (parcels.Count >= 1)) | ||
478 | { | ||
479 | foreach (ILandObject parcel_interface in parcels) | ||
480 | { | 522 | { |
481 | // Play it safe | 523 | mapitem = new mapItemReply( |
482 | if (!(parcel_interface is LandObject)) | 524 | xstart + (uint)sog.AbsolutePosition.X, |
483 | continue; | 525 | ystart + (uint)sog.AbsolutePosition.Y, |
526 | UUID.Zero, | ||
527 | sog.Name, | ||
528 | 0, // color (not used) | ||
529 | 0 // 0 = telehub / 1 = infohub | ||
530 | ); | ||
531 | mapitems.Add(mapitem); | ||
532 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); | ||
533 | } | ||
534 | break; | ||
484 | 535 | ||
485 | LandObject land = (LandObject)parcel_interface; | 536 | case (int)GridItemType.AdultLandForSale: |
486 | LandData parcel = land.LandData; | 537 | case (int)GridItemType.LandForSale: |
487 | 538 | ||
488 | // Show land for sale | 539 | // Service 7 (MAP_ITEM_LAND_FOR_SALE) |
489 | if ((parcel.Flags & (uint)ParcelFlags.ForSale) == (uint)ParcelFlags.ForSale) | 540 | adultRegion = m_scene.RegionInfo.RegionSettings.Maturity == 2; |
490 | { | 541 | if (adultRegion) |
491 | Vector3 min = parcel.AABBMin; | 542 | { |
492 | Vector3 max = parcel.AABBMax; | 543 | if (itemtype == (int)GridItemType.LandForSale) |
493 | float x = (min.X+max.X)/2; | 544 | break; |
494 | float y = (min.Y+max.Y)/2; | 545 | } |
546 | else | ||
547 | { | ||
548 | if (itemtype == (int)GridItemType.AdultLandForSale) | ||
549 | break; | ||
550 | } | ||
551 | |||
552 | // Parcels | ||
553 | ILandChannel landChannel = m_scene.LandChannel; | ||
554 | List<ILandObject> parcels = landChannel.AllParcels(); | ||
495 | 555 | ||
496 | mapitem = new mapItemReply( | 556 | if ((parcels != null) && (parcels.Count >= 1)) |
557 | { | ||
558 | foreach (ILandObject parcel_interface in parcels) | ||
559 | { | ||
560 | // Play it safe | ||
561 | if (!(parcel_interface is LandObject)) | ||
562 | continue; | ||
563 | |||
564 | LandObject land = (LandObject)parcel_interface; | ||
565 | LandData parcel = land.LandData; | ||
566 | |||
567 | // Show land for sale | ||
568 | if ((parcel.Flags & (uint)ParcelFlags.ForSale) == (uint)ParcelFlags.ForSale) | ||
569 | { | ||
570 | Vector3 min = parcel.AABBMin; | ||
571 | Vector3 max = parcel.AABBMax; | ||
572 | float x = (min.X + max.X) / 2; | ||
573 | float y = (min.Y + max.Y) / 2; | ||
574 | mapitem = new mapItemReply( | ||
497 | xstart + (uint)x, | 575 | xstart + (uint)x, |
498 | ystart + (uint)y, | 576 | ystart + (uint)y, |
499 | parcel.GlobalID, | 577 | parcel.GlobalID, |
500 | parcel.Name, | 578 | parcel.Name, |
501 | parcel.Area, | 579 | parcel.Area, |
502 | parcel.SalePrice | 580 | parcel.SalePrice |
503 | ); | 581 | ); |
504 | mapitems.Add(mapitem); | 582 | mapitems.Add(mapitem); |
583 | } | ||
505 | } | 584 | } |
506 | } | 585 | } |
507 | } | 586 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); |
508 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); | 587 | break; |
509 | } | ||
510 | else | ||
511 | { | ||
512 | // Remote Map Item Request | ||
513 | 588 | ||
514 | // ensures that the blockingqueue doesn't get borked if the GetAgents() timing changes. | 589 | case (uint)GridItemType.PgEvent: |
515 | RequestMapItems("",remoteClient.AgentId,flags,EstateID,godlike,itemtype,regionhandle); | 590 | case (uint)GridItemType.MatureEvent: |
591 | case (uint)GridItemType.AdultEvent: | ||
592 | case (uint)GridItemType.Classified: | ||
593 | case (uint)GridItemType.Popular: | ||
594 | // TODO | ||
595 | // just dont not cry about them | ||
596 | break; | ||
597 | |||
598 | default: | ||
599 | // unkown map item type | ||
600 | m_log.DebugFormat("[WORLD MAP]: Unknown MapItem type {1}", itemtype); | ||
601 | break; | ||
516 | } | 602 | } |
517 | } | 603 | } |
518 | else if (itemtype == (int)GridItemType.Telehub) // Service 1 (MAP_ITEM_TELEHUB) | 604 | else |
519 | { | 605 | { |
520 | if (regionhandle == 0 || regionhandle == m_scene.RegionInfo.RegionHandle) | 606 | // send all items till we get a better fix |
521 | { | ||
522 | List<mapItemReply> mapitems = new List<mapItemReply>(); | ||
523 | mapItemReply mapitem = new mapItemReply(); | ||
524 | 607 | ||
525 | SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject); | 608 | // Service 6 right now (MAP_ITEM_AGENTS_LOCATION; green dots) |
526 | if (sog != null) | ||
527 | { | ||
528 | mapitem = new mapItemReply( | ||
529 | xstart + (uint)sog.AbsolutePosition.X, | ||
530 | ystart + (uint)sog.AbsolutePosition.Y, | ||
531 | UUID.Zero, | ||
532 | sog.Name, | ||
533 | 0, // color (not used) | ||
534 | 0 // 0 = telehub / 1 = infohub | ||
535 | ); | ||
536 | mapitems.Add(mapitem); | ||
537 | 609 | ||
538 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); | 610 | int tc = Environment.TickCount; |
539 | } | 611 | if (m_scene.GetRootAgentCount() <= 1) |
612 | { | ||
613 | mapitem = new mapItemReply( | ||
614 | xstart + 1, | ||
615 | ystart + 1, | ||
616 | UUID.Zero, | ||
617 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), | ||
618 | 0, 0); | ||
619 | mapitems.Add(mapitem); | ||
540 | } | 620 | } |
541 | else | 621 | else |
542 | { | 622 | { |
543 | // Remote Map Item Request | 623 | m_scene.ForEachRootScenePresence(delegate (ScenePresence sp) |
544 | RequestMapItems("",remoteClient.AgentId,flags,EstateID,godlike,itemtype,regionhandle); | 624 | { |
625 | // Don't send a green dot for yourself | ||
626 | if (sp.UUID != remoteClient.AgentId) | ||
627 | { | ||
628 | mapitem = new mapItemReply( | ||
629 | xstart + (uint)sp.AbsolutePosition.X, | ||
630 | ystart + (uint)sp.AbsolutePosition.Y, | ||
631 | UUID.Zero, | ||
632 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), | ||
633 | 1, 0); | ||
634 | mapitems.Add(mapitem); | ||
635 | } | ||
636 | }); | ||
545 | } | 637 | } |
546 | } | 638 | remoteClient.SendMapItemReply(mapitems.ToArray(), 6, flags); |
547 | } | 639 | mapitems.Clear(); |
548 | 640 | ||
549 | private int nAsyncRequests = 0; | 641 | // Service 1 (MAP_ITEM_TELEHUB) |
550 | /// <summary> | 642 | |
551 | /// Processing thread main() loop for doing remote mapitem requests | 643 | SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject); |
552 | /// </summary> | 644 | if (sog != null) |
553 | public void process() | ||
554 | { | ||
555 | //const int MAX_ASYNC_REQUESTS = 20; | ||
556 | try | ||
557 | { | ||
558 | while (true) | ||
559 | { | 645 | { |
560 | MapRequestState st = requests.Dequeue(1000); | 646 | mapitem = new mapItemReply( |
647 | xstart + (uint)sog.AbsolutePosition.X, | ||
648 | ystart + (uint)sog.AbsolutePosition.Y, | ||
649 | UUID.Zero, | ||
650 | sog.Name, | ||
651 | 0, // color (not used) | ||
652 | 0 // 0 = telehub / 1 = infohub | ||
653 | ); | ||
654 | mapitems.Add(mapitem); | ||
655 | remoteClient.SendMapItemReply(mapitems.ToArray(), 1, flags); | ||
656 | mapitems.Clear(); | ||
657 | } | ||
561 | 658 | ||
562 | // end gracefully | 659 | // Service 7 (MAP_ITEM_LAND_FOR_SALE) |
563 | if (st.agentID == STOP_UUID) | 660 | |
564 | break; | 661 | uint its = 7; |
662 | if (m_scene.RegionInfo.RegionSettings.Maturity == 2) | ||
663 | its = 10; | ||
664 | |||
665 | // Parcels | ||
666 | ILandChannel landChannel = m_scene.LandChannel; | ||
667 | List<ILandObject> parcels = landChannel.AllParcels(); | ||
565 | 668 | ||
566 | if (st.agentID != UUID.Zero) | 669 | if ((parcels != null) && (parcels.Count >= 1)) |
670 | { | ||
671 | foreach (ILandObject parcel_interface in parcels) | ||
567 | { | 672 | { |
568 | bool dorequest = true; | 673 | // Play it safe |
569 | lock (m_rootAgents) | 674 | if (!(parcel_interface is LandObject)) |
570 | { | 675 | continue; |
571 | if (!m_rootAgents.Contains(st.agentID)) | 676 | |
572 | dorequest = false; | 677 | LandObject land = (LandObject)parcel_interface; |
573 | } | 678 | LandData parcel = land.LandData; |
574 | 679 | ||
575 | if (dorequest && !m_blacklistedregions.ContainsKey(st.regionhandle)) | 680 | // Show land for sale |
681 | if ((parcel.Flags & (uint)ParcelFlags.ForSale) == (uint)ParcelFlags.ForSale) | ||
576 | { | 682 | { |
577 | while (nAsyncRequests >= MAX_ASYNC_REQUESTS) // hit the break | 683 | Vector3 min = parcel.AABBMin; |
578 | Thread.Sleep(80); | 684 | Vector3 max = parcel.AABBMax; |
579 | 685 | float x = (min.X + max.X) / 2; | |
580 | RequestMapItemsDelegate d = RequestMapItemsAsync; | 686 | float y = (min.Y + max.Y) / 2; |
581 | d.BeginInvoke(st.agentID, st.flags, st.EstateID, st.godlike, st.itemtype, st.regionhandle, RequestMapItemsCompleted, null); | 687 | mapitem = new mapItemReply( |
582 | //OSDMap response = RequestMapItemsAsync(st.agentID, st.flags, st.EstateID, st.godlike, st.itemtype, st.regionhandle); | 688 | xstart + (uint)x, |
583 | //RequestMapItemsCompleted(response); | 689 | ystart + (uint)y, |
584 | Interlocked.Increment(ref nAsyncRequests); | 690 | parcel.GlobalID, |
691 | parcel.Name, | ||
692 | parcel.Area, | ||
693 | parcel.SalePrice | ||
694 | ); | ||
695 | mapitems.Add(mapitem); | ||
585 | } | 696 | } |
586 | } | 697 | } |
587 | 698 | if(mapitems.Count >0) | |
588 | Watchdog.UpdateThread(); | 699 | remoteClient.SendMapItemReply(mapitems.ToArray(), its, flags); |
700 | mapitems.Clear(); | ||
589 | } | 701 | } |
590 | } | 702 | } |
591 | catch (Exception e) | ||
592 | { | ||
593 | m_log.ErrorFormat("[WORLD MAP]: Map item request thread terminated abnormally with exception {0}", e); | ||
594 | } | ||
595 | |||
596 | threadrunning = false; | ||
597 | Watchdog.RemoveThread(); | ||
598 | } | 703 | } |
599 | 704 | ||
600 | const int MAX_ASYNC_REQUESTS = 20; | 705 | private int nAsyncRequests = 0; |
601 | |||
602 | /// <summary> | 706 | /// <summary> |
603 | /// Enqueues the map item request into the services throttle processing thread | 707 | /// Processing thread main() loop for doing remote mapitem requests |
604 | /// </summary> | 708 | /// </summary> |
605 | /// <param name="state"></param> | 709 | public void process() |
606 | public void EnqueueMapItemRequest(MapRequestState st) | ||
607 | { | 710 | { |
711 | const int MAX_ASYNC_REQUESTS = 20; | ||
712 | ScenePresence av = null; | ||
713 | MapRequestState st = null; | ||
608 | 714 | ||
609 | m_ServiceThrottle.Enqueue("map-item", st.regionhandle.ToString() + st.agentID.ToString(), delegate | 715 | try |
610 | { | 716 | { |
611 | if (st.agentID != UUID.Zero) | 717 | while (true) |
612 | { | 718 | { |
613 | bool dorequest = true; | 719 | av = null; |
614 | lock (m_rootAgents) | 720 | st = null; |
615 | { | ||
616 | if (!m_rootAgents.Contains(st.agentID)) | ||
617 | dorequest = false; | ||
618 | } | ||
619 | 721 | ||
620 | if (dorequest && !m_blacklistedregions.ContainsKey(st.regionhandle)) | 722 | st = requests.Dequeue(4500); |
621 | { | 723 | Watchdog.UpdateThread(); |
622 | if (nAsyncRequests >= MAX_ASYNC_REQUESTS) // hit the break | ||
623 | { | ||
624 | // AH!!! Recursive ! | ||
625 | // Put this request back in the queue and return | ||
626 | EnqueueMapItemRequest(st); | ||
627 | return; | ||
628 | } | ||
629 | 724 | ||
630 | RequestMapItemsDelegate d = RequestMapItemsAsync; | 725 | if (st == null || st.agentID == UUID.Zero) |
631 | d.BeginInvoke(st.agentID, st.flags, st.EstateID, st.godlike, st.itemtype, st.regionhandle, RequestMapItemsCompleted, null); | 726 | continue; |
632 | //OSDMap response = RequestMapItemsAsync(st.agentID, st.flags, st.EstateID, st.godlike, st.itemtype, st.regionhandle); | ||
633 | //RequestMapItemsCompleted(response); | ||
634 | Interlocked.Increment(ref nAsyncRequests); | ||
635 | } | ||
636 | } | ||
637 | }); | ||
638 | } | ||
639 | 727 | ||
640 | /// <summary> | 728 | // end gracefully |
641 | /// Sends the mapitem response to the IClientAPI | 729 | if (st.agentID == STOP_UUID) |
642 | /// </summary> | 730 | break; |
643 | /// <param name="response">The OSDMap Response for the mapitem</param> | ||
644 | private void RequestMapItemsCompleted(IAsyncResult iar) | ||
645 | { | ||
646 | AsyncResult result = (AsyncResult)iar; | ||
647 | RequestMapItemsDelegate icon = (RequestMapItemsDelegate)result.AsyncDelegate; | ||
648 | 731 | ||
649 | OSDMap response = (OSDMap)icon.EndInvoke(iar); | 732 | // agent gone? |
650 | 733 | ||
651 | Interlocked.Decrement(ref nAsyncRequests); | 734 | m_scene.TryGetScenePresence(st.agentID, out av); |
735 | if (av == null || av.IsChildAgent || av.IsDeleted || av.IsInTransit) | ||
736 | continue; | ||
652 | 737 | ||
653 | if (!response.ContainsKey("requestID")) | 738 | // region unreachable? |
654 | return; | 739 | if (m_blacklistedregions.Contains(st.regionhandle)) |
740 | continue; | ||
655 | 741 | ||
656 | UUID requestID = response["requestID"].AsUUID(); | 742 | bool dorequest = true; |
743 | OSDMap responseMap = null; | ||
657 | 744 | ||
658 | if (requestID != UUID.Zero) | 745 | // check if we are already serving this region |
659 | { | 746 | lock (m_cachedRegionMapItemsResponses) |
660 | MapRequestState mrs = new MapRequestState(); | ||
661 | mrs.agentID = UUID.Zero; | ||
662 | lock (m_openRequests) | ||
663 | { | ||
664 | if (m_openRequests.ContainsKey(requestID)) | ||
665 | { | 747 | { |
666 | mrs = m_openRequests[requestID]; | 748 | if (m_cachedRegionMapItemsResponses.Contains(st.regionhandle)) |
667 | m_openRequests.Remove(requestID); | 749 | { |
750 | m_cachedRegionMapItemsResponses.TryGetValue(st.regionhandle, out responseMap); | ||
751 | dorequest = false; | ||
752 | } | ||
753 | else | ||
754 | m_cachedRegionMapItemsResponses.Add(st.regionhandle, null, expireResponsesTime); // a bit more time for the access | ||
668 | } | 755 | } |
669 | } | ||
670 | 756 | ||
671 | if (mrs.agentID != UUID.Zero) | 757 | if (dorequest) |
672 | { | ||
673 | ScenePresence av = null; | ||
674 | m_scene.TryGetScenePresence(mrs.agentID, out av); | ||
675 | if (av != null) | ||
676 | { | 758 | { |
677 | if (response.ContainsKey(mrs.itemtype.ToString())) | 759 | // nothig for region, fire a request |
760 | Interlocked.Increment(ref nAsyncRequests); | ||
761 | MapRequestState rst = st; | ||
762 | Util.FireAndForget(x => | ||
678 | { | 763 | { |
679 | List<mapItemReply> returnitems = new List<mapItemReply>(); | 764 | RequestMapItemsAsync(rst.agentID, rst.flags, rst.EstateID, rst.godlike, rst.itemtype, rst.regionhandle); |
680 | OSDArray itemarray = (OSDArray)response[mrs.itemtype.ToString()]; | 765 | }); |
681 | for (int i = 0; i < itemarray.Count; i++) | 766 | } |
682 | { | 767 | else |
683 | OSDMap mapitem = (OSDMap)itemarray[i]; | 768 | { |
684 | mapItemReply mi = new mapItemReply(); | 769 | // do we have the response? |
685 | mi.FromOSD(mapitem); | 770 | if (responseMap != null) |
686 | returnitems.Add(mi); | ||
687 | } | ||
688 | av.ControllingClient.SendMapItemReply(returnitems.ToArray(), mrs.itemtype, mrs.flags); | ||
689 | } | ||
690 | |||
691 | // Service 7 (MAP_ITEM_LAND_FOR_SALE) | ||
692 | uint itemtype = (uint)GridItemType.LandForSale; | ||
693 | |||
694 | if (response.ContainsKey(itemtype.ToString())) | ||
695 | { | 771 | { |
696 | List<mapItemReply> returnitems = new List<mapItemReply>(); | 772 | if(av!=null) |
697 | OSDArray itemarray = (OSDArray)response[itemtype.ToString()]; | ||
698 | for (int i = 0; i < itemarray.Count; i++) | ||
699 | { | 773 | { |
700 | OSDMap mapitem = (OSDMap)itemarray[i]; | 774 | // this will mainly only send green dots now |
701 | mapItemReply mi = new mapItemReply(); | 775 | if (responseMap.ContainsKey(st.itemtype.ToString())) |
702 | mi.FromOSD(mapitem); | 776 | { |
703 | returnitems.Add(mi); | 777 | List<mapItemReply> returnitems = new List<mapItemReply>(); |
778 | OSDArray itemarray = (OSDArray)responseMap[st.itemtype.ToString()]; | ||
779 | for (int i = 0; i < itemarray.Count; i++) | ||
780 | { | ||
781 | OSDMap mapitem = (OSDMap)itemarray[i]; | ||
782 | mapItemReply mi = new mapItemReply(); | ||
783 | mi.x = (uint)mapitem["X"].AsInteger(); | ||
784 | mi.y = (uint)mapitem["Y"].AsInteger(); | ||
785 | mi.id = mapitem["ID"].AsUUID(); | ||
786 | mi.Extra = mapitem["Extra"].AsInteger(); | ||
787 | mi.Extra2 = mapitem["Extra2"].AsInteger(); | ||
788 | mi.name = mapitem["Name"].AsString(); | ||
789 | returnitems.Add(mi); | ||
790 | } | ||
791 | av.ControllingClient.SendMapItemReply(returnitems.ToArray(), st.itemtype, st.flags & 0xffff); | ||
792 | } | ||
704 | } | 793 | } |
705 | av.ControllingClient.SendMapItemReply(returnitems.ToArray(), itemtype, mrs.flags); | ||
706 | } | 794 | } |
707 | 795 | else | |
708 | // Service 1 (MAP_ITEM_TELEHUB) | ||
709 | itemtype = (uint)GridItemType.Telehub; | ||
710 | |||
711 | if (response.ContainsKey(itemtype.ToString())) | ||
712 | { | 796 | { |
713 | List<mapItemReply> returnitems = new List<mapItemReply>(); | 797 | // request still beeing processed, enqueue it back |
714 | OSDArray itemarray = (OSDArray)response[itemtype.ToString()]; | 798 | requests.Enqueue(st); |
715 | for (int i = 0; i < itemarray.Count; i++) | 799 | if (requests.Count() < 3) |
716 | { | 800 | Thread.Sleep(100); |
717 | OSDMap mapitem = (OSDMap)itemarray[i]; | ||
718 | mapItemReply mi = new mapItemReply(); | ||
719 | mi.FromOSD(mapitem); | ||
720 | returnitems.Add(mi); | ||
721 | } | ||
722 | av.ControllingClient.SendMapItemReply(returnitems.ToArray(), itemtype, mrs.flags); | ||
723 | } | 801 | } |
724 | } | 802 | } |
803 | |||
804 | while (nAsyncRequests >= MAX_ASYNC_REQUESTS) // hit the break | ||
805 | { | ||
806 | Thread.Sleep(100); | ||
807 | Watchdog.UpdateThread(); | ||
808 | } | ||
725 | } | 809 | } |
726 | } | 810 | } |
811 | |||
812 | catch (Exception e) | ||
813 | { | ||
814 | m_log.ErrorFormat("[WORLD MAP]: Map item request thread terminated abnormally with exception {0}", e); | ||
815 | } | ||
816 | |||
817 | threadrunning = false; | ||
818 | Watchdog.RemoveThread(); | ||
727 | } | 819 | } |
728 | 820 | ||
729 | /// <summary> | 821 | /// <summary> |
@@ -746,11 +838,12 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
746 | st.godlike = godlike; | 838 | st.godlike = godlike; |
747 | st.itemtype = itemtype; | 839 | st.itemtype = itemtype; |
748 | st.regionhandle = regionhandle; | 840 | st.regionhandle = regionhandle; |
749 | EnqueueMapItemRequest(st); | 841 | |
842 | requests.Enqueue(st); | ||
750 | } | 843 | } |
751 | 844 | ||
752 | private delegate OSDMap RequestMapItemsDelegate(UUID id, uint flags, | 845 | uint[] itemTypesForcedSend = new uint[] { 6, 1, 7, 10 }; // green dots, infohub, land sells |
753 | uint EstateID, bool godlike, uint itemtype, ulong regionhandle); | 846 | |
754 | /// <summary> | 847 | /// <summary> |
755 | /// Does the actual remote mapitem request | 848 | /// Does the actual remote mapitem request |
756 | /// This should be called from an asynchronous thread | 849 | /// This should be called from an asynchronous thread |
@@ -765,94 +858,55 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
765 | /// <param name="itemtype">passed in from packet</param> | 858 | /// <param name="itemtype">passed in from packet</param> |
766 | /// <param name="regionhandle">Region we're looking up</param> | 859 | /// <param name="regionhandle">Region we're looking up</param> |
767 | /// <returns></returns> | 860 | /// <returns></returns> |
768 | private OSDMap RequestMapItemsAsync(UUID id, uint flags, | 861 | private void RequestMapItemsAsync(UUID id, uint flags, |
769 | uint EstateID, bool godlike, uint itemtype, ulong regionhandle) | 862 | uint EstateID, bool godlike, uint itemtype, ulong regionhandle) |
770 | { | 863 | { |
771 | // m_log.DebugFormat("[WORLDMAP]: RequestMapItemsAsync; region handle: {0} {1}", regionhandle, itemtype); | 864 | // m_log.DebugFormat("[WORLDMAP]: RequestMapItemsAsync; region handle: {0} {1}", regionhandle, itemtype); |
772 | 865 | ||
773 | string httpserver = ""; | 866 | string httpserver = ""; |
774 | bool blacklisted = false; | 867 | 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 | 868 | ||
783 | m_blacklistedregions.Remove(regionhandle); | 869 | lock (m_blacklistedregions) |
784 | } | 870 | blacklisted = m_blacklistedregions.Contains(regionhandle); |
785 | else | ||
786 | blacklisted = true; | ||
787 | } | ||
788 | } | ||
789 | 871 | ||
790 | if (blacklisted) | 872 | if (blacklisted) |
791 | return new OSDMap(); | 873 | { |
874 | Interlocked.Decrement(ref nAsyncRequests); | ||
875 | return; | ||
876 | } | ||
792 | 877 | ||
793 | UUID requestID = UUID.Random(); | 878 | UUID requestID = UUID.Random(); |
794 | lock (m_cachedRegionMapItemsAddress) | 879 | lock (m_cachedRegionMapItemsAddress) |
795 | { | 880 | m_cachedRegionMapItemsAddress.TryGetValue(regionhandle, out httpserver); |
796 | if (m_cachedRegionMapItemsAddress.ContainsKey(regionhandle)) | 881 | |
797 | httpserver = m_cachedRegionMapItemsAddress[regionhandle]; | 882 | if (httpserver == null || httpserver.Length == 0) |
798 | } | ||
799 | if (httpserver.Length == 0) | ||
800 | { | 883 | { |
801 | uint x = 0, y = 0; | 884 | uint x = 0, y = 0; |
802 | Util.RegionHandleToWorldLoc(regionhandle, out x, out y); | 885 | Util.RegionHandleToWorldLoc(regionhandle, out x, out y); |
886 | |||
803 | GridRegion mreg = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, (int)x, (int)y); | 887 | GridRegion mreg = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, (int)x, (int)y); |
804 | 888 | ||
805 | if (mreg != null) | 889 | if (mreg != null) |
806 | { | 890 | { |
807 | httpserver = mreg.ServerURI + "MAP/MapItems/" + regionhandle.ToString(); | 891 | httpserver = mreg.ServerURI + "MAP/MapItems/" + regionhandle.ToString(); |
808 | lock (m_cachedRegionMapItemsAddress) | 892 | lock (m_cachedRegionMapItemsAddress) |
809 | { | 893 | m_cachedRegionMapItemsAddress.AddOrUpdate(regionhandle, httpserver, 2.0 * expireBlackListTime); |
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 | } | 894 | } |
823 | } | 895 | } |
824 | 896 | ||
825 | blacklisted = false; | ||
826 | lock (m_blacklistedurls) | 897 | lock (m_blacklistedurls) |
827 | { | 898 | { |
828 | if (m_blacklistedurls.ContainsKey(httpserver)) | 899 | if (httpserver == null || httpserver.Length == 0 || m_blacklistedurls.Contains(httpserver)) |
829 | { | 900 | { |
830 | if (Environment.TickCount > (m_blacklistedurls[httpserver] + blacklistTimeout)) | 901 | // Can't find the http server or its blocked |
831 | { | 902 | lock (m_blacklistedregions) |
832 | m_log.DebugFormat("[WORLD MAP]: Unblock blacklisted URL {0}", httpserver); | 903 | m_blacklistedregions.AddOrUpdate(regionhandle, 0, expireBlackListTime); |
833 | 904 | ||
834 | m_blacklistedurls.Remove(httpserver); | 905 | Interlocked.Decrement(ref nAsyncRequests); |
835 | } | 906 | return; |
836 | else | ||
837 | blacklisted = true; | ||
838 | } | 907 | } |
839 | } | 908 | } |
840 | 909 | ||
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; | 910 | WebRequest mapitemsrequest = null; |
857 | try | 911 | try |
858 | { | 912 | { |
@@ -861,132 +915,144 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
861 | catch (Exception e) | 915 | catch (Exception e) |
862 | { | 916 | { |
863 | m_log.DebugFormat("[WORLD MAP]: Access to {0} failed with {1}", httpserver, e); | 917 | m_log.DebugFormat("[WORLD MAP]: Access to {0} failed with {1}", httpserver, e); |
864 | return new OSDMap(); | 918 | Interlocked.Decrement(ref nAsyncRequests); |
919 | return; | ||
865 | } | 920 | } |
866 | 921 | ||
867 | mapitemsrequest.Method = "POST"; | 922 | mapitemsrequest.Method = "POST"; |
868 | mapitemsrequest.ContentType = "application/xml+llsd"; | 923 | mapitemsrequest.ContentType = "application/xml+llsd"; |
869 | OSDMap RAMap = new OSDMap(); | ||
870 | 924 | ||
925 | OSDMap RAMap = new OSDMap(); | ||
871 | // string RAMapString = RAMap.ToString(); | 926 | // string RAMapString = RAMap.ToString(); |
872 | OSD LLSDofRAMap = RAMap; // RENAME if this works | 927 | OSD LLSDofRAMap = RAMap; // RENAME if this works |
873 | 928 | ||
874 | byte[] buffer = OSDParser.SerializeLLSDXmlBytes(LLSDofRAMap); | 929 | byte[] buffer = OSDParser.SerializeLLSDXmlBytes(LLSDofRAMap); |
930 | |||
875 | OSDMap responseMap = new OSDMap(); | 931 | OSDMap responseMap = new OSDMap(); |
876 | responseMap["requestID"] = OSD.FromUUID(requestID); | ||
877 | 932 | ||
878 | Stream os = null; | ||
879 | try | 933 | try |
880 | { // send the Post | 934 | { // send the Post |
881 | mapitemsrequest.ContentLength = buffer.Length; //Count bytes to send | 935 | mapitemsrequest.ContentLength = buffer.Length; //Count bytes to send |
882 | os = mapitemsrequest.GetRequestStream(); | 936 | using (Stream os = mapitemsrequest.GetRequestStream()) |
883 | os.Write(buffer, 0, buffer.Length); //Send it | 937 | os.Write(buffer, 0, buffer.Length); //Send it |
884 | //m_log.DebugFormat("[WORLD MAP]: Getting MapItems from {0}", httpserver); | 938 | //m_log.DebugFormat("[WORLD MAP]: Getting MapItems from {0}", httpserver); |
885 | } | 939 | } |
886 | catch (WebException ex) | 940 | catch (WebException ex) |
887 | { | 941 | { |
888 | m_log.WarnFormat("[WORLD MAP]: Bad send on GetMapItems {0}", ex.Message); | 942 | m_log.WarnFormat("[WORLD MAP]: Bad send on GetMapItems {0}", ex.Message); |
889 | responseMap["connect"] = OSD.FromBoolean(false); | 943 | m_log.WarnFormat("[WORLD MAP]: Blacklisted url {0}", httpserver); |
890 | lock (m_blacklistedurls) | 944 | lock (m_blacklistedurls) |
891 | { | 945 | m_blacklistedurls.AddOrUpdate(httpserver, 0, expireBlackListTime); |
892 | if (!m_blacklistedurls.ContainsKey(httpserver)) | 946 | lock (m_blacklistedregions) |
893 | m_blacklistedurls.Add(httpserver, Environment.TickCount); | 947 | m_blacklistedregions.AddOrUpdate(regionhandle, 0, expireBlackListTime); |
894 | } | ||
895 | |||
896 | m_log.WarnFormat("[WORLD MAP]: Blacklisted {0}", httpserver); | ||
897 | 948 | ||
898 | return responseMap; | 949 | Interlocked.Decrement(ref nAsyncRequests); |
950 | return; | ||
899 | } | 951 | } |
900 | catch | 952 | catch |
901 | { | 953 | { |
902 | m_log.DebugFormat("[WORLD MAP]: RequestMapItems failed for {0}", httpserver); | 954 | m_log.DebugFormat("[WORLD MAP]: RequestMapItems failed for {0}", httpserver); |
903 | responseMap["connect"] = OSD.FromBoolean(false); | 955 | Interlocked.Decrement(ref nAsyncRequests); |
904 | return responseMap; | 956 | return; |
905 | } | ||
906 | finally | ||
907 | { | ||
908 | if (os != null) | ||
909 | os.Dispose(); | ||
910 | } | 957 | } |
911 | 958 | ||
912 | string response_mapItems_reply = null; | 959 | string response_mapItems_reply = null; |
913 | { | 960 | { // get the response |
914 | try | 961 | try |
915 | { | 962 | { |
916 | using (WebResponse webResponse = mapitemsrequest.GetResponse()) | 963 | using (WebResponse webResponse = mapitemsrequest.GetResponse()) |
917 | { | 964 | { |
918 | if (webResponse != null) | 965 | if (webResponse != null) |
919 | { | 966 | { |
920 | using (Stream s = webResponse.GetResponseStream()) | 967 | using (StreamReader sr = new StreamReader(webResponse.GetResponseStream())) |
921 | using (StreamReader sr = new StreamReader(s)) | 968 | { |
922 | response_mapItems_reply = sr.ReadToEnd().Trim(); | 969 | response_mapItems_reply = sr.ReadToEnd().Trim(); |
970 | } | ||
923 | } | 971 | } |
924 | else | 972 | else |
925 | { | 973 | { |
926 | return new OSDMap(); | 974 | Interlocked.Decrement(ref nAsyncRequests); |
927 | } | 975 | return; |
928 | } | 976 | } |
977 | } | ||
929 | } | 978 | } |
930 | catch (WebException) | 979 | catch (WebException) |
931 | { | 980 | { |
932 | responseMap["connect"] = OSD.FromBoolean(false); | ||
933 | lock (m_blacklistedurls) | 981 | lock (m_blacklistedurls) |
934 | { | 982 | m_blacklistedurls.AddOrUpdate(httpserver, 0, expireBlackListTime); |
935 | if (!m_blacklistedurls.ContainsKey(httpserver)) | 983 | lock (m_blacklistedregions) |
936 | m_blacklistedurls.Add(httpserver, Environment.TickCount); | 984 | m_blacklistedregions.AddOrUpdate(regionhandle, 0, expireBlackListTime); |
937 | } | ||
938 | 985 | ||
939 | m_log.WarnFormat("[WORLD MAP]: Blacklisted {0}", httpserver); | 986 | m_log.WarnFormat("[WORLD MAP]: Blacklisted url {0}", httpserver); |
940 | 987 | ||
941 | return responseMap; | 988 | Interlocked.Decrement(ref nAsyncRequests); |
989 | return; | ||
942 | } | 990 | } |
943 | catch | 991 | catch |
944 | { | 992 | { |
945 | m_log.DebugFormat("[WORLD MAP]: RequestMapItems failed for {0}", httpserver); | 993 | m_log.DebugFormat("[WORLD MAP]: RequestMapItems failed for {0}", httpserver); |
946 | responseMap["connect"] = OSD.FromBoolean(false); | ||
947 | lock (m_blacklistedregions) | 994 | lock (m_blacklistedregions) |
948 | { | 995 | m_blacklistedregions.AddOrUpdate(regionhandle, 0, expireBlackListTime); |
949 | if (!m_blacklistedregions.ContainsKey(regionhandle)) | ||
950 | m_blacklistedregions.Add(regionhandle, Environment.TickCount); | ||
951 | } | ||
952 | 996 | ||
953 | return responseMap; | 997 | Interlocked.Decrement(ref nAsyncRequests); |
998 | return; | ||
954 | } | 999 | } |
955 | 1000 | ||
956 | OSD rezResponse = null; | ||
957 | try | 1001 | try |
958 | { | 1002 | { |
959 | rezResponse = OSDParser.DeserializeLLSDXml(response_mapItems_reply); | 1003 | responseMap = (OSDMap)OSDParser.DeserializeLLSDXml(response_mapItems_reply); |
960 | |||
961 | responseMap = (OSDMap)rezResponse; | ||
962 | responseMap["requestID"] = OSD.FromUUID(requestID); | ||
963 | } | 1004 | } |
964 | catch (Exception ex) | 1005 | catch (Exception ex) |
965 | { | 1006 | { |
966 | m_log.InfoFormat("[WORLD MAP]: exception on parse of RequestMapItems reply from {0}: {1}", httpserver, ex.Message); | 1007 | 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) | 1008 | lock (m_blacklistedregions) |
970 | { | 1009 | m_blacklistedregions.AddOrUpdate(regionhandle, 0, expireBlackListTime); |
971 | if (!m_blacklistedregions.ContainsKey(regionhandle)) | ||
972 | m_blacklistedregions.Add(regionhandle, Environment.TickCount); | ||
973 | } | ||
974 | 1010 | ||
975 | return responseMap; | 1011 | Interlocked.Decrement(ref nAsyncRequests); |
1012 | return; | ||
976 | } | 1013 | } |
977 | } | 1014 | } |
978 | 1015 | ||
979 | if (!responseMap.ContainsKey(itemtype.ToString())) // remote sim doesnt have the stated region handle | 1016 | // cache the response that may include other valid items |
1017 | lock (m_cachedRegionMapItemsResponses) | ||
1018 | m_cachedRegionMapItemsResponses.AddOrUpdate(regionhandle, responseMap, expireResponsesTime); | ||
1019 | |||
1020 | flags &= 0xffff; | ||
1021 | |||
1022 | if (id != UUID.Zero) | ||
980 | { | 1023 | { |
981 | m_log.DebugFormat("[WORLD MAP]: Remote sim does not have the stated region. Blacklisting."); | 1024 | ScenePresence av = null; |
982 | lock (m_blacklistedregions) | 1025 | m_scene.TryGetScenePresence(id, out av); |
1026 | if (av != null && !av.IsChildAgent && !av.IsDeleted && !av.IsInTransit) | ||
983 | { | 1027 | { |
984 | if (!m_blacklistedregions.ContainsKey(regionhandle)) | 1028 | // send all the items or viewers will never ask for them, except green dots |
985 | m_blacklistedregions.Add(regionhandle, Environment.TickCount); | 1029 | foreach (uint itfs in itemTypesForcedSend) |
1030 | { | ||
1031 | if (responseMap.ContainsKey(itfs.ToString())) | ||
1032 | { | ||
1033 | List<mapItemReply> returnitems = new List<mapItemReply>(); | ||
1034 | // OSDArray itemarray = (OSDArray)responseMap[itemtype.ToString()]; | ||
1035 | OSDArray itemarray = (OSDArray)responseMap[itfs.ToString()]; | ||
1036 | for (int i = 0; i < itemarray.Count; i++) | ||
1037 | { | ||
1038 | OSDMap mapitem = (OSDMap)itemarray[i]; | ||
1039 | mapItemReply mi = new mapItemReply(); | ||
1040 | mi.x = (uint)mapitem["X"].AsInteger(); | ||
1041 | mi.y = (uint)mapitem["Y"].AsInteger(); | ||
1042 | mi.id = mapitem["ID"].AsUUID(); | ||
1043 | mi.Extra = mapitem["Extra"].AsInteger(); | ||
1044 | mi.Extra2 = mapitem["Extra2"].AsInteger(); | ||
1045 | mi.name = mapitem["Name"].AsString(); | ||
1046 | returnitems.Add(mi); | ||
1047 | } | ||
1048 | // av.ControllingClient.SendMapItemReply(returnitems.ToArray(), itemtype, flags); | ||
1049 | av.ControllingClient.SendMapItemReply(returnitems.ToArray(), itfs, flags); | ||
1050 | } | ||
1051 | } | ||
986 | } | 1052 | } |
987 | } | 1053 | } |
988 | 1054 | ||
989 | return responseMap; | 1055 | Interlocked.Decrement(ref nAsyncRequests); |
990 | } | 1056 | } |
991 | 1057 | ||
992 | /// <summary> | 1058 | /// <summary> |
@@ -996,87 +1062,196 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
996 | /// <param name="minY"></param> | 1062 | /// <param name="minY"></param> |
997 | /// <param name="maxX"></param> | 1063 | /// <param name="maxX"></param> |
998 | /// <param name="maxY"></param> | 1064 | /// <param name="maxY"></param> |
999 | public virtual void RequestMapBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag) | 1065 | public void RequestMapBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag) |
1000 | { | 1066 | { |
1001 | if ((flag & 0x10000) != 0) // user clicked on qthe map a tile that isn't visible | 1067 | // m_log.DebugFormat("[WoldMapModule] RequestMapBlocks {0}={1}={2}={3} {4}", minX, minY, maxX, maxY, flag); |
1068 | |||
1069 | GetAndSendBlocks(remoteClient, minX, minY, maxX, maxY, flag); | ||
1070 | } | ||
1071 | |||
1072 | private const double SPAMBLOCKTIMEms = 300000; // 5 minutes | ||
1073 | private Dictionary<UUID,double> spamBlocked = new Dictionary<UUID,double>(); | ||
1074 | |||
1075 | protected virtual List<MapBlockData> GetAndSendBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag) | ||
1076 | { | ||
1077 | // anti spam because of FireStorm 4.7.7 absurd request repeat rates | ||
1078 | // possible others | ||
1079 | |||
1080 | double now = Util.GetTimeStampMS(); | ||
1081 | UUID agentID = remoteClient.AgentId; | ||
1082 | |||
1083 | lock (m_mapBlockRequestEvent) | ||
1002 | { | 1084 | { |
1003 | List<MapBlockData> response = new List<MapBlockData>(); | 1085 | if(spamBlocked.ContainsKey(agentID)) |
1004 | |||
1005 | // this should return one mapblock at most. It is triggered by a click | ||
1006 | // on an unloaded square. | ||
1007 | // But make sure: Look whether the one we requested is in there | ||
1008 | List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID, | ||
1009 | (int)Util.RegionToWorldLoc((uint)minX), (int)Util.RegionToWorldLoc((uint)maxX), | ||
1010 | (int)Util.RegionToWorldLoc((uint)minY), (int)Util.RegionToWorldLoc((uint)maxY) ); | ||
1011 | |||
1012 | m_log.DebugFormat("[WORLD MAP MODULE] RequestMapBlocks min=<{0},{1}>, max=<{2},{3}>, flag={4}, cntFound={5}", | ||
1013 | minX, minY, maxX, maxY, flag.ToString("X"), regions.Count); | ||
1014 | if (regions != null) | ||
1015 | { | 1086 | { |
1016 | foreach (GridRegion r in regions) | 1087 | if(spamBlocked[agentID] < now && |
1088 | (!m_mapBlockRequests.ContainsKey(agentID) || | ||
1089 | m_mapBlockRequests[agentID].Count == 0 )) | ||
1017 | { | 1090 | { |
1018 | if (r.RegionLocX == Util.RegionToWorldLoc((uint)minX) | 1091 | spamBlocked.Remove(agentID); |
1019 | && r.RegionLocY == Util.RegionToWorldLoc((uint)minY) ) | 1092 | m_log.DebugFormat("[WoldMapModule] RequestMapBlocks release spammer {0}", agentID); |
1093 | } | ||
1094 | else | ||
1095 | return new List<MapBlockData>(); | ||
1096 | } | ||
1097 | else | ||
1098 | { | ||
1099 | // ugly slow expire spammers | ||
1100 | if(spamBlocked.Count > 0) | ||
1101 | { | ||
1102 | UUID k = UUID.Zero; | ||
1103 | bool expireone = false; | ||
1104 | foreach(UUID k2 in spamBlocked.Keys) | ||
1020 | { | 1105 | { |
1021 | // found it => add it to response | 1106 | if(spamBlocked[k2] < now && |
1022 | // Version 2 viewers can handle the larger regions | 1107 | (!m_mapBlockRequests.ContainsKey(k2) || |
1023 | if ((flag & 2) == 2) | 1108 | m_mapBlockRequests[k2].Count == 0 )) |
1024 | response.AddRange(Map2BlockFromGridRegion(r, flag)); | 1109 | { |
1025 | else | 1110 | m_log.DebugFormat("[WoldMapModule] RequestMapBlocks release spammer {0}", k2); |
1026 | response.Add(MapBlockFromGridRegion(r, flag)); | 1111 | k = k2; |
1027 | break; | 1112 | expireone = true; |
1113 | } | ||
1114 | break; // doing one at a time | ||
1028 | } | 1115 | } |
1116 | if(expireone) | ||
1117 | spamBlocked.Remove(k); | ||
1029 | } | 1118 | } |
1030 | } | 1119 | } |
1031 | 1120 | ||
1032 | if (response.Count == 0) | 1121 | // m_log.DebugFormat("[WoldMapModule] RequestMapBlocks {0}={1}={2}={3} {4}", minX, minY, maxX, maxY, flag); |
1122 | |||
1123 | MapBlockRequestData req = new MapBlockRequestData(); | ||
1124 | |||
1125 | req.client = remoteClient; | ||
1126 | req.minX = minX; | ||
1127 | req.maxX = maxX; | ||
1128 | req.minY = minY; | ||
1129 | req.maxY = maxY; | ||
1130 | req.flags = flag; | ||
1131 | |||
1132 | if (!m_mapBlockRequests.ContainsKey(agentID)) | ||
1133 | m_mapBlockRequests[agentID] = new Queue<MapBlockRequestData>(); | ||
1134 | if(m_mapBlockRequests[agentID].Count < 150 ) | ||
1135 | m_mapBlockRequests[agentID].Enqueue(req); | ||
1136 | else | ||
1033 | { | 1137 | { |
1034 | // response still empty => couldn't find the map-tile the user clicked on => tell the client | 1138 | spamBlocked[agentID] = now + SPAMBLOCKTIMEms; |
1035 | MapBlockData block = new MapBlockData(); | 1139 | m_log.DebugFormat("[WoldMapModule] RequestMapBlocks blocking spammer {0} for {1} s",agentID, SPAMBLOCKTIMEms/1000.0); |
1036 | block.X = (ushort)minX; | ||
1037 | block.Y = (ushort)minY; | ||
1038 | block.Access = (byte)SimAccess.Down; // means 'simulator is offline' | ||
1039 | // block.Access = (byte)SimAccess.NonExistent; | ||
1040 | response.Add(block); | ||
1041 | } | 1140 | } |
1042 | // The lower 16 bits are an unsigned int16 | 1141 | m_mapBlockRequestEvent.Set(); |
1043 | remoteClient.SendMapBlock(response, flag & 0xffff); | ||
1044 | } | 1142 | } |
1045 | else | 1143 | |
1144 | return new List<MapBlockData>(); | ||
1145 | } | ||
1146 | |||
1147 | protected void MapBlockSendThread() | ||
1148 | { | ||
1149 | List<MapBlockRequestData> thisRunData = new List<MapBlockRequestData>(); | ||
1150 | while (true) | ||
1046 | { | 1151 | { |
1047 | // normal mapblock request. Use the provided values | 1152 | while(!m_mapBlockRequestEvent.WaitOne(4900)) |
1048 | GetAndSendBlocks(remoteClient, minX, minY, maxX, maxY, flag); | 1153 | { |
1154 | Watchdog.UpdateThread(); | ||
1155 | if(m_scene == null) | ||
1156 | return; | ||
1157 | } | ||
1158 | Watchdog.UpdateThread(); | ||
1159 | lock (m_mapBlockRequestEvent) | ||
1160 | { | ||
1161 | int total = 0; | ||
1162 | foreach (Queue<MapBlockRequestData> q in m_mapBlockRequests.Values) | ||
1163 | { | ||
1164 | if (q.Count > 0) | ||
1165 | thisRunData.Add(q.Dequeue()); | ||
1166 | |||
1167 | total += q.Count; | ||
1168 | } | ||
1169 | |||
1170 | if (total == 0) | ||
1171 | m_mapBlockRequestEvent.Reset(); | ||
1172 | } | ||
1173 | |||
1174 | if(thisRunData.Count > 0) | ||
1175 | { | ||
1176 | foreach (MapBlockRequestData req in thisRunData) | ||
1177 | { | ||
1178 | // Null client stops thread | ||
1179 | if (req.client == null) | ||
1180 | return; | ||
1181 | |||
1182 | GetAndSendBlocksInternal(req.client, req.minX, req.minY, req.maxX, req.maxY, req.flags); | ||
1183 | } | ||
1184 | |||
1185 | thisRunData.Clear(); | ||
1186 | } | ||
1187 | |||
1188 | Thread.Sleep(50); | ||
1049 | } | 1189 | } |
1050 | } | 1190 | } |
1051 | 1191 | ||
1052 | protected virtual List<MapBlockData> GetAndSendBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag) | 1192 | protected virtual List<MapBlockData> GetAndSendBlocksInternal(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag) |
1053 | { | 1193 | { |
1194 | List<MapBlockData> allBlocks = new List<MapBlockData>(); | ||
1054 | List<MapBlockData> mapBlocks = new List<MapBlockData>(); | 1195 | List<MapBlockData> mapBlocks = new List<MapBlockData>(); |
1055 | List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID, | 1196 | List<GridRegion> regions = m_scene.GridService.GetRegionRange(m_scene.RegionInfo.ScopeID, |
1056 | (int)Util.RegionToWorldLoc((uint)(minX - 4)), (int)Util.RegionToWorldLoc((uint)(maxX + 4)), | 1197 | minX * (int)Constants.RegionSize, |
1057 | (int)Util.RegionToWorldLoc((uint)(minY - 4)), (int)Util.RegionToWorldLoc((uint)(maxY + 4)) ); | 1198 | maxX * (int)Constants.RegionSize, |
1058 | //m_log.DebugFormat("{0} GetAndSendBlocks. min=<{1},{2}>, max=<{3},{4}>, cntFound={5}", | 1199 | minY * (int)Constants.RegionSize, |
1059 | // LogHeader, minX, minY, maxX, maxY, regions.Count); | 1200 | maxY * (int)Constants.RegionSize); |
1201 | |||
1202 | // only send a negative answer for a single region request | ||
1203 | // corresponding to a click on the map. Current viewers | ||
1204 | // keep displaying "loading.." without this | ||
1205 | if (regions.Count == 0 && (flag & 0x10000) != 0 && minX == maxX && minY == maxY) | ||
1206 | { | ||
1207 | MapBlockData block = new MapBlockData(); | ||
1208 | block.X = (ushort)minX; | ||
1209 | block.Y = (ushort)minY; | ||
1210 | block.MapImageId = UUID.Zero; | ||
1211 | block.Access = (byte)SimAccess.NonExistent; | ||
1212 | allBlocks.Add(block); | ||
1213 | mapBlocks.Add(block); | ||
1214 | remoteClient.SendMapBlock(mapBlocks, flag & 0xffff); | ||
1215 | return allBlocks; | ||
1216 | } | ||
1217 | |||
1218 | flag &= 0xffff; | ||
1219 | |||
1060 | foreach (GridRegion r in regions) | 1220 | foreach (GridRegion r in regions) |
1061 | { | 1221 | { |
1062 | // Version 2 viewers can handle the larger regions | 1222 | if (r == null) |
1063 | if ((flag & 2) == 2) | 1223 | continue; |
1064 | mapBlocks.AddRange(Map2BlockFromGridRegion(r, flag)); | 1224 | MapBlockData block = new MapBlockData(); |
1065 | else | 1225 | MapBlockFromGridRegion(block, r, flag); |
1066 | mapBlocks.Add(MapBlockFromGridRegion(r, flag)); | 1226 | mapBlocks.Add(block); |
1227 | allBlocks.Add(block); | ||
1228 | |||
1229 | if (mapBlocks.Count >= 10) | ||
1230 | { | ||
1231 | remoteClient.SendMapBlock(mapBlocks, flag); | ||
1232 | mapBlocks.Clear(); | ||
1233 | Thread.Sleep(50); | ||
1234 | } | ||
1067 | } | 1235 | } |
1068 | remoteClient.SendMapBlock(mapBlocks, flag & 0xffff); | 1236 | if (mapBlocks.Count > 0) |
1237 | remoteClient.SendMapBlock(mapBlocks, flag); | ||
1069 | 1238 | ||
1070 | return mapBlocks; | 1239 | return allBlocks; |
1071 | } | 1240 | } |
1072 | 1241 | ||
1073 | // Fill a passed MapBlockData from a GridRegion | 1242 | public void MapBlockFromGridRegion(MapBlockData block, GridRegion r, uint flag) |
1074 | public MapBlockData MapBlockFromGridRegion(GridRegion r, uint flag) | ||
1075 | { | 1243 | { |
1076 | MapBlockData block = new MapBlockData(); | 1244 | if (r == null) |
1245 | { | ||
1246 | // we should not get here ?? | ||
1247 | // block.Access = (byte)SimAccess.Down; this is for a grid reply on r | ||
1248 | block.Access = (byte)SimAccess.NonExistent; | ||
1249 | block.MapImageId = UUID.Zero; | ||
1250 | return; | ||
1251 | } | ||
1077 | 1252 | ||
1078 | block.Access = r.Access; | 1253 | block.Access = r.Access; |
1079 | switch (flag & 0xffff) | 1254 | switch (flag) |
1080 | { | 1255 | { |
1081 | case 0: | 1256 | case 0: |
1082 | block.MapImageId = r.TerrainImage; | 1257 | block.MapImageId = r.TerrainImage; |
@@ -1089,50 +1264,13 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1089 | break; | 1264 | break; |
1090 | } | 1265 | } |
1091 | block.Name = r.RegionName; | 1266 | block.Name = r.RegionName; |
1092 | block.X = (ushort)Util.WorldToRegionLoc((uint)r.RegionLocX); | 1267 | block.X = (ushort)(r.RegionLocX / Constants.RegionSize); |
1093 | block.Y = (ushort)Util.WorldToRegionLoc((uint)r.RegionLocY); | 1268 | block.Y = (ushort)(r.RegionLocY / Constants.RegionSize); |
1094 | block.SizeX = (ushort) r.RegionSizeX; | 1269 | block.SizeX = (ushort)r.RegionSizeX; |
1095 | block.SizeY = (ushort) r.RegionSizeY; | 1270 | block.SizeY = (ushort)r.RegionSizeY; |
1096 | 1271 | ||
1097 | return block; | ||
1098 | } | 1272 | } |
1099 | 1273 | ||
1100 | public List<MapBlockData> Map2BlockFromGridRegion(GridRegion r, uint flag) | ||
1101 | { | ||
1102 | List<MapBlockData> blocks = new List<MapBlockData>(); | ||
1103 | MapBlockData block = new MapBlockData(); | ||
1104 | if (r == null) | ||
1105 | { | ||
1106 | block.Access = (byte)SimAccess.Down; | ||
1107 | block.MapImageId = UUID.Zero; | ||
1108 | blocks.Add(block); | ||
1109 | } | ||
1110 | else | ||
1111 | { | ||
1112 | block.Access = r.Access; | ||
1113 | switch (flag & 0xffff) | ||
1114 | { | ||
1115 | case 0: | ||
1116 | block.MapImageId = r.TerrainImage; | ||
1117 | break; | ||
1118 | case 2: | ||
1119 | block.MapImageId = r.ParcelImage; | ||
1120 | break; | ||
1121 | default: | ||
1122 | block.MapImageId = UUID.Zero; | ||
1123 | break; | ||
1124 | } | ||
1125 | block.Name = r.RegionName; | ||
1126 | block.X = (ushort)(r.RegionLocX / Constants.RegionSize); | ||
1127 | block.Y = (ushort)(r.RegionLocY / Constants.RegionSize); | ||
1128 | block.SizeX = (ushort)r.RegionSizeX; | ||
1129 | block.SizeY = (ushort)r.RegionSizeY; | ||
1130 | blocks.Add(block); | ||
1131 | } | ||
1132 | return blocks; | ||
1133 | } | ||
1134 | |||
1135 | |||
1136 | public Hashtable OnHTTPThrottled(Hashtable keysvals) | 1274 | public Hashtable OnHTTPThrottled(Hashtable keysvals) |
1137 | { | 1275 | { |
1138 | Hashtable reply = new Hashtable(); | 1276 | Hashtable reply = new Hashtable(); |
@@ -1145,67 +1283,71 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1145 | 1283 | ||
1146 | public Hashtable OnHTTPGetMapImage(Hashtable keysvals) | 1284 | public Hashtable OnHTTPGetMapImage(Hashtable keysvals) |
1147 | { | 1285 | { |
1148 | m_log.Debug("[WORLD MAP]: Sending map image jpeg"); | ||
1149 | Hashtable reply = new Hashtable(); | 1286 | Hashtable reply = new Hashtable(); |
1150 | int statuscode = 200; | 1287 | int statuscode = 200; |
1151 | byte[] jpeg = new byte[0]; | 1288 | byte[] jpeg = new byte[0]; |
1152 | 1289 | ||
1153 | if (myMapImageJPEG.Length == 0) | 1290 | if (m_scene.RegionInfo.RegionSettings.TerrainImageID != UUID.Zero) |
1154 | { | 1291 | { |
1155 | MemoryStream imgstream = null; | 1292 | m_log.Debug("[WORLD MAP]: Sending map image jpeg"); |
1156 | Bitmap mapTexture = new Bitmap(1,1); | ||
1157 | ManagedImage managedImage; | ||
1158 | Image image = (Image)mapTexture; | ||
1159 | 1293 | ||
1160 | try | 1294 | if (myMapImageJPEG.Length == 0) |
1161 | { | 1295 | { |
1162 | // Taking our jpeg2000 data, decoding it, then saving it to a byte array with regular jpeg data | 1296 | MemoryStream imgstream = null; |
1297 | Bitmap mapTexture = new Bitmap(1, 1); | ||
1298 | ManagedImage managedImage; | ||
1299 | Image image = (Image)mapTexture; | ||
1300 | |||
1301 | try | ||
1302 | { | ||
1303 | // Taking our jpeg2000 data, decoding it, then saving it to a byte array with regular jpeg data | ||
1163 | 1304 | ||
1164 | imgstream = new MemoryStream(); | 1305 | imgstream = new MemoryStream(); |
1165 | 1306 | ||
1166 | // non-async because we know we have the asset immediately. | 1307 | // non-async because we know we have the asset immediately. |
1167 | AssetBase mapasset = m_scene.AssetService.Get(m_scene.RegionInfo.RegionSettings.TerrainImageID.ToString()); | 1308 | AssetBase mapasset = m_scene.AssetService.Get(m_scene.RegionInfo.RegionSettings.TerrainImageID.ToString()); |
1168 | 1309 | ||
1169 | // Decode image to System.Drawing.Image | 1310 | // Decode image to System.Drawing.Image |
1170 | if (OpenJPEG.DecodeToImage(mapasset.Data, out managedImage, out image)) | 1311 | if (OpenJPEG.DecodeToImage(mapasset.Data, out managedImage, out image)) |
1171 | { | 1312 | { |
1172 | // Save to bitmap | 1313 | // Save to bitmap |
1173 | mapTexture = new Bitmap(image); | 1314 | mapTexture = new Bitmap(image); |
1174 | 1315 | ||
1175 | EncoderParameters myEncoderParameters = new EncoderParameters(); | 1316 | EncoderParameters myEncoderParameters = new EncoderParameters(); |
1176 | myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 95L); | 1317 | myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 95L); |
1177 | 1318 | ||
1178 | // Save bitmap to stream | 1319 | // Save bitmap to stream |
1179 | mapTexture.Save(imgstream, GetEncoderInfo("image/jpeg"), myEncoderParameters); | 1320 | mapTexture.Save(imgstream, GetEncoderInfo("image/jpeg"), myEncoderParameters); |
1180 | 1321 | ||
1181 | // Write the stream to a byte array for output | 1322 | // Write the stream to a byte array for output |
1182 | jpeg = imgstream.ToArray(); | 1323 | jpeg = imgstream.ToArray(); |
1183 | myMapImageJPEG = jpeg; | 1324 | myMapImageJPEG = jpeg; |
1325 | } | ||
1184 | } | 1326 | } |
1185 | } | 1327 | catch (Exception) |
1186 | catch (Exception) | 1328 | { |
1187 | { | 1329 | // Dummy! |
1188 | // Dummy! | 1330 | m_log.Warn("[WORLD MAP]: Unable to generate Map image"); |
1189 | m_log.Warn("[WORLD MAP]: Unable to generate Map image"); | 1331 | } |
1190 | } | 1332 | finally |
1191 | finally | 1333 | { |
1192 | { | 1334 | // Reclaim memory, these are unmanaged resources |
1193 | // Reclaim memory, these are unmanaged resources | 1335 | // If we encountered an exception, one or more of these will be null |
1194 | // If we encountered an exception, one or more of these will be null | 1336 | if (mapTexture != null) |
1195 | if (mapTexture != null) | 1337 | mapTexture.Dispose(); |
1196 | mapTexture.Dispose(); | ||
1197 | 1338 | ||
1198 | if (image != null) | 1339 | if (image != null) |
1199 | image.Dispose(); | 1340 | image.Dispose(); |
1200 | 1341 | ||
1201 | if (imgstream != null) | 1342 | if (imgstream != null) |
1202 | imgstream.Dispose(); | 1343 | imgstream.Dispose(); |
1344 | } | ||
1345 | } | ||
1346 | else | ||
1347 | { | ||
1348 | // Use cached version so we don't have to loose our mind | ||
1349 | jpeg = myMapImageJPEG; | ||
1203 | } | 1350 | } |
1204 | } | ||
1205 | else | ||
1206 | { | ||
1207 | // Use cached version so we don't have to loose our mind | ||
1208 | jpeg = myMapImageJPEG; | ||
1209 | } | 1351 | } |
1210 | 1352 | ||
1211 | reply["str_response_string"] = Convert.ToBase64String(jpeg); | 1353 | reply["str_response_string"] = Convert.ToBase64String(jpeg); |
@@ -1267,22 +1409,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1267 | 1409 | ||
1268 | foreach (GridRegion r in regions) | 1410 | foreach (GridRegion r in regions) |
1269 | { | 1411 | { |
1270 | MapBlockData mapBlock = MapBlockFromGridRegion(r, 0); | 1412 | MapBlockData mapBlock = new MapBlockData(); |
1413 | MapBlockFromGridRegion(mapBlock, r, 0); | ||
1271 | AssetBase texAsset = m_scene.AssetService.Get(mapBlock.MapImageId.ToString()); | 1414 | AssetBase texAsset = m_scene.AssetService.Get(mapBlock.MapImageId.ToString()); |
1272 | 1415 | ||
1273 | if (texAsset != null) | 1416 | if (texAsset != null) |
1274 | { | 1417 | { |
1275 | textures.Add(texAsset); | 1418 | textures.Add(texAsset); |
1276 | } | 1419 | } |
1277 | //else | ||
1278 | //{ | ||
1279 | // // WHAT?!? This doesn't seem right. Commenting (diva) | ||
1280 | // texAsset = m_scene.AssetService.Get(mapBlock.MapImageId.ToString()); | ||
1281 | // if (texAsset != null) | ||
1282 | // { | ||
1283 | // textures.Add(texAsset); | ||
1284 | // } | ||
1285 | //} | ||
1286 | } | 1420 | } |
1287 | 1421 | ||
1288 | foreach (AssetBase asset in textures) | 1422 | foreach (AssetBase asset in textures) |
@@ -1308,6 +1442,10 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1308 | 1442 | ||
1309 | mapTexture.Save(exportPath, ImageFormat.Jpeg); | 1443 | mapTexture.Save(exportPath, ImageFormat.Jpeg); |
1310 | 1444 | ||
1445 | g.Dispose(); | ||
1446 | mapTexture.Dispose(); | ||
1447 | sea.Dispose(); | ||
1448 | |||
1311 | m_log.InfoFormat( | 1449 | m_log.InfoFormat( |
1312 | "[WORLD MAP]: Successfully exported world map for {0} to {1}", | 1450 | "[WORLD MAP]: Successfully exported world map for {0} to {1}", |
1313 | m_scene.RegionInfo.RegionName, exportPath); | 1451 | m_scene.RegionInfo.RegionName, exportPath); |
@@ -1320,17 +1458,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1320 | if (consoleScene != null && consoleScene != m_scene) | 1458 | if (consoleScene != null && consoleScene != m_scene) |
1321 | return; | 1459 | return; |
1322 | 1460 | ||
1323 | if (m_mapImageGenerator == null) | 1461 | GenerateMaptile(); |
1324 | { | ||
1325 | Console.WriteLine("No map image generator available for {0}", m_scene.Name); | ||
1326 | return; | ||
1327 | } | ||
1328 | |||
1329 | using (Bitmap mapbmp = m_mapImageGenerator.CreateMapTile()) | ||
1330 | { | ||
1331 | GenerateMaptile(mapbmp); | ||
1332 | m_mapImageServiceModule.UploadMapTile(m_scene, mapbmp); | ||
1333 | } | ||
1334 | } | 1462 | } |
1335 | 1463 | ||
1336 | public OSD HandleRemoteMapItemRequest(string path, OSD request, string endpoint) | 1464 | public OSD HandleRemoteMapItemRequest(string path, OSD request, string endpoint) |
@@ -1338,9 +1466,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1338 | uint xstart = 0; | 1466 | uint xstart = 0; |
1339 | uint ystart = 0; | 1467 | uint ystart = 0; |
1340 | 1468 | ||
1341 | Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle,out xstart,out ystart); | 1469 | Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle, out xstart, out ystart); |
1342 | // m_log.DebugFormat("{0} HandleRemoteMapItemRequest. loc=<{1},{2}>", | ||
1343 | // LogHeader, Util.WorldToRegionLoc(xstart), Util.WorldToRegionLoc(ystart)); | ||
1344 | 1470 | ||
1345 | // Service 6 (MAP_ITEM_AGENTS_LOCATION; green dots) | 1471 | // Service 6 (MAP_ITEM_AGENTS_LOCATION; green dots) |
1346 | 1472 | ||
@@ -1362,8 +1488,8 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1362 | } | 1488 | } |
1363 | else | 1489 | else |
1364 | { | 1490 | { |
1365 | OSDArray responsearr = new OSDArray(m_scene.GetRootAgentCount()); | 1491 | OSDArray responsearr = new OSDArray(); // Don't preallocate. MT (m_scene.GetRootAgentCount()); |
1366 | m_scene.ForEachRootScenePresence(delegate(ScenePresence sp) | 1492 | m_scene.ForEachRootScenePresence(delegate (ScenePresence sp) |
1367 | { | 1493 | { |
1368 | OSDMap responsemapdata = new OSDMap(); | 1494 | OSDMap responsemapdata = new OSDMap(); |
1369 | responsemapdata["X"] = OSD.FromInteger((int)(xstart + sp.AbsolutePosition.X)); | 1495 | responsemapdata["X"] = OSD.FromInteger((int)(xstart + sp.AbsolutePosition.X)); |
@@ -1377,28 +1503,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1377 | responsemap["6"] = responsearr; | 1503 | responsemap["6"] = responsearr; |
1378 | } | 1504 | } |
1379 | 1505 | ||
1380 | // Service 7 (MAP_ITEM_LAND_FOR_SALE) | 1506 | // Service 7/10 (MAP_ITEM_LAND_FOR_SALE/ADULT) |
1381 | 1507 | ||
1382 | ILandChannel landChannel = m_scene.LandChannel; | 1508 | ILandChannel landChannel = m_scene.LandChannel; |
1383 | List<ILandObject> parcels = landChannel.AllParcels(); | 1509 | List<ILandObject> parcels = landChannel.AllParcels(); |
1384 | 1510 | ||
1385 | if ((parcels == null) || (parcels.Count == 0)) | 1511 | if ((parcels != null) && (parcels.Count >= 0)) |
1386 | { | 1512 | { |
1387 | OSDMap responsemapdata = new OSDMap(); | 1513 | OSDArray responsearr = new OSDArray(parcels.Count); |
1388 | responsemapdata["X"] = OSD.FromInteger((int)(xstart + 1)); | ||
1389 | responsemapdata["Y"] = OSD.FromInteger((int)(ystart + 1)); | ||
1390 | responsemapdata["ID"] = OSD.FromUUID(UUID.Zero); | ||
1391 | responsemapdata["Name"] = OSD.FromString(""); | ||
1392 | responsemapdata["Extra"] = OSD.FromInteger(0); | ||
1393 | responsemapdata["Extra2"] = OSD.FromInteger(0); | ||
1394 | OSDArray responsearr = new OSDArray(); | ||
1395 | responsearr.Add(responsemapdata); | ||
1396 | |||
1397 | responsemap["7"] = responsearr; | ||
1398 | } | ||
1399 | else | ||
1400 | { | ||
1401 | OSDArray responsearr = new OSDArray(m_scene.GetRootAgentCount()); | ||
1402 | foreach (ILandObject parcel_interface in parcels) | 1514 | foreach (ILandObject parcel_interface in parcels) |
1403 | { | 1515 | { |
1404 | // Play it safe | 1516 | // Play it safe |
@@ -1413,8 +1525,8 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1413 | { | 1525 | { |
1414 | Vector3 min = parcel.AABBMin; | 1526 | Vector3 min = parcel.AABBMin; |
1415 | Vector3 max = parcel.AABBMax; | 1527 | Vector3 max = parcel.AABBMax; |
1416 | float x = (min.X+max.X)/2; | 1528 | float x = (min.X + max.X) / 2; |
1417 | float y = (min.Y+max.Y)/2; | 1529 | float y = (min.Y + max.Y) / 2; |
1418 | 1530 | ||
1419 | OSDMap responsemapdata = new OSDMap(); | 1531 | OSDMap responsemapdata = new OSDMap(); |
1420 | responsemapdata["X"] = OSD.FromInteger((int)(xstart + x)); | 1532 | responsemapdata["X"] = OSD.FromInteger((int)(xstart + x)); |
@@ -1427,7 +1539,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1427 | responsearr.Add(responsemapdata); | 1539 | responsearr.Add(responsemapdata); |
1428 | } | 1540 | } |
1429 | } | 1541 | } |
1430 | responsemap["7"] = responsearr; | 1542 | |
1543 | if(responsearr.Count > 0) | ||
1544 | { | ||
1545 | if(m_scene.RegionInfo.RegionSettings.Maturity == 2) | ||
1546 | responsemap["10"] = responsearr; | ||
1547 | else | ||
1548 | responsemap["7"] = responsearr; | ||
1549 | } | ||
1431 | } | 1550 | } |
1432 | 1551 | ||
1433 | if (m_scene.RegionInfo.RegionSettings.TelehubObject != UUID.Zero) | 1552 | if (m_scene.RegionInfo.RegionSettings.TelehubObject != UUID.Zero) |
@@ -1459,55 +1578,110 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1459 | if (m_scene.Heightmap == null) | 1578 | if (m_scene.Heightmap == null) |
1460 | return; | 1579 | return; |
1461 | 1580 | ||
1581 | if (m_mapImageGenerator == null) | ||
1582 | { | ||
1583 | Console.WriteLine("No map image generator available for {0}", m_scene.Name); | ||
1584 | return; | ||
1585 | } | ||
1462 | m_log.DebugFormat("[WORLD MAP]: Generating map image for {0}", m_scene.Name); | 1586 | m_log.DebugFormat("[WORLD MAP]: Generating map image for {0}", m_scene.Name); |
1463 | 1587 | ||
1464 | using (Bitmap mapbmp = m_mapImageGenerator.CreateMapTile()) | 1588 | using (Bitmap mapbmp = m_mapImageGenerator.CreateMapTile()) |
1465 | { | 1589 | { |
1466 | // V1 (This Module) | ||
1467 | GenerateMaptile(mapbmp); | 1590 | GenerateMaptile(mapbmp); |
1468 | 1591 | ||
1469 | // v2/3 (MapImageServiceModule) | 1592 | if (m_mapImageServiceModule != null) |
1470 | m_mapImageServiceModule.UploadMapTile(m_scene, mapbmp); | 1593 | m_mapImageServiceModule.UploadMapTile(m_scene, mapbmp); |
1471 | } | 1594 | } |
1472 | } | 1595 | } |
1473 | 1596 | ||
1474 | private void GenerateMaptile(Bitmap mapbmp) | 1597 | private void GenerateMaptile(Bitmap mapbmp) |
1475 | { | 1598 | { |
1476 | byte[] data; | 1599 | bool needRegionSave = false; |
1477 | 1600 | ||
1478 | try | 1601 | // remove old assets |
1602 | UUID lastID = m_scene.RegionInfo.RegionSettings.TerrainImageID; | ||
1603 | if (lastID != UUID.Zero) | ||
1479 | { | 1604 | { |
1480 | data = OpenJPEG.EncodeFromImage(mapbmp, true); | 1605 | m_scene.AssetService.Delete(lastID.ToString()); |
1606 | m_scene.RegionInfo.RegionSettings.TerrainImageID = UUID.Zero; | ||
1607 | myMapImageJPEG = new byte[0]; | ||
1608 | needRegionSave = true; | ||
1481 | } | 1609 | } |
1482 | catch (Exception e) // LEGIT: Catching problems caused by OpenJPEG p/invoke | 1610 | |
1611 | lastID = m_scene.RegionInfo.RegionSettings.ParcelImageID; | ||
1612 | if (lastID != UUID.Zero) | ||
1483 | { | 1613 | { |
1484 | m_log.Error("[WORLD MAP]: Failed generating terrain map: " + e); | 1614 | m_scene.AssetService.Delete(lastID.ToString()); |
1485 | return; | 1615 | m_scene.RegionInfo.RegionSettings.ParcelImageID = UUID.Zero; |
1616 | needRegionSave = true; | ||
1486 | } | 1617 | } |
1487 | 1618 | ||
1488 | byte[] overlay = GenerateOverlay(); | 1619 | if(mapbmp != null) |
1620 | { | ||
1621 | try | ||
1622 | { | ||
1623 | byte[] data; | ||
1624 | |||
1625 | // if large region limit its size since new viewers will not use it | ||
1626 | // but it is still usable for ossl | ||
1627 | if(m_scene.RegionInfo.RegionSizeX > Constants.RegionSize || | ||
1628 | m_scene.RegionInfo.RegionSizeY > Constants.RegionSize) | ||
1629 | { | ||
1630 | int bx = mapbmp.Width; | ||
1631 | int by = mapbmp.Height; | ||
1632 | int mb = bx; | ||
1633 | if(mb < by) | ||
1634 | mb = by; | ||
1635 | if(mb > Constants.RegionSize && mb > 0) | ||
1636 | { | ||
1637 | float scale = (float)Constants.RegionSize/(float)mb; | ||
1638 | Size newsize = new Size(); | ||
1639 | newsize.Width = (int)(bx * scale); | ||
1640 | newsize.Height = (int)(by * scale); | ||
1641 | |||
1642 | using(Bitmap scaledbmp = new Bitmap(mapbmp,newsize)) | ||
1643 | data = OpenJPEG.EncodeFromImage(scaledbmp, true); | ||
1644 | } | ||
1645 | else | ||
1646 | data = OpenJPEG.EncodeFromImage(mapbmp, true); | ||
1647 | } | ||
1648 | else | ||
1649 | data = OpenJPEG.EncodeFromImage(mapbmp, true); | ||
1650 | |||
1651 | if (data != null && data.Length > 0) | ||
1652 | { | ||
1653 | UUID terrainImageID = UUID.Random(); | ||
1654 | |||
1655 | AssetBase asset = new AssetBase( | ||
1656 | terrainImageID, | ||
1657 | "terrainImage_" + m_scene.RegionInfo.RegionID.ToString(), | ||
1658 | (sbyte)AssetType.Texture, | ||
1659 | m_scene.RegionInfo.RegionID.ToString()); | ||
1660 | asset.Data = data; | ||
1661 | asset.Description = m_scene.RegionInfo.RegionName; | ||
1662 | asset.Temporary = false; | ||
1663 | asset.Flags = AssetFlags.Maptile; | ||
1489 | 1664 | ||
1490 | UUID terrainImageID = UUID.Random(); | 1665 | // Store the new one |
1491 | UUID parcelImageID = UUID.Zero; | 1666 | m_log.DebugFormat("[WORLD MAP]: Storing map image {0} for {1}", asset.ID, m_scene.RegionInfo.RegionName); |
1492 | 1667 | ||
1493 | AssetBase asset = new AssetBase( | 1668 | m_scene.AssetService.Store(asset); |
1494 | terrainImageID, | ||
1495 | "terrainImage_" + m_scene.RegionInfo.RegionID.ToString(), | ||
1496 | (sbyte)AssetType.Texture, | ||
1497 | m_scene.RegionInfo.RegionID.ToString()); | ||
1498 | asset.Data = data; | ||
1499 | asset.Description = m_scene.RegionInfo.RegionName; | ||
1500 | asset.Temporary = false; | ||
1501 | asset.Flags = AssetFlags.Maptile; | ||
1502 | 1669 | ||
1503 | // Store the new one | 1670 | m_scene.RegionInfo.RegionSettings.TerrainImageID = terrainImageID; |
1504 | m_log.DebugFormat("[WORLD MAP]: Storing map tile {0} for {1}", asset.ID, m_scene.RegionInfo.RegionName); | 1671 | needRegionSave = true; |
1505 | 1672 | } | |
1506 | m_scene.AssetService.Store(asset); | 1673 | } |
1674 | catch (Exception e) | ||
1675 | { | ||
1676 | m_log.Error("[WORLD MAP]: Failed generating terrain map: " + e); | ||
1677 | } | ||
1678 | } | ||
1507 | 1679 | ||
1680 | // V2/3 still seem to need this, or we are missing something somewhere | ||
1681 | byte[] overlay = GenerateOverlay(); | ||
1508 | if (overlay != null) | 1682 | if (overlay != null) |
1509 | { | 1683 | { |
1510 | parcelImageID = UUID.Random(); | 1684 | UUID parcelImageID = UUID.Random(); |
1511 | 1685 | ||
1512 | AssetBase parcels = new AssetBase( | 1686 | AssetBase parcels = new AssetBase( |
1513 | parcelImageID, | 1687 | parcelImageID, |
@@ -1520,20 +1694,13 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1520 | parcels.Flags = AssetFlags.Maptile; | 1694 | parcels.Flags = AssetFlags.Maptile; |
1521 | 1695 | ||
1522 | m_scene.AssetService.Store(parcels); | 1696 | m_scene.AssetService.Store(parcels); |
1697 | |||
1698 | m_scene.RegionInfo.RegionSettings.ParcelImageID = parcelImageID; | ||
1699 | needRegionSave = true; | ||
1523 | } | 1700 | } |
1524 | 1701 | ||
1525 | // Switch to the new one | 1702 | if (needRegionSave) |
1526 | UUID lastTerrainImageID = m_scene.RegionInfo.RegionSettings.TerrainImageID; | 1703 | m_scene.RegionInfo.RegionSettings.Save(); |
1527 | UUID lastParcelImageID = m_scene.RegionInfo.RegionSettings.ParcelImageID; | ||
1528 | m_scene.RegionInfo.RegionSettings.TerrainImageID = terrainImageID; | ||
1529 | m_scene.RegionInfo.RegionSettings.ParcelImageID = parcelImageID; | ||
1530 | m_scene.RegionInfo.RegionSettings.Save(); | ||
1531 | |||
1532 | // Delete the old one | ||
1533 | // m_log.DebugFormat("[WORLDMAP]: Deleting old map tile {0}", lastTerrainImageID); | ||
1534 | m_scene.AssetService.Delete(lastTerrainImageID.ToString()); | ||
1535 | if (lastParcelImageID != UUID.Zero) | ||
1536 | m_scene.AssetService.Delete(lastParcelImageID.ToString()); | ||
1537 | } | 1704 | } |
1538 | 1705 | ||
1539 | private void MakeRootAgent(ScenePresence avatar) | 1706 | private void MakeRootAgent(ScenePresence avatar) |
@@ -1553,6 +1720,12 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1553 | { | 1720 | { |
1554 | m_rootAgents.Remove(avatar.UUID); | 1721 | m_rootAgents.Remove(avatar.UUID); |
1555 | } | 1722 | } |
1723 | |||
1724 | lock (m_mapBlockRequestEvent) | ||
1725 | { | ||
1726 | if (m_mapBlockRequests.ContainsKey(avatar.UUID)) | ||
1727 | m_mapBlockRequests.Remove(avatar.UUID); | ||
1728 | } | ||
1556 | } | 1729 | } |
1557 | 1730 | ||
1558 | public void OnRegionUp(GridRegion otherRegion) | 1731 | public void OnRegionUp(GridRegion otherRegion) |
@@ -1562,46 +1735,67 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1562 | 1735 | ||
1563 | lock (m_blacklistedregions) | 1736 | lock (m_blacklistedregions) |
1564 | { | 1737 | { |
1565 | if (!m_blacklistedregions.ContainsKey(regionhandle)) | 1738 | if (m_blacklistedregions.Contains(regionhandle)) |
1566 | m_blacklistedregions.Remove(regionhandle); | 1739 | m_blacklistedregions.Remove(regionhandle); |
1567 | } | 1740 | } |
1568 | 1741 | ||
1569 | lock (m_blacklistedurls) | 1742 | lock (m_blacklistedurls) |
1570 | { | 1743 | { |
1571 | if (m_blacklistedurls.ContainsKey(httpserver)) | 1744 | if (m_blacklistedurls.Contains(httpserver)) |
1572 | m_blacklistedurls.Remove(httpserver); | 1745 | m_blacklistedurls.Remove(httpserver); |
1573 | } | 1746 | } |
1574 | 1747 | ||
1575 | lock (m_cachedRegionMapItemsAddress) | 1748 | lock (m_cachedRegionMapItemsAddress) |
1576 | { | 1749 | { |
1577 | if (!m_cachedRegionMapItemsAddress.ContainsKey(regionhandle)) | 1750 | m_cachedRegionMapItemsAddress.AddOrUpdate(regionhandle, |
1578 | m_cachedRegionMapItemsAddress.Remove(regionhandle); | 1751 | httpserver, 5.0 * expireBlackListTime); |
1579 | } | 1752 | } |
1580 | } | 1753 | } |
1581 | 1754 | ||
1582 | private Byte[] GenerateOverlay() | 1755 | private Byte[] GenerateOverlay() |
1583 | { | 1756 | { |
1757 | int landTileSize = LandManagementModule.LandUnit; | ||
1758 | |||
1584 | // These need to be ints for bitmap generation | 1759 | // These need to be ints for bitmap generation |
1585 | int regionSizeX = (int)m_scene.RegionInfo.RegionSizeX; | 1760 | int regionSizeX = (int)m_scene.RegionInfo.RegionSizeX; |
1586 | int regionSizeY = (int)m_scene.RegionInfo.RegionSizeY; | ||
1587 | |||
1588 | int landTileSize = LandManagementModule.LandUnit; | ||
1589 | int regionLandTilesX = regionSizeX / landTileSize; | 1761 | int regionLandTilesX = regionSizeX / landTileSize; |
1762 | |||
1763 | int regionSizeY = (int)m_scene.RegionInfo.RegionSizeY; | ||
1590 | int regionLandTilesY = regionSizeY / landTileSize; | 1764 | int regionLandTilesY = regionSizeY / landTileSize; |
1591 | 1765 | ||
1592 | using (Bitmap overlay = new Bitmap(regionSizeX, regionSizeY)) | 1766 | bool landForSale = false; |
1767 | ILandObject land; | ||
1768 | |||
1769 | // scan terrain avoiding potencial merges of large bitmaps | ||
1770 | //TODO create the sell bitmap at landchannel / landmaster ? | ||
1771 | // and auction also, still not suported | ||
1772 | |||
1773 | bool[,] saleBitmap = new bool[regionLandTilesX, regionLandTilesY]; | ||
1774 | for (int x = 0, xx = 0; x < regionLandTilesX; x++ ,xx += landTileSize) | ||
1593 | { | 1775 | { |
1594 | bool[,] saleBitmap = new bool[regionLandTilesX, regionLandTilesY]; | 1776 | for (int y = 0, yy = 0; y < regionLandTilesY; y++, yy += landTileSize) |
1595 | for (int x = 0; x < regionLandTilesX; x++) | ||
1596 | { | 1777 | { |
1597 | for (int y = 0; y < regionLandTilesY; y++) | 1778 | land = m_scene.LandChannel.GetLandObject(xx, yy); |
1779 | if (land != null && (land.LandData.Flags & (uint)ParcelFlags.ForSale) != 0) | ||
1780 | { | ||
1781 | saleBitmap[x, y] = true; | ||
1782 | landForSale = true; | ||
1783 | } | ||
1784 | else | ||
1598 | saleBitmap[x, y] = false; | 1785 | saleBitmap[x, y] = false; |
1599 | } | 1786 | } |
1787 | } | ||
1600 | 1788 | ||
1601 | bool landForSale = false; | 1789 | if (!landForSale) |
1790 | { | ||
1791 | m_log.DebugFormat("[WORLD MAP]: Region {0} has no parcels for sale, not generating overlay", m_scene.RegionInfo.RegionName); | ||
1792 | return null; | ||
1793 | } | ||
1602 | 1794 | ||
1603 | List<ILandObject> parcels = m_scene.LandChannel.AllParcels(); | 1795 | m_log.DebugFormat("[WORLD MAP]: Region {0} has parcels for sale, generating overlay", m_scene.RegionInfo.RegionName); |
1604 | 1796 | ||
1797 | using (Bitmap overlay = new Bitmap(regionSizeX, regionSizeY)) | ||
1798 | { | ||
1605 | Color background = Color.FromArgb(0, 0, 0, 0); | 1799 | Color background = Color.FromArgb(0, 0, 0, 0); |
1606 | 1800 | ||
1607 | using (Graphics g = Graphics.FromImage(overlay)) | 1801 | using (Graphics g = Graphics.FromImage(overlay)) |
@@ -1609,36 +1803,19 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1609 | using (SolidBrush transparent = new SolidBrush(background)) | 1803 | using (SolidBrush transparent = new SolidBrush(background)) |
1610 | g.FillRectangle(transparent, 0, 0, regionSizeX, regionSizeY); | 1804 | g.FillRectangle(transparent, 0, 0, regionSizeX, regionSizeY); |
1611 | 1805 | ||
1612 | foreach (ILandObject land in parcels) | 1806 | // make it a bit transparent |
1807 | using (SolidBrush yellow = new SolidBrush(Color.FromArgb(192, 249, 223, 9))) | ||
1613 | { | 1808 | { |
1614 | // m_log.DebugFormat("[WORLD MAP]: Parcel {0} flags {1}", land.LandData.Name, land.LandData.Flags); | 1809 | for (int x = 0; x < regionLandTilesX; x++) |
1615 | if ((land.LandData.Flags & (uint)ParcelFlags.ForSale) != 0) | ||
1616 | { | 1810 | { |
1617 | landForSale = true; | 1811 | for (int y = 0; y < regionLandTilesY; y++) |
1618 | |||
1619 | saleBitmap = land.MergeLandBitmaps(saleBitmap, land.GetLandBitmap()); | ||
1620 | } | ||
1621 | } | ||
1622 | |||
1623 | if (!landForSale) | ||
1624 | { | ||
1625 | m_log.DebugFormat("[WORLD MAP]: Region {0} has no parcels for sale, not generating overlay", m_scene.RegionInfo.RegionName); | ||
1626 | return null; | ||
1627 | } | ||
1628 | |||
1629 | m_log.DebugFormat("[WORLD MAP]: Region {0} has parcels for sale, generating overlay", m_scene.RegionInfo.RegionName); | ||
1630 | |||
1631 | using (SolidBrush yellow = new SolidBrush(Color.FromArgb(255, 249, 223, 9))) | ||
1632 | { | ||
1633 | for (int x = 0 ; x < regionLandTilesX ; x++) | ||
1634 | { | ||
1635 | for (int y = 0 ; y < regionLandTilesY ; y++) | ||
1636 | { | 1812 | { |
1637 | if (saleBitmap[x, y]) | 1813 | if (saleBitmap[x, y]) |
1638 | g.FillRectangle( | 1814 | g.FillRectangle( |
1639 | yellow, x * landTileSize, | 1815 | yellow, |
1640 | regionSizeX - landTileSize - (y * landTileSize), | 1816 | x * landTileSize, |
1641 | landTileSize, | 1817 | regionSizeX - landTileSize - (y * landTileSize), |
1818 | landTileSize, | ||
1642 | landTileSize); | 1819 | landTileSize); |
1643 | } | 1820 | } |
1644 | } | 1821 | } |
@@ -1659,7 +1836,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1659 | } | 1836 | } |
1660 | } | 1837 | } |
1661 | 1838 | ||
1662 | public struct MapRequestState | 1839 | public class MapRequestState |
1663 | { | 1840 | { |
1664 | public UUID agentID; | 1841 | public UUID agentID; |
1665 | public uint flags; | 1842 | public uint flags; |
@@ -1668,4 +1845,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1668 | public uint itemtype; | 1845 | public uint itemtype; |
1669 | public ulong regionhandle; | 1846 | public ulong regionhandle; |
1670 | } | 1847 | } |
1848 | |||
1849 | public struct MapBlockRequestData | ||
1850 | { | ||
1851 | public IClientAPI client; | ||
1852 | public int minX; | ||
1853 | public int minY; | ||
1854 | public int maxX; | ||
1855 | public int maxY; | ||
1856 | public uint flags; | ||
1857 | } | ||
1671 | } | 1858 | } |