diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/Framework/EntityTransfer')
3 files changed, 1731 insertions, 765 deletions
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index c43edd2..a2417c4 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs | |||
@@ -33,9 +33,10 @@ using System.Threading; | |||
33 | using OpenSim.Framework; | 33 | using OpenSim.Framework; |
34 | using OpenSim.Framework.Capabilities; | 34 | using OpenSim.Framework.Capabilities; |
35 | using OpenSim.Framework.Client; | 35 | using OpenSim.Framework.Client; |
36 | using OpenSim.Framework.Monitoring; | ||
36 | using OpenSim.Region.Framework.Interfaces; | 37 | using OpenSim.Region.Framework.Interfaces; |
37 | using OpenSim.Region.Framework.Scenes; | 38 | using OpenSim.Region.Framework.Scenes; |
38 | using OpenSim.Region.Physics.Manager; | 39 | using OpenSim.Region.PhysicsModules.SharedBase; |
39 | using OpenSim.Services.Interfaces; | 40 | using OpenSim.Services.Interfaces; |
40 | 41 | ||
41 | using GridRegion = OpenSim.Services.Interfaces.GridRegion; | 42 | using GridRegion = OpenSim.Services.Interfaces.GridRegion; |
@@ -51,15 +52,61 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
51 | public class EntityTransferModule : INonSharedRegionModule, IEntityTransferModule | 52 | public class EntityTransferModule : INonSharedRegionModule, IEntityTransferModule |
52 | { | 53 | { |
53 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 54 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
55 | private static readonly string LogHeader = "[ENTITY TRANSFER MODULE]"; | ||
54 | 56 | ||
57 | public const int DefaultMaxTransferDistance = 4095; | ||
55 | public const bool WaitForAgentArrivedAtDestinationDefault = true; | 58 | public const bool WaitForAgentArrivedAtDestinationDefault = true; |
56 | 59 | ||
57 | /// <summary> | 60 | /// <summary> |
61 | /// The maximum distance, in standard region units (256m) that an agent is allowed to transfer. | ||
62 | /// </summary> | ||
63 | public int MaxTransferDistance { get; set; } | ||
64 | |||
65 | /// <summary> | ||
58 | /// If true then on a teleport, the source region waits for a callback from the destination region. If | 66 | /// If true then on a teleport, the source region waits for a callback from the destination region. If |
59 | /// a callback fails to arrive within a set time then the user is pulled back into the source region. | 67 | /// a callback fails to arrive within a set time then the user is pulled back into the source region. |
60 | /// </summary> | 68 | /// </summary> |
61 | public bool WaitForAgentArrivedAtDestination { get; set; } | 69 | public bool WaitForAgentArrivedAtDestination { get; set; } |
62 | 70 | ||
71 | /// <summary> | ||
72 | /// If true then we ask the viewer to disable teleport cancellation and ignore teleport requests. | ||
73 | /// </summary> | ||
74 | /// <remarks> | ||
75 | /// This is useful in situations where teleport is very likely to always succeed and we want to avoid a | ||
76 | /// situation where avatars can be come 'stuck' due to a failed teleport cancellation. Unfortunately, the | ||
77 | /// nature of the teleport protocol makes it extremely difficult (maybe impossible) to make teleport | ||
78 | /// cancellation consistently suceed. | ||
79 | /// </remarks> | ||
80 | public bool DisableInterRegionTeleportCancellation { get; set; } | ||
81 | |||
82 | /// <summary> | ||
83 | /// Number of times inter-region teleport was attempted. | ||
84 | /// </summary> | ||
85 | private Stat m_interRegionTeleportAttempts; | ||
86 | |||
87 | /// <summary> | ||
88 | /// Number of times inter-region teleport was aborted (due to simultaneous client logout). | ||
89 | /// </summary> | ||
90 | private Stat m_interRegionTeleportAborts; | ||
91 | |||
92 | /// <summary> | ||
93 | /// Number of times inter-region teleport was successfully cancelled by the client. | ||
94 | /// </summary> | ||
95 | private Stat m_interRegionTeleportCancels; | ||
96 | |||
97 | /// <summary> | ||
98 | /// Number of times inter-region teleport failed due to server/client/network problems (e.g. viewer failed to | ||
99 | /// connect with destination region). | ||
100 | /// </summary> | ||
101 | /// <remarks> | ||
102 | /// This is not necessarily a problem for this simulator - in open-grid/hg conditions, viewer connectivity to | ||
103 | /// destination simulator is unknown. | ||
104 | /// </remarks> | ||
105 | private Stat m_interRegionTeleportFailures; | ||
106 | |||
107 | protected string m_ThisHomeURI; | ||
108 | protected string m_GatekeeperURI; | ||
109 | |||
63 | protected bool m_Enabled = false; | 110 | protected bool m_Enabled = false; |
64 | 111 | ||
65 | public Scene Scene { get; private set; } | 112 | public Scene Scene { get; private set; } |
@@ -70,10 +117,56 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
70 | /// </summary> | 117 | /// </summary> |
71 | private EntityTransferStateMachine m_entityTransferStateMachine; | 118 | private EntityTransferStateMachine m_entityTransferStateMachine; |
72 | 119 | ||
73 | private ExpiringCache<UUID, ExpiringCache<ulong, DateTime>> m_bannedRegions = | 120 | // For performance, we keed a cached of banned regions so we don't keep going |
74 | new ExpiringCache<UUID, ExpiringCache<ulong, DateTime>>(); | 121 | // to the grid service. |
122 | private class BannedRegionCache | ||
123 | { | ||
124 | private ExpiringCache<UUID, ExpiringCache<ulong, DateTime>> m_bannedRegions = | ||
125 | new ExpiringCache<UUID, ExpiringCache<ulong, DateTime>>(); | ||
126 | ExpiringCache<ulong, DateTime> m_idCache; | ||
127 | DateTime m_banUntil; | ||
128 | public BannedRegionCache() | ||
129 | { | ||
130 | } | ||
131 | // Return 'true' if there is a valid ban entry for this agent in this region | ||
132 | public bool IfBanned(ulong pRegionHandle, UUID pAgentID) | ||
133 | { | ||
134 | bool ret = false; | ||
135 | if (m_bannedRegions.TryGetValue(pAgentID, out m_idCache)) | ||
136 | { | ||
137 | if (m_idCache.TryGetValue(pRegionHandle, out m_banUntil)) | ||
138 | { | ||
139 | if (DateTime.Now < m_banUntil) | ||
140 | { | ||
141 | ret = true; | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | return ret; | ||
146 | } | ||
147 | // Add this agent in this region as a banned person | ||
148 | public void Add(ulong pRegionHandle, UUID pAgentID) | ||
149 | { | ||
150 | if (!m_bannedRegions.TryGetValue(pAgentID, out m_idCache)) | ||
151 | { | ||
152 | m_idCache = new ExpiringCache<ulong, DateTime>(); | ||
153 | m_bannedRegions.Add(pAgentID, m_idCache, TimeSpan.FromSeconds(45)); | ||
154 | } | ||
155 | m_idCache.Add(pRegionHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); | ||
156 | } | ||
157 | // Remove the agent from the region's banned list | ||
158 | public void Remove(ulong pRegionHandle, UUID pAgentID) | ||
159 | { | ||
160 | if (m_bannedRegions.TryGetValue(pAgentID, out m_idCache)) | ||
161 | { | ||
162 | m_idCache.Remove(pRegionHandle); | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | private BannedRegionCache m_bannedRegionCache = new BannedRegionCache(); | ||
75 | 167 | ||
76 | private IEventQueue m_eqModule; | 168 | private IEventQueue m_eqModule; |
169 | private IRegionCombinerModule m_regionCombinerModule; | ||
77 | 170 | ||
78 | #region ISharedRegionModule | 171 | #region ISharedRegionModule |
79 | 172 | ||
@@ -107,11 +200,32 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
107 | /// <param name="source"></param> | 200 | /// <param name="source"></param> |
108 | protected virtual void InitialiseCommon(IConfigSource source) | 201 | protected virtual void InitialiseCommon(IConfigSource source) |
109 | { | 202 | { |
203 | IConfig hypergridConfig = source.Configs["Hypergrid"]; | ||
204 | if (hypergridConfig != null) | ||
205 | { | ||
206 | m_ThisHomeURI = hypergridConfig.GetString("HomeURI", string.Empty); | ||
207 | if (m_ThisHomeURI != string.Empty && !m_ThisHomeURI.EndsWith("/")) | ||
208 | m_ThisHomeURI += '/'; | ||
209 | |||
210 | m_GatekeeperURI = hypergridConfig.GetString("GatekeeperURI", string.Empty); | ||
211 | if (m_GatekeeperURI != string.Empty && !m_GatekeeperURI.EndsWith("/")) | ||
212 | m_GatekeeperURI += '/'; | ||
213 | } | ||
214 | |||
110 | IConfig transferConfig = source.Configs["EntityTransfer"]; | 215 | IConfig transferConfig = source.Configs["EntityTransfer"]; |
111 | if (transferConfig != null) | 216 | if (transferConfig != null) |
112 | { | 217 | { |
218 | DisableInterRegionTeleportCancellation | ||
219 | = transferConfig.GetBoolean("DisableInterRegionTeleportCancellation", false); | ||
220 | |||
113 | WaitForAgentArrivedAtDestination | 221 | WaitForAgentArrivedAtDestination |
114 | = transferConfig.GetBoolean("wait_for_callback", WaitForAgentArrivedAtDestinationDefault); | 222 | = transferConfig.GetBoolean("wait_for_callback", WaitForAgentArrivedAtDestinationDefault); |
223 | |||
224 | MaxTransferDistance = transferConfig.GetInt("max_distance", DefaultMaxTransferDistance); | ||
225 | } | ||
226 | else | ||
227 | { | ||
228 | MaxTransferDistance = DefaultMaxTransferDistance; | ||
115 | } | 229 | } |
116 | 230 | ||
117 | m_entityTransferStateMachine = new EntityTransferStateMachine(this); | 231 | m_entityTransferStateMachine = new EntityTransferStateMachine(this); |
@@ -130,19 +244,87 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
130 | 244 | ||
131 | Scene = scene; | 245 | Scene = scene; |
132 | 246 | ||
247 | m_interRegionTeleportAttempts = | ||
248 | new Stat( | ||
249 | "InterRegionTeleportAttempts", | ||
250 | "Number of inter-region teleports attempted.", | ||
251 | "This does not count attempts which failed due to pre-conditions (e.g. target simulator refused access).\n" | ||
252 | + "You can get successfully teleports by subtracting aborts, cancels and teleport failures from this figure.", | ||
253 | "", | ||
254 | "entitytransfer", | ||
255 | Scene.Name, | ||
256 | StatType.Push, | ||
257 | null, | ||
258 | StatVerbosity.Debug); | ||
259 | |||
260 | m_interRegionTeleportAborts = | ||
261 | new Stat( | ||
262 | "InterRegionTeleportAborts", | ||
263 | "Number of inter-region teleports aborted due to client actions.", | ||
264 | "The chief action is simultaneous logout whilst teleporting.", | ||
265 | "", | ||
266 | "entitytransfer", | ||
267 | Scene.Name, | ||
268 | StatType.Push, | ||
269 | null, | ||
270 | StatVerbosity.Debug); | ||
271 | |||
272 | m_interRegionTeleportCancels = | ||
273 | new Stat( | ||
274 | "InterRegionTeleportCancels", | ||
275 | "Number of inter-region teleports cancelled by the client.", | ||
276 | null, | ||
277 | "", | ||
278 | "entitytransfer", | ||
279 | Scene.Name, | ||
280 | StatType.Push, | ||
281 | null, | ||
282 | StatVerbosity.Debug); | ||
283 | |||
284 | m_interRegionTeleportFailures = | ||
285 | new Stat( | ||
286 | "InterRegionTeleportFailures", | ||
287 | "Number of inter-region teleports that failed due to server/client/network issues.", | ||
288 | "This number may not be very helpful in open-grid/hg situations as the network connectivity/quality of destinations is uncontrollable.", | ||
289 | "", | ||
290 | "entitytransfer", | ||
291 | Scene.Name, | ||
292 | StatType.Push, | ||
293 | null, | ||
294 | StatVerbosity.Debug); | ||
295 | |||
296 | StatsManager.RegisterStat(m_interRegionTeleportAttempts); | ||
297 | StatsManager.RegisterStat(m_interRegionTeleportAborts); | ||
298 | StatsManager.RegisterStat(m_interRegionTeleportCancels); | ||
299 | StatsManager.RegisterStat(m_interRegionTeleportFailures); | ||
300 | |||
133 | scene.RegisterModuleInterface<IEntityTransferModule>(this); | 301 | scene.RegisterModuleInterface<IEntityTransferModule>(this); |
134 | scene.EventManager.OnNewClient += OnNewClient; | 302 | scene.EventManager.OnNewClient += OnNewClient; |
135 | } | 303 | } |
136 | 304 | ||
137 | protected virtual void OnNewClient(IClientAPI client) | 305 | protected virtual void OnNewClient(IClientAPI client) |
138 | { | 306 | { |
139 | client.OnTeleportHomeRequest += TeleportHome; | 307 | client.OnTeleportHomeRequest += TriggerTeleportHome; |
140 | client.OnTeleportLandmarkRequest += RequestTeleportLandmark; | 308 | client.OnTeleportLandmarkRequest += RequestTeleportLandmark; |
309 | |||
310 | if (!DisableInterRegionTeleportCancellation) | ||
311 | client.OnTeleportCancel += OnClientCancelTeleport; | ||
312 | |||
313 | client.OnConnectionClosed += OnConnectionClosed; | ||
141 | } | 314 | } |
142 | 315 | ||
143 | public virtual void Close() {} | 316 | public virtual void Close() {} |
144 | 317 | ||
145 | public virtual void RemoveRegion(Scene scene) {} | 318 | public virtual void RemoveRegion(Scene scene) |
319 | { | ||
320 | if (m_Enabled) | ||
321 | { | ||
322 | StatsManager.DeregisterStat(m_interRegionTeleportAttempts); | ||
323 | StatsManager.DeregisterStat(m_interRegionTeleportAborts); | ||
324 | StatsManager.DeregisterStat(m_interRegionTeleportCancels); | ||
325 | StatsManager.DeregisterStat(m_interRegionTeleportFailures); | ||
326 | } | ||
327 | } | ||
146 | 328 | ||
147 | public virtual void RegionLoaded(Scene scene) | 329 | public virtual void RegionLoaded(Scene scene) |
148 | { | 330 | { |
@@ -150,12 +332,32 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
150 | return; | 332 | return; |
151 | 333 | ||
152 | m_eqModule = Scene.RequestModuleInterface<IEventQueue>(); | 334 | m_eqModule = Scene.RequestModuleInterface<IEventQueue>(); |
335 | m_regionCombinerModule = Scene.RequestModuleInterface<IRegionCombinerModule>(); | ||
153 | } | 336 | } |
154 | 337 | ||
155 | #endregion | 338 | #endregion |
156 | 339 | ||
157 | #region Agent Teleports | 340 | #region Agent Teleports |
158 | 341 | ||
342 | private void OnConnectionClosed(IClientAPI client) | ||
343 | { | ||
344 | if (client.IsLoggingOut && m_entityTransferStateMachine.UpdateInTransit(client.AgentId, AgentTransferState.Aborting)) | ||
345 | { | ||
346 | m_log.DebugFormat( | ||
347 | "[ENTITY TRANSFER MODULE]: Aborted teleport request from {0} in {1} due to simultaneous logout", | ||
348 | client.Name, Scene.Name); | ||
349 | } | ||
350 | } | ||
351 | |||
352 | private void OnClientCancelTeleport(IClientAPI client) | ||
353 | { | ||
354 | m_entityTransferStateMachine.UpdateInTransit(client.AgentId, AgentTransferState.Cancelling); | ||
355 | |||
356 | m_log.DebugFormat( | ||
357 | "[ENTITY TRANSFER MODULE]: Received teleport cancel request from {0} in {1}", client.Name, Scene.Name); | ||
358 | } | ||
359 | |||
360 | // Attempt to teleport the ScenePresence to the specified position in the specified region (spec'ed by its handle). | ||
159 | public void Teleport(ScenePresence sp, ulong regionHandle, Vector3 position, Vector3 lookAt, uint teleportFlags) | 361 | public void Teleport(ScenePresence sp, ulong regionHandle, Vector3 position, Vector3 lookAt, uint teleportFlags) |
160 | { | 362 | { |
161 | if (sp.Scene.Permissions.IsGridGod(sp.UUID)) | 363 | if (sp.Scene.Permissions.IsGridGod(sp.UUID)) |
@@ -167,13 +369,26 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
167 | if (!sp.Scene.Permissions.CanTeleport(sp.UUID)) | 369 | if (!sp.Scene.Permissions.CanTeleport(sp.UUID)) |
168 | return; | 370 | return; |
169 | 371 | ||
170 | // Reset animations; the viewer does that in teleports. | ||
171 | sp.Animator.ResetAnimations(); | ||
172 | |||
173 | string destinationRegionName = "(not found)"; | 372 | string destinationRegionName = "(not found)"; |
174 | 373 | ||
374 | // Record that this agent is in transit so that we can prevent simultaneous requests and do later detection | ||
375 | // of whether the destination region completes the teleport. | ||
376 | if (!m_entityTransferStateMachine.SetInTransit(sp.UUID)) | ||
377 | { | ||
378 | m_log.DebugFormat( | ||
379 | "[ENTITY TRANSFER MODULE]: Ignoring teleport request of {0} {1} to {2}@{3} - agent is already in transit.", | ||
380 | sp.Name, sp.UUID, position, regionHandle); | ||
381 | |||
382 | sp.ControllingClient.SendTeleportFailed("Previous teleport process incomplete. Please retry shortly."); | ||
383 | |||
384 | return; | ||
385 | } | ||
386 | |||
175 | try | 387 | try |
176 | { | 388 | { |
389 | // Reset animations; the viewer does that in teleports. | ||
390 | sp.Animator.ResetAnimations(); | ||
391 | |||
177 | if (regionHandle == sp.Scene.RegionInfo.RegionHandle) | 392 | if (regionHandle == sp.Scene.RegionInfo.RegionHandle) |
178 | { | 393 | { |
179 | destinationRegionName = sp.Scene.RegionInfo.RegionName; | 394 | destinationRegionName = sp.Scene.RegionInfo.RegionName; |
@@ -182,12 +397,17 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
182 | } | 397 | } |
183 | else // Another region possibly in another simulator | 398 | else // Another region possibly in another simulator |
184 | { | 399 | { |
185 | GridRegion finalDestination; | 400 | GridRegion finalDestination = null; |
186 | TeleportAgentToDifferentRegion( | 401 | try |
187 | sp, regionHandle, position, lookAt, teleportFlags, out finalDestination); | 402 | { |
188 | 403 | TeleportAgentToDifferentRegion( | |
189 | if (finalDestination != null) | 404 | sp, regionHandle, position, lookAt, teleportFlags, out finalDestination); |
190 | destinationRegionName = finalDestination.RegionName; | 405 | } |
406 | finally | ||
407 | { | ||
408 | if (finalDestination != null) | ||
409 | destinationRegionName = finalDestination.RegionName; | ||
410 | } | ||
191 | } | 411 | } |
192 | } | 412 | } |
193 | catch (Exception e) | 413 | catch (Exception e) |
@@ -197,11 +417,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
197 | sp.Name, sp.AbsolutePosition, sp.Scene.RegionInfo.RegionName, position, destinationRegionName, | 417 | sp.Name, sp.AbsolutePosition, sp.Scene.RegionInfo.RegionName, position, destinationRegionName, |
198 | e.Message, e.StackTrace); | 418 | e.Message, e.StackTrace); |
199 | 419 | ||
200 | // Make sure that we clear the in-transit flag so that future teleport attempts don't always fail. | ||
201 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | ||
202 | |||
203 | sp.ControllingClient.SendTeleportFailed("Internal error"); | 420 | sp.ControllingClient.SendTeleportFailed("Internal error"); |
204 | } | 421 | } |
422 | finally | ||
423 | { | ||
424 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | ||
425 | } | ||
205 | } | 426 | } |
206 | 427 | ||
207 | /// <summary> | 428 | /// <summary> |
@@ -210,30 +431,21 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
210 | /// <param name="sp"></param> | 431 | /// <param name="sp"></param> |
211 | /// <param name="position"></param> | 432 | /// <param name="position"></param> |
212 | /// <param name="lookAt"></param> | 433 | /// <param name="lookAt"></param> |
213 | /// <param name="teleportFlags"></param | 434 | /// <param name="teleportFlags"></param> |
214 | private void TeleportAgentWithinRegion(ScenePresence sp, Vector3 position, Vector3 lookAt, uint teleportFlags) | 435 | private void TeleportAgentWithinRegion(ScenePresence sp, Vector3 position, Vector3 lookAt, uint teleportFlags) |
215 | { | 436 | { |
216 | m_log.DebugFormat( | 437 | m_log.DebugFormat( |
217 | "[ENTITY TRANSFER MODULE]: Teleport for {0} to {1} within {2}", | 438 | "[ENTITY TRANSFER MODULE]: Teleport for {0} to {1} within {2}", |
218 | sp.Name, position, sp.Scene.RegionInfo.RegionName); | 439 | sp.Name, position, sp.Scene.RegionInfo.RegionName); |
219 | 440 | ||
220 | if (!m_entityTransferStateMachine.SetInTransit(sp.UUID)) | ||
221 | { | ||
222 | m_log.DebugFormat( | ||
223 | "[ENTITY TRANSFER MODULE]: Ignoring within region teleport request of {0} {1} to {2} - agent is already in transit.", | ||
224 | sp.Name, sp.UUID, position); | ||
225 | |||
226 | return; | ||
227 | } | ||
228 | |||
229 | // Teleport within the same region | 441 | // Teleport within the same region |
230 | if (IsOutsideRegion(sp.Scene, position) || position.Z < 0) | 442 | if (!sp.Scene.PositionIsInCurrentRegion(position) || position.Z < 0) |
231 | { | 443 | { |
232 | Vector3 emergencyPos = new Vector3(128, 128, 128); | 444 | Vector3 emergencyPos = new Vector3(128, 128, 128); |
233 | 445 | ||
234 | m_log.WarnFormat( | 446 | m_log.WarnFormat( |
235 | "[ENTITY TRANSFER MODULE]: RequestTeleportToLocation() was given an illegal position of {0} for avatar {1}, {2}. Substituting {3}", | 447 | "[ENTITY TRANSFER MODULE]: RequestTeleportToLocation() was given an illegal position of {0} for avatar {1}, {2} in {3}. Substituting {4}", |
236 | position, sp.Name, sp.UUID, emergencyPos); | 448 | position, sp.Name, sp.UUID, Scene.Name, emergencyPos); |
237 | 449 | ||
238 | position = emergencyPos; | 450 | position = emergencyPos; |
239 | } | 451 | } |
@@ -243,10 +455,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
243 | float posZLimit = 22; | 455 | float posZLimit = 22; |
244 | 456 | ||
245 | // TODO: Check other Scene HeightField | 457 | // TODO: Check other Scene HeightField |
246 | if (position.X > 0 && position.X <= (int)Constants.RegionSize && position.Y > 0 && position.Y <= (int)Constants.RegionSize) | 458 | posZLimit = (float)sp.Scene.Heightmap[(int)position.X, (int)position.Y]; |
247 | { | ||
248 | posZLimit = (float)sp.Scene.Heightmap[(int)position.X, (int)position.Y]; | ||
249 | } | ||
250 | 459 | ||
251 | float newPosZ = posZLimit + localAVHeight; | 460 | float newPosZ = posZLimit + localAVHeight; |
252 | if (posZLimit >= (position.Z - (localAVHeight / 2)) && !(Single.IsInfinity(newPosZ) || Single.IsNaN(newPosZ))) | 461 | if (posZLimit >= (position.Z - (localAVHeight / 2)) && !(Single.IsInfinity(newPosZ) || Single.IsNaN(newPosZ))) |
@@ -254,11 +463,15 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
254 | position.Z = newPosZ; | 463 | position.Z = newPosZ; |
255 | } | 464 | } |
256 | 465 | ||
466 | if (sp.Flying) | ||
467 | teleportFlags |= (uint)TeleportFlags.IsFlying; | ||
468 | |||
257 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring); | 469 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring); |
258 | 470 | ||
259 | sp.ControllingClient.SendTeleportStart(teleportFlags); | 471 | sp.ControllingClient.SendTeleportStart(teleportFlags); |
260 | 472 | ||
261 | sp.ControllingClient.SendLocalTeleport(position, lookAt, teleportFlags); | 473 | sp.ControllingClient.SendLocalTeleport(position, lookAt, teleportFlags); |
474 | sp.TeleportFlags = (Constants.TeleportFlags)teleportFlags; | ||
262 | sp.Velocity = Vector3.Zero; | 475 | sp.Velocity = Vector3.Zero; |
263 | sp.Teleport(position); | 476 | sp.Teleport(position); |
264 | 477 | ||
@@ -270,7 +483,6 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
270 | } | 483 | } |
271 | 484 | ||
272 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); | 485 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); |
273 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | ||
274 | } | 486 | } |
275 | 487 | ||
276 | /// <summary> | 488 | /// <summary> |
@@ -286,21 +498,23 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
286 | ScenePresence sp, ulong regionHandle, Vector3 position, | 498 | ScenePresence sp, ulong regionHandle, Vector3 position, |
287 | Vector3 lookAt, uint teleportFlags, out GridRegion finalDestination) | 499 | Vector3 lookAt, uint teleportFlags, out GridRegion finalDestination) |
288 | { | 500 | { |
289 | uint x = 0, y = 0; | 501 | // Get destination region taking into account that the address could be an offset |
290 | Utils.LongToUInts(regionHandle, out x, out y); | 502 | // region inside a varregion. |
291 | GridRegion reg = Scene.GridService.GetRegionByPosition(sp.Scene.RegionInfo.ScopeID, (int)x, (int)y); | 503 | GridRegion reg = GetTeleportDestinationRegion(sp.Scene.GridService, sp.Scene.RegionInfo.ScopeID, regionHandle, ref position); |
292 | 504 | ||
293 | if (reg != null) | 505 | if (reg != null) |
294 | { | 506 | { |
295 | finalDestination = GetFinalDestination(reg); | 507 | string homeURI = Scene.GetAgentHomeURI(sp.ControllingClient.AgentId); |
508 | |||
509 | string message; | ||
510 | finalDestination = GetFinalDestination(reg, sp.ControllingClient.AgentId, homeURI, out message); | ||
296 | 511 | ||
297 | if (finalDestination == null) | 512 | if (finalDestination == null) |
298 | { | 513 | { |
299 | m_log.WarnFormat( | 514 | m_log.WarnFormat( "{0} Final destination is having problems. Unable to teleport {1} {2}: {3}", |
300 | "[ENTITY TRANSFER MODULE]: Final destination is having problems. Unable to teleport {0} {1}", | 515 | LogHeader, sp.Name, sp.UUID, message); |
301 | sp.Name, sp.UUID); | ||
302 | 516 | ||
303 | sp.ControllingClient.SendTeleportFailed("Problem at destination"); | 517 | sp.ControllingClient.SendTeleportFailed(message); |
304 | return; | 518 | return; |
305 | } | 519 | } |
306 | 520 | ||
@@ -321,10 +535,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
321 | return; | 535 | return; |
322 | } | 536 | } |
323 | 537 | ||
538 | if (message != null) | ||
539 | sp.ControllingClient.SendAgentAlertMessage(message, true); | ||
540 | |||
324 | // | 541 | // |
325 | // This is it | 542 | // This is it |
326 | // | 543 | // |
327 | DoTeleport(sp, reg, finalDestination, position, lookAt, teleportFlags); | 544 | DoTeleportInternal(sp, reg, finalDestination, position, lookAt, teleportFlags); |
328 | // | 545 | // |
329 | // | 546 | // |
330 | // | 547 | // |
@@ -339,12 +556,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
339 | 556 | ||
340 | // and set the map-tile to '(Offline)' | 557 | // and set the map-tile to '(Offline)' |
341 | uint regX, regY; | 558 | uint regX, regY; |
342 | Utils.LongToUInts(regionHandle, out regX, out regY); | 559 | Util.RegionHandleToRegionLoc(regionHandle, out regX, out regY); |
343 | 560 | ||
344 | MapBlockData block = new MapBlockData(); | 561 | MapBlockData block = new MapBlockData(); |
345 | block.X = (ushort)(regX / Constants.RegionSize); | 562 | block.X = (ushort)regX; |
346 | block.Y = (ushort)(regY / Constants.RegionSize); | 563 | block.Y = (ushort)regY; |
347 | block.Access = 254; // == not there | 564 | block.Access = (byte)SimAccess.Down; |
348 | 565 | ||
349 | List<MapBlockData> blocks = new List<MapBlockData>(); | 566 | List<MapBlockData> blocks = new List<MapBlockData>(); |
350 | blocks.Add(block); | 567 | blocks.Add(block); |
@@ -352,6 +569,31 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
352 | } | 569 | } |
353 | } | 570 | } |
354 | 571 | ||
572 | // The teleport address could be an address in a subregion of a larger varregion. | ||
573 | // Find the real base region and adjust the teleport location to account for the | ||
574 | // larger region. | ||
575 | private GridRegion GetTeleportDestinationRegion(IGridService gridService, UUID scope, ulong regionHandle, ref Vector3 position) | ||
576 | { | ||
577 | uint x = 0, y = 0; | ||
578 | Util.RegionHandleToWorldLoc(regionHandle, out x, out y); | ||
579 | |||
580 | // Compute the world location we're teleporting to | ||
581 | double worldX = (double)x + position.X; | ||
582 | double worldY = (double)y + position.Y; | ||
583 | |||
584 | // Find the region that contains the position | ||
585 | GridRegion reg = GetRegionContainingWorldLocation(gridService, scope, worldX, worldY); | ||
586 | |||
587 | if (reg != null) | ||
588 | { | ||
589 | // modify the position for the offset into the actual region returned | ||
590 | position.X += x - reg.RegionLocX; | ||
591 | position.Y += y - reg.RegionLocY; | ||
592 | } | ||
593 | |||
594 | return reg; | ||
595 | } | ||
596 | |||
355 | // Nothing to validate here | 597 | // Nothing to validate here |
356 | protected virtual bool ValidateGenericConditions(ScenePresence sp, GridRegion reg, GridRegion finalDestination, uint teleportFlags, out string reason) | 598 | protected virtual bool ValidateGenericConditions(ScenePresence sp, GridRegion reg, GridRegion finalDestination, uint teleportFlags, out string reason) |
357 | { | 599 | { |
@@ -359,6 +601,32 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
359 | return true; | 601 | return true; |
360 | } | 602 | } |
361 | 603 | ||
604 | /// <summary> | ||
605 | /// Determines whether this instance is within the max transfer distance. | ||
606 | /// </summary> | ||
607 | /// <param name="sourceRegion"></param> | ||
608 | /// <param name="destRegion"></param> | ||
609 | /// <returns> | ||
610 | /// <c>true</c> if this instance is within max transfer distance; otherwise, <c>false</c>. | ||
611 | /// </returns> | ||
612 | private bool IsWithinMaxTeleportDistance(RegionInfo sourceRegion, GridRegion destRegion) | ||
613 | { | ||
614 | if(MaxTransferDistance == 0) | ||
615 | return true; | ||
616 | |||
617 | // m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Source co-ords are x={0} y={1}", curRegionX, curRegionY); | ||
618 | // | ||
619 | // m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Final dest is x={0} y={1} {2}@{3}", | ||
620 | // destRegionX, destRegionY, finalDestination.RegionID, finalDestination.ServerURI); | ||
621 | |||
622 | // Insanely, RegionLoc on RegionInfo is the 256m map co-ord whilst GridRegion.RegionLoc is the raw meters position. | ||
623 | return Math.Abs(sourceRegion.RegionLocX - destRegion.RegionCoordX) <= MaxTransferDistance | ||
624 | && Math.Abs(sourceRegion.RegionLocY - destRegion.RegionCoordY) <= MaxTransferDistance; | ||
625 | } | ||
626 | |||
627 | /// <summary> | ||
628 | /// Wraps DoTeleportInternal() and manages the transfer state. | ||
629 | /// </summary> | ||
362 | public void DoTeleport( | 630 | public void DoTeleport( |
363 | ScenePresence sp, GridRegion reg, GridRegion finalDestination, | 631 | ScenePresence sp, GridRegion reg, GridRegion finalDestination, |
364 | Vector3 position, Vector3 lookAt, uint teleportFlags) | 632 | Vector3 position, Vector3 lookAt, uint teleportFlags) |
@@ -370,18 +638,45 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
370 | m_log.DebugFormat( | 638 | m_log.DebugFormat( |
371 | "[ENTITY TRANSFER MODULE]: Ignoring teleport request of {0} {1} to {2} ({3}) {4}/{5} - agent is already in transit.", | 639 | "[ENTITY TRANSFER MODULE]: Ignoring teleport request of {0} {1} to {2} ({3}) {4}/{5} - agent is already in transit.", |
372 | sp.Name, sp.UUID, reg.ServerURI, finalDestination.ServerURI, finalDestination.RegionName, position); | 640 | sp.Name, sp.UUID, reg.ServerURI, finalDestination.ServerURI, finalDestination.RegionName, position); |
373 | 641 | sp.ControllingClient.SendTeleportFailed("Agent is already in transit."); | |
374 | return; | 642 | return; |
375 | } | 643 | } |
644 | |||
645 | try | ||
646 | { | ||
647 | DoTeleportInternal(sp, reg, finalDestination, position, lookAt, teleportFlags); | ||
648 | } | ||
649 | catch (Exception e) | ||
650 | { | ||
651 | m_log.ErrorFormat( | ||
652 | "[ENTITY TRANSFER MODULE]: Exception on teleport of {0} from {1}@{2} to {3}@{4}: {5}{6}", | ||
653 | sp.Name, sp.AbsolutePosition, sp.Scene.RegionInfo.RegionName, position, finalDestination.RegionName, | ||
654 | e.Message, e.StackTrace); | ||
376 | 655 | ||
377 | if (reg == null || finalDestination == null) | 656 | sp.ControllingClient.SendTeleportFailed("Internal error"); |
657 | } | ||
658 | finally | ||
378 | { | 659 | { |
379 | sp.ControllingClient.SendTeleportFailed("Unable to locate destination"); | ||
380 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | 660 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); |
661 | } | ||
662 | } | ||
381 | 663 | ||
664 | /// <summary> | ||
665 | /// Teleports the agent to another region. | ||
666 | /// This method doesn't manage the transfer state; the caller must do that. | ||
667 | /// </summary> | ||
668 | private void DoTeleportInternal( | ||
669 | ScenePresence sp, GridRegion reg, GridRegion finalDestination, | ||
670 | Vector3 position, Vector3 lookAt, uint teleportFlags) | ||
671 | { | ||
672 | if (reg == null || finalDestination == null) | ||
673 | { | ||
674 | sp.ControllingClient.SendTeleportFailed("Unable to locate destination"); | ||
382 | return; | 675 | return; |
383 | } | 676 | } |
384 | 677 | ||
678 | string homeURI = Scene.GetAgentHomeURI(sp.ControllingClient.AgentId); | ||
679 | |||
385 | m_log.DebugFormat( | 680 | m_log.DebugFormat( |
386 | "[ENTITY TRANSFER MODULE]: Teleporting {0} {1} from {2} to {3} ({4}) {5}/{6}", | 681 | "[ENTITY TRANSFER MODULE]: Teleporting {0} {1} from {2} to {3} ({4}) {5}/{6}", |
387 | sp.Name, sp.UUID, sp.Scene.RegionInfo.RegionName, | 682 | sp.Name, sp.UUID, sp.Scene.RegionInfo.RegionName, |
@@ -389,10 +684,21 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
389 | 684 | ||
390 | RegionInfo sourceRegion = sp.Scene.RegionInfo; | 685 | RegionInfo sourceRegion = sp.Scene.RegionInfo; |
391 | 686 | ||
392 | uint newRegionX = (uint)(reg.RegionHandle >> 40); | 687 | if (!IsWithinMaxTeleportDistance(sourceRegion, finalDestination)) |
393 | uint newRegionY = (((uint)(reg.RegionHandle)) >> 8); | 688 | { |
394 | uint oldRegionX = (uint)(sp.Scene.RegionInfo.RegionHandle >> 40); | 689 | sp.ControllingClient.SendTeleportFailed( |
395 | uint oldRegionY = (((uint)(sp.Scene.RegionInfo.RegionHandle)) >> 8); | 690 | string.Format( |
691 | "Can't teleport to {0} ({1},{2}) from {3} ({4},{5}), destination is more than {6} regions way", | ||
692 | finalDestination.RegionName, finalDestination.RegionCoordX, finalDestination.RegionCoordY, | ||
693 | sourceRegion.RegionName, sourceRegion.RegionLocX, sourceRegion.RegionLocY, | ||
694 | MaxTransferDistance)); | ||
695 | |||
696 | return; | ||
697 | } | ||
698 | |||
699 | uint newRegionX, newRegionY, oldRegionX, oldRegionY; | ||
700 | Util.RegionHandleToRegionLoc(reg.RegionHandle, out newRegionX, out newRegionY); | ||
701 | Util.RegionHandleToRegionLoc(sp.Scene.RegionInfo.RegionHandle, out oldRegionX, out oldRegionY); | ||
396 | 702 | ||
397 | ulong destinationHandle = finalDestination.RegionHandle; | 703 | ulong destinationHandle = finalDestination.RegionHandle; |
398 | 704 | ||
@@ -400,11 +706,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
400 | // This may be a costly operation. The reg.ExternalEndPoint field is not a passive field, | 706 | // This may be a costly operation. The reg.ExternalEndPoint field is not a passive field, |
401 | // it's actually doing a lot of work. | 707 | // it's actually doing a lot of work. |
402 | IPEndPoint endPoint = finalDestination.ExternalEndPoint; | 708 | IPEndPoint endPoint = finalDestination.ExternalEndPoint; |
403 | 709 | if (endPoint == null || endPoint.Address == null) | |
404 | if (endPoint.Address == null) | ||
405 | { | 710 | { |
406 | sp.ControllingClient.SendTeleportFailed("Remote Region appears to be down"); | 711 | sp.ControllingClient.SendTeleportFailed("Remote Region appears to be down"); |
407 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | ||
408 | 712 | ||
409 | return; | 713 | return; |
410 | } | 714 | } |
@@ -412,35 +716,41 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
412 | if (!sp.ValidateAttachments()) | 716 | if (!sp.ValidateAttachments()) |
413 | m_log.DebugFormat( | 717 | m_log.DebugFormat( |
414 | "[ENTITY TRANSFER MODULE]: Failed validation of all attachments for teleport of {0} from {1} to {2}. Continuing.", | 718 | "[ENTITY TRANSFER MODULE]: Failed validation of all attachments for teleport of {0} from {1} to {2}. Continuing.", |
415 | sp.Name, sp.Scene.RegionInfo.RegionName, finalDestination.RegionName); | 719 | sp.Name, sp.Scene.Name, finalDestination.RegionName); |
416 | |||
417 | // if (!sp.ValidateAttachments()) | ||
418 | // { | ||
419 | // sp.ControllingClient.SendTeleportFailed("Inconsistent attachment state"); | ||
420 | // return; | ||
421 | // } | ||
422 | 720 | ||
423 | string reason; | 721 | string reason; |
424 | string version; | 722 | EntityTransferContext ctx = new EntityTransferContext(); |
723 | |||
425 | if (!Scene.SimulationService.QueryAccess( | 724 | if (!Scene.SimulationService.QueryAccess( |
426 | finalDestination, sp.ControllingClient.AgentId, Vector3.Zero, out version, out reason)) | 725 | finalDestination, sp.ControllingClient.AgentId, homeURI, true, position, sp.Scene.GetFormatsOffered(), ctx, out reason)) |
427 | { | 726 | { |
428 | sp.ControllingClient.SendTeleportFailed(reason); | 727 | sp.ControllingClient.SendTeleportFailed(reason); |
429 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | ||
430 | 728 | ||
431 | m_log.DebugFormat( | 729 | m_log.DebugFormat( |
432 | "[ENTITY TRANSFER MODULE]: {0} was stopped from teleporting from {1} to {2} because {3}", | 730 | "[ENTITY TRANSFER MODULE]: {0} was stopped from teleporting from {1} to {2} because: {3}", |
433 | sp.Name, sp.Scene.RegionInfo.RegionName, finalDestination.RegionName, reason); | 731 | sp.Name, sp.Scene.Name, finalDestination.RegionName, reason); |
434 | 732 | ||
435 | return; | 733 | return; |
436 | } | 734 | } |
437 | 735 | ||
438 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Destination is running version {0}", version); | 736 | // Before this point, teleport 'failure' is due to checkable pre-conditions such as whether the target |
737 | // simulator can be found and is explicitly prepared to allow access. Therefore, we will not count these | ||
738 | // as server attempts. | ||
739 | m_interRegionTeleportAttempts.Value++; | ||
740 | |||
741 | m_log.DebugFormat( | ||
742 | "[ENTITY TRANSFER MODULE]: {0} transfer protocol version to {1} is {2} / {3}", | ||
743 | sp.Scene.Name, finalDestination.RegionName, ctx.OutboundVersion, ctx.InboundVersion); | ||
439 | 744 | ||
440 | // Fixing a bug where teleporting while sitting results in the avatar ending up removed from | 745 | // Fixing a bug where teleporting while sitting results in the avatar ending up removed from |
441 | // both regions | 746 | // both regions |
442 | if (sp.ParentID != (uint)0) | 747 | if (sp.ParentID != (uint)0) |
443 | sp.StandUp(); | 748 | sp.StandUp(); |
749 | else if (sp.Flying) | ||
750 | teleportFlags |= (uint)TeleportFlags.IsFlying; | ||
751 | |||
752 | if (DisableInterRegionTeleportCancellation) | ||
753 | teleportFlags |= (uint)TeleportFlags.DisableCancel; | ||
444 | 754 | ||
445 | // At least on LL 3.3.4, this is not strictly necessary - a teleport will succeed without sending this to | 755 | // At least on LL 3.3.4, this is not strictly necessary - a teleport will succeed without sending this to |
446 | // the viewer. However, it might mean that the viewer does not see the black teleport screen (untested). | 756 | // the viewer. However, it might mean that the viewer does not see the black teleport screen (untested). |
@@ -454,13 +764,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
454 | // once we reach here... | 764 | // once we reach here... |
455 | //avatar.Scene.RemoveCapsHandler(avatar.UUID); | 765 | //avatar.Scene.RemoveCapsHandler(avatar.UUID); |
456 | 766 | ||
457 | string capsPath = String.Empty; | ||
458 | |||
459 | AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); | 767 | AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); |
460 | AgentCircuitData agentCircuit = sp.ControllingClient.RequestClientInfo(); | 768 | AgentCircuitData agentCircuit = sp.ControllingClient.RequestClientInfo(); |
461 | agentCircuit.startpos = position; | 769 | agentCircuit.startpos = position; |
462 | agentCircuit.child = true; | 770 | agentCircuit.child = true; |
463 | agentCircuit.Appearance = sp.Appearance; | 771 | agentCircuit.Appearance = new AvatarAppearance(); |
772 | agentCircuit.Appearance.PackLegacyWearables = true; | ||
464 | if (currentAgentCircuit != null) | 773 | if (currentAgentCircuit != null) |
465 | { | 774 | { |
466 | agentCircuit.ServiceURLs = currentAgentCircuit.ServiceURLs; | 775 | agentCircuit.ServiceURLs = currentAgentCircuit.ServiceURLs; |
@@ -471,23 +780,66 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
471 | agentCircuit.Id0 = currentAgentCircuit.Id0; | 780 | agentCircuit.Id0 = currentAgentCircuit.Id0; |
472 | } | 781 | } |
473 | 782 | ||
474 | if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY)) | 783 | // if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY)) |
784 | float dist = (float)Math.Max(sp.Scene.DefaultDrawDistance, | ||
785 | (float)Math.Max(sp.Scene.RegionInfo.RegionSizeX, sp.Scene.RegionInfo.RegionSizeY)); | ||
786 | if (NeedsNewAgent(dist, oldRegionX, newRegionX, oldRegionY, newRegionY)) | ||
475 | { | 787 | { |
476 | // brand new agent, let's create a new caps seed | 788 | // brand new agent, let's create a new caps seed |
477 | agentCircuit.CapsPath = CapsUtil.GetRandomCapsObjectPath(); | 789 | agentCircuit.CapsPath = CapsUtil.GetRandomCapsObjectPath(); |
478 | } | 790 | } |
479 | 791 | ||
480 | // Let's create an agent there if one doesn't exist yet. | 792 | // We're going to fallback to V1 if the destination gives us anything smaller than 0.2 |
793 | if (ctx.OutboundVersion >= 0.2f) | ||
794 | TransferAgent_V2(sp, agentCircuit, reg, finalDestination, endPoint, teleportFlags, oldRegionX, newRegionX, oldRegionY, newRegionY, ctx, out reason); | ||
795 | else | ||
796 | TransferAgent_V1(sp, agentCircuit, reg, finalDestination, endPoint, teleportFlags, oldRegionX, newRegionX, oldRegionY, newRegionY, ctx, out reason); | ||
797 | } | ||
798 | |||
799 | private void TransferAgent_V1(ScenePresence sp, AgentCircuitData agentCircuit, GridRegion reg, GridRegion finalDestination, | ||
800 | IPEndPoint endPoint, uint teleportFlags, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, EntityTransferContext ctx, out string reason) | ||
801 | { | ||
802 | ulong destinationHandle = finalDestination.RegionHandle; | ||
803 | AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); | ||
804 | |||
805 | m_log.DebugFormat( | ||
806 | "[ENTITY TRANSFER MODULE]: Using TP V1 for {0} going from {1} to {2}", | ||
807 | sp.Name, Scene.Name, finalDestination.RegionName); | ||
808 | |||
809 | // Let's create an agent there if one doesn't exist yet. | ||
810 | // NOTE: logout will always be false for a non-HG teleport. | ||
481 | bool logout = false; | 811 | bool logout = false; |
482 | if (!CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout)) | 812 | if (!CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout)) |
483 | { | 813 | { |
484 | sp.ControllingClient.SendTeleportFailed(String.Format("Teleport refused: {0}", reason)); | 814 | m_interRegionTeleportFailures.Value++; |
485 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | ||
486 | 815 | ||
487 | m_log.DebugFormat( | 816 | m_log.DebugFormat( |
488 | "[ENTITY TRANSFER MODULE]: Teleport of {0} from {1} to {2} was refused because {3}", | 817 | "[ENTITY TRANSFER MODULE]: Teleport of {0} from {1} to {2} was refused because {3}", |
489 | sp.Name, sp.Scene.RegionInfo.RegionName, finalDestination.RegionName, reason); | 818 | sp.Name, sp.Scene.RegionInfo.RegionName, finalDestination.RegionName, reason); |
490 | 819 | ||
820 | sp.ControllingClient.SendTeleportFailed(reason); | ||
821 | |||
822 | return; | ||
823 | } | ||
824 | |||
825 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling) | ||
826 | { | ||
827 | m_interRegionTeleportCancels.Value++; | ||
828 | |||
829 | m_log.DebugFormat( | ||
830 | "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request", | ||
831 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
832 | |||
833 | return; | ||
834 | } | ||
835 | else if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) | ||
836 | { | ||
837 | m_interRegionTeleportAborts.Value++; | ||
838 | |||
839 | m_log.DebugFormat( | ||
840 | "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after CreateAgent due to previous client close.", | ||
841 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
842 | |||
491 | return; | 843 | return; |
492 | } | 844 | } |
493 | 845 | ||
@@ -497,9 +849,16 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
497 | // OK, it got this agent. Let's close some child agents | 849 | // OK, it got this agent. Let's close some child agents |
498 | sp.CloseChildAgents(newRegionX, newRegionY); | 850 | sp.CloseChildAgents(newRegionX, newRegionY); |
499 | 851 | ||
500 | IClientIPEndpoint ipepClient; | 852 | IClientIPEndpoint ipepClient; |
501 | if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY)) | 853 | string capsPath = String.Empty; |
854 | float dist = (float)Math.Max(sp.Scene.DefaultDrawDistance, | ||
855 | (float)Math.Max(sp.Scene.RegionInfo.RegionSizeX, sp.Scene.RegionInfo.RegionSizeY)); | ||
856 | if (NeedsNewAgent(dist, oldRegionX, newRegionX, oldRegionY, newRegionY)) | ||
502 | { | 857 | { |
858 | m_log.DebugFormat( | ||
859 | "[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for incoming agent {3} from {4}", | ||
860 | finalDestination.RegionName, newRegionX, newRegionY, sp.Name, Scene.Name); | ||
861 | |||
503 | //sp.ControllingClient.SendTeleportProgress(teleportFlags, "Creating agent..."); | 862 | //sp.ControllingClient.SendTeleportProgress(teleportFlags, "Creating agent..."); |
504 | #region IP Translation for NAT | 863 | #region IP Translation for NAT |
505 | // Uses ipepClient above | 864 | // Uses ipepClient above |
@@ -512,21 +871,30 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
512 | 871 | ||
513 | if (m_eqModule != null) | 872 | if (m_eqModule != null) |
514 | { | 873 | { |
515 | m_eqModule.EnableSimulator(destinationHandle, endPoint, sp.UUID); | 874 | // The EnableSimulator message makes the client establish a connection with the destination |
516 | 875 | // simulator by sending the initial UseCircuitCode UDP packet to the destination containing the | |
517 | // ES makes the client send a UseCircuitCode message to the destination, | 876 | // correct circuit code. |
518 | // which triggers a bunch of things there. | 877 | m_eqModule.EnableSimulator(destinationHandle, endPoint, sp.UUID, |
519 | // So let's wait | 878 | finalDestination.RegionSizeX, finalDestination.RegionSizeY); |
879 | m_log.DebugFormat("{0} Sent EnableSimulator. regName={1}, size=<{2},{3}>", LogHeader, | ||
880 | finalDestination.RegionName, finalDestination.RegionSizeX, finalDestination.RegionSizeY); | ||
881 | |||
882 | // XXX: Is this wait necessary? We will always end up waiting on UpdateAgent for the destination | ||
883 | // simulator to confirm that it has established communication with the viewer. | ||
520 | Thread.Sleep(200); | 884 | Thread.Sleep(200); |
521 | 885 | ||
522 | // At least on LL 3.3.4 for teleports between different regions on the same simulator this appears | 886 | // At least on LL 3.3.4 for teleports between different regions on the same simulator this appears |
523 | // unnecessary - teleport will succeed and SEED caps will be requested without it (though possibly | 887 | // unnecessary - teleport will succeed and SEED caps will be requested without it (though possibly |
524 | // only on TeleportFinish). This is untested for region teleport between different simulators | 888 | // only on TeleportFinish). This is untested for region teleport between different simulators |
525 | // though this probably also works. | 889 | // though this probably also works. |
526 | m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath); | 890 | m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath, finalDestination.RegionHandle, |
891 | finalDestination.RegionSizeX, finalDestination.RegionSizeY); | ||
527 | } | 892 | } |
528 | else | 893 | else |
529 | { | 894 | { |
895 | // XXX: This is a little misleading since we're information the client of its avatar destination, | ||
896 | // which may or may not be a neighbour region of the source region. This path is probably little | ||
897 | // used anyway (with EQ being the one used). But it is currently being used for test code. | ||
530 | sp.ControllingClient.InformClientOfNeighbour(destinationHandle, endPoint); | 898 | sp.ControllingClient.InformClientOfNeighbour(destinationHandle, endPoint); |
531 | } | 899 | } |
532 | } | 900 | } |
@@ -539,31 +907,77 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
539 | // Let's send a full update of the agent. This is a synchronous call. | 907 | // Let's send a full update of the agent. This is a synchronous call. |
540 | AgentData agent = new AgentData(); | 908 | AgentData agent = new AgentData(); |
541 | sp.CopyTo(agent); | 909 | sp.CopyTo(agent); |
542 | agent.Position = position; | 910 | if (ctx.OutboundVersion < 0.5f) |
911 | agent.Appearance.PackLegacyWearables = true; | ||
912 | agent.Position = agentCircuit.startpos; | ||
543 | SetCallbackURL(agent, sp.Scene.RegionInfo); | 913 | SetCallbackURL(agent, sp.Scene.RegionInfo); |
544 | 914 | ||
545 | //sp.ControllingClient.SendTeleportProgress(teleportFlags, "Updating agent..."); | ||
546 | 915 | ||
916 | // We will check for an abort before UpdateAgent since UpdateAgent will require an active viewer to | ||
917 | // establish th econnection to the destination which makes it return true. | ||
918 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) | ||
919 | { | ||
920 | m_interRegionTeleportAborts.Value++; | ||
921 | |||
922 | m_log.DebugFormat( | ||
923 | "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} before UpdateAgent", | ||
924 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
925 | |||
926 | return; | ||
927 | } | ||
928 | |||
929 | // A common teleport failure occurs when we can send CreateAgent to the | ||
930 | // destination region but the viewer cannot establish the connection (e.g. due to network issues between | ||
931 | // the viewer and the destination). In this case, UpdateAgent timesout after 10 seconds, although then | ||
932 | // there's a further 10 second wait whilst we attempt to tell the destination to delete the agent in Fail(). | ||
547 | if (!UpdateAgent(reg, finalDestination, agent, sp)) | 933 | if (!UpdateAgent(reg, finalDestination, agent, sp)) |
548 | { | 934 | { |
549 | // Region doesn't take it | 935 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) |
936 | { | ||
937 | m_interRegionTeleportAborts.Value++; | ||
938 | |||
939 | m_log.DebugFormat( | ||
940 | "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after UpdateAgent due to previous client close.", | ||
941 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
942 | |||
943 | return; | ||
944 | } | ||
945 | |||
550 | m_log.WarnFormat( | 946 | m_log.WarnFormat( |
551 | "[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1} from {2}. Returning avatar to source region.", | 947 | "[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1}. Keeping avatar in {2}", |
552 | sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName); | 948 | sp.Name, finalDestination.RegionName, sp.Scene.Name); |
553 | 949 | ||
554 | Fail(sp, finalDestination, logout); | 950 | Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Connection between viewer and destination region could not be established."); |
555 | return; | 951 | return; |
556 | } | 952 | } |
557 | 953 | ||
558 | sp.ControllingClient.SendTeleportProgress(teleportFlags | (uint)TeleportFlags.DisableCancel, "sending_dest"); | 954 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling) |
955 | { | ||
956 | m_interRegionTeleportCancels.Value++; | ||
957 | |||
958 | m_log.DebugFormat( | ||
959 | "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after UpdateAgent on client request", | ||
960 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
961 | |||
962 | CleanupFailedInterRegionTeleport(sp, currentAgentCircuit.SessionID.ToString(), finalDestination); | ||
963 | |||
964 | return; | ||
965 | } | ||
559 | 966 | ||
560 | m_log.DebugFormat( | 967 | m_log.DebugFormat( |
561 | "[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}", | 968 | "[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}", |
562 | capsPath, sp.Scene.RegionInfo.RegionName, sp.Name); | 969 | capsPath, sp.Scene.RegionInfo.RegionName, sp.Name); |
563 | 970 | ||
971 | // We need to set this here to avoid an unlikely race condition when teleporting to a neighbour simulator, | ||
972 | // where that neighbour simulator could otherwise request a child agent create on the source which then | ||
973 | // closes our existing agent which is still signalled as root. | ||
974 | sp.IsChildAgent = true; | ||
975 | |||
976 | // OK, send TPFinish to the client, so that it starts the process of contacting the destination region | ||
564 | if (m_eqModule != null) | 977 | if (m_eqModule != null) |
565 | { | 978 | { |
566 | m_eqModule.TeleportFinishEvent(destinationHandle, 13, endPoint, 0, teleportFlags, capsPath, sp.UUID); | 979 | m_eqModule.TeleportFinishEvent(destinationHandle, 13, endPoint, 0, teleportFlags, capsPath, sp.UUID, |
980 | finalDestination.RegionSizeX, finalDestination.RegionSizeY); | ||
567 | } | 981 | } |
568 | else | 982 | else |
569 | { | 983 | { |
@@ -571,31 +985,43 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
571 | teleportFlags, capsPath); | 985 | teleportFlags, capsPath); |
572 | } | 986 | } |
573 | 987 | ||
574 | // Let's set this to true tentatively. This does not trigger OnChildAgent | ||
575 | sp.IsChildAgent = true; | ||
576 | |||
577 | // TeleportFinish makes the client send CompleteMovementIntoRegion (at the destination), which | 988 | // TeleportFinish makes the client send CompleteMovementIntoRegion (at the destination), which |
578 | // trigers a whole shebang of things there, including MakeRoot. So let's wait for confirmation | 989 | // trigers a whole shebang of things there, including MakeRoot. So let's wait for confirmation |
579 | // that the client contacted the destination before we close things here. | 990 | // that the client contacted the destination before we close things here. |
580 | if (!m_entityTransferStateMachine.WaitForAgentArrivedAtDestination(sp.UUID)) | 991 | if (!m_entityTransferStateMachine.WaitForAgentArrivedAtDestination(sp.UUID)) |
581 | { | 992 | { |
993 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) | ||
994 | { | ||
995 | m_interRegionTeleportAborts.Value++; | ||
996 | |||
997 | m_log.DebugFormat( | ||
998 | "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after WaitForAgentArrivedAtDestination due to previous client close.", | ||
999 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
1000 | |||
1001 | return; | ||
1002 | } | ||
1003 | |||
582 | m_log.WarnFormat( | 1004 | m_log.WarnFormat( |
583 | "[ENTITY TRANSFER MODULE]: Teleport of {0} to {1} from {2} failed due to no callback from destination region. Returning avatar to source region.", | 1005 | "[ENTITY TRANSFER MODULE]: Teleport of {0} to {1} from {2} failed due to no callback from destination region. Returning avatar to source region.", |
584 | sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName); | 1006 | sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName); |
585 | 1007 | ||
586 | Fail(sp, finalDestination, logout); | 1008 | Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Destination region did not signal teleport completion."); |
1009 | |||
587 | return; | 1010 | return; |
588 | } | 1011 | } |
589 | 1012 | ||
590 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); | 1013 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); |
591 | 1014 | ||
1015 | /* | ||
1016 | // TODO: This may be 0.6. Check if still needed | ||
592 | // For backwards compatibility | 1017 | // For backwards compatibility |
593 | if (version == "Unknown" || version == string.Empty) | 1018 | if (version == 0f) |
594 | { | 1019 | { |
595 | // CrossAttachmentsIntoNewRegion is a synchronous call. We shouldn't need to wait after it | 1020 | // CrossAttachmentsIntoNewRegion is a synchronous call. We shouldn't need to wait after it |
596 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Old simulator, sending attachments one by one..."); | 1021 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Old simulator, sending attachments one by one..."); |
597 | CrossAttachmentsIntoNewRegion(finalDestination, sp, true); | 1022 | CrossAttachmentsIntoNewRegion(finalDestination, sp, true); |
598 | } | 1023 | } |
1024 | */ | ||
599 | 1025 | ||
600 | // May need to logout or other cleanup | 1026 | // May need to logout or other cleanup |
601 | AgentHasMovedAway(sp, logout); | 1027 | AgentHasMovedAway(sp, logout); |
@@ -606,12 +1032,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
606 | // Now let's make it officially a child agent | 1032 | // Now let's make it officially a child agent |
607 | sp.MakeChildAgent(); | 1033 | sp.MakeChildAgent(); |
608 | 1034 | ||
609 | // sp.Scene.CleanDroppedAttachments(); | ||
610 | |||
611 | // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone | 1035 | // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone |
612 | 1036 | ||
613 | if (NeedsClosing(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) | 1037 | if (NeedsClosing(sp.Scene.DefaultDrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) |
614 | { | 1038 | { |
1039 | if (!sp.Scene.IncomingPreCloseClient(sp)) | ||
1040 | return; | ||
1041 | |||
615 | // We need to delay here because Imprudence viewers, unlike v1 or v3, have a short (<200ms, <500ms) delay before | 1042 | // We need to delay here because Imprudence viewers, unlike v1 or v3, have a short (<200ms, <500ms) delay before |
616 | // they regard the new region as the current region after receiving the AgentMovementComplete | 1043 | // they regard the new region as the current region after receiving the AgentMovementComplete |
617 | // response. If close is sent before then, it will cause the viewer to quit instead. | 1044 | // response. If close is sent before then, it will cause the viewer to quit instead. |
@@ -620,51 +1047,243 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
620 | // an agent cannot teleport back to this region if it has teleported away. | 1047 | // an agent cannot teleport back to this region if it has teleported away. |
621 | Thread.Sleep(2000); | 1048 | Thread.Sleep(2000); |
622 | 1049 | ||
623 | sp.Scene.IncomingCloseAgent(sp.UUID, false); | 1050 | sp.Scene.CloseAgent(sp.UUID, false); |
624 | } | 1051 | } |
625 | else | 1052 | else |
626 | { | 1053 | { |
627 | // now we have a child agent in this region. | 1054 | // now we have a child agent in this region. |
628 | sp.Reset(); | 1055 | sp.Reset(); |
629 | } | 1056 | } |
1057 | } | ||
630 | 1058 | ||
631 | // Commented pending deletion since this method no longer appears to do anything at all | 1059 | private void TransferAgent_V2(ScenePresence sp, AgentCircuitData agentCircuit, GridRegion reg, GridRegion finalDestination, |
632 | // // REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE! | 1060 | IPEndPoint endPoint, uint teleportFlags, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, EntityTransferContext ctx, out string reason) |
633 | // if (sp.Scene.NeedSceneCacheClear(sp.UUID)) | 1061 | { |
634 | // { | 1062 | ulong destinationHandle = finalDestination.RegionHandle; |
635 | // m_log.DebugFormat( | 1063 | AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); |
636 | // "[ENTITY TRANSFER MODULE]: User {0} is going to another region, profile cache removed", | 1064 | |
637 | // sp.UUID); | 1065 | // Let's create an agent there if one doesn't exist yet. |
638 | // } | 1066 | // NOTE: logout will always be false for a non-HG teleport. |
1067 | bool logout = false; | ||
1068 | if (!CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout)) | ||
1069 | { | ||
1070 | m_interRegionTeleportFailures.Value++; | ||
1071 | |||
1072 | m_log.DebugFormat( | ||
1073 | "[ENTITY TRANSFER MODULE]: Teleport of {0} from {1} to {2} was refused because {3}", | ||
1074 | sp.Name, sp.Scene.RegionInfo.RegionName, finalDestination.RegionName, reason); | ||
1075 | |||
1076 | sp.ControllingClient.SendTeleportFailed(reason); | ||
1077 | |||
1078 | return; | ||
1079 | } | ||
1080 | |||
1081 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling) | ||
1082 | { | ||
1083 | m_interRegionTeleportCancels.Value++; | ||
1084 | |||
1085 | m_log.DebugFormat( | ||
1086 | "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request", | ||
1087 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
1088 | |||
1089 | return; | ||
1090 | } | ||
1091 | else if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) | ||
1092 | { | ||
1093 | m_interRegionTeleportAborts.Value++; | ||
1094 | |||
1095 | m_log.DebugFormat( | ||
1096 | "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after CreateAgent due to previous client close.", | ||
1097 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
1098 | |||
1099 | return; | ||
1100 | } | ||
1101 | |||
1102 | // Past this point we have to attempt clean up if the teleport fails, so update transfer state. | ||
1103 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring); | ||
1104 | |||
1105 | IClientIPEndpoint ipepClient; | ||
1106 | string capsPath = String.Empty; | ||
1107 | float dist = (float)Math.Max(sp.Scene.DefaultDrawDistance, | ||
1108 | (float)Math.Max(sp.Scene.RegionInfo.RegionSizeX, sp.Scene.RegionInfo.RegionSizeY)); | ||
1109 | if (NeedsNewAgent(dist, oldRegionX, newRegionX, oldRegionY, newRegionY)) | ||
1110 | { | ||
1111 | m_log.DebugFormat( | ||
1112 | "[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for agent {3} from {4}", | ||
1113 | finalDestination.RegionName, newRegionX, newRegionY, sp.Name, Scene.Name); | ||
1114 | |||
1115 | //sp.ControllingClient.SendTeleportProgress(teleportFlags, "Creating agent..."); | ||
1116 | #region IP Translation for NAT | ||
1117 | // Uses ipepClient above | ||
1118 | if (sp.ClientView.TryGet(out ipepClient)) | ||
1119 | { | ||
1120 | endPoint.Address = NetworkUtil.GetIPFor(ipepClient.EndPoint, endPoint.Address); | ||
1121 | } | ||
1122 | #endregion | ||
1123 | capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath); | ||
1124 | } | ||
1125 | else | ||
1126 | { | ||
1127 | agentCircuit.CapsPath = sp.Scene.CapsModule.GetChildSeed(sp.UUID, reg.RegionHandle); | ||
1128 | capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath); | ||
1129 | } | ||
1130 | |||
1131 | // We need to set this here to avoid an unlikely race condition when teleporting to a neighbour simulator, | ||
1132 | // where that neighbour simulator could otherwise request a child agent create on the source which then | ||
1133 | // closes our existing agent which is still signalled as root. | ||
1134 | //sp.IsChildAgent = true; | ||
1135 | |||
1136 | // New protocol: send TP Finish directly, without prior ES or EAC. That's what happens in the Linden grid | ||
1137 | if (m_eqModule != null) | ||
1138 | m_eqModule.TeleportFinishEvent(destinationHandle, 13, endPoint, 0, teleportFlags, capsPath, sp.UUID, | ||
1139 | finalDestination.RegionSizeX, finalDestination.RegionSizeY); | ||
1140 | else | ||
1141 | sp.ControllingClient.SendRegionTeleport(destinationHandle, 13, endPoint, 4, | ||
1142 | teleportFlags, capsPath); | ||
1143 | |||
1144 | m_log.DebugFormat( | ||
1145 | "[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}", | ||
1146 | capsPath, sp.Scene.RegionInfo.RegionName, sp.Name); | ||
1147 | |||
1148 | // Let's send a full update of the agent. | ||
1149 | AgentData agent = new AgentData(); | ||
1150 | sp.CopyTo(agent); | ||
1151 | if (ctx.OutboundVersion < 0.5f) | ||
1152 | agent.Appearance.PackLegacyWearables = true; | ||
1153 | agent.Position = agentCircuit.startpos; | ||
1154 | agent.SenderWantsToWaitForRoot = true; | ||
1155 | //SetCallbackURL(agent, sp.Scene.RegionInfo); | ||
1156 | |||
1157 | // Reset the do not close flag. This must be done before the destination opens child connections (here | ||
1158 | // triggered by UpdateAgent) to avoid race conditions. However, we also want to reset it as late as possible | ||
1159 | // to avoid a situation where an unexpectedly early call to Scene.NewUserConnection() wrongly results | ||
1160 | // in no close. | ||
1161 | sp.DoNotCloseAfterTeleport = false; | ||
1162 | |||
1163 | // Send the Update. If this returns true, we know the client has contacted the destination | ||
1164 | // via CompleteMovementIntoRegion, so we can let go. | ||
1165 | // If it returns false, something went wrong, and we need to abort. | ||
1166 | if (!UpdateAgent(reg, finalDestination, agent, sp)) | ||
1167 | { | ||
1168 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) | ||
1169 | { | ||
1170 | m_interRegionTeleportAborts.Value++; | ||
1171 | |||
1172 | m_log.DebugFormat( | ||
1173 | "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after UpdateAgent due to previous client close.", | ||
1174 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
1175 | |||
1176 | return; | ||
1177 | } | ||
1178 | |||
1179 | m_log.WarnFormat( | ||
1180 | "[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1}. Keeping avatar in {2}", | ||
1181 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
1182 | |||
1183 | Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Connection between viewer and destination region could not be established."); | ||
1184 | return; | ||
1185 | } | ||
1186 | |||
1187 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); | ||
1188 | |||
1189 | // Need to signal neighbours whether child agents may need closing irrespective of whether this | ||
1190 | // one needed closing. We also need to close child agents as quickly as possible to avoid complicated | ||
1191 | // race conditions with rapid agent releporting (e.g. from A1 to a non-neighbour B, back | ||
1192 | // to a neighbour A2 then off to a non-neighbour C). Closing child agents any later requires complex | ||
1193 | // distributed checks to avoid problems in rapid reteleporting scenarios and where child agents are | ||
1194 | // abandoned without proper close by viewer but then re-used by an incoming connection. | ||
1195 | sp.CloseChildAgents(newRegionX, newRegionY); | ||
1196 | |||
1197 | // May need to logout or other cleanup | ||
1198 | AgentHasMovedAway(sp, logout); | ||
1199 | |||
1200 | // Well, this is it. The agent is over there. | ||
1201 | KillEntity(sp.Scene, sp.LocalId); | ||
639 | 1202 | ||
640 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | 1203 | // Now let's make it officially a child agent |
1204 | sp.MakeChildAgent(); | ||
1205 | |||
1206 | // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone | ||
1207 | if (NeedsClosing(sp.Scene.DefaultDrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) | ||
1208 | { | ||
1209 | if (!sp.Scene.IncomingPreCloseClient(sp)) | ||
1210 | return; | ||
1211 | |||
1212 | // RED ALERT!!!! | ||
1213 | // PLEASE DO NOT DECREASE THIS WAIT TIME UNDER ANY CIRCUMSTANCES. | ||
1214 | // THE VIEWERS SEEM TO NEED SOME TIME AFTER RECEIVING MoveAgentIntoRegion | ||
1215 | // BEFORE THEY SETTLE IN THE NEW REGION. | ||
1216 | // DECREASING THE WAIT TIME HERE WILL EITHER RESULT IN A VIEWER CRASH OR | ||
1217 | // IN THE AVIE BEING PLACED IN INFINITY FOR A COUPLE OF SECONDS. | ||
1218 | Thread.Sleep(15000); | ||
1219 | |||
1220 | // OK, it got this agent. Let's close everything | ||
1221 | // If we shouldn't close the agent due to some other region renewing the connection | ||
1222 | // then this will be handled in IncomingCloseAgent under lock conditions | ||
1223 | m_log.DebugFormat( | ||
1224 | "[ENTITY TRANSFER MODULE]: Closing agent {0} in {1} after teleport", sp.Name, Scene.Name); | ||
1225 | |||
1226 | sp.Scene.CloseAgent(sp.UUID, false); | ||
1227 | } | ||
1228 | else | ||
1229 | { | ||
1230 | // now we have a child agent in this region. | ||
1231 | sp.Reset(); | ||
1232 | } | ||
641 | } | 1233 | } |
642 | 1234 | ||
643 | protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout) | 1235 | /// <summary> |
1236 | /// Clean up an inter-region teleport that did not complete, either because of simulator failure or cancellation. | ||
1237 | /// </summary> | ||
1238 | /// <remarks> | ||
1239 | /// All operations here must be idempotent so that we can call this method at any point in the teleport process | ||
1240 | /// up until we send the TeleportFinish event quene event to the viewer. | ||
1241 | /// <remarks> | ||
1242 | /// <param name='sp'> </param> | ||
1243 | /// <param name='finalDestination'></param> | ||
1244 | protected virtual void CleanupFailedInterRegionTeleport(ScenePresence sp, string auth_token, GridRegion finalDestination) | ||
644 | { | 1245 | { |
645 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); | 1246 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); |
646 | 1247 | ||
647 | // Client never contacted destination. Let's restore everything back | 1248 | if (sp.IsChildAgent) // We had set it to child before attempted TP (V1) |
648 | sp.ControllingClient.SendTeleportFailed("Problems connecting to destination."); | 1249 | { |
1250 | sp.IsChildAgent = false; | ||
1251 | ReInstantiateScripts(sp); | ||
649 | 1252 | ||
650 | // Fail. Reset it back | 1253 | EnableChildAgents(sp); |
651 | sp.IsChildAgent = false; | 1254 | } |
652 | ReInstantiateScripts(sp); | 1255 | // Finally, kill the agent we just created at the destination. |
1256 | // XXX: Possibly this should be done asynchronously. | ||
1257 | Scene.SimulationService.CloseAgent(finalDestination, sp.UUID, auth_token); | ||
1258 | } | ||
653 | 1259 | ||
654 | EnableChildAgents(sp); | 1260 | /// <summary> |
1261 | /// Signal that the inter-region teleport failed and perform cleanup. | ||
1262 | /// </summary> | ||
1263 | /// <param name='sp'></param> | ||
1264 | /// <param name='finalDestination'></param> | ||
1265 | /// <param name='logout'></param> | ||
1266 | /// <param name='reason'>Human readable reason for teleport failure. Will be sent to client.</param> | ||
1267 | protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout, string auth_code, string reason) | ||
1268 | { | ||
1269 | CleanupFailedInterRegionTeleport(sp, auth_code, finalDestination); | ||
655 | 1270 | ||
656 | // Finally, kill the agent we just created at the destination. | 1271 | m_interRegionTeleportFailures.Value++; |
657 | Scene.SimulationService.CloseAgent(finalDestination, sp.UUID); | ||
658 | 1272 | ||
659 | sp.Scene.EventManager.TriggerTeleportFail(sp.ControllingClient, logout); | 1273 | sp.ControllingClient.SendTeleportFailed( |
1274 | string.Format( | ||
1275 | "Problems connecting to destination {0}, reason: {1}", finalDestination.RegionName, reason)); | ||
660 | 1276 | ||
661 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); | 1277 | sp.Scene.EventManager.TriggerTeleportFail(sp.ControllingClient, logout); |
662 | } | 1278 | } |
663 | 1279 | ||
664 | protected virtual bool CreateAgent(ScenePresence sp, GridRegion reg, GridRegion finalDestination, AgentCircuitData agentCircuit, uint teleportFlags, out string reason, out bool logout) | 1280 | protected virtual bool CreateAgent(ScenePresence sp, GridRegion reg, GridRegion finalDestination, AgentCircuitData agentCircuit, uint teleportFlags, out string reason, out bool logout) |
665 | { | 1281 | { |
1282 | GridRegion source = new GridRegion(Scene.RegionInfo); | ||
1283 | source.RawServerURI = m_GatekeeperURI; | ||
1284 | |||
666 | logout = false; | 1285 | logout = false; |
667 | bool success = Scene.SimulationService.CreateAgent(finalDestination, agentCircuit, teleportFlags, out reason); | 1286 | bool success = Scene.SimulationService.CreateAgent(source, finalDestination, agentCircuit, teleportFlags, out reason); |
668 | 1287 | ||
669 | if (success) | 1288 | if (success) |
670 | sp.Scene.EventManager.TriggerTeleportStart(sp.ControllingClient, reg, finalDestination, teleportFlags, logout); | 1289 | sp.Scene.EventManager.TriggerTeleportStart(sp.ControllingClient, reg, finalDestination, teleportFlags, logout); |
@@ -702,14 +1321,32 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
702 | scene.SendKillObject(new List<uint> { localID }); | 1321 | scene.SendKillObject(new List<uint> { localID }); |
703 | } | 1322 | } |
704 | 1323 | ||
705 | protected virtual GridRegion GetFinalDestination(GridRegion region) | 1324 | protected virtual GridRegion GetFinalDestination(GridRegion region, UUID agentID, string agentHomeURI, out string message) |
706 | { | 1325 | { |
1326 | message = null; | ||
707 | return region; | 1327 | return region; |
708 | } | 1328 | } |
709 | 1329 | ||
1330 | // This returns 'true' if the new region already has a child agent for our | ||
1331 | // incoming agent. The implication is that, if 'false', we have to create the | ||
1332 | // child and then teleport into the region. | ||
710 | protected virtual bool NeedsNewAgent(float drawdist, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY) | 1333 | protected virtual bool NeedsNewAgent(float drawdist, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY) |
711 | { | 1334 | { |
712 | return Util.IsOutsideView(drawdist, oldRegionX, newRegionX, oldRegionY, newRegionY); | 1335 | if (m_regionCombinerModule != null && m_regionCombinerModule.IsRootForMegaregion(Scene.RegionInfo.RegionID)) |
1336 | { | ||
1337 | Vector2 swCorner, neCorner; | ||
1338 | GetMegaregionViewRange(out swCorner, out neCorner); | ||
1339 | |||
1340 | m_log.DebugFormat( | ||
1341 | "[ENTITY TRANSFER MODULE]: Megaregion view of {0} is from {1} to {2} with new agent check for {3},{4}", | ||
1342 | Scene.Name, swCorner, neCorner, newRegionX, newRegionY); | ||
1343 | |||
1344 | return !(newRegionX >= swCorner.X && newRegionX <= neCorner.X && newRegionY >= swCorner.Y && newRegionY <= neCorner.Y); | ||
1345 | } | ||
1346 | else | ||
1347 | { | ||
1348 | return Util.IsOutsideView(drawdist, oldRegionX, newRegionX, oldRegionY, newRegionY); | ||
1349 | } | ||
713 | } | 1350 | } |
714 | 1351 | ||
715 | protected virtual bool NeedsClosing(float drawdist, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, GridRegion reg) | 1352 | protected virtual bool NeedsClosing(float drawdist, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, GridRegion reg) |
@@ -717,20 +1354,6 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
717 | return Util.IsOutsideView(drawdist, oldRegionX, newRegionX, oldRegionY, newRegionY); | 1354 | return Util.IsOutsideView(drawdist, oldRegionX, newRegionX, oldRegionY, newRegionY); |
718 | } | 1355 | } |
719 | 1356 | ||
720 | protected virtual bool IsOutsideRegion(Scene s, Vector3 pos) | ||
721 | { | ||
722 | if (s.TestBorderCross(pos, Cardinals.N)) | ||
723 | return true; | ||
724 | if (s.TestBorderCross(pos, Cardinals.S)) | ||
725 | return true; | ||
726 | if (s.TestBorderCross(pos, Cardinals.E)) | ||
727 | return true; | ||
728 | if (s.TestBorderCross(pos, Cardinals.W)) | ||
729 | return true; | ||
730 | |||
731 | return false; | ||
732 | } | ||
733 | |||
734 | #endregion | 1357 | #endregion |
735 | 1358 | ||
736 | #region Landmark Teleport | 1359 | #region Landmark Teleport |
@@ -758,7 +1381,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
758 | 1381 | ||
759 | #region Teleport Home | 1382 | #region Teleport Home |
760 | 1383 | ||
761 | public virtual void TeleportHome(UUID id, IClientAPI client) | 1384 | public virtual void TriggerTeleportHome(UUID id, IClientAPI client) |
1385 | { | ||
1386 | TeleportHome(id, client); | ||
1387 | } | ||
1388 | |||
1389 | public virtual bool TeleportHome(UUID id, IClientAPI client) | ||
762 | { | 1390 | { |
763 | m_log.DebugFormat( | 1391 | m_log.DebugFormat( |
764 | "[ENTITY TRANSFER MODULE]: Request to teleport {0} {1} home", client.Name, client.AgentId); | 1392 | "[ENTITY TRANSFER MODULE]: Request to teleport {0} {1} home", client.Name, client.AgentId); |
@@ -768,12 +1396,20 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
768 | 1396 | ||
769 | if (uinfo != null) | 1397 | if (uinfo != null) |
770 | { | 1398 | { |
1399 | if (uinfo.HomeRegionID == UUID.Zero) | ||
1400 | { | ||
1401 | // can't find the Home region: Tell viewer and abort | ||
1402 | m_log.ErrorFormat("{0} No grid user info found for {1} {2}. Cannot send home.", | ||
1403 | LogHeader, client.Name, client.AgentId); | ||
1404 | client.SendTeleportFailed("You don't have a home position set."); | ||
1405 | return false; | ||
1406 | } | ||
771 | GridRegion regionInfo = Scene.GridService.GetRegionByUUID(UUID.Zero, uinfo.HomeRegionID); | 1407 | GridRegion regionInfo = Scene.GridService.GetRegionByUUID(UUID.Zero, uinfo.HomeRegionID); |
772 | if (regionInfo == null) | 1408 | if (regionInfo == null) |
773 | { | 1409 | { |
774 | // can't find the Home region: Tell viewer and abort | 1410 | // can't find the Home region: Tell viewer and abort |
775 | client.SendTeleportFailed("Your home region could not be found."); | 1411 | client.SendTeleportFailed("Your home region could not be found."); |
776 | return; | 1412 | return false; |
777 | } | 1413 | } |
778 | 1414 | ||
779 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Home region of {0} is {1} ({2}-{3})", | 1415 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Home region of {0} is {1} ({2}-{3})", |
@@ -783,13 +1419,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
783 | ((Scene)(client.Scene)).RequestTeleportLocation( | 1419 | ((Scene)(client.Scene)).RequestTeleportLocation( |
784 | client, regionInfo.RegionHandle, uinfo.HomePosition, uinfo.HomeLookAt, | 1420 | client, regionInfo.RegionHandle, uinfo.HomePosition, uinfo.HomeLookAt, |
785 | (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaHome)); | 1421 | (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaHome)); |
1422 | return true; | ||
786 | } | 1423 | } |
787 | else | 1424 | else |
788 | { | 1425 | { |
789 | m_log.ErrorFormat( | 1426 | // can't find the Home region: Tell viewer and abort |
790 | "[ENTITY TRANSFER MODULE]: No grid user information found for {0} {1}. Cannot send home.", | 1427 | client.SendTeleportFailed("Your home region could not be found."); |
791 | client.Name, client.AgentId); | ||
792 | } | 1428 | } |
1429 | return false; | ||
793 | } | 1430 | } |
794 | 1431 | ||
795 | #endregion | 1432 | #endregion |
@@ -797,230 +1434,112 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
797 | 1434 | ||
798 | #region Agent Crossings | 1435 | #region Agent Crossings |
799 | 1436 | ||
800 | public bool Cross(ScenePresence agent, bool isFlying) | 1437 | // Given a position relative to the current region (which has previously been tested to |
1438 | // see that it is actually outside the current region), find the new region that the | ||
1439 | // point is actually in. | ||
1440 | // Returns the coordinates and information of the new region or 'null' of it doesn't exist. | ||
1441 | public GridRegion GetDestination(Scene scene, UUID agentID, Vector3 pos, | ||
1442 | EntityTransferContext ctx, out Vector3 newpos, out string failureReason) | ||
801 | { | 1443 | { |
802 | Scene scene = agent.Scene; | 1444 | newpos = pos; |
803 | Vector3 pos = agent.AbsolutePosition; | 1445 | failureReason = string.Empty; |
804 | Vector3 newpos = new Vector3(pos.X, pos.Y, pos.Z); | 1446 | string homeURI = scene.GetAgentHomeURI(agentID); |
805 | uint neighbourx = scene.RegionInfo.RegionLocX; | ||
806 | uint neighboury = scene.RegionInfo.RegionLocY; | ||
807 | const float boundaryDistance = 1.7f; | ||
808 | Vector3 northCross = new Vector3(0, boundaryDistance, 0); | ||
809 | Vector3 southCross = new Vector3(0, -1 * boundaryDistance, 0); | ||
810 | Vector3 eastCross = new Vector3(boundaryDistance, 0, 0); | ||
811 | Vector3 westCross = new Vector3(-1 * boundaryDistance, 0, 0); | ||
812 | |||
813 | // distance into new region to place avatar | ||
814 | const float enterDistance = 0.5f; | ||
815 | 1447 | ||
816 | if (scene.TestBorderCross(pos + westCross, Cardinals.W)) | 1448 | // m_log.DebugFormat( |
817 | { | 1449 | // "[ENTITY TRANSFER MODULE]: Crossing agent {0} at pos {1} in {2}", agent.Name, pos, scene.Name); |
818 | if (scene.TestBorderCross(pos + northCross, Cardinals.N)) | ||
819 | { | ||
820 | Border b = scene.GetCrossedBorder(pos + northCross, Cardinals.N); | ||
821 | neighboury += (uint)(int)(b.BorderLine.Z / (int)Constants.RegionSize); | ||
822 | } | ||
823 | else if (scene.TestBorderCross(pos + southCross, Cardinals.S)) | ||
824 | { | ||
825 | Border b = scene.GetCrossedBorder(pos + southCross, Cardinals.S); | ||
826 | if (b.TriggerRegionX == 0 && b.TriggerRegionY == 0) | ||
827 | { | ||
828 | neighboury--; | ||
829 | newpos.Y = Constants.RegionSize - enterDistance; | ||
830 | } | ||
831 | else | ||
832 | { | ||
833 | agent.IsInTransit = true; | ||
834 | |||
835 | neighboury = b.TriggerRegionY; | ||
836 | neighbourx = b.TriggerRegionX; | ||
837 | |||
838 | Vector3 newposition = pos; | ||
839 | newposition.X += (scene.RegionInfo.RegionLocX - neighbourx) * Constants.RegionSize; | ||
840 | newposition.Y += (scene.RegionInfo.RegionLocY - neighboury) * Constants.RegionSize; | ||
841 | agent.ControllingClient.SendAgentAlertMessage( | ||
842 | String.Format("Moving you to region {0},{1}", neighbourx, neighboury), false); | ||
843 | InformClientToInitateTeleportToLocation(agent, neighbourx, neighboury, newposition, scene); | ||
844 | return true; | ||
845 | } | ||
846 | } | ||
847 | |||
848 | Border ba = scene.GetCrossedBorder(pos + westCross, Cardinals.W); | ||
849 | if (ba.TriggerRegionX == 0 && ba.TriggerRegionY == 0) | ||
850 | { | ||
851 | neighbourx--; | ||
852 | newpos.X = Constants.RegionSize - enterDistance; | ||
853 | } | ||
854 | else | ||
855 | { | ||
856 | agent.IsInTransit = true; | ||
857 | 1450 | ||
858 | neighboury = ba.TriggerRegionY; | 1451 | // Compute world location of the object's position |
859 | neighbourx = ba.TriggerRegionX; | 1452 | double presenceWorldX = (double)scene.RegionInfo.WorldLocX + pos.X; |
1453 | double presenceWorldY = (double)scene.RegionInfo.WorldLocY + pos.Y; | ||
860 | 1454 | ||
861 | Vector3 newposition = pos; | 1455 | // Call the grid service to lookup the region containing the new position. |
862 | newposition.X += (scene.RegionInfo.RegionLocX - neighbourx) * Constants.RegionSize; | 1456 | GridRegion neighbourRegion = GetRegionContainingWorldLocation(scene.GridService, scene.RegionInfo.ScopeID, |
863 | newposition.Y += (scene.RegionInfo.RegionLocY - neighboury) * Constants.RegionSize; | 1457 | presenceWorldX, presenceWorldY, |
864 | agent.ControllingClient.SendAgentAlertMessage( | 1458 | Math.Max(scene.RegionInfo.RegionSizeX, scene.RegionInfo.RegionSizeY)); |
865 | String.Format("Moving you to region {0},{1}", neighbourx, neighboury), false); | ||
866 | InformClientToInitateTeleportToLocation(agent, neighbourx, neighboury, newposition, scene); | ||
867 | 1459 | ||
868 | return true; | 1460 | if (neighbourRegion != null) |
869 | } | ||
870 | |||
871 | } | ||
872 | else if (scene.TestBorderCross(pos + eastCross, Cardinals.E)) | ||
873 | { | 1461 | { |
874 | Border b = scene.GetCrossedBorder(pos + eastCross, Cardinals.E); | 1462 | // Compute the entity's position relative to the new region |
875 | neighbourx += (uint)(int)(b.BorderLine.Z / (int)Constants.RegionSize); | 1463 | newpos = new Vector3((float)(presenceWorldX - (double)neighbourRegion.RegionLocX), |
876 | newpos.X = enterDistance; | 1464 | (float)(presenceWorldY - (double)neighbourRegion.RegionLocY), |
1465 | pos.Z); | ||
877 | 1466 | ||
878 | if (scene.TestBorderCross(pos + southCross, Cardinals.S)) | 1467 | if (m_bannedRegionCache.IfBanned(neighbourRegion.RegionHandle, agentID)) |
879 | { | ||
880 | Border ba = scene.GetCrossedBorder(pos + southCross, Cardinals.S); | ||
881 | if (ba.TriggerRegionX == 0 && ba.TriggerRegionY == 0) | ||
882 | { | ||
883 | neighboury--; | ||
884 | newpos.Y = Constants.RegionSize - enterDistance; | ||
885 | } | ||
886 | else | ||
887 | { | ||
888 | agent.IsInTransit = true; | ||
889 | |||
890 | neighboury = ba.TriggerRegionY; | ||
891 | neighbourx = ba.TriggerRegionX; | ||
892 | Vector3 newposition = pos; | ||
893 | newposition.X += (scene.RegionInfo.RegionLocX - neighbourx) * Constants.RegionSize; | ||
894 | newposition.Y += (scene.RegionInfo.RegionLocY - neighboury) * Constants.RegionSize; | ||
895 | agent.ControllingClient.SendAgentAlertMessage( | ||
896 | String.Format("Moving you to region {0},{1}", neighbourx, neighboury), false); | ||
897 | InformClientToInitateTeleportToLocation(agent, neighbourx, neighboury, newposition, scene); | ||
898 | return true; | ||
899 | } | ||
900 | } | ||
901 | else if (scene.TestBorderCross(pos + northCross, Cardinals.N)) | ||
902 | { | 1468 | { |
903 | Border c = scene.GetCrossedBorder(pos + northCross, Cardinals.N); | 1469 | failureReason = "Cannot region cross into banned parcel"; |
904 | neighboury += (uint)(int)(c.BorderLine.Z / (int)Constants.RegionSize); | 1470 | neighbourRegion = null; |
905 | newpos.Y = enterDistance; | ||
906 | } | ||
907 | } | ||
908 | else if (scene.TestBorderCross(pos + southCross, Cardinals.S)) | ||
909 | { | ||
910 | Border b = scene.GetCrossedBorder(pos + southCross, Cardinals.S); | ||
911 | if (b.TriggerRegionX == 0 && b.TriggerRegionY == 0) | ||
912 | { | ||
913 | neighboury--; | ||
914 | newpos.Y = Constants.RegionSize - enterDistance; | ||
915 | } | 1471 | } |
916 | else | 1472 | else |
917 | { | 1473 | { |
918 | agent.IsInTransit = true; | 1474 | // If not banned, make sure this agent is not in the list. |
919 | 1475 | m_bannedRegionCache.Remove(neighbourRegion.RegionHandle, agentID); | |
920 | neighboury = b.TriggerRegionY; | ||
921 | neighbourx = b.TriggerRegionX; | ||
922 | Vector3 newposition = pos; | ||
923 | newposition.X += (scene.RegionInfo.RegionLocX - neighbourx) * Constants.RegionSize; | ||
924 | newposition.Y += (scene.RegionInfo.RegionLocY - neighboury) * Constants.RegionSize; | ||
925 | agent.ControllingClient.SendAgentAlertMessage( | ||
926 | String.Format("Moving you to region {0},{1}", neighbourx, neighboury), false); | ||
927 | InformClientToInitateTeleportToLocation(agent, neighbourx, neighboury, newposition, scene); | ||
928 | return true; | ||
929 | } | 1476 | } |
930 | } | ||
931 | else if (scene.TestBorderCross(pos + northCross, Cardinals.N)) | ||
932 | { | ||
933 | Border b = scene.GetCrossedBorder(pos + northCross, Cardinals.N); | ||
934 | neighboury += (uint)(int)(b.BorderLine.Z / (int)Constants.RegionSize); | ||
935 | newpos.Y = enterDistance; | ||
936 | } | ||
937 | |||
938 | /* | ||
939 | |||
940 | if (pos.X < boundaryDistance) //West | ||
941 | { | ||
942 | neighbourx--; | ||
943 | newpos.X = Constants.RegionSize - enterDistance; | ||
944 | } | ||
945 | else if (pos.X > Constants.RegionSize - boundaryDistance) // East | ||
946 | { | ||
947 | neighbourx++; | ||
948 | newpos.X = enterDistance; | ||
949 | } | ||
950 | 1477 | ||
951 | if (pos.Y < boundaryDistance) // South | 1478 | // Check to see if we have access to the target region. |
952 | { | 1479 | if (neighbourRegion != null |
953 | neighboury--; | 1480 | && !scene.SimulationService.QueryAccess(neighbourRegion, agentID, homeURI, false, newpos, scene.GetFormatsOffered(), ctx, out failureReason)) |
954 | newpos.Y = Constants.RegionSize - enterDistance; | ||
955 | } | ||
956 | else if (pos.Y > Constants.RegionSize - boundaryDistance) // North | ||
957 | { | ||
958 | neighboury++; | ||
959 | newpos.Y = enterDistance; | ||
960 | } | ||
961 | */ | ||
962 | |||
963 | ulong neighbourHandle = Utils.UIntsToLong((uint)(neighbourx * Constants.RegionSize), (uint)(neighboury * Constants.RegionSize)); | ||
964 | |||
965 | int x = (int)(neighbourx * Constants.RegionSize), y = (int)(neighboury * Constants.RegionSize); | ||
966 | |||
967 | ExpiringCache<ulong, DateTime> r; | ||
968 | DateTime banUntil; | ||
969 | |||
970 | if (m_bannedRegions.TryGetValue(agent.ControllingClient.AgentId, out r)) | ||
971 | { | ||
972 | if (r.TryGetValue(neighbourHandle, out banUntil)) | ||
973 | { | 1481 | { |
974 | if (DateTime.Now < banUntil) | 1482 | // remember banned |
975 | return false; | 1483 | m_bannedRegionCache.Add(neighbourRegion.RegionHandle, agentID); |
976 | r.Remove(neighbourHandle); | 1484 | neighbourRegion = null; |
977 | } | 1485 | } |
978 | } | 1486 | } |
979 | else | 1487 | else |
980 | { | 1488 | { |
981 | r = null; | 1489 | // The destination region just doesn't exist |
1490 | failureReason = "Cannot cross into non-existent region"; | ||
982 | } | 1491 | } |
983 | 1492 | ||
984 | GridRegion neighbourRegion = scene.GridService.GetRegionByPosition(scene.RegionInfo.ScopeID, (int)x, (int)y); | 1493 | if (neighbourRegion == null) |
1494 | m_log.DebugFormat("{0} GetDestination: region not found. Old region name={1} at <{2},{3}> of size <{4},{5}>. Old pos={6}", | ||
1495 | LogHeader, scene.RegionInfo.RegionName, | ||
1496 | scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY, | ||
1497 | scene.RegionInfo.RegionSizeX, scene.RegionInfo.RegionSizeY, | ||
1498 | pos); | ||
1499 | else | ||
1500 | m_log.DebugFormat("{0} GetDestination: new region={1} at <{2},{3}> of size <{4},{5}>, newpos=<{6},{7}>", | ||
1501 | LogHeader, neighbourRegion.RegionName, | ||
1502 | neighbourRegion.RegionLocX, neighbourRegion.RegionLocY, neighbourRegion.RegionSizeX, neighbourRegion.RegionSizeY, | ||
1503 | newpos.X, newpos.Y); | ||
985 | 1504 | ||
986 | string reason; | 1505 | return neighbourRegion; |
987 | string version; | 1506 | } |
988 | if (!scene.SimulationService.QueryAccess(neighbourRegion, agent.ControllingClient.AgentId, newpos, out version, out reason)) | ||
989 | { | ||
990 | agent.ControllingClient.SendAlertMessage("Cannot region cross into banned parcel"); | ||
991 | if (r == null) | ||
992 | { | ||
993 | r = new ExpiringCache<ulong, DateTime>(); | ||
994 | r.Add(neighbourHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); | ||
995 | 1507 | ||
996 | m_bannedRegions.Add(agent.ControllingClient.AgentId, r, TimeSpan.FromSeconds(45)); | 1508 | public bool Cross(ScenePresence agent, bool isFlying) |
997 | } | 1509 | { |
998 | else | 1510 | Vector3 newpos; |
999 | { | 1511 | EntityTransferContext ctx = new EntityTransferContext(); |
1000 | r.Add(neighbourHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); | 1512 | string failureReason; |
1001 | } | 1513 | |
1514 | GridRegion neighbourRegion = GetDestination(agent.Scene, agent.UUID, agent.AbsolutePosition, | ||
1515 | ctx, out newpos, out failureReason); | ||
1516 | if (neighbourRegion == null) | ||
1517 | { | ||
1518 | agent.ControllingClient.SendAlertMessage(failureReason); | ||
1002 | return false; | 1519 | return false; |
1003 | } | 1520 | } |
1004 | 1521 | ||
1005 | agent.IsInTransit = true; | 1522 | agent.IsInTransit = true; |
1006 | 1523 | ||
1007 | CrossAgentToNewRegionDelegate d = CrossAgentToNewRegionAsync; | 1524 | CrossAgentToNewRegionDelegate d = CrossAgentToNewRegionAsync; |
1008 | d.BeginInvoke(agent, newpos, neighbourx, neighboury, neighbourRegion, isFlying, version, CrossAgentToNewRegionCompleted, d); | 1525 | d.BeginInvoke(agent, newpos, neighbourRegion, isFlying, ctx, CrossAgentToNewRegionCompleted, d); |
1526 | |||
1527 | Scene.EventManager.TriggerCrossAgentToNewRegion(agent, isFlying, neighbourRegion); | ||
1009 | 1528 | ||
1010 | return true; | 1529 | return true; |
1011 | } | 1530 | } |
1012 | 1531 | ||
1013 | 1532 | ||
1014 | public delegate void InformClientToInitateTeleportToLocationDelegate(ScenePresence agent, uint regionX, uint regionY, | 1533 | public delegate void InformClientToInitiateTeleportToLocationDelegate(ScenePresence agent, uint regionX, uint regionY, |
1015 | Vector3 position, | 1534 | Vector3 position, |
1016 | Scene initiatingScene); | 1535 | Scene initiatingScene); |
1017 | 1536 | ||
1018 | private void InformClientToInitateTeleportToLocation(ScenePresence agent, uint regionX, uint regionY, Vector3 position, Scene initiatingScene) | 1537 | private void InformClientToInitiateTeleportToLocation(ScenePresence agent, uint regionX, uint regionY, Vector3 position, Scene initiatingScene) |
1019 | { | 1538 | { |
1020 | 1539 | ||
1021 | // This assumes that we know what our neighbours are. | 1540 | // This assumes that we know what our neighbours are. |
1022 | 1541 | ||
1023 | InformClientToInitateTeleportToLocationDelegate d = InformClientToInitiateTeleportToLocationAsync; | 1542 | InformClientToInitiateTeleportToLocationDelegate d = InformClientToInitiateTeleportToLocationAsync; |
1024 | d.BeginInvoke(agent, regionX, regionY, position, initiatingScene, | 1543 | d.BeginInvoke(agent, regionX, regionY, position, initiatingScene, |
1025 | InformClientToInitiateTeleportToLocationCompleted, | 1544 | InformClientToInitiateTeleportToLocationCompleted, |
1026 | d); | 1545 | d); |
@@ -1030,16 +1549,24 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1030 | Scene initiatingScene) | 1549 | Scene initiatingScene) |
1031 | { | 1550 | { |
1032 | Thread.Sleep(10000); | 1551 | Thread.Sleep(10000); |
1033 | 1552 | ||
1553 | m_log.DebugFormat( | ||
1554 | "[ENTITY TRANSFER MODULE]: Auto-reteleporting {0} to correct megaregion location {1},{2},{3} from {4}", | ||
1555 | agent.Name, regionX, regionY, position, initiatingScene.Name); | ||
1556 | |||
1557 | agent.Scene.RequestTeleportLocation( | ||
1558 | agent.ControllingClient, | ||
1559 | Util.RegionLocToHandle(regionX, regionY), | ||
1560 | position, | ||
1561 | agent.Lookat, | ||
1562 | (uint)Constants.TeleportFlags.ViaLocation); | ||
1563 | |||
1564 | /* | ||
1034 | IMessageTransferModule im = initiatingScene.RequestModuleInterface<IMessageTransferModule>(); | 1565 | IMessageTransferModule im = initiatingScene.RequestModuleInterface<IMessageTransferModule>(); |
1035 | if (im != null) | 1566 | if (im != null) |
1036 | { | 1567 | { |
1037 | UUID gotoLocation = Util.BuildFakeParcelID( | 1568 | UUID gotoLocation = Util.BuildFakeParcelID( |
1038 | Util.UIntsToLong( | 1569 | Util.RegionLocToHandle(regionX, regionY), |
1039 | (regionX * | ||
1040 | (uint)Constants.RegionSize), | ||
1041 | (regionY * | ||
1042 | (uint)Constants.RegionSize)), | ||
1043 | (uint)(int)position.X, | 1570 | (uint)(int)position.X, |
1044 | (uint)(int)position.Y, | 1571 | (uint)(int)position.Y, |
1045 | (uint)(int)position.Z); | 1572 | (uint)(int)position.Z); |
@@ -1065,53 +1592,73 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1065 | }); | 1592 | }); |
1066 | 1593 | ||
1067 | } | 1594 | } |
1595 | */ | ||
1068 | } | 1596 | } |
1069 | 1597 | ||
1070 | private void InformClientToInitiateTeleportToLocationCompleted(IAsyncResult iar) | 1598 | private void InformClientToInitiateTeleportToLocationCompleted(IAsyncResult iar) |
1071 | { | 1599 | { |
1072 | InformClientToInitateTeleportToLocationDelegate icon = | 1600 | InformClientToInitiateTeleportToLocationDelegate icon = |
1073 | (InformClientToInitateTeleportToLocationDelegate)iar.AsyncState; | 1601 | (InformClientToInitiateTeleportToLocationDelegate)iar.AsyncState; |
1074 | icon.EndInvoke(iar); | 1602 | icon.EndInvoke(iar); |
1075 | } | 1603 | } |
1076 | 1604 | ||
1077 | public delegate ScenePresence CrossAgentToNewRegionDelegate(ScenePresence agent, Vector3 pos, uint neighbourx, uint neighboury, GridRegion neighbourRegion, bool isFlying, string version); | 1605 | public bool CrossAgentToNewRegionPrep(ScenePresence agent, GridRegion neighbourRegion) |
1606 | { | ||
1607 | if (neighbourRegion == null) | ||
1608 | return false; | ||
1609 | |||
1610 | m_entityTransferStateMachine.SetInTransit(agent.UUID); | ||
1611 | |||
1612 | agent.RemoveFromPhysicalScene(); | ||
1613 | |||
1614 | return true; | ||
1615 | } | ||
1078 | 1616 | ||
1079 | /// <summary> | 1617 | /// <summary> |
1080 | /// This Closes child agents on neighbouring regions | 1618 | /// This Closes child agents on neighbouring regions |
1081 | /// Calls an asynchronous method to do so.. so it doesn't lag the sim. | 1619 | /// Calls an asynchronous method to do so.. so it doesn't lag the sim. |
1082 | /// </summary> | 1620 | /// </summary> |
1083 | protected ScenePresence CrossAgentToNewRegionAsync( | 1621 | public ScenePresence CrossAgentToNewRegionAsync( |
1084 | ScenePresence agent, Vector3 pos, uint neighbourx, uint neighboury, GridRegion neighbourRegion, | 1622 | ScenePresence agent, Vector3 pos, GridRegion neighbourRegion, |
1085 | bool isFlying, string version) | 1623 | bool isFlying, EntityTransferContext ctx) |
1086 | { | 1624 | { |
1087 | if (neighbourRegion == null) | ||
1088 | return agent; | ||
1089 | |||
1090 | try | 1625 | try |
1091 | { | 1626 | { |
1092 | m_entityTransferStateMachine.SetInTransit(agent.UUID); | 1627 | m_log.DebugFormat("{0}: CrossAgentToNewRegionAsync: new region={1} at <{2},{3}>. newpos={4}", |
1628 | LogHeader, neighbourRegion.RegionName, neighbourRegion.RegionLocX, neighbourRegion.RegionLocY, pos); | ||
1093 | 1629 | ||
1094 | ulong neighbourHandle = Utils.UIntsToLong((uint)(neighbourx * Constants.RegionSize), (uint)(neighboury * Constants.RegionSize)); | 1630 | if (!CrossAgentToNewRegionPrep(agent, neighbourRegion)) |
1095 | 1631 | { | |
1096 | m_log.DebugFormat( | 1632 | m_log.DebugFormat("{0}: CrossAgentToNewRegionAsync: prep failed. Resetting transfer state", LogHeader); |
1097 | "[ENTITY TRANSFER MODULE]: Crossing agent {0} {1} to {2}-{3} running version {4}", | 1633 | m_entityTransferStateMachine.ResetFromTransit(agent.UUID); |
1098 | agent.Firstname, agent.Lastname, neighbourx, neighboury, version); | 1634 | } |
1099 | |||
1100 | Scene m_scene = agent.Scene; | ||
1101 | 1635 | ||
1102 | if (!agent.ValidateAttachments()) | 1636 | if (!CrossAgentIntoNewRegionMain(agent, pos, neighbourRegion, isFlying, ctx)) |
1103 | m_log.DebugFormat( | 1637 | { |
1104 | "[ENTITY TRANSFER MODULE]: Failed validation of all attachments for region crossing of {0} from {1} to {2}. Continuing.", | 1638 | m_log.DebugFormat("{0}: CrossAgentToNewRegionAsync: cross main failed. Resetting transfer state", LogHeader); |
1105 | agent.Name, agent.Scene.RegionInfo.RegionName, neighbourRegion.RegionName); | 1639 | m_entityTransferStateMachine.ResetFromTransit(agent.UUID); |
1640 | } | ||
1106 | 1641 | ||
1107 | pos = pos + agent.Velocity; | 1642 | CrossAgentToNewRegionPost(agent, pos, neighbourRegion, isFlying, ctx); |
1108 | Vector3 vel2 = new Vector3(agent.Velocity.X, agent.Velocity.Y, 0); | 1643 | } |
1644 | catch (Exception e) | ||
1645 | { | ||
1646 | m_log.Error(string.Format("{0}: CrossAgentToNewRegionAsync: failed with exception ", LogHeader), e); | ||
1647 | } | ||
1109 | 1648 | ||
1110 | agent.RemoveFromPhysicalScene(); | 1649 | return agent; |
1650 | } | ||
1111 | 1651 | ||
1652 | public bool CrossAgentIntoNewRegionMain(ScenePresence agent, Vector3 pos, GridRegion neighbourRegion, bool isFlying, EntityTransferContext ctx) | ||
1653 | { | ||
1654 | try | ||
1655 | { | ||
1112 | AgentData cAgent = new AgentData(); | 1656 | AgentData cAgent = new AgentData(); |
1113 | agent.CopyTo(cAgent); | 1657 | agent.CopyTo(cAgent); |
1658 | if (ctx.OutboundVersion < 0.5f) | ||
1659 | cAgent.Appearance.PackLegacyWearables = true; | ||
1114 | cAgent.Position = pos; | 1660 | cAgent.Position = pos; |
1661 | |||
1115 | if (isFlying) | 1662 | if (isFlying) |
1116 | cAgent.ControlFlags |= (uint)AgentManager.ControlFlags.AGENT_CONTROL_FLY; | 1663 | cAgent.ControlFlags |= (uint)AgentManager.ControlFlags.AGENT_CONTROL_FLY; |
1117 | 1664 | ||
@@ -1121,102 +1668,121 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1121 | // Beyond this point, extra cleanup is needed beyond removing transit state | 1668 | // Beyond this point, extra cleanup is needed beyond removing transit state |
1122 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.Transferring); | 1669 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.Transferring); |
1123 | 1670 | ||
1124 | if (!m_scene.SimulationService.UpdateAgent(neighbourRegion, cAgent)) | 1671 | if (!agent.Scene.SimulationService.UpdateAgent(neighbourRegion, cAgent)) |
1125 | { | 1672 | { |
1126 | // region doesn't take it | 1673 | // region doesn't take it |
1127 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.CleaningUp); | 1674 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.CleaningUp); |
1128 | 1675 | ||
1676 | m_log.WarnFormat( | ||
1677 | "[ENTITY TRANSFER MODULE]: Region {0} would not accept update for agent {1} on cross attempt. Returning to original region.", | ||
1678 | neighbourRegion.RegionName, agent.Name); | ||
1679 | |||
1129 | ReInstantiateScripts(agent); | 1680 | ReInstantiateScripts(agent); |
1130 | agent.AddToPhysicalScene(isFlying); | 1681 | agent.AddToPhysicalScene(isFlying); |
1131 | m_entityTransferStateMachine.ResetFromTransit(agent.UUID); | ||
1132 | 1682 | ||
1133 | return agent; | 1683 | return false; |
1134 | } | 1684 | } |
1135 | 1685 | ||
1136 | //AgentCircuitData circuitdata = m_controllingClient.RequestClientInfo(); | 1686 | } |
1137 | agent.ControllingClient.RequestClientInfo(); | 1687 | catch (Exception e) |
1688 | { | ||
1689 | m_log.ErrorFormat( | ||
1690 | "[ENTITY TRANSFER MODULE]: Problem crossing user {0} to new region {1} from {2}. Exception {3}{4}", | ||
1691 | agent.Name, neighbourRegion.RegionName, agent.Scene.RegionInfo.RegionName, e.Message, e.StackTrace); | ||
1692 | |||
1693 | // TODO: Might be worth attempting other restoration here such as reinstantiation of scripts, etc. | ||
1694 | return false; | ||
1695 | } | ||
1138 | 1696 | ||
1139 | //m_log.Debug("BEFORE CROSS"); | 1697 | return true; |
1140 | //Scene.DumpChildrenSeeds(UUID); | 1698 | } |
1141 | //DumpKnownRegions(); | ||
1142 | string agentcaps; | ||
1143 | if (!agent.KnownRegions.TryGetValue(neighbourRegion.RegionHandle, out agentcaps)) | ||
1144 | { | ||
1145 | m_log.ErrorFormat("[ENTITY TRANSFER MODULE]: No ENTITY TRANSFER MODULE information for region handle {0}, exiting CrossToNewRegion.", | ||
1146 | neighbourRegion.RegionHandle); | ||
1147 | return agent; | ||
1148 | } | ||
1149 | // No turning back | ||
1150 | agent.IsChildAgent = true; | ||
1151 | 1699 | ||
1152 | string capsPath = neighbourRegion.ServerURI + CapsUtil.GetCapsSeedPath(agentcaps); | 1700 | public void CrossAgentToNewRegionPost(ScenePresence agent, Vector3 pos, GridRegion neighbourRegion, |
1701 | bool isFlying, EntityTransferContext ctx) | ||
1702 | { | ||
1703 | agent.ControllingClient.RequestClientInfo(); | ||
1153 | 1704 | ||
1154 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} to client {1}", capsPath, agent.UUID); | 1705 | string agentcaps; |
1706 | if (!agent.KnownRegions.TryGetValue(neighbourRegion.RegionHandle, out agentcaps)) | ||
1707 | { | ||
1708 | m_log.ErrorFormat("[ENTITY TRANSFER MODULE]: No ENTITY TRANSFER MODULE information for region handle {0}, exiting CrossToNewRegion.", | ||
1709 | neighbourRegion.RegionHandle); | ||
1710 | return; | ||
1711 | } | ||
1155 | 1712 | ||
1156 | if (m_eqModule != null) | 1713 | // No turning back |
1157 | { | 1714 | agent.IsChildAgent = true; |
1158 | m_eqModule.CrossRegion( | 1715 | |
1159 | neighbourHandle, pos, vel2 /* agent.Velocity */, neighbourRegion.ExternalEndPoint, | 1716 | string capsPath = neighbourRegion.ServerURI + CapsUtil.GetCapsSeedPath(agentcaps); |
1160 | capsPath, agent.UUID, agent.ControllingClient.SessionId); | ||
1161 | } | ||
1162 | else | ||
1163 | { | ||
1164 | agent.ControllingClient.CrossRegion(neighbourHandle, pos, agent.Velocity, neighbourRegion.ExternalEndPoint, | ||
1165 | capsPath); | ||
1166 | } | ||
1167 | 1717 | ||
1168 | // SUCCESS! | 1718 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} to client {1}", capsPath, agent.UUID); |
1169 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.ReceivedAtDestination); | ||
1170 | 1719 | ||
1171 | // Unlike a teleport, here we do not wait for the destination region to confirm the receipt. | 1720 | Vector3 vel2 = new Vector3(agent.Velocity.X, agent.Velocity.Y, 0); |
1172 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.CleaningUp); | ||
1173 | 1721 | ||
1174 | agent.MakeChildAgent(); | 1722 | if (m_eqModule != null) |
1723 | { | ||
1724 | m_eqModule.CrossRegion( | ||
1725 | neighbourRegion.RegionHandle, pos + agent.Velocity, vel2 /* agent.Velocity */, | ||
1726 | neighbourRegion.ExternalEndPoint, | ||
1727 | capsPath, agent.UUID, agent.ControllingClient.SessionId, | ||
1728 | neighbourRegion.RegionSizeX, neighbourRegion.RegionSizeY); | ||
1729 | } | ||
1730 | else | ||
1731 | { | ||
1732 | m_log.ErrorFormat("{0} Using old CrossRegion packet. Varregion will not work!!", LogHeader); | ||
1733 | agent.ControllingClient.CrossRegion(neighbourRegion.RegionHandle, pos + agent.Velocity, agent.Velocity, neighbourRegion.ExternalEndPoint, | ||
1734 | capsPath); | ||
1735 | } | ||
1175 | 1736 | ||
1176 | // FIXME: Possibly this should occur lower down after other commands to close other agents, | 1737 | // SUCCESS! |
1177 | // but not sure yet what the side effects would be. | 1738 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.ReceivedAtDestination); |
1178 | m_entityTransferStateMachine.ResetFromTransit(agent.UUID); | ||
1179 | 1739 | ||
1180 | // now we have a child agent in this region. Request all interesting data about other (root) agents | 1740 | // Unlike a teleport, here we do not wait for the destination region to confirm the receipt. |
1181 | agent.SendOtherAgentsAvatarDataToMe(); | 1741 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.CleaningUp); |
1182 | agent.SendOtherAgentsAppearanceToMe(); | ||
1183 | 1742 | ||
1184 | // Backwards compatibility. Best effort | 1743 | agent.MakeChildAgent(); |
1185 | if (version == "Unknown" || version == string.Empty) | ||
1186 | { | ||
1187 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: neighbor with old version, passing attachments one by one..."); | ||
1188 | Thread.Sleep(3000); // wait a little now that we're not waiting for the callback | ||
1189 | CrossAttachmentsIntoNewRegion(neighbourRegion, agent, true); | ||
1190 | } | ||
1191 | 1744 | ||
1192 | // Next, let's close the child agent connections that are too far away. | 1745 | // FIXME: Possibly this should occur lower down after other commands to close other agents, |
1193 | agent.CloseChildAgents(neighbourx, neighboury); | 1746 | // but not sure yet what the side effects would be. |
1747 | m_entityTransferStateMachine.ResetFromTransit(agent.UUID); | ||
1194 | 1748 | ||
1195 | AgentHasMovedAway(agent, false); | 1749 | // now we have a child agent in this region. Request all interesting data about other (root) agents |
1750 | agent.SendOtherAgentsAvatarDataToClient(); | ||
1751 | agent.SendOtherAgentsAppearanceToClient(); | ||
1196 | 1752 | ||
1197 | // // the user may change their profile information in other region, | 1753 | // TODO: Check since what version this wasn't needed anymore. May be as old as 0.6 |
1198 | // // so the userinfo in UserProfileCache is not reliable any more, delete it | 1754 | /* |
1199 | // // REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE! | 1755 | // Backwards compatibility. Best effort |
1200 | // if (agent.Scene.NeedSceneCacheClear(agent.UUID)) | 1756 | if (version == 0f) |
1201 | // { | ||
1202 | // m_log.DebugFormat( | ||
1203 | // "[ENTITY TRANSFER MODULE]: User {0} is going to another region", agent.UUID); | ||
1204 | // } | ||
1205 | |||
1206 | //m_log.Debug("AFTER CROSS"); | ||
1207 | //Scene.DumpChildrenSeeds(UUID); | ||
1208 | //DumpKnownRegions(); | ||
1209 | } | ||
1210 | catch (Exception e) | ||
1211 | { | 1757 | { |
1212 | m_log.ErrorFormat( | 1758 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: neighbor with old version, passing attachments one by one..."); |
1213 | "[ENTITY TRANSFER MODULE]: Problem crossing user {0} to new region {1} from {2}. Exception {3}{4}", | 1759 | Thread.Sleep(3000); // wait a little now that we're not waiting for the callback |
1214 | agent.Name, neighbourRegion.RegionName, agent.Scene.RegionInfo.RegionName, e.Message, e.StackTrace); | 1760 | CrossAttachmentsIntoNewRegion(neighbourRegion, agent, true); |
1215 | |||
1216 | // TODO: Might be worth attempting other restoration here such as reinstantiation of scripts, etc. | ||
1217 | } | 1761 | } |
1762 | */ | ||
1763 | // Next, let's close the child agent connections that are too far away. | ||
1764 | uint neighbourx; | ||
1765 | uint neighboury; | ||
1766 | Util.RegionHandleToRegionLoc(neighbourRegion.RegionHandle, out neighbourx, out neighboury); | ||
1218 | 1767 | ||
1219 | return agent; | 1768 | agent.CloseChildAgents(neighbourx, neighboury); |
1769 | |||
1770 | AgentHasMovedAway(agent, false); | ||
1771 | |||
1772 | // the user may change their profile information in other region, | ||
1773 | // so the userinfo in UserProfileCache is not reliable any more, delete it | ||
1774 | // REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE! | ||
1775 | // if (agent.Scene.NeedSceneCacheClear(agent.UUID)) | ||
1776 | // { | ||
1777 | // m_log.DebugFormat( | ||
1778 | // "[ENTITY TRANSFER MODULE]: User {0} is going to another region", agent.UUID); | ||
1779 | // } | ||
1780 | |||
1781 | //m_log.Debug("AFTER CROSS"); | ||
1782 | //Scene.DumpChildrenSeeds(UUID); | ||
1783 | //DumpKnownRegions(); | ||
1784 | |||
1785 | return; | ||
1220 | } | 1786 | } |
1221 | 1787 | ||
1222 | private void CrossAgentToNewRegionCompleted(IAsyncResult iar) | 1788 | private void CrossAgentToNewRegionCompleted(IAsyncResult iar) |
@@ -1256,7 +1822,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1256 | agent.InventoryFolder = UUID.Zero; | 1822 | agent.InventoryFolder = UUID.Zero; |
1257 | agent.startpos = new Vector3(128, 128, 70); | 1823 | agent.startpos = new Vector3(128, 128, 70); |
1258 | agent.child = true; | 1824 | agent.child = true; |
1259 | agent.Appearance = sp.Appearance; | 1825 | agent.Appearance = new AvatarAppearance(); |
1826 | agent.Appearance.PackLegacyWearables = true; | ||
1260 | agent.CapsPath = CapsUtil.GetRandomCapsObjectPath(); | 1827 | agent.CapsPath = CapsUtil.GetRandomCapsObjectPath(); |
1261 | 1828 | ||
1262 | agent.ChildrenCapSeeds = new Dictionary<ulong, string>(sp.Scene.CapsModule.GetChildrenSeeds(sp.UUID)); | 1829 | agent.ChildrenCapSeeds = new Dictionary<ulong, string>(sp.Scene.CapsModule.GetChildrenSeeds(sp.UUID)); |
@@ -1270,7 +1837,15 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1270 | //foreach (ulong h in agent.ChildrenCapSeeds.Keys) | 1837 | //foreach (ulong h in agent.ChildrenCapSeeds.Keys) |
1271 | // m_log.DebugFormat("[XXX] --> {0}", h); | 1838 | // m_log.DebugFormat("[XXX] --> {0}", h); |
1272 | //m_log.DebugFormat("[XXX] Adding {0}", region.RegionHandle); | 1839 | //m_log.DebugFormat("[XXX] Adding {0}", region.RegionHandle); |
1273 | agent.ChildrenCapSeeds.Add(region.RegionHandle, agent.CapsPath); | 1840 | if (agent.ChildrenCapSeeds.ContainsKey(region.RegionHandle)) |
1841 | { | ||
1842 | m_log.WarnFormat( | ||
1843 | "[ENTITY TRANSFER]: Overwriting caps seed {0} with {1} for region {2} (handle {3}) for {4} in {5}", | ||
1844 | agent.ChildrenCapSeeds[region.RegionHandle], agent.CapsPath, | ||
1845 | region.RegionName, region.RegionHandle, sp.Name, Scene.Name); | ||
1846 | } | ||
1847 | |||
1848 | agent.ChildrenCapSeeds[region.RegionHandle] = agent.CapsPath; | ||
1274 | 1849 | ||
1275 | if (sp.Scene.CapsModule != null) | 1850 | if (sp.Scene.CapsModule != null) |
1276 | { | 1851 | { |
@@ -1287,10 +1862,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1287 | agent.Id0 = currentAgentCircuit.Id0; | 1862 | agent.Id0 = currentAgentCircuit.Id0; |
1288 | } | 1863 | } |
1289 | 1864 | ||
1290 | InformClientOfNeighbourDelegate d = InformClientOfNeighbourAsync; | 1865 | IPEndPoint external = region.ExternalEndPoint; |
1291 | d.BeginInvoke(sp, agent, region, region.ExternalEndPoint, true, | 1866 | if (external != null) |
1867 | { | ||
1868 | InformClientOfNeighbourDelegate d = InformClientOfNeighbourAsync; | ||
1869 | d.BeginInvoke(sp, agent, region, external, true, | ||
1292 | InformClientOfNeighbourCompleted, | 1870 | InformClientOfNeighbourCompleted, |
1293 | d); | 1871 | d); |
1872 | } | ||
1294 | } | 1873 | } |
1295 | #endregion | 1874 | #endregion |
1296 | 1875 | ||
@@ -1310,7 +1889,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1310 | 1889 | ||
1311 | if (m_regionInfo != null) | 1890 | if (m_regionInfo != null) |
1312 | { | 1891 | { |
1313 | neighbours = RequestNeighbours(sp, m_regionInfo.RegionLocX, m_regionInfo.RegionLocY); | 1892 | neighbours = GetNeighbours(sp, m_regionInfo.RegionLocX, m_regionInfo.RegionLocY); |
1314 | } | 1893 | } |
1315 | else | 1894 | else |
1316 | { | 1895 | { |
@@ -1336,10 +1915,10 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1336 | List<ulong> newRegions = NewNeighbours(neighbourHandles, previousRegionNeighbourHandles); | 1915 | List<ulong> newRegions = NewNeighbours(neighbourHandles, previousRegionNeighbourHandles); |
1337 | List<ulong> oldRegions = OldNeighbours(neighbourHandles, previousRegionNeighbourHandles); | 1916 | List<ulong> oldRegions = OldNeighbours(neighbourHandles, previousRegionNeighbourHandles); |
1338 | 1917 | ||
1339 | //Dump("Current Neighbors", neighbourHandles); | 1918 | // Dump("Current Neighbors", neighbourHandles); |
1340 | //Dump("Previous Neighbours", previousRegionNeighbourHandles); | 1919 | // Dump("Previous Neighbours", previousRegionNeighbourHandles); |
1341 | //Dump("New Neighbours", newRegions); | 1920 | // Dump("New Neighbours", newRegions); |
1342 | //Dump("Old Neighbours", oldRegions); | 1921 | // Dump("Old Neighbours", oldRegions); |
1343 | 1922 | ||
1344 | /// Update the scene presence's known regions here on this region | 1923 | /// Update the scene presence's known regions here on this region |
1345 | sp.DropOldNeighbours(oldRegions); | 1924 | sp.DropOldNeighbours(oldRegions); |
@@ -1347,8 +1926,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1347 | /// Collect as many seeds as possible | 1926 | /// Collect as many seeds as possible |
1348 | Dictionary<ulong, string> seeds; | 1927 | Dictionary<ulong, string> seeds; |
1349 | if (sp.Scene.CapsModule != null) | 1928 | if (sp.Scene.CapsModule != null) |
1350 | seeds | 1929 | seeds = new Dictionary<ulong, string>(sp.Scene.CapsModule.GetChildrenSeeds(sp.UUID)); |
1351 | = new Dictionary<ulong, string>(sp.Scene.CapsModule.GetChildrenSeeds(sp.UUID)); | ||
1352 | else | 1930 | else |
1353 | seeds = new Dictionary<ulong, string>(); | 1931 | seeds = new Dictionary<ulong, string>(); |
1354 | 1932 | ||
@@ -1368,7 +1946,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1368 | agent.InventoryFolder = UUID.Zero; | 1946 | agent.InventoryFolder = UUID.Zero; |
1369 | agent.startpos = sp.AbsolutePosition + CalculateOffset(sp, neighbour); | 1947 | agent.startpos = sp.AbsolutePosition + CalculateOffset(sp, neighbour); |
1370 | agent.child = true; | 1948 | agent.child = true; |
1371 | agent.Appearance = sp.Appearance; | 1949 | agent.Appearance = new AvatarAppearance(); |
1950 | agent.Appearance.PackLegacyWearables = true; | ||
1372 | if (currentAgentCircuit != null) | 1951 | if (currentAgentCircuit != null) |
1373 | { | 1952 | { |
1374 | agent.ServiceURLs = currentAgentCircuit.ServiceURLs; | 1953 | agent.ServiceURLs = currentAgentCircuit.ServiceURLs; |
@@ -1418,6 +1997,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1418 | newAgent = true; | 1997 | newAgent = true; |
1419 | else | 1998 | else |
1420 | newAgent = false; | 1999 | newAgent = false; |
2000 | // continue; | ||
1421 | 2001 | ||
1422 | if (neighbour.RegionHandle != sp.Scene.RegionInfo.RegionHandle) | 2002 | if (neighbour.RegionHandle != sp.Scene.RegionInfo.RegionHandle) |
1423 | { | 2003 | { |
@@ -1464,15 +2044,195 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1464 | } | 2044 | } |
1465 | } | 2045 | } |
1466 | 2046 | ||
2047 | // Computes the difference between two region bases. | ||
2048 | // Returns a vector of world coordinates (meters) from base of first region to the second. | ||
2049 | // The first region is the home region of the passed scene presence. | ||
1467 | Vector3 CalculateOffset(ScenePresence sp, GridRegion neighbour) | 2050 | Vector3 CalculateOffset(ScenePresence sp, GridRegion neighbour) |
1468 | { | 2051 | { |
1469 | int rRegionX = (int)sp.Scene.RegionInfo.RegionLocX; | 2052 | /* |
1470 | int rRegionY = (int)sp.Scene.RegionInfo.RegionLocY; | 2053 | int rRegionX = (int)sp.Scene.RegionInfo.LegacyRegionLocX; |
2054 | int rRegionY = (int)sp.Scene.RegionInfo.LegacyRegionLocY; | ||
1471 | int tRegionX = neighbour.RegionLocX / (int)Constants.RegionSize; | 2055 | int tRegionX = neighbour.RegionLocX / (int)Constants.RegionSize; |
1472 | int tRegionY = neighbour.RegionLocY / (int)Constants.RegionSize; | 2056 | int tRegionY = neighbour.RegionLocY / (int)Constants.RegionSize; |
1473 | int shiftx = (rRegionX - tRegionX) * (int)Constants.RegionSize; | 2057 | int shiftx = (rRegionX - tRegionX) * (int)Constants.RegionSize; |
1474 | int shifty = (rRegionY - tRegionY) * (int)Constants.RegionSize; | 2058 | int shifty = (rRegionY - tRegionY) * (int)Constants.RegionSize; |
1475 | return new Vector3(shiftx, shifty, 0f); | 2059 | return new Vector3(shiftx, shifty, 0f); |
2060 | */ | ||
2061 | return new Vector3( sp.Scene.RegionInfo.WorldLocX - neighbour.RegionLocX, | ||
2062 | sp.Scene.RegionInfo.WorldLocY - neighbour.RegionLocY, | ||
2063 | 0f); | ||
2064 | } | ||
2065 | |||
2066 | public GridRegion GetRegionContainingWorldLocation(IGridService pGridService, UUID pScopeID, double px, double py) | ||
2067 | { | ||
2068 | // Since we don't know how big the regions could be, we have to search a very large area | ||
2069 | // to find possible regions. | ||
2070 | return GetRegionContainingWorldLocation(pGridService, pScopeID, px, py, Constants.MaximumRegionSize); | ||
2071 | } | ||
2072 | |||
2073 | #region NotFoundLocationCache class | ||
2074 | // A collection of not found locations to make future lookups 'not found' lookups quick. | ||
2075 | // A simple expiring cache that keeps not found locations for some number of seconds. | ||
2076 | // A 'not found' location is presumed to be anywhere in the minimum sized region that | ||
2077 | // contains that point. A conservitive estimate. | ||
2078 | private class NotFoundLocationCache | ||
2079 | { | ||
2080 | private struct NotFoundLocation | ||
2081 | { | ||
2082 | public double minX, maxX, minY, maxY; | ||
2083 | public DateTime expireTime; | ||
2084 | } | ||
2085 | private List<NotFoundLocation> m_notFoundLocations = new List<NotFoundLocation>(); | ||
2086 | public NotFoundLocationCache() | ||
2087 | { | ||
2088 | } | ||
2089 | // Add an area to the list of 'not found' places. The area is the snapped region | ||
2090 | // area around the added point. | ||
2091 | public void Add(double pX, double pY) | ||
2092 | { | ||
2093 | lock (m_notFoundLocations) | ||
2094 | { | ||
2095 | if (!LockedContains(pX, pY)) | ||
2096 | { | ||
2097 | NotFoundLocation nfl = new NotFoundLocation(); | ||
2098 | // A not found location is not found for at least a whole region sized area | ||
2099 | nfl.minX = pX - (pX % (double)Constants.RegionSize); | ||
2100 | nfl.minY = pY - (pY % (double)Constants.RegionSize); | ||
2101 | nfl.maxX = nfl.minX + (double)Constants.RegionSize; | ||
2102 | nfl.maxY = nfl.minY + (double)Constants.RegionSize; | ||
2103 | nfl.expireTime = DateTime.Now + TimeSpan.FromSeconds(30); | ||
2104 | m_notFoundLocations.Add(nfl); | ||
2105 | } | ||
2106 | } | ||
2107 | |||
2108 | } | ||
2109 | // Test to see of this point is in any of the 'not found' areas. | ||
2110 | // Return 'true' if the point is found inside the 'not found' areas. | ||
2111 | public bool Contains(double pX, double pY) | ||
2112 | { | ||
2113 | bool ret = false; | ||
2114 | lock (m_notFoundLocations) | ||
2115 | ret = LockedContains(pX, pY); | ||
2116 | return ret; | ||
2117 | } | ||
2118 | private bool LockedContains(double pX, double pY) | ||
2119 | { | ||
2120 | bool ret = false; | ||
2121 | this.DoExpiration(); | ||
2122 | foreach (NotFoundLocation nfl in m_notFoundLocations) | ||
2123 | { | ||
2124 | if (pX >= nfl.minX && pX < nfl.maxX && pY >= nfl.minY && pY < nfl.maxY) | ||
2125 | { | ||
2126 | ret = true; | ||
2127 | break; | ||
2128 | } | ||
2129 | } | ||
2130 | return ret; | ||
2131 | } | ||
2132 | private void DoExpiration() | ||
2133 | { | ||
2134 | List<NotFoundLocation> m_toRemove = null; | ||
2135 | DateTime now = DateTime.Now; | ||
2136 | foreach (NotFoundLocation nfl in m_notFoundLocations) | ||
2137 | { | ||
2138 | if (nfl.expireTime < now) | ||
2139 | { | ||
2140 | if (m_toRemove == null) | ||
2141 | m_toRemove = new List<NotFoundLocation>(); | ||
2142 | m_toRemove.Add(nfl); | ||
2143 | } | ||
2144 | } | ||
2145 | if (m_toRemove != null) | ||
2146 | { | ||
2147 | foreach (NotFoundLocation nfl in m_toRemove) | ||
2148 | m_notFoundLocations.Remove(nfl); | ||
2149 | m_toRemove.Clear(); | ||
2150 | } | ||
2151 | } | ||
2152 | } | ||
2153 | #endregion // NotFoundLocationCache class | ||
2154 | private NotFoundLocationCache m_notFoundLocationCache = new NotFoundLocationCache(); | ||
2155 | |||
2156 | // Given a world position (fractional meter coordinate), get the GridRegion info for | ||
2157 | // the region containing that point. | ||
2158 | // Someday this should be a method on GridService. | ||
2159 | // 'pSizeHint' is the size of the source region but since the destination point can be anywhere | ||
2160 | // the size of the target region is unknown thus the search area might have to be very large. | ||
2161 | // Return 'null' if no such region exists. | ||
2162 | public GridRegion GetRegionContainingWorldLocation(IGridService pGridService, UUID pScopeID, | ||
2163 | double px, double py, uint pSizeHint) | ||
2164 | { | ||
2165 | m_log.DebugFormat("{0} GetRegionContainingWorldLocation: query, loc=<{1},{2}>", LogHeader, px, py); | ||
2166 | GridRegion ret = null; | ||
2167 | const double fudge = 2.0; | ||
2168 | |||
2169 | // One problem with this routine is negative results. That is, this can be called lots of times | ||
2170 | // for regions that don't exist. m_notFoundLocationCache remembers 'not found' results so they | ||
2171 | // will be quick 'not found's next time. | ||
2172 | // NotFoundLocationCache is an expiring cache so it will eventually forget about 'not found' and | ||
2173 | // thus re-ask the GridService about the location. | ||
2174 | if (m_notFoundLocationCache.Contains(px, py)) | ||
2175 | { | ||
2176 | m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Not found via cache. loc=<{1},{2}>", LogHeader, px, py); | ||
2177 | return null; | ||
2178 | } | ||
2179 | |||
2180 | // As an optimization, since most regions will be legacy sized regions (256x256), first try to get | ||
2181 | // the region at the appropriate legacy region location. | ||
2182 | uint possibleX = (uint)Math.Floor(px); | ||
2183 | possibleX -= possibleX % Constants.RegionSize; | ||
2184 | uint possibleY = (uint)Math.Floor(py); | ||
2185 | possibleY -= possibleY % Constants.RegionSize; | ||
2186 | ret = pGridService.GetRegionByPosition(pScopeID, (int)possibleX, (int)possibleY); | ||
2187 | if (ret != null) | ||
2188 | { | ||
2189 | m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Found region using legacy size. rloc=<{1},{2}>. Rname={3}", | ||
2190 | LogHeader, possibleX, possibleY, ret.RegionName); | ||
2191 | } | ||
2192 | |||
2193 | if (ret == null) | ||
2194 | { | ||
2195 | // If the simple lookup failed, search the larger area for a region that contains this point | ||
2196 | double range = (double)pSizeHint + fudge; | ||
2197 | while (ret == null && range <= (Constants.MaximumRegionSize + Constants.RegionSize)) | ||
2198 | { | ||
2199 | // Get from the grid service a list of regions that might contain this point. | ||
2200 | // The region origin will be in the zero direction so only subtract the range. | ||
2201 | List<GridRegion> possibleRegions = pGridService.GetRegionRange(pScopeID, | ||
2202 | (int)(px - range), (int)(px), | ||
2203 | (int)(py - range), (int)(py)); | ||
2204 | m_log.DebugFormat("{0} GetRegionContainingWorldLocation: possibleRegions cnt={1}, range={2}", | ||
2205 | LogHeader, possibleRegions.Count, range); | ||
2206 | if (possibleRegions != null && possibleRegions.Count > 0) | ||
2207 | { | ||
2208 | // If we found some regions, check to see if the point is within | ||
2209 | foreach (GridRegion gr in possibleRegions) | ||
2210 | { | ||
2211 | m_log.DebugFormat("{0} GetRegionContainingWorldLocation: possibleRegion nm={1}, regionLoc=<{2},{3}>, regionSize=<{4},{5}>", | ||
2212 | LogHeader, gr.RegionName, gr.RegionLocX, gr.RegionLocY, gr.RegionSizeX, gr.RegionSizeY); | ||
2213 | if (px >= (double)gr.RegionLocX && px < (double)(gr.RegionLocX + gr.RegionSizeX) | ||
2214 | && py >= (double)gr.RegionLocY && py < (double)(gr.RegionLocY + gr.RegionSizeY)) | ||
2215 | { | ||
2216 | // Found a region that contains the point | ||
2217 | ret = gr; | ||
2218 | m_log.DebugFormat("{0} GetRegionContainingWorldLocation: found. RegionName={1}", LogHeader, ret.RegionName); | ||
2219 | break; | ||
2220 | } | ||
2221 | } | ||
2222 | } | ||
2223 | // Larger search area for next time around if not found | ||
2224 | range *= 2; | ||
2225 | } | ||
2226 | } | ||
2227 | |||
2228 | if (ret == null) | ||
2229 | { | ||
2230 | // remember this location was not found so we can quickly not find it next time | ||
2231 | m_notFoundLocationCache.Add(px, py); | ||
2232 | m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Not found. Remembering loc=<{1},{2}>", LogHeader, px, py); | ||
2233 | } | ||
2234 | |||
2235 | return ret; | ||
1476 | } | 2236 | } |
1477 | 2237 | ||
1478 | private void InformClientOfNeighbourCompleted(IAsyncResult iar) | 2238 | private void InformClientOfNeighbourCompleted(IAsyncResult iar) |
@@ -1500,7 +2260,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1500 | Thread.Sleep(500); | 2260 | Thread.Sleep(500); |
1501 | 2261 | ||
1502 | Scene scene = sp.Scene; | 2262 | Scene scene = sp.Scene; |
1503 | 2263 | ||
1504 | m_log.DebugFormat( | 2264 | m_log.DebugFormat( |
1505 | "[ENTITY TRANSFER MODULE]: Informing {0} {1} about neighbour {2} {3} at ({4},{5})", | 2265 | "[ENTITY TRANSFER MODULE]: Informing {0} {1} about neighbour {2} {3} at ({4},{5})", |
1506 | sp.Name, sp.UUID, reg.RegionName, endPoint, reg.RegionCoordX, reg.RegionCoordY); | 2266 | sp.Name, sp.UUID, reg.RegionName, endPoint, reg.RegionCoordX, reg.RegionCoordY); |
@@ -1509,7 +2269,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1509 | 2269 | ||
1510 | string reason = String.Empty; | 2270 | string reason = String.Empty; |
1511 | 2271 | ||
1512 | bool regionAccepted = scene.SimulationService.CreateAgent(reg, a, (uint)TeleportFlags.Default, out reason); | 2272 | bool regionAccepted = scene.SimulationService.CreateAgent(null, reg, a, (uint)TeleportFlags.Default, out reason); |
1513 | 2273 | ||
1514 | if (regionAccepted && newAgent) | 2274 | if (regionAccepted && newAgent) |
1515 | { | 2275 | { |
@@ -1523,12 +2283,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1523 | } | 2283 | } |
1524 | #endregion | 2284 | #endregion |
1525 | 2285 | ||
1526 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: {0} is sending {1} EnableSimulator for neighbour region {2} @ {3} " + | 2286 | m_log.DebugFormat("{0} {1} is sending {2} EnableSimulator for neighbour region {3}(loc=<{4},{5}>,siz=<{6},{7}>) " + |
1527 | "and EstablishAgentCommunication with seed cap {4}", | 2287 | "and EstablishAgentCommunication with seed cap {8}", LogHeader, |
1528 | scene.RegionInfo.RegionName, sp.Name, reg.RegionName, reg.RegionHandle, capsPath); | 2288 | scene.RegionInfo.RegionName, sp.Name, |
2289 | reg.RegionName, reg.RegionLocX, reg.RegionLocY, reg.RegionSizeX, reg.RegionSizeY , capsPath); | ||
1529 | 2290 | ||
1530 | m_eqModule.EnableSimulator(reg.RegionHandle, endPoint, sp.UUID); | 2291 | m_eqModule.EnableSimulator(reg.RegionHandle, endPoint, sp.UUID, reg.RegionSizeX, reg.RegionSizeY); |
1531 | m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath); | 2292 | m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath, reg.RegionHandle, reg.RegionSizeX, reg.RegionSizeY); |
1532 | } | 2293 | } |
1533 | else | 2294 | else |
1534 | { | 2295 | { |
@@ -1546,68 +2307,86 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1546 | } | 2307 | } |
1547 | 2308 | ||
1548 | /// <summary> | 2309 | /// <summary> |
1549 | /// Return the list of regions that are considered to be neighbours to the given scene. | 2310 | /// Gets the range considered in view of this megaregion (assuming this is a megaregion). |
2311 | /// </summary> | ||
2312 | /// <remarks>Expressed in 256m units</remarks> | ||
2313 | /// <param name='swCorner'></param> | ||
2314 | /// <param name='neCorner'></param> | ||
2315 | private void GetMegaregionViewRange(out Vector2 swCorner, out Vector2 neCorner) | ||
2316 | { | ||
2317 | Vector2 extent = Vector2.Zero; | ||
2318 | |||
2319 | if (m_regionCombinerModule != null) | ||
2320 | { | ||
2321 | Vector2 megaRegionSize = m_regionCombinerModule.GetSizeOfMegaregion(Scene.RegionInfo.RegionID); | ||
2322 | extent.X = (float)Util.WorldToRegionLoc((uint)megaRegionSize.X); | ||
2323 | extent.Y = (float)Util.WorldToRegionLoc((uint)megaRegionSize.Y); | ||
2324 | } | ||
2325 | |||
2326 | swCorner.X = Scene.RegionInfo.RegionLocX - 1; | ||
2327 | swCorner.Y = Scene.RegionInfo.RegionLocY - 1; | ||
2328 | neCorner.X = Scene.RegionInfo.RegionLocX + extent.X; | ||
2329 | neCorner.Y = Scene.RegionInfo.RegionLocY + extent.Y; | ||
2330 | } | ||
2331 | |||
2332 | /// <summary> | ||
2333 | /// Return the list of online regions that are considered to be neighbours to the given scene. | ||
1550 | /// </summary> | 2334 | /// </summary> |
1551 | /// <param name="pScene"></param> | 2335 | /// <param name="avatar"></param> |
1552 | /// <param name="pRegionLocX"></param> | 2336 | /// <param name="pRegionLocX"></param> |
1553 | /// <param name="pRegionLocY"></param> | 2337 | /// <param name="pRegionLocY"></param> |
1554 | /// <returns></returns> | 2338 | /// <returns></returns> |
1555 | protected List<GridRegion> RequestNeighbours(ScenePresence avatar, uint pRegionLocX, uint pRegionLocY) | 2339 | protected List<GridRegion> GetNeighbours(ScenePresence avatar, uint pRegionLocX, uint pRegionLocY) |
1556 | { | 2340 | { |
1557 | Scene pScene = avatar.Scene; | 2341 | Scene pScene = avatar.Scene; |
1558 | RegionInfo m_regionInfo = pScene.RegionInfo; | 2342 | RegionInfo m_regionInfo = pScene.RegionInfo; |
1559 | 2343 | List<GridRegion> neighbours; | |
1560 | Border[] northBorders = pScene.NorthBorders.ToArray(); | ||
1561 | Border[] southBorders = pScene.SouthBorders.ToArray(); | ||
1562 | Border[] eastBorders = pScene.EastBorders.ToArray(); | ||
1563 | Border[] westBorders = pScene.WestBorders.ToArray(); | ||
1564 | 2344 | ||
1565 | // Leaving this as a "megaregions" computation vs "non-megaregions" computation; it isn't | 2345 | // Leaving this as a "megaregions" computation vs "non-megaregions" computation; it isn't |
1566 | // clear what should be done with a "far view" given that megaregions already extended the | 2346 | // clear what should be done with a "far view" given that megaregions already extended the |
1567 | // view to include everything in the megaregion | 2347 | // view to include everything in the megaregion |
1568 | if (northBorders.Length <= 1 && southBorders.Length <= 1 && eastBorders.Length <= 1 && westBorders.Length <= 1) | 2348 | if (m_regionCombinerModule == null || !m_regionCombinerModule.IsRootForMegaregion(Scene.RegionInfo.RegionID)) |
1569 | { | 2349 | { |
1570 | int dd = avatar.DrawDistance < Constants.RegionSize ? (int)Constants.RegionSize : (int)avatar.DrawDistance; | 2350 | // The area to check is as big as the current region. |
1571 | 2351 | // We presume all adjacent regions are the same size as this region. | |
1572 | int startX = (int)pRegionLocX * (int)Constants.RegionSize - dd + (int)(Constants.RegionSize/2); | 2352 | uint dd = Math.Max((uint)avatar.Scene.DefaultDrawDistance, |
1573 | int startY = (int)pRegionLocY * (int)Constants.RegionSize - dd + (int)(Constants.RegionSize/2); | 2353 | Math.Max(Scene.RegionInfo.RegionSizeX, Scene.RegionInfo.RegionSizeY)); |
1574 | 2354 | ||
1575 | int endX = (int)pRegionLocX * (int)Constants.RegionSize + dd + (int)(Constants.RegionSize/2); | 2355 | uint startX = Util.RegionToWorldLoc(pRegionLocX) - dd + Constants.RegionSize/2; |
1576 | int endY = (int)pRegionLocY * (int)Constants.RegionSize + dd + (int)(Constants.RegionSize/2); | 2356 | uint startY = Util.RegionToWorldLoc(pRegionLocY) - dd + Constants.RegionSize/2; |
1577 | 2357 | ||
1578 | List<GridRegion> neighbours = | 2358 | uint endX = Util.RegionToWorldLoc(pRegionLocX) + dd + Constants.RegionSize/2; |
1579 | avatar.Scene.GridService.GetRegionRange(m_regionInfo.ScopeID, startX, endX, startY, endY); | 2359 | uint endY = Util.RegionToWorldLoc(pRegionLocY) + dd + Constants.RegionSize/2; |
1580 | 2360 | ||
1581 | neighbours.RemoveAll(delegate(GridRegion r) { return r.RegionID == m_regionInfo.RegionID; }); | 2361 | neighbours |
1582 | return neighbours; | 2362 | = avatar.Scene.GridService.GetRegionRange( |
2363 | m_regionInfo.ScopeID, (int)startX, (int)endX, (int)startY, (int)endY); | ||
1583 | } | 2364 | } |
1584 | else | 2365 | else |
1585 | { | 2366 | { |
1586 | Vector2 extent = Vector2.Zero; | 2367 | Vector2 swCorner, neCorner; |
1587 | for (int i = 0; i < eastBorders.Length; i++) | 2368 | GetMegaregionViewRange(out swCorner, out neCorner); |
1588 | { | ||
1589 | extent.X = (eastBorders[i].BorderLine.Z > extent.X) ? eastBorders[i].BorderLine.Z : extent.X; | ||
1590 | } | ||
1591 | for (int i = 0; i < northBorders.Length; i++) | ||
1592 | { | ||
1593 | extent.Y = (northBorders[i].BorderLine.Z > extent.Y) ? northBorders[i].BorderLine.Z : extent.Y; | ||
1594 | } | ||
1595 | 2369 | ||
1596 | // Loss of fraction on purpose | 2370 | neighbours |
1597 | extent.X = ((int)extent.X / (int)Constants.RegionSize) + 1; | 2371 | = pScene.GridService.GetRegionRange( |
1598 | extent.Y = ((int)extent.Y / (int)Constants.RegionSize) + 1; | 2372 | m_regionInfo.ScopeID, |
1599 | 2373 | (int)Util.RegionToWorldLoc((uint)swCorner.X), (int)Util.RegionToWorldLoc((uint)neCorner.X), | |
1600 | int startX = (int)(pRegionLocX - 1) * (int)Constants.RegionSize; | 2374 | (int)Util.RegionToWorldLoc((uint)swCorner.Y), (int)Util.RegionToWorldLoc((uint)neCorner.Y)); |
1601 | int startY = (int)(pRegionLocY - 1) * (int)Constants.RegionSize; | 2375 | } |
1602 | 2376 | ||
1603 | int endX = ((int)pRegionLocX + (int)extent.X) * (int)Constants.RegionSize; | 2377 | // neighbours.ForEach( |
1604 | int endY = ((int)pRegionLocY + (int)extent.Y) * (int)Constants.RegionSize; | 2378 | // n => |
2379 | // m_log.DebugFormat( | ||
2380 | // "[ENTITY TRANSFER MODULE]: Region flags for {0} as seen by {1} are {2}", | ||
2381 | // n.RegionName, Scene.Name, n.RegionFlags != null ? n.RegionFlags.ToString() : "not present")); | ||
1605 | 2382 | ||
1606 | List<GridRegion> neighbours = pScene.GridService.GetRegionRange(m_regionInfo.ScopeID, startX, endX, startY, endY); | 2383 | // The r.RegionFlags == null check only needs to be made for simulators before 2015-01-14 (pre 0.8.1). |
1607 | neighbours.RemoveAll(delegate(GridRegion r) { return r.RegionID == m_regionInfo.RegionID; }); | 2384 | neighbours.RemoveAll( |
2385 | r => | ||
2386 | r.RegionID == m_regionInfo.RegionID | ||
2387 | || (r.RegionFlags != null && (r.RegionFlags & OpenSim.Framework.RegionFlags.RegionOnline) == 0)); | ||
1608 | 2388 | ||
1609 | return neighbours; | 2389 | return neighbours; |
1610 | } | ||
1611 | } | 2390 | } |
1612 | 2391 | ||
1613 | private List<ulong> NewNeighbours(List<ulong> currentNeighbours, List<ulong> previousNeighbours) | 2392 | private List<ulong> NewNeighbours(List<ulong> currentNeighbours, List<ulong> previousNeighbours) |
@@ -1665,10 +2444,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1665 | /// Move the given scene object into a new region depending on which region its absolute position has moved | 2444 | /// Move the given scene object into a new region depending on which region its absolute position has moved |
1666 | /// into. | 2445 | /// into. |
1667 | /// | 2446 | /// |
1668 | /// This method locates the new region handle and offsets the prim position for the new region | 2447 | /// Using the objects new world location, ask the grid service for a the new region and adjust the prim |
2448 | /// position to be relative to the new region. | ||
1669 | /// </summary> | 2449 | /// </summary> |
1670 | /// <param name="attemptedPosition">the attempted out of region position of the scene object</param> | ||
1671 | /// <param name="grp">the scene object that we're crossing</param> | 2450 | /// <param name="grp">the scene object that we're crossing</param> |
2451 | /// <param name="attemptedPosition">the attempted out of region position of the scene object. This position is | ||
2452 | /// relative to the region the object currently is in.</param> | ||
2453 | /// <param name="silent">if 'true', the deletion of the client from the region is not broadcast to the clients</param> | ||
1672 | public void Cross(SceneObjectGroup grp, Vector3 attemptedPosition, bool silent) | 2454 | public void Cross(SceneObjectGroup grp, Vector3 attemptedPosition, bool silent) |
1673 | { | 2455 | { |
1674 | if (grp == null) | 2456 | if (grp == null) |
@@ -1694,204 +2476,49 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1694 | return; | 2476 | return; |
1695 | } | 2477 | } |
1696 | 2478 | ||
1697 | int thisx = (int)scene.RegionInfo.RegionLocX; | 2479 | // Remember the old group position in case the region lookup fails so position can be restored. |
1698 | int thisy = (int)scene.RegionInfo.RegionLocY; | 2480 | Vector3 oldGroupPosition = grp.RootPart.GroupPosition; |
1699 | Vector3 EastCross = new Vector3(0.1f, 0, 0); | ||
1700 | Vector3 WestCross = new Vector3(-0.1f, 0, 0); | ||
1701 | Vector3 NorthCross = new Vector3(0, 0.1f, 0); | ||
1702 | Vector3 SouthCross = new Vector3(0, -0.1f, 0); | ||
1703 | |||
1704 | |||
1705 | // use this if no borders were crossed! | ||
1706 | ulong newRegionHandle | ||
1707 | = Util.UIntsToLong((uint)((thisx) * Constants.RegionSize), | ||
1708 | (uint)((thisy) * Constants.RegionSize)); | ||
1709 | |||
1710 | Vector3 pos = attemptedPosition; | ||
1711 | |||
1712 | int changeX = 1; | ||
1713 | int changeY = 1; | ||
1714 | |||
1715 | if (scene.TestBorderCross(attemptedPosition + WestCross, Cardinals.W)) | ||
1716 | { | ||
1717 | if (scene.TestBorderCross(attemptedPosition + SouthCross, Cardinals.S)) | ||
1718 | { | ||
1719 | |||
1720 | Border crossedBorderx = scene.GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W); | ||
1721 | |||
1722 | if (crossedBorderx.BorderLine.Z > 0) | ||
1723 | { | ||
1724 | pos.X = ((pos.X + crossedBorderx.BorderLine.Z)); | ||
1725 | changeX = (int)(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize); | ||
1726 | } | ||
1727 | else | ||
1728 | pos.X = ((pos.X + Constants.RegionSize)); | ||
1729 | |||
1730 | Border crossedBordery = scene.GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S); | ||
1731 | //(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize) | ||
1732 | |||
1733 | if (crossedBordery.BorderLine.Z > 0) | ||
1734 | { | ||
1735 | pos.Y = ((pos.Y + crossedBordery.BorderLine.Z)); | ||
1736 | changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize); | ||
1737 | } | ||
1738 | else | ||
1739 | pos.Y = ((pos.Y + Constants.RegionSize)); | ||
1740 | |||
1741 | |||
1742 | |||
1743 | newRegionHandle | ||
1744 | = Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize), | ||
1745 | (uint)((thisy - changeY) * Constants.RegionSize)); | ||
1746 | // x - 1 | ||
1747 | // y - 1 | ||
1748 | } | ||
1749 | else if (scene.TestBorderCross(attemptedPosition + NorthCross, Cardinals.N)) | ||
1750 | { | ||
1751 | Border crossedBorderx = scene.GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W); | ||
1752 | |||
1753 | if (crossedBorderx.BorderLine.Z > 0) | ||
1754 | { | ||
1755 | pos.X = ((pos.X + crossedBorderx.BorderLine.Z)); | ||
1756 | changeX = (int)(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize); | ||
1757 | } | ||
1758 | else | ||
1759 | pos.X = ((pos.X + Constants.RegionSize)); | ||
1760 | |||
1761 | |||
1762 | Border crossedBordery = scene.GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S); | ||
1763 | //(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize) | ||
1764 | |||
1765 | if (crossedBordery.BorderLine.Z > 0) | ||
1766 | { | ||
1767 | pos.Y = ((pos.Y + crossedBordery.BorderLine.Z)); | ||
1768 | changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize); | ||
1769 | } | ||
1770 | else | ||
1771 | pos.Y = ((pos.Y + Constants.RegionSize)); | ||
1772 | |||
1773 | newRegionHandle | ||
1774 | = Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize), | ||
1775 | (uint)((thisy + changeY) * Constants.RegionSize)); | ||
1776 | // x - 1 | ||
1777 | // y + 1 | ||
1778 | } | ||
1779 | else | ||
1780 | { | ||
1781 | Border crossedBorderx = scene.GetCrossedBorder(attemptedPosition + WestCross, Cardinals.W); | ||
1782 | |||
1783 | if (crossedBorderx.BorderLine.Z > 0) | ||
1784 | { | ||
1785 | pos.X = ((pos.X + crossedBorderx.BorderLine.Z)); | ||
1786 | changeX = (int)(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize); | ||
1787 | } | ||
1788 | else | ||
1789 | pos.X = ((pos.X + Constants.RegionSize)); | ||
1790 | |||
1791 | newRegionHandle | ||
1792 | = Util.UIntsToLong((uint)((thisx - changeX) * Constants.RegionSize), | ||
1793 | (uint)(thisy * Constants.RegionSize)); | ||
1794 | // x - 1 | ||
1795 | } | ||
1796 | } | ||
1797 | else if (scene.TestBorderCross(attemptedPosition + EastCross, Cardinals.E)) | ||
1798 | { | ||
1799 | if (scene.TestBorderCross(attemptedPosition + SouthCross, Cardinals.S)) | ||
1800 | { | ||
1801 | |||
1802 | pos.X = ((pos.X - Constants.RegionSize)); | ||
1803 | Border crossedBordery = scene.GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S); | ||
1804 | //(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize) | ||
1805 | |||
1806 | if (crossedBordery.BorderLine.Z > 0) | ||
1807 | { | ||
1808 | pos.Y = ((pos.Y + crossedBordery.BorderLine.Z)); | ||
1809 | changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize); | ||
1810 | } | ||
1811 | else | ||
1812 | pos.Y = ((pos.Y + Constants.RegionSize)); | ||
1813 | |||
1814 | 2481 | ||
1815 | newRegionHandle | 2482 | // Compute the absolute position of the object. |
1816 | = Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize), | 2483 | double objectWorldLocX = (double)scene.RegionInfo.WorldLocX + attemptedPosition.X; |
1817 | (uint)((thisy - changeY) * Constants.RegionSize)); | 2484 | double objectWorldLocY = (double)scene.RegionInfo.WorldLocY + attemptedPosition.Y; |
1818 | // x + 1 | ||
1819 | // y - 1 | ||
1820 | } | ||
1821 | else if (scene.TestBorderCross(attemptedPosition + NorthCross, Cardinals.N)) | ||
1822 | { | ||
1823 | pos.X = ((pos.X - Constants.RegionSize)); | ||
1824 | pos.Y = ((pos.Y - Constants.RegionSize)); | ||
1825 | newRegionHandle | ||
1826 | = Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize), | ||
1827 | (uint)((thisy + changeY) * Constants.RegionSize)); | ||
1828 | // x + 1 | ||
1829 | // y + 1 | ||
1830 | } | ||
1831 | else | ||
1832 | { | ||
1833 | pos.X = ((pos.X - Constants.RegionSize)); | ||
1834 | newRegionHandle | ||
1835 | = Util.UIntsToLong((uint)((thisx + changeX) * Constants.RegionSize), | ||
1836 | (uint)(thisy * Constants.RegionSize)); | ||
1837 | // x + 1 | ||
1838 | } | ||
1839 | } | ||
1840 | else if (scene.TestBorderCross(attemptedPosition + SouthCross, Cardinals.S)) | ||
1841 | { | ||
1842 | Border crossedBordery = scene.GetCrossedBorder(attemptedPosition + SouthCross, Cardinals.S); | ||
1843 | //(crossedBorderx.BorderLine.Z / (int)Constants.RegionSize) | ||
1844 | 2485 | ||
1845 | if (crossedBordery.BorderLine.Z > 0) | 2486 | // Ask the grid service for the region that contains the passed address |
1846 | { | 2487 | GridRegion destination = GetRegionContainingWorldLocation(scene.GridService, scene.RegionInfo.ScopeID, |
1847 | pos.Y = ((pos.Y + crossedBordery.BorderLine.Z)); | 2488 | objectWorldLocX, objectWorldLocY); |
1848 | changeY = (int)(crossedBordery.BorderLine.Z / (int)Constants.RegionSize); | ||
1849 | } | ||
1850 | else | ||
1851 | pos.Y = ((pos.Y + Constants.RegionSize)); | ||
1852 | 2489 | ||
1853 | newRegionHandle | 2490 | Vector3 pos = Vector3.Zero; |
1854 | = Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy - changeY) * Constants.RegionSize)); | 2491 | if (destination != null) |
1855 | // y - 1 | ||
1856 | } | ||
1857 | else if (scene.TestBorderCross(attemptedPosition + NorthCross, Cardinals.N)) | ||
1858 | { | 2492 | { |
1859 | 2493 | // Adjust the object's relative position from the old region (attemptedPosition) | |
1860 | pos.Y = ((pos.Y - Constants.RegionSize)); | 2494 | // to be relative to the new region (pos). |
1861 | newRegionHandle | 2495 | pos = new Vector3( (float)(objectWorldLocX - (double)destination.RegionLocX), |
1862 | = Util.UIntsToLong((uint)(thisx * Constants.RegionSize), (uint)((thisy + changeY) * Constants.RegionSize)); | 2496 | (float)(objectWorldLocY - (double)destination.RegionLocY), |
1863 | // y + 1 | 2497 | attemptedPosition.Z); |
1864 | } | 2498 | } |
1865 | 2499 | ||
1866 | // Offset the positions for the new region across the border | ||
1867 | Vector3 oldGroupPosition = grp.RootPart.GroupPosition; | ||
1868 | |||
1869 | // If we fail to cross the border, then reset the position of the scene object on that border. | ||
1870 | uint x = 0, y = 0; | ||
1871 | Utils.LongToUInts(newRegionHandle, out x, out y); | ||
1872 | GridRegion destination = scene.GridService.GetRegionByPosition(scene.RegionInfo.ScopeID, (int)x, (int)y); | ||
1873 | |||
1874 | if (destination == null || !CrossPrimGroupIntoNewRegion(destination, pos, grp, silent)) | 2500 | if (destination == null || !CrossPrimGroupIntoNewRegion(destination, pos, grp, silent)) |
1875 | { | 2501 | { |
1876 | m_log.InfoFormat("[ENTITY TRANSFER MODULE] cross region transfer failed for object {0}",grp.UUID); | 2502 | m_log.InfoFormat("[ENTITY TRANSFER MODULE] cross region transfer failed for object {0}", grp.UUID); |
1877 | 2503 | ||
1878 | // We are going to move the object back to the old position so long as the old position | 2504 | // We are going to move the object back to the old position so long as the old position |
1879 | // is in the region | 2505 | // is in the region |
1880 | oldGroupPosition.X = Util.Clamp<float>(oldGroupPosition.X,1.0f,(float)Constants.RegionSize-1); | 2506 | oldGroupPosition.X = Util.Clamp<float>(oldGroupPosition.X, 1.0f, (float)(scene.RegionInfo.RegionSizeX - 1)); |
1881 | oldGroupPosition.Y = Util.Clamp<float>(oldGroupPosition.Y,1.0f,(float)Constants.RegionSize-1); | 2507 | oldGroupPosition.Y = Util.Clamp<float>(oldGroupPosition.Y, 1.0f, (float)(scene.RegionInfo.RegionSizeY - 1)); |
1882 | oldGroupPosition.Z = Util.Clamp<float>(oldGroupPosition.Z,1.0f,4096.0f); | 2508 | oldGroupPosition.Z = Util.Clamp<float>(oldGroupPosition.Z, 1.0f, Constants.RegionHeight); |
1883 | 2509 | ||
1884 | grp.RootPart.GroupPosition = oldGroupPosition; | 2510 | grp.AbsolutePosition = oldGroupPosition; |
2511 | grp.Velocity = Vector3.Zero; | ||
2512 | if (grp.RootPart.PhysActor != null) | ||
2513 | grp.RootPart.PhysActor.CrossingFailure(); | ||
1885 | 2514 | ||
1886 | // Need to turn off the physics flags, otherwise the object will continue to attempt to | 2515 | if (grp.RootPart.KeyframeMotion != null) |
1887 | // move out of the region creating an infinite loop of failed attempts to cross | 2516 | grp.RootPart.KeyframeMotion.CrossingFailure(); |
1888 | grp.UpdatePrimFlags(grp.RootPart.LocalId,false,grp.IsTemporary,grp.IsPhantom,false); | ||
1889 | 2517 | ||
1890 | grp.ScheduleGroupForFullUpdate(); | 2518 | grp.ScheduleGroupForFullUpdate(); |
1891 | } | 2519 | } |
1892 | } | 2520 | } |
1893 | 2521 | ||
1894 | |||
1895 | /// <summary> | 2522 | /// <summary> |
1896 | /// Move the given scene object into a new region | 2523 | /// Move the given scene object into a new region |
1897 | /// </summary> | 2524 | /// </summary> |
@@ -1942,17 +2569,30 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1942 | grp, e); | 2569 | grp, e); |
1943 | } | 2570 | } |
1944 | } | 2571 | } |
2572 | /* | ||
2573 | * done on caller ( not in attachments crossing for now) | ||
1945 | else | 2574 | else |
1946 | { | 2575 | { |
2576 | |||
1947 | if (!grp.IsDeleted) | 2577 | if (!grp.IsDeleted) |
1948 | { | 2578 | { |
1949 | PhysicsActor pa = grp.RootPart.PhysActor; | 2579 | PhysicsActor pa = grp.RootPart.PhysActor; |
1950 | if (pa != null) | 2580 | if (pa != null) |
2581 | { | ||
1951 | pa.CrossingFailure(); | 2582 | pa.CrossingFailure(); |
2583 | if (grp.RootPart.KeyframeMotion != null) | ||
2584 | { | ||
2585 | // moved to KeyframeMotion.CrossingFailure | ||
2586 | // grp.RootPart.Velocity = Vector3.Zero; | ||
2587 | grp.RootPart.KeyframeMotion.CrossingFailure(); | ||
2588 | // grp.SendGroupRootTerseUpdate(); | ||
2589 | } | ||
2590 | } | ||
1952 | } | 2591 | } |
1953 | 2592 | ||
1954 | m_log.ErrorFormat("[ENTITY TRANSFER MODULE]: Prim crossing failed for {0}", grp); | 2593 | m_log.ErrorFormat("[ENTITY TRANSFER MODULE]: Prim crossing failed for {0}", grp); |
1955 | } | 2594 | } |
2595 | */ | ||
1956 | } | 2596 | } |
1957 | else | 2597 | else |
1958 | { | 2598 | { |
@@ -2007,7 +2647,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
2007 | 2647 | ||
2008 | public bool IsInTransit(UUID id) | 2648 | public bool IsInTransit(UUID id) |
2009 | { | 2649 | { |
2010 | return m_entityTransferStateMachine.IsInTransit(id); | 2650 | return m_entityTransferStateMachine.GetAgentTransferState(id) != null; |
2011 | } | 2651 | } |
2012 | 2652 | ||
2013 | protected void ReInstantiateScripts(ScenePresence sp) | 2653 | protected void ReInstantiateScripts(ScenePresence sp) |
@@ -2036,5 +2676,69 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
2036 | } | 2676 | } |
2037 | #endregion | 2677 | #endregion |
2038 | 2678 | ||
2679 | public virtual bool HandleIncomingSceneObject(SceneObjectGroup so, Vector3 newPosition) | ||
2680 | { | ||
2681 | // If the user is banned, we won't let any of their objects | ||
2682 | // enter. Period. | ||
2683 | // | ||
2684 | if (Scene.RegionInfo.EstateSettings.IsBanned(so.OwnerID)) | ||
2685 | { | ||
2686 | m_log.DebugFormat( | ||
2687 | "[ENTITY TRANSFER MODULE]: Denied prim crossing of {0} {1} into {2} for banned avatar {3}", | ||
2688 | so.Name, so.UUID, Scene.Name, so.OwnerID); | ||
2689 | |||
2690 | return false; | ||
2691 | } | ||
2692 | |||
2693 | if (newPosition != Vector3.Zero) | ||
2694 | so.RootPart.GroupPosition = newPosition; | ||
2695 | |||
2696 | if (!Scene.AddSceneObject(so)) | ||
2697 | { | ||
2698 | m_log.DebugFormat( | ||
2699 | "[ENTITY TRANSFER MODULE]: Problem adding scene object {0} {1} into {2} ", | ||
2700 | so.Name, so.UUID, Scene.Name); | ||
2701 | |||
2702 | return false; | ||
2703 | } | ||
2704 | |||
2705 | if (!so.IsAttachment) | ||
2706 | { | ||
2707 | // FIXME: It would be better to never add the scene object at all rather than add it and then delete | ||
2708 | // it | ||
2709 | if (!Scene.Permissions.CanObjectEntry(so.UUID, true, so.AbsolutePosition)) | ||
2710 | { | ||
2711 | // Deny non attachments based on parcel settings | ||
2712 | // | ||
2713 | m_log.Info("[ENTITY TRANSFER MODULE]: Denied prim crossing because of parcel settings"); | ||
2714 | |||
2715 | Scene.DeleteSceneObject(so, false); | ||
2716 | |||
2717 | return false; | ||
2718 | } | ||
2719 | |||
2720 | // For attachments, we need to wait until the agent is root | ||
2721 | // before we restart the scripts, or else some functions won't work. | ||
2722 | so.RootPart.ParentGroup.CreateScriptInstances( | ||
2723 | 0, false, Scene.DefaultScriptEngine, GetStateSource(so)); | ||
2724 | |||
2725 | so.ResumeScripts(); | ||
2726 | |||
2727 | if (so.RootPart.KeyframeMotion != null) | ||
2728 | so.RootPart.KeyframeMotion.UpdateSceneObject(so); | ||
2729 | } | ||
2730 | |||
2731 | return true; | ||
2732 | } | ||
2733 | |||
2734 | private int GetStateSource(SceneObjectGroup sog) | ||
2735 | { | ||
2736 | ScenePresence sp = Scene.GetScenePresence(sog.OwnerID); | ||
2737 | |||
2738 | if (sp != null) | ||
2739 | return sp.GetStateSource(); | ||
2740 | |||
2741 | return 2; // StateSource.PrimCrossing | ||
2742 | } | ||
2039 | } | 2743 | } |
2040 | } \ No newline at end of file | 2744 | } |
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs index d0cab49..a3109e0 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs | |||
@@ -38,7 +38,7 @@ using OpenSim.Framework.Capabilities; | |||
38 | using OpenSim.Framework.Client; | 38 | using OpenSim.Framework.Client; |
39 | using OpenSim.Region.Framework.Interfaces; | 39 | using OpenSim.Region.Framework.Interfaces; |
40 | using OpenSim.Region.Framework.Scenes; | 40 | using OpenSim.Region.Framework.Scenes; |
41 | using OpenSim.Region.Physics.Manager; | 41 | using OpenSim.Region.PhysicsModules.SharedBase; |
42 | using OpenSim.Services.Interfaces; | 42 | using OpenSim.Services.Interfaces; |
43 | using GridRegion = OpenSim.Services.Interfaces.GridRegion; | 43 | using GridRegion = OpenSim.Services.Interfaces.GridRegion; |
44 | 44 | ||
@@ -51,10 +51,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
51 | /// This is a state machine. | 51 | /// This is a state machine. |
52 | /// | 52 | /// |
53 | /// [Entry] => Preparing | 53 | /// [Entry] => Preparing |
54 | /// Preparing => { Transferring || CleaningUp || [Exit] } | 54 | /// Preparing => { Transferring || Cancelling || CleaningUp || Aborting || [Exit] } |
55 | /// Transferring => { ReceivedAtDestination || CleaningUp } | 55 | /// Transferring => { ReceivedAtDestination || Cancelling || CleaningUp || Aborting } |
56 | /// ReceivedAtDestination => CleaningUp | 56 | /// Cancelling => CleaningUp || Aborting |
57 | /// ReceivedAtDestination => CleaningUp || Aborting | ||
57 | /// CleaningUp => [Exit] | 58 | /// CleaningUp => [Exit] |
59 | /// Aborting => [Exit] | ||
58 | /// | 60 | /// |
59 | /// In other words, agents normally travel throwing Preparing => Transferring => ReceivedAtDestination => CleaningUp | 61 | /// In other words, agents normally travel throwing Preparing => Transferring => ReceivedAtDestination => CleaningUp |
60 | /// However, any state can transition to CleaningUp if the teleport has failed. | 62 | /// However, any state can transition to CleaningUp if the teleport has failed. |
@@ -64,7 +66,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
64 | Preparing, // The agent is being prepared for transfer | 66 | Preparing, // The agent is being prepared for transfer |
65 | Transferring, // The agent is in the process of being transferred to a destination | 67 | Transferring, // The agent is in the process of being transferred to a destination |
66 | ReceivedAtDestination, // The destination has notified us that the agent has been successfully received | 68 | ReceivedAtDestination, // The destination has notified us that the agent has been successfully received |
67 | CleaningUp // The agent is being changed to child/removed after a transfer | 69 | CleaningUp, // The agent is being changed to child/removed after a transfer |
70 | Cancelling, // The user has cancelled the teleport but we have yet to act upon this. | ||
71 | Aborting // The transfer is aborting. Unlike Cancelling, no compensating actions should be performed | ||
68 | } | 72 | } |
69 | 73 | ||
70 | /// <summary> | 74 | /// <summary> |
@@ -73,6 +77,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
73 | public class EntityTransferStateMachine | 77 | public class EntityTransferStateMachine |
74 | { | 78 | { |
75 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 79 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
80 | private static readonly string LogHeader = "[ENTITY TRANSFER STATE MACHINE]"; | ||
76 | 81 | ||
77 | /// <summary> | 82 | /// <summary> |
78 | /// If true then on a teleport, the source region waits for a callback from the destination region. If | 83 | /// If true then on a teleport, the source region waits for a callback from the destination region. If |
@@ -96,6 +101,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
96 | /// <returns>true if the agent was not already in transit, false if it was</returns> | 101 | /// <returns>true if the agent was not already in transit, false if it was</returns> |
97 | internal bool SetInTransit(UUID id) | 102 | internal bool SetInTransit(UUID id) |
98 | { | 103 | { |
104 | m_log.DebugFormat("{0} SetInTransit. agent={1}, newState=Preparing", LogHeader, id); | ||
99 | lock (m_agentsInTransit) | 105 | lock (m_agentsInTransit) |
100 | { | 106 | { |
101 | if (!m_agentsInTransit.ContainsKey(id)) | 107 | if (!m_agentsInTransit.ContainsKey(id)) |
@@ -115,42 +121,117 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
115 | /// <param name='newState'></param> | 121 | /// <param name='newState'></param> |
116 | /// <returns></returns> | 122 | /// <returns></returns> |
117 | /// <exception cref='Exception'>Illegal transitions will throw an Exception</exception> | 123 | /// <exception cref='Exception'>Illegal transitions will throw an Exception</exception> |
118 | internal void UpdateInTransit(UUID id, AgentTransferState newState) | 124 | internal bool UpdateInTransit(UUID id, AgentTransferState newState) |
119 | { | 125 | { |
126 | m_log.DebugFormat("{0} UpdateInTransit. agent={1}, newState={2}", LogHeader, id, newState); | ||
127 | |||
128 | bool transitionOkay = false; | ||
129 | |||
130 | // We don't want to throw an exception on cancel since this can come it at any time. | ||
131 | bool failIfNotOkay = true; | ||
132 | |||
133 | // Should be a failure message if failure is not okay. | ||
134 | string failureMessage = null; | ||
135 | |||
136 | AgentTransferState? oldState = null; | ||
137 | |||
120 | lock (m_agentsInTransit) | 138 | lock (m_agentsInTransit) |
121 | { | 139 | { |
122 | // Illegal to try and update an agent that's not actually in transit. | 140 | // Illegal to try and update an agent that's not actually in transit. |
123 | if (!m_agentsInTransit.ContainsKey(id)) | 141 | if (!m_agentsInTransit.ContainsKey(id)) |
124 | throw new Exception( | 142 | { |
125 | string.Format( | 143 | if (newState != AgentTransferState.Cancelling && newState != AgentTransferState.Aborting) |
126 | "Agent with ID {0} is not registered as in transit in {1}", | 144 | failureMessage = string.Format( |
127 | id, m_mod.Scene.RegionInfo.RegionName)); | 145 | "Agent with ID {0} is not registered as in transit in {1}", |
128 | 146 | id, m_mod.Scene.RegionInfo.RegionName); | |
129 | AgentTransferState oldState = m_agentsInTransit[id]; | 147 | else |
148 | failIfNotOkay = false; | ||
149 | } | ||
150 | else | ||
151 | { | ||
152 | oldState = m_agentsInTransit[id]; | ||
130 | 153 | ||
131 | bool transitionOkay = false; | 154 | if (newState == AgentTransferState.Aborting) |
155 | { | ||
156 | transitionOkay = true; | ||
157 | } | ||
158 | else if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp) | ||
159 | { | ||
160 | transitionOkay = true; | ||
161 | } | ||
162 | else if (newState == AgentTransferState.Transferring && oldState == AgentTransferState.Preparing) | ||
163 | { | ||
164 | transitionOkay = true; | ||
165 | } | ||
166 | else if (newState == AgentTransferState.ReceivedAtDestination && oldState == AgentTransferState.Transferring) | ||
167 | { | ||
168 | transitionOkay = true; | ||
169 | } | ||
170 | else | ||
171 | { | ||
172 | if (newState == AgentTransferState.Cancelling | ||
173 | && (oldState == AgentTransferState.Preparing || oldState == AgentTransferState.Transferring)) | ||
174 | { | ||
175 | transitionOkay = true; | ||
176 | } | ||
177 | else | ||
178 | { | ||
179 | failIfNotOkay = false; | ||
180 | } | ||
181 | } | ||
132 | 182 | ||
133 | if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp) | 183 | if (!transitionOkay) |
134 | transitionOkay = true; | 184 | failureMessage |
135 | else if (newState == AgentTransferState.Transferring && oldState == AgentTransferState.Preparing) | 185 | = string.Format( |
136 | transitionOkay = true; | 186 | "Agent with ID {0} is not allowed to move from old transit state {1} to new state {2} in {3}", |
137 | else if (newState == AgentTransferState.ReceivedAtDestination && oldState == AgentTransferState.Transferring) | 187 | id, oldState, newState, m_mod.Scene.RegionInfo.RegionName); |
138 | transitionOkay = true; | 188 | } |
139 | 189 | ||
140 | if (transitionOkay) | 190 | if (transitionOkay) |
191 | { | ||
141 | m_agentsInTransit[id] = newState; | 192 | m_agentsInTransit[id] = newState; |
142 | else | 193 | |
143 | throw new Exception( | 194 | // m_log.DebugFormat( |
144 | string.Format( | 195 | // "[ENTITY TRANSFER STATE MACHINE]: Changed agent with id {0} from state {1} to {2} in {3}", |
145 | "Agent with ID {0} is not allowed to move from old transit state {1} to new state {2} in {3}", | 196 | // id, oldState, newState, m_mod.Scene.Name); |
146 | id, oldState, newState, m_mod.Scene.RegionInfo.RegionName)); | 197 | } |
198 | else if (failIfNotOkay) | ||
199 | { | ||
200 | m_log.DebugFormat("{0} UpdateInTransit. Throwing transition failure = {1}", LogHeader, failureMessage); | ||
201 | throw new Exception(failureMessage); | ||
202 | } | ||
203 | // else | ||
204 | // { | ||
205 | // if (oldState != null) | ||
206 | // m_log.DebugFormat( | ||
207 | // "[ENTITY TRANSFER STATE MACHINE]: Ignored change of agent with id {0} from state {1} to {2} in {3}", | ||
208 | // id, oldState, newState, m_mod.Scene.Name); | ||
209 | // else | ||
210 | // m_log.DebugFormat( | ||
211 | // "[ENTITY TRANSFER STATE MACHINE]: Ignored change of agent with id {0} to state {1} in {2} since agent not in transit", | ||
212 | // id, newState, m_mod.Scene.Name); | ||
213 | // } | ||
147 | } | 214 | } |
215 | |||
216 | return transitionOkay; | ||
148 | } | 217 | } |
149 | 218 | ||
150 | internal bool IsInTransit(UUID id) | 219 | /// <summary> |
220 | /// Gets the current agent transfer state. | ||
221 | /// </summary> | ||
222 | /// <returns>Null if the agent is not in transit</returns> | ||
223 | /// <param name='id'> | ||
224 | /// Identifier. | ||
225 | /// </param> | ||
226 | internal AgentTransferState? GetAgentTransferState(UUID id) | ||
151 | { | 227 | { |
152 | lock (m_agentsInTransit) | 228 | lock (m_agentsInTransit) |
153 | return m_agentsInTransit.ContainsKey(id); | 229 | { |
230 | if (!m_agentsInTransit.ContainsKey(id)) | ||
231 | return null; | ||
232 | else | ||
233 | return m_agentsInTransit[id]; | ||
234 | } | ||
154 | } | 235 | } |
155 | 236 | ||
156 | /// <summary> | 237 | /// <summary> |
@@ -203,14 +284,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
203 | 284 | ||
204 | lock (m_agentsInTransit) | 285 | lock (m_agentsInTransit) |
205 | { | 286 | { |
206 | if (!IsInTransit(id)) | 287 | AgentTransferState? currentState = GetAgentTransferState(id); |
288 | |||
289 | if (currentState == null) | ||
207 | throw new Exception( | 290 | throw new Exception( |
208 | string.Format( | 291 | string.Format( |
209 | "Asked to wait for destination callback for agent with ID {0} in {1} but agent is not in transit", | 292 | "Asked to wait for destination callback for agent with ID {0} in {1} but agent is not in transit", |
210 | id, m_mod.Scene.RegionInfo.RegionName)); | 293 | id, m_mod.Scene.RegionInfo.RegionName)); |
211 | 294 | ||
212 | AgentTransferState currentState = m_agentsInTransit[id]; | ||
213 | |||
214 | if (currentState != AgentTransferState.Transferring && currentState != AgentTransferState.ReceivedAtDestination) | 295 | if (currentState != AgentTransferState.Transferring && currentState != AgentTransferState.ReceivedAtDestination) |
215 | throw new Exception( | 296 | throw new Exception( |
216 | string.Format( | 297 | string.Format( |
@@ -222,8 +303,15 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
222 | 303 | ||
223 | // There should be no race condition here since no other code should be removing the agent transfer or | 304 | // There should be no race condition here since no other code should be removing the agent transfer or |
224 | // changing the state to another other than Transferring => ReceivedAtDestination. | 305 | // changing the state to another other than Transferring => ReceivedAtDestination. |
225 | while (m_agentsInTransit[id] != AgentTransferState.ReceivedAtDestination && count-- > 0) | 306 | |
307 | while (count-- > 0) | ||
226 | { | 308 | { |
309 | lock (m_agentsInTransit) | ||
310 | { | ||
311 | if (m_agentsInTransit[id] == AgentTransferState.ReceivedAtDestination) | ||
312 | break; | ||
313 | } | ||
314 | |||
227 | // m_log.Debug(" >>> Waiting... " + count); | 315 | // m_log.Debug(" >>> Waiting... " + count); |
228 | Thread.Sleep(100); | 316 | Thread.Sleep(100); |
229 | } | 317 | } |
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs index b188741..fa23590 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs | |||
@@ -1,4 +1,4 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) Contributors, http://opensimulator.org/ | 2 | * Copyright (c) Contributors, http://opensimulator.org/ |
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | 3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. |
4 | * | 4 | * |
@@ -31,6 +31,7 @@ using System.Reflection; | |||
31 | 31 | ||
32 | using OpenSim.Framework; | 32 | using OpenSim.Framework; |
33 | using OpenSim.Framework.Client; | 33 | using OpenSim.Framework.Client; |
34 | using OpenSim.Framework.Monitoring; | ||
34 | using OpenSim.Region.Framework.Interfaces; | 35 | using OpenSim.Region.Framework.Interfaces; |
35 | using OpenSim.Region.Framework.Scenes; | 36 | using OpenSim.Region.Framework.Scenes; |
36 | using OpenSim.Services.Connectors.Hypergrid; | 37 | using OpenSim.Services.Connectors.Hypergrid; |
@@ -55,6 +56,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
55 | private int m_levelHGTeleport = 0; | 56 | private int m_levelHGTeleport = 0; |
56 | 57 | ||
57 | private GatekeeperServiceConnector m_GatekeeperConnector; | 58 | private GatekeeperServiceConnector m_GatekeeperConnector; |
59 | private IUserAgentService m_UAS; | ||
58 | 60 | ||
59 | protected bool m_RestrictAppearanceAbroad; | 61 | protected bool m_RestrictAppearanceAbroad; |
60 | protected string m_AccountName; | 62 | protected string m_AccountName; |
@@ -109,6 +111,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
109 | } | 111 | } |
110 | } | 112 | } |
111 | 113 | ||
114 | /// <summary> | ||
115 | /// Used for processing analysis of incoming attachments in a controlled fashion. | ||
116 | /// </summary> | ||
117 | private JobEngine m_incomingSceneObjectEngine; | ||
118 | |||
112 | #region ISharedRegionModule | 119 | #region ISharedRegionModule |
113 | 120 | ||
114 | public override string Name | 121 | public override string Name |
@@ -152,39 +159,33 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
152 | if (m_Enabled) | 159 | if (m_Enabled) |
153 | { | 160 | { |
154 | scene.RegisterModuleInterface<IUserAgentVerificationModule>(this); | 161 | scene.RegisterModuleInterface<IUserAgentVerificationModule>(this); |
155 | scene.EventManager.OnIncomingSceneObject += OnIncomingSceneObject; | 162 | //scene.EventManager.OnIncomingSceneObject += OnIncomingSceneObject; |
156 | } | 163 | |
157 | } | 164 | m_incomingSceneObjectEngine |
158 | 165 | = new JobEngine( | |
159 | void OnIncomingSceneObject(SceneObjectGroup so) | 166 | string.Format("HG Incoming Scene Object Engine ({0})", scene.Name), |
160 | { | 167 | "HG INCOMING SCENE OBJECT ENGINE"); |
161 | if (!so.IsAttachment) | 168 | |
162 | return; | 169 | StatsManager.RegisterStat( |
163 | 170 | new Stat( | |
164 | if (so.Scene.UserManagementModule.IsLocalGridUser(so.AttachedAvatar)) | 171 | "HGIncomingAttachmentsWaiting", |
165 | return; | 172 | "Number of incoming attachments waiting for processing.", |
166 | 173 | "", | |
167 | // foreign user | 174 | "", |
168 | AgentCircuitData aCircuit = so.Scene.AuthenticateHandler.GetAgentCircuitData(so.AttachedAvatar); | 175 | "entitytransfer", |
169 | if (aCircuit != null && (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0) | 176 | Name, |
170 | { | 177 | StatType.Pull, |
171 | if (aCircuit.ServiceURLs != null && aCircuit.ServiceURLs.ContainsKey("AssetServerURI")) | 178 | MeasuresOfInterest.None, |
172 | { | 179 | stat => stat.Value = m_incomingSceneObjectEngine.JobsWaiting, |
173 | string url = aCircuit.ServiceURLs["AssetServerURI"].ToString(); | 180 | StatVerbosity.Debug)); |
174 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Incoming attachement {0} for HG user {1} with asset server {2}", so.Name, so.AttachedAvatar, url); | 181 | |
175 | Dictionary<UUID, AssetType> ids = new Dictionary<UUID, AssetType>(); | 182 | m_incomingSceneObjectEngine.Start(); |
176 | HGUuidGatherer uuidGatherer = new HGUuidGatherer(so.Scene.AssetService, url); | ||
177 | uuidGatherer.GatherAssetUuids(so, ids); | ||
178 | |||
179 | foreach (KeyValuePair<UUID, AssetType> kvp in ids) | ||
180 | uuidGatherer.FetchAsset(kvp.Key); | ||
181 | } | ||
182 | } | 183 | } |
183 | } | 184 | } |
184 | 185 | ||
185 | protected override void OnNewClient(IClientAPI client) | 186 | protected override void OnNewClient(IClientAPI client) |
186 | { | 187 | { |
187 | client.OnTeleportHomeRequest += TeleportHome; | 188 | client.OnTeleportHomeRequest += TriggerTeleportHome; |
188 | client.OnTeleportLandmarkRequest += RequestTeleportLandmark; | 189 | client.OnTeleportLandmarkRequest += RequestTeleportLandmark; |
189 | client.OnConnectionClosed += new Action<IClientAPI>(OnConnectionClosed); | 190 | client.OnConnectionClosed += new Action<IClientAPI>(OnConnectionClosed); |
190 | } | 191 | } |
@@ -194,34 +195,44 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
194 | base.RegionLoaded(scene); | 195 | base.RegionLoaded(scene); |
195 | 196 | ||
196 | if (m_Enabled) | 197 | if (m_Enabled) |
198 | { | ||
197 | m_GatekeeperConnector = new GatekeeperServiceConnector(scene.AssetService); | 199 | m_GatekeeperConnector = new GatekeeperServiceConnector(scene.AssetService); |
200 | m_UAS = scene.RequestModuleInterface<IUserAgentService>(); | ||
201 | if (m_UAS == null) | ||
202 | m_UAS = new UserAgentServiceConnector(m_ThisHomeURI); | ||
203 | |||
204 | } | ||
198 | } | 205 | } |
199 | 206 | ||
200 | public override void RemoveRegion(Scene scene) | 207 | public override void RemoveRegion(Scene scene) |
201 | { | 208 | { |
202 | base.AddRegion(scene); | 209 | base.RemoveRegion(scene); |
203 | 210 | ||
204 | if (m_Enabled) | 211 | if (m_Enabled) |
212 | { | ||
205 | scene.UnregisterModuleInterface<IUserAgentVerificationModule>(this); | 213 | scene.UnregisterModuleInterface<IUserAgentVerificationModule>(this); |
214 | m_incomingSceneObjectEngine.Stop(); | ||
215 | } | ||
206 | } | 216 | } |
207 | 217 | ||
208 | #endregion | 218 | #endregion |
209 | 219 | ||
210 | #region HG overrides of IEntiryTransferModule | 220 | #region HG overrides of IEntityTransferModule |
211 | 221 | ||
212 | protected override GridRegion GetFinalDestination(GridRegion region) | 222 | protected override GridRegion GetFinalDestination(GridRegion region, UUID agentID, string agentHomeURI, out string message) |
213 | { | 223 | { |
214 | int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, region.RegionID); | 224 | int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, region.RegionID); |
215 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: region {0} flags: {1}", region.RegionName, flags); | 225 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: region {0} flags: {1}", region.RegionName, flags); |
226 | message = null; | ||
216 | 227 | ||
217 | if ((flags & (int)OpenSim.Framework.RegionFlags.Hyperlink) != 0) | 228 | if ((flags & (int)OpenSim.Framework.RegionFlags.Hyperlink) != 0) |
218 | { | 229 | { |
219 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Destination region is hyperlink"); | 230 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Destination region is hyperlink"); |
220 | GridRegion real_destination = m_GatekeeperConnector.GetHyperlinkRegion(region, region.RegionID); | 231 | GridRegion real_destination = m_GatekeeperConnector.GetHyperlinkRegion(region, region.RegionID, agentID, agentHomeURI, out message); |
221 | if (real_destination != null) | 232 | if (real_destination != null) |
222 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: GetFinalDestination serveruri -> {0}", real_destination.ServerURI); | 233 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: GetFinalDestination: ServerURI={0}", real_destination.ServerURI); |
223 | else | 234 | else |
224 | m_log.WarnFormat("[HG ENTITY TRANSFER MODULE]: GetHyperlinkRegion to Gatekeeper {0} failed", region.ServerURI); | 235 | m_log.WarnFormat("[HG ENTITY TRANSFER MODULE]: GetHyperlinkRegion of region {0} from Gatekeeper {1} failed: {2}", region.RegionID, region.ServerURI, message); |
225 | return real_destination; | 236 | return real_destination; |
226 | } | 237 | } |
227 | 238 | ||
@@ -272,12 +283,21 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
272 | if (agentCircuit.ServiceURLs.ContainsKey("HomeURI")) | 283 | if (agentCircuit.ServiceURLs.ContainsKey("HomeURI")) |
273 | { | 284 | { |
274 | string userAgentDriver = agentCircuit.ServiceURLs["HomeURI"].ToString(); | 285 | string userAgentDriver = agentCircuit.ServiceURLs["HomeURI"].ToString(); |
275 | IUserAgentService connector = new UserAgentServiceConnector(userAgentDriver); | 286 | IUserAgentService connector; |
276 | bool success = connector.LoginAgentToGrid(agentCircuit, reg, finalDestination, out reason); | 287 | |
288 | if (userAgentDriver.Equals(m_ThisHomeURI) && m_UAS != null) | ||
289 | connector = m_UAS; | ||
290 | else | ||
291 | connector = new UserAgentServiceConnector(userAgentDriver); | ||
292 | |||
293 | GridRegion source = new GridRegion(Scene.RegionInfo); | ||
294 | source.RawServerURI = m_GatekeeperURI; | ||
295 | |||
296 | bool success = connector.LoginAgentToGrid(source, agentCircuit, reg, finalDestination, false, out reason); | ||
277 | logout = success; // flag for later logout from this grid; this is an HG TP | 297 | logout = success; // flag for later logout from this grid; this is an HG TP |
278 | 298 | ||
279 | if (success) | 299 | if (success) |
280 | sp.Scene.EventManager.TriggerTeleportStart(sp.ControllingClient, reg, finalDestination, teleportFlags, logout); | 300 | Scene.EventManager.TriggerTeleportStart(sp.ControllingClient, reg, finalDestination, teleportFlags, logout); |
281 | 301 | ||
282 | return success; | 302 | return success; |
283 | } | 303 | } |
@@ -409,7 +429,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
409 | // return base.UpdateAgent(reg, finalDestination, agentData, sp); | 429 | // return base.UpdateAgent(reg, finalDestination, agentData, sp); |
410 | //} | 430 | //} |
411 | 431 | ||
412 | public override void TeleportHome(UUID id, IClientAPI client) | 432 | public override void TriggerTeleportHome(UUID id, IClientAPI client) |
433 | { | ||
434 | TeleportHome(id, client); | ||
435 | } | ||
436 | |||
437 | public override bool TeleportHome(UUID id, IClientAPI client) | ||
413 | { | 438 | { |
414 | m_log.DebugFormat( | 439 | m_log.DebugFormat( |
415 | "[ENTITY TRANSFER MODULE]: Request to teleport {0} {1} home", client.Name, client.AgentId); | 440 | "[ENTITY TRANSFER MODULE]: Request to teleport {0} {1} home", client.Name, client.AgentId); |
@@ -420,8 +445,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
420 | { | 445 | { |
421 | // local grid user | 446 | // local grid user |
422 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: User is local"); | 447 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: User is local"); |
423 | base.TeleportHome(id, client); | 448 | return base.TeleportHome(id, client); |
424 | return; | ||
425 | } | 449 | } |
426 | 450 | ||
427 | // Foreign user wants to go home | 451 | // Foreign user wants to go home |
@@ -431,17 +455,27 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
431 | { | 455 | { |
432 | client.SendTeleportFailed("Your information has been lost"); | 456 | client.SendTeleportFailed("Your information has been lost"); |
433 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Unable to locate agent's gateway information"); | 457 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Unable to locate agent's gateway information"); |
434 | return; | 458 | return false; |
435 | } | 459 | } |
436 | 460 | ||
437 | IUserAgentService userAgentService = new UserAgentServiceConnector(aCircuit.ServiceURLs["HomeURI"].ToString()); | 461 | IUserAgentService userAgentService = new UserAgentServiceConnector(aCircuit.ServiceURLs["HomeURI"].ToString()); |
438 | Vector3 position = Vector3.UnitY, lookAt = Vector3.UnitY; | 462 | Vector3 position = Vector3.UnitY, lookAt = Vector3.UnitY; |
439 | GridRegion finalDestination = userAgentService.GetHomeRegion(aCircuit.AgentID, out position, out lookAt); | 463 | |
464 | GridRegion finalDestination = null; | ||
465 | try | ||
466 | { | ||
467 | finalDestination = userAgentService.GetHomeRegion(aCircuit.AgentID, out position, out lookAt); | ||
468 | } | ||
469 | catch (Exception e) | ||
470 | { | ||
471 | m_log.Debug("[HG ENTITY TRANSFER MODULE]: GetHomeRegion call failed ", e); | ||
472 | } | ||
473 | |||
440 | if (finalDestination == null) | 474 | if (finalDestination == null) |
441 | { | 475 | { |
442 | client.SendTeleportFailed("Your home region could not be found"); | 476 | client.SendTeleportFailed("Your home region could not be found"); |
443 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Agent's home region not found"); | 477 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Agent's home region not found"); |
444 | return; | 478 | return false; |
445 | } | 479 | } |
446 | 480 | ||
447 | ScenePresence sp = ((Scene)(client.Scene)).GetScenePresence(client.AgentId); | 481 | ScenePresence sp = ((Scene)(client.Scene)).GetScenePresence(client.AgentId); |
@@ -449,7 +483,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
449 | { | 483 | { |
450 | client.SendTeleportFailed("Internal error"); | 484 | client.SendTeleportFailed("Internal error"); |
451 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Agent not found in the scene where it is supposed to be"); | 485 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Agent not found in the scene where it is supposed to be"); |
452 | return; | 486 | return false; |
453 | } | 487 | } |
454 | 488 | ||
455 | GridRegion homeGatekeeper = MakeRegion(aCircuit); | 489 | GridRegion homeGatekeeper = MakeRegion(aCircuit); |
@@ -460,6 +494,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
460 | DoTeleport( | 494 | DoTeleport( |
461 | sp, homeGatekeeper, finalDestination, | 495 | sp, homeGatekeeper, finalDestination, |
462 | position, lookAt, (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaHome)); | 496 | position, lookAt, (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaHome)); |
497 | return true; | ||
463 | } | 498 | } |
464 | 499 | ||
465 | /// <summary> | 500 | /// <summary> |
@@ -484,35 +519,174 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
484 | // Local region? | 519 | // Local region? |
485 | if (info != null) | 520 | if (info != null) |
486 | { | 521 | { |
487 | ((Scene)(remoteClient.Scene)).RequestTeleportLocation(remoteClient, info.RegionHandle, lm.Position, | 522 | Scene.RequestTeleportLocation( |
523 | remoteClient, info.RegionHandle, lm.Position, | ||
488 | Vector3.Zero, (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaLandmark)); | 524 | Vector3.Zero, (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaLandmark)); |
489 | |||
490 | return; | ||
491 | } | 525 | } |
492 | else | 526 | else |
493 | { | 527 | { |
494 | // Foreign region | 528 | // Foreign region |
495 | Scene scene = (Scene)(remoteClient.Scene); | ||
496 | GatekeeperServiceConnector gConn = new GatekeeperServiceConnector(); | 529 | GatekeeperServiceConnector gConn = new GatekeeperServiceConnector(); |
497 | GridRegion gatekeeper = new GridRegion(); | 530 | GridRegion gatekeeper = new GridRegion(); |
498 | gatekeeper.ServerURI = lm.Gatekeeper; | 531 | gatekeeper.ServerURI = lm.Gatekeeper; |
499 | GridRegion finalDestination = gConn.GetHyperlinkRegion(gatekeeper, new UUID(lm.RegionID)); | 532 | string homeURI = Scene.GetAgentHomeURI(remoteClient.AgentId); |
533 | |||
534 | string message; | ||
535 | GridRegion finalDestination = gConn.GetHyperlinkRegion(gatekeeper, new UUID(lm.RegionID), remoteClient.AgentId, homeURI, out message); | ||
500 | 536 | ||
501 | if (finalDestination != null) | 537 | if (finalDestination != null) |
502 | { | 538 | { |
503 | ScenePresence sp = scene.GetScenePresence(remoteClient.AgentId); | 539 | ScenePresence sp = Scene.GetScenePresence(remoteClient.AgentId); |
504 | IEntityTransferModule transferMod = scene.RequestModuleInterface<IEntityTransferModule>(); | ||
505 | 540 | ||
506 | if (transferMod != null && sp != null) | 541 | if (sp != null) |
507 | transferMod.DoTeleport( | 542 | { |
543 | if (message != null) | ||
544 | sp.ControllingClient.SendAgentAlertMessage(message, true); | ||
545 | |||
546 | // Validate assorted conditions | ||
547 | string reason = string.Empty; | ||
548 | if (!ValidateGenericConditions(sp, gatekeeper, finalDestination, 0, out reason)) | ||
549 | { | ||
550 | sp.ControllingClient.SendTeleportFailed(reason); | ||
551 | return; | ||
552 | } | ||
553 | |||
554 | DoTeleport( | ||
508 | sp, gatekeeper, finalDestination, lm.Position, Vector3.UnitX, | 555 | sp, gatekeeper, finalDestination, lm.Position, Vector3.UnitX, |
509 | (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaLandmark)); | 556 | (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaLandmark)); |
557 | } | ||
558 | } | ||
559 | else | ||
560 | { | ||
561 | remoteClient.SendTeleportFailed(message); | ||
510 | } | 562 | } |
511 | 563 | ||
512 | } | 564 | } |
565 | } | ||
566 | |||
567 | private void RemoveIncomingSceneObjectJobs(string commonIdToRemove) | ||
568 | { | ||
569 | List<JobEngine.Job> jobsToReinsert = new List<JobEngine.Job>(); | ||
570 | int jobsRemoved = 0; | ||
513 | 571 | ||
514 | // can't find the region: Tell viewer and abort | 572 | JobEngine.Job job; |
515 | remoteClient.SendTeleportFailed("The teleport destination could not be found."); | 573 | while ((job = m_incomingSceneObjectEngine.RemoveNextJob()) != null) |
574 | { | ||
575 | if (job.CommonId != commonIdToRemove) | ||
576 | jobsToReinsert.Add(job); | ||
577 | else | ||
578 | jobsRemoved++; | ||
579 | } | ||
580 | |||
581 | m_log.DebugFormat( | ||
582 | "[HG ENTITY TRANSFER]: Removing {0} jobs with common ID {1} and reinserting {2} other jobs", | ||
583 | jobsRemoved, commonIdToRemove, jobsToReinsert.Count); | ||
584 | |||
585 | if (jobsToReinsert.Count > 0) | ||
586 | { | ||
587 | foreach (JobEngine.Job jobToReinsert in jobsToReinsert) | ||
588 | m_incomingSceneObjectEngine.QueueJob(jobToReinsert); | ||
589 | } | ||
590 | } | ||
591 | |||
592 | public override bool HandleIncomingSceneObject(SceneObjectGroup so, Vector3 newPosition) | ||
593 | { | ||
594 | // FIXME: We must make it so that we can use SOG.IsAttachment here. At the moment it is always null! | ||
595 | if (!so.IsAttachmentCheckFull()) | ||
596 | return base.HandleIncomingSceneObject(so, newPosition); | ||
597 | |||
598 | // Equally, we can't use so.AttachedAvatar here. | ||
599 | if (so.OwnerID == UUID.Zero || Scene.UserManagementModule.IsLocalGridUser(so.OwnerID)) | ||
600 | return base.HandleIncomingSceneObject(so, newPosition); | ||
601 | |||
602 | // foreign user | ||
603 | AgentCircuitData aCircuit = Scene.AuthenticateHandler.GetAgentCircuitData(so.OwnerID); | ||
604 | if (aCircuit != null) | ||
605 | { | ||
606 | if ((aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) == 0) | ||
607 | { | ||
608 | // We have already pulled the necessary attachments from the source grid. | ||
609 | base.HandleIncomingSceneObject(so, newPosition); | ||
610 | } | ||
611 | else | ||
612 | { | ||
613 | if (aCircuit.ServiceURLs != null && aCircuit.ServiceURLs.ContainsKey("AssetServerURI")) | ||
614 | { | ||
615 | m_incomingSceneObjectEngine.QueueJob( | ||
616 | string.Format("HG UUID Gather for attachment {0} for {1}", so.Name, aCircuit.Name), | ||
617 | () => | ||
618 | { | ||
619 | string url = aCircuit.ServiceURLs["AssetServerURI"].ToString(); | ||
620 | // m_log.DebugFormat( | ||
621 | // "[HG ENTITY TRANSFER MODULE]: Incoming attachment {0} for HG user {1} with asset service {2}", | ||
622 | // so.Name, so.AttachedAvatar, url); | ||
623 | |||
624 | IDictionary<UUID, sbyte> ids = new Dictionary<UUID, sbyte>(); | ||
625 | HGUuidGatherer uuidGatherer | ||
626 | = new HGUuidGatherer(Scene.AssetService, url, ids); | ||
627 | uuidGatherer.AddForInspection(so); | ||
628 | |||
629 | while (!uuidGatherer.Complete) | ||
630 | { | ||
631 | int tickStart = Util.EnvironmentTickCount(); | ||
632 | |||
633 | UUID? nextUuid = uuidGatherer.NextUuidToInspect; | ||
634 | uuidGatherer.GatherNext(); | ||
635 | |||
636 | // m_log.DebugFormat( | ||
637 | // "[HG ENTITY TRANSFER]: Gathered attachment asset uuid {0} for object {1} for HG user {2} took {3} ms with asset service {4}", | ||
638 | // nextUuid, so.Name, so.OwnerID, Util.EnvironmentTickCountSubtract(tickStart), url); | ||
639 | |||
640 | int ticksElapsed = Util.EnvironmentTickCountSubtract(tickStart); | ||
641 | |||
642 | if (ticksElapsed > 30000) | ||
643 | { | ||
644 | m_log.WarnFormat( | ||
645 | "[HG ENTITY TRANSFER]: Removing incoming scene object jobs for HG user {0} as gather of {1} from {2} took {3} ms to respond (> {4} ms)", | ||
646 | so.OwnerID, so.Name, url, ticksElapsed, 30000); | ||
647 | |||
648 | RemoveIncomingSceneObjectJobs(so.OwnerID.ToString()); | ||
649 | |||
650 | return; | ||
651 | } | ||
652 | } | ||
653 | |||
654 | // m_log.DebugFormat( | ||
655 | // "[HG ENTITY TRANSFER]: Fetching {0} assets for attachment {1} for HG user {2} with asset service {3}", | ||
656 | // ids.Count, so.Name, so.OwnerID, url); | ||
657 | |||
658 | foreach (KeyValuePair<UUID, sbyte> kvp in ids) | ||
659 | { | ||
660 | int tickStart = Util.EnvironmentTickCount(); | ||
661 | |||
662 | uuidGatherer.FetchAsset(kvp.Key); | ||
663 | |||
664 | int ticksElapsed = Util.EnvironmentTickCountSubtract(tickStart); | ||
665 | |||
666 | if (ticksElapsed > 30000) | ||
667 | { | ||
668 | m_log.WarnFormat( | ||
669 | "[HG ENTITY TRANSFER]: Removing incoming scene object jobs for HG user {0} as fetch of {1} from {2} took {3} ms to respond (> {4} ms)", | ||
670 | so.OwnerID, kvp.Key, url, ticksElapsed, 30000); | ||
671 | |||
672 | RemoveIncomingSceneObjectJobs(so.OwnerID.ToString()); | ||
673 | |||
674 | return; | ||
675 | } | ||
676 | } | ||
677 | |||
678 | base.HandleIncomingSceneObject(so, newPosition); | ||
679 | |||
680 | // m_log.DebugFormat( | ||
681 | // "[HG ENTITY TRANSFER MODULE]: Completed incoming attachment {0} for HG user {1} with asset server {2}", | ||
682 | // so.Name, so.OwnerID, url); | ||
683 | }, | ||
684 | so.OwnerID.ToString()); | ||
685 | } | ||
686 | } | ||
687 | } | ||
688 | |||
689 | return true; | ||
516 | } | 690 | } |
517 | 691 | ||
518 | #endregion | 692 | #endregion |
@@ -549,12 +723,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
549 | if (uMan != null && uMan.IsLocalGridUser(obj.AgentId)) | 723 | if (uMan != null && uMan.IsLocalGridUser(obj.AgentId)) |
550 | { | 724 | { |
551 | // local grid user | 725 | // local grid user |
726 | m_UAS.LogoutAgent(obj.AgentId, obj.SessionId); | ||
552 | return; | 727 | return; |
553 | } | 728 | } |
554 | 729 | ||
555 | AgentCircuitData aCircuit = ((Scene)(obj.Scene)).AuthenticateHandler.GetAgentCircuitData(obj.CircuitCode); | 730 | AgentCircuitData aCircuit = ((Scene)(obj.Scene)).AuthenticateHandler.GetAgentCircuitData(obj.CircuitCode); |
556 | 731 | if (aCircuit != null && aCircuit.ServiceURLs != null && aCircuit.ServiceURLs.ContainsKey("HomeURI")) | |
557 | if (aCircuit.ServiceURLs.ContainsKey("HomeURI")) | ||
558 | { | 732 | { |
559 | string url = aCircuit.ServiceURLs["HomeURI"].ToString(); | 733 | string url = aCircuit.ServiceURLs["HomeURI"].ToString(); |
560 | IUserAgentService security = new UserAgentServiceConnector(url); | 734 | IUserAgentService security = new UserAgentServiceConnector(url); |