diff options
author | Justin Clark-Casey (justincc) | 2012-05-28 23:06:00 +0100 |
---|---|---|
committer | Justin Clark-Casey (justincc) | 2012-05-28 23:06:00 +0100 |
commit | 79f3ce2e9f3563165ee640f917f944ec4b370ac7 (patch) | |
tree | bab16f99077a5145ca87d16e753d973b14527361 | |
parent | If handling the failure of teleport, move agent state to CleaningUp when we s... (diff) | |
download | opensim-SC_OLD-79f3ce2e9f3563165ee640f917f944ec4b370ac7.zip opensim-SC_OLD-79f3ce2e9f3563165ee640f917f944ec4b370ac7.tar.gz opensim-SC_OLD-79f3ce2e9f3563165ee640f917f944ec4b370ac7.tar.bz2 opensim-SC_OLD-79f3ce2e9f3563165ee640f917f944ec4b370ac7.tar.xz |
refactor: factor out entity transfer state machine into a separate class to make code more analyzable
3 files changed, 330 insertions, 239 deletions
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index ab1424d..f2926ea 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs | |||
@@ -46,35 +46,12 @@ using Nini.Config; | |||
46 | 46 | ||
47 | namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | 47 | namespace OpenSim.Region.CoreModules.Framework.EntityTransfer |
48 | { | 48 | { |
49 | /// <summary> | ||
50 | /// The possible states that an agent can be in when its being transferred between regions. | ||
51 | /// </summary> | ||
52 | /// <remarks> | ||
53 | /// This is a state machine. | ||
54 | /// | ||
55 | /// [Entry] => Preparing | ||
56 | /// Preparing => { Transferring || CleaningUp || [Exit] } | ||
57 | /// Transferring => { ReceivedAtDestination || CleaningUp } | ||
58 | /// ReceivedAtDestination => CleaningUp | ||
59 | /// CleaningUp => [Exit] | ||
60 | /// | ||
61 | /// In other words, agents normally travel throwing Preparing => Transferring => ReceivedAtDestination => CleaningUp | ||
62 | /// However, any state can transition to CleaningUp if the teleport has failed. | ||
63 | /// </remarks> | ||
64 | enum AgentTransferState | ||
65 | { | ||
66 | Preparing, // The agent is being prepared for transfer | ||
67 | Transferring, // The agent is in the process of being transferred to a destination | ||
68 | ReceivedAtDestination, // The destination has notified us that the agent has been successfully received | ||
69 | CleaningUp // The agent is being changed to child/removed after a transfer | ||
70 | } | ||
71 | |||
72 | public class EntityTransferModule : INonSharedRegionModule, IEntityTransferModule | 49 | public class EntityTransferModule : INonSharedRegionModule, IEntityTransferModule |
73 | { | 50 | { |
74 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 51 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
75 | 52 | ||
76 | public const int DefaultMaxTransferDistance = 4095; | 53 | public const int DefaultMaxTransferDistance = 4095; |
77 | public const bool EnableWaitForCallbackFromTeleportDestDefault = true; | 54 | public const bool WaitForAgentArrivedAtDestinationDefault = true; |
78 | 55 | ||
79 | /// <summary> | 56 | /// <summary> |
80 | /// The maximum distance, in standard region units (256m) that an agent is allowed to transfer. | 57 | /// The maximum distance, in standard region units (256m) that an agent is allowed to transfer. |
@@ -85,13 +62,17 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
85 | /// If true then on a teleport, the source region waits for a callback from the destination region. If | 62 | /// If true then on a teleport, the source region waits for a callback from the destination region. If |
86 | /// a callback fails to arrive within a set time then the user is pulled back into the source region. | 63 | /// a callback fails to arrive within a set time then the user is pulled back into the source region. |
87 | /// </summary> | 64 | /// </summary> |
88 | public bool EnableWaitForCallbackFromTeleportDest { get; set; } | 65 | public bool WaitForAgentArrivedAtDestination { get; set; } |
89 | 66 | ||
90 | protected bool m_Enabled = false; | 67 | protected bool m_Enabled = false; |
91 | 68 | ||
92 | protected Scene m_scene; | 69 | public Scene Scene { get; private set; } |
93 | 70 | ||
94 | private Dictionary<UUID, AgentTransferState> m_agentsInTransit; | 71 | /// <summary> |
72 | /// Handles recording and manipulation of state for entities that are in transfer within or between regions | ||
73 | /// (cross or teleport). | ||
74 | /// </summary> | ||
75 | private EntityTransferStateMachine m_entityTransferStateMachine; | ||
95 | 76 | ||
96 | private ExpiringCache<UUID, ExpiringCache<ulong, DateTime>> m_bannedRegions = | 77 | private ExpiringCache<UUID, ExpiringCache<ulong, DateTime>> m_bannedRegions = |
97 | new ExpiringCache<UUID, ExpiringCache<ulong, DateTime>>(); | 78 | new ExpiringCache<UUID, ExpiringCache<ulong, DateTime>>(); |
@@ -133,8 +114,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
133 | IConfig transferConfig = source.Configs["EntityTransfer"]; | 114 | IConfig transferConfig = source.Configs["EntityTransfer"]; |
134 | if (transferConfig != null) | 115 | if (transferConfig != null) |
135 | { | 116 | { |
136 | EnableWaitForCallbackFromTeleportDest | 117 | WaitForAgentArrivedAtDestination |
137 | = transferConfig.GetBoolean("wait_for_callback", EnableWaitForCallbackFromTeleportDestDefault); | 118 | = transferConfig.GetBoolean("wait_for_callback", WaitForAgentArrivedAtDestinationDefault); |
138 | 119 | ||
139 | MaxTransferDistance = transferConfig.GetInt("max_distance", DefaultMaxTransferDistance); | 120 | MaxTransferDistance = transferConfig.GetInt("max_distance", DefaultMaxTransferDistance); |
140 | } | 121 | } |
@@ -143,7 +124,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
143 | MaxTransferDistance = DefaultMaxTransferDistance; | 124 | MaxTransferDistance = DefaultMaxTransferDistance; |
144 | } | 125 | } |
145 | 126 | ||
146 | m_agentsInTransit = new Dictionary<UUID, AgentTransferState>(); | 127 | m_entityTransferStateMachine = new EntityTransferStateMachine(this); |
128 | |||
147 | m_Enabled = true; | 129 | m_Enabled = true; |
148 | } | 130 | } |
149 | 131 | ||
@@ -156,7 +138,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
156 | if (!m_Enabled) | 138 | if (!m_Enabled) |
157 | return; | 139 | return; |
158 | 140 | ||
159 | m_scene = scene; | 141 | Scene = scene; |
160 | 142 | ||
161 | scene.RegisterModuleInterface<IEntityTransferModule>(this); | 143 | scene.RegisterModuleInterface<IEntityTransferModule>(this); |
162 | scene.EventManager.OnNewClient += OnNewClient; | 144 | scene.EventManager.OnNewClient += OnNewClient; |
@@ -177,7 +159,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
177 | if (!m_Enabled) | 159 | if (!m_Enabled) |
178 | return; | 160 | return; |
179 | 161 | ||
180 | m_eqModule = m_scene.RequestModuleInterface<IEventQueue>(); | 162 | m_eqModule = Scene.RequestModuleInterface<IEventQueue>(); |
181 | } | 163 | } |
182 | 164 | ||
183 | #endregion | 165 | #endregion |
@@ -226,7 +208,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
226 | e.Message, e.StackTrace); | 208 | e.Message, e.StackTrace); |
227 | 209 | ||
228 | // Make sure that we clear the in-transit flag so that future teleport attempts don't always fail. | 210 | // Make sure that we clear the in-transit flag so that future teleport attempts don't always fail. |
229 | ResetFromTransit(sp.UUID); | 211 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); |
230 | 212 | ||
231 | sp.ControllingClient.SendTeleportFailed("Internal error"); | 213 | sp.ControllingClient.SendTeleportFailed("Internal error"); |
232 | } | 214 | } |
@@ -245,7 +227,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
245 | "[ENTITY TRANSFER MODULE]: Teleport for {0} to {1} within {2}", | 227 | "[ENTITY TRANSFER MODULE]: Teleport for {0} to {1} within {2}", |
246 | sp.Name, position, sp.Scene.RegionInfo.RegionName); | 228 | sp.Name, position, sp.Scene.RegionInfo.RegionName); |
247 | 229 | ||
248 | if (!SetInTransit(sp.UUID)) | 230 | if (!m_entityTransferStateMachine.SetInTransit(sp.UUID)) |
249 | { | 231 | { |
250 | m_log.DebugFormat( | 232 | m_log.DebugFormat( |
251 | "[ENTITY TRANSFER MODULE]: Ignoring within region teleport request of {0} {1} to {2} - agent is already in transit.", | 233 | "[ENTITY TRANSFER MODULE]: Ignoring within region teleport request of {0} {1} to {2} - agent is already in transit.", |
@@ -282,7 +264,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
282 | position.Z = newPosZ; | 264 | position.Z = newPosZ; |
283 | } | 265 | } |
284 | 266 | ||
285 | UpdateInTransit(sp.UUID, AgentTransferState.Transferring); | 267 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring); |
286 | 268 | ||
287 | sp.ControllingClient.SendTeleportStart(teleportFlags); | 269 | sp.ControllingClient.SendTeleportStart(teleportFlags); |
288 | 270 | ||
@@ -290,15 +272,15 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
290 | sp.Velocity = Vector3.Zero; | 272 | sp.Velocity = Vector3.Zero; |
291 | sp.Teleport(position); | 273 | sp.Teleport(position); |
292 | 274 | ||
293 | UpdateInTransit(sp.UUID, AgentTransferState.ReceivedAtDestination); | 275 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.ReceivedAtDestination); |
294 | 276 | ||
295 | foreach (SceneObjectGroup grp in sp.GetAttachments()) | 277 | foreach (SceneObjectGroup grp in sp.GetAttachments()) |
296 | { | 278 | { |
297 | sp.Scene.EventManager.TriggerOnScriptChangedEvent(grp.LocalId, (uint)Changed.TELEPORT); | 279 | sp.Scene.EventManager.TriggerOnScriptChangedEvent(grp.LocalId, (uint)Changed.TELEPORT); |
298 | } | 280 | } |
299 | 281 | ||
300 | UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); | 282 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); |
301 | ResetFromTransit(sp.UUID); | 283 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); |
302 | } | 284 | } |
303 | 285 | ||
304 | /// <summary> | 286 | /// <summary> |
@@ -316,7 +298,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
316 | { | 298 | { |
317 | uint x = 0, y = 0; | 299 | uint x = 0, y = 0; |
318 | Utils.LongToUInts(regionHandle, out x, out y); | 300 | Utils.LongToUInts(regionHandle, out x, out y); |
319 | GridRegion reg = m_scene.GridService.GetRegionByPosition(sp.Scene.RegionInfo.ScopeID, (int)x, (int)y); | 301 | GridRegion reg = Scene.GridService.GetRegionByPosition(sp.Scene.RegionInfo.ScopeID, (int)x, (int)y); |
320 | 302 | ||
321 | if (reg != null) | 303 | if (reg != null) |
322 | { | 304 | { |
@@ -398,7 +380,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
398 | { | 380 | { |
399 | // Record that this agent is in transit so that we can prevent simultaneous requests and do later detection | 381 | // Record that this agent is in transit so that we can prevent simultaneous requests and do later detection |
400 | // of whether the destination region completes the teleport. | 382 | // of whether the destination region completes the teleport. |
401 | if (!SetInTransit(sp.UUID)) | 383 | if (!m_entityTransferStateMachine.SetInTransit(sp.UUID)) |
402 | { | 384 | { |
403 | m_log.DebugFormat( | 385 | m_log.DebugFormat( |
404 | "[ENTITY TRANSFER MODULE]: Ignoring teleport request of {0} {1} to {2} ({3}) {4}/{5} - agent is already in transit.", | 386 | "[ENTITY TRANSFER MODULE]: Ignoring teleport request of {0} {1} to {2} ({3}) {4}/{5} - agent is already in transit.", |
@@ -410,7 +392,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
410 | if (reg == null || finalDestination == null) | 392 | if (reg == null || finalDestination == null) |
411 | { | 393 | { |
412 | sp.ControllingClient.SendTeleportFailed("Unable to locate destination"); | 394 | sp.ControllingClient.SendTeleportFailed("Unable to locate destination"); |
413 | ResetFromTransit(sp.UUID); | 395 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); |
414 | 396 | ||
415 | return; | 397 | return; |
416 | } | 398 | } |
@@ -431,7 +413,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
431 | sourceRegion.RegionName, sourceRegion.RegionLocX, sourceRegion.RegionLocY, | 413 | sourceRegion.RegionName, sourceRegion.RegionLocX, sourceRegion.RegionLocY, |
432 | MaxTransferDistance)); | 414 | MaxTransferDistance)); |
433 | 415 | ||
434 | ResetFromTransit(sp.UUID); | 416 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); |
435 | 417 | ||
436 | return; | 418 | return; |
437 | } | 419 | } |
@@ -451,7 +433,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
451 | if (endPoint.Address == null) | 433 | if (endPoint.Address == null) |
452 | { | 434 | { |
453 | sp.ControllingClient.SendTeleportFailed("Remote Region appears to be down"); | 435 | sp.ControllingClient.SendTeleportFailed("Remote Region appears to be down"); |
454 | ResetFromTransit(sp.UUID); | 436 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); |
455 | 437 | ||
456 | return; | 438 | return; |
457 | } | 439 | } |
@@ -469,11 +451,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
469 | 451 | ||
470 | string reason; | 452 | string reason; |
471 | string version; | 453 | string version; |
472 | if (!m_scene.SimulationService.QueryAccess( | 454 | if (!Scene.SimulationService.QueryAccess( |
473 | finalDestination, sp.ControllingClient.AgentId, Vector3.Zero, out version, out reason)) | 455 | finalDestination, sp.ControllingClient.AgentId, Vector3.Zero, out version, out reason)) |
474 | { | 456 | { |
475 | sp.ControllingClient.SendTeleportFailed(reason); | 457 | sp.ControllingClient.SendTeleportFailed(reason); |
476 | ResetFromTransit(sp.UUID); | 458 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); |
477 | 459 | ||
478 | m_log.DebugFormat( | 460 | m_log.DebugFormat( |
479 | "[ENTITY TRANSFER MODULE]: {0} was stopped from teleporting from {1} to {2} because {3}", | 461 | "[ENTITY TRANSFER MODULE]: {0} was stopped from teleporting from {1} to {2} because {3}", |
@@ -527,7 +509,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
527 | if (!CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout)) | 509 | if (!CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout)) |
528 | { | 510 | { |
529 | sp.ControllingClient.SendTeleportFailed(String.Format("Teleport refused: {0}", reason)); | 511 | sp.ControllingClient.SendTeleportFailed(String.Format("Teleport refused: {0}", reason)); |
530 | ResetFromTransit(sp.UUID); | 512 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); |
531 | 513 | ||
532 | m_log.DebugFormat( | 514 | m_log.DebugFormat( |
533 | "[ENTITY TRANSFER MODULE]: Teleport of {0} from {1} to {2} was refused because {3}", | 515 | "[ENTITY TRANSFER MODULE]: Teleport of {0} from {1} to {2} was refused because {3}", |
@@ -537,7 +519,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
537 | } | 519 | } |
538 | 520 | ||
539 | // Past this point we have to attempt clean up if the teleport fails, so update transfer state. | 521 | // Past this point we have to attempt clean up if the teleport fails, so update transfer state. |
540 | UpdateInTransit(sp.UUID, AgentTransferState.Transferring); | 522 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring); |
541 | 523 | ||
542 | // OK, it got this agent. Let's close some child agents | 524 | // OK, it got this agent. Let's close some child agents |
543 | sp.CloseChildAgents(newRegionX, newRegionY); | 525 | sp.CloseChildAgents(newRegionX, newRegionY); |
@@ -619,7 +601,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
619 | // TeleportFinish makes the client send CompleteMovementIntoRegion (at the destination), which | 601 | // TeleportFinish makes the client send CompleteMovementIntoRegion (at the destination), which |
620 | // trigers a whole shebang of things there, including MakeRoot. So let's wait for confirmation | 602 | // trigers a whole shebang of things there, including MakeRoot. So let's wait for confirmation |
621 | // that the client contacted the destination before we close things here. | 603 | // that the client contacted the destination before we close things here. |
622 | if (EnableWaitForCallbackFromTeleportDest && !WaitForCallback(sp.UUID)) | 604 | if (!m_entityTransferStateMachine.WaitForAgentArrivedAtDestination(sp.UUID)) |
623 | { | 605 | { |
624 | m_log.WarnFormat( | 606 | m_log.WarnFormat( |
625 | "[ENTITY TRANSFER MODULE]: Teleport of {0} to {1} from {2} failed due to no callback from destination region. Returning avatar to source region.", | 607 | "[ENTITY TRANSFER MODULE]: Teleport of {0} to {1} from {2} failed due to no callback from destination region. Returning avatar to source region.", |
@@ -629,7 +611,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
629 | return; | 611 | return; |
630 | } | 612 | } |
631 | 613 | ||
632 | UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); | 614 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); |
633 | 615 | ||
634 | // For backwards compatibility | 616 | // For backwards compatibility |
635 | if (version == "Unknown" || version == string.Empty) | 617 | if (version == "Unknown" || version == string.Empty) |
@@ -679,12 +661,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
679 | sp.UUID); | 661 | sp.UUID); |
680 | } | 662 | } |
681 | 663 | ||
682 | ResetFromTransit(sp.UUID); | 664 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); |
683 | } | 665 | } |
684 | 666 | ||
685 | protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout) | 667 | protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout) |
686 | { | 668 | { |
687 | UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); | 669 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); |
688 | 670 | ||
689 | // Client never contacted destination. Let's restore everything back | 671 | // Client never contacted destination. Let's restore everything back |
690 | sp.ControllingClient.SendTeleportFailed("Problems connecting to destination."); | 672 | sp.ControllingClient.SendTeleportFailed("Problems connecting to destination."); |
@@ -696,17 +678,17 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
696 | EnableChildAgents(sp); | 678 | EnableChildAgents(sp); |
697 | 679 | ||
698 | // Finally, kill the agent we just created at the destination. | 680 | // Finally, kill the agent we just created at the destination. |
699 | m_scene.SimulationService.CloseAgent(finalDestination, sp.UUID); | 681 | Scene.SimulationService.CloseAgent(finalDestination, sp.UUID); |
700 | 682 | ||
701 | sp.Scene.EventManager.TriggerTeleportFail(sp.ControllingClient, logout); | 683 | sp.Scene.EventManager.TriggerTeleportFail(sp.ControllingClient, logout); |
702 | 684 | ||
703 | ResetFromTransit(sp.UUID); | 685 | m_entityTransferStateMachine.ResetFromTransit(sp.UUID); |
704 | } | 686 | } |
705 | 687 | ||
706 | protected virtual bool CreateAgent(ScenePresence sp, GridRegion reg, GridRegion finalDestination, AgentCircuitData agentCircuit, uint teleportFlags, out string reason, out bool logout) | 688 | protected virtual bool CreateAgent(ScenePresence sp, GridRegion reg, GridRegion finalDestination, AgentCircuitData agentCircuit, uint teleportFlags, out string reason, out bool logout) |
707 | { | 689 | { |
708 | logout = false; | 690 | logout = false; |
709 | bool success = m_scene.SimulationService.CreateAgent(finalDestination, agentCircuit, teleportFlags, out reason); | 691 | bool success = Scene.SimulationService.CreateAgent(finalDestination, agentCircuit, teleportFlags, out reason); |
710 | 692 | ||
711 | if (success) | 693 | if (success) |
712 | sp.Scene.EventManager.TriggerTeleportStart(sp.ControllingClient, reg, finalDestination, teleportFlags, logout); | 694 | sp.Scene.EventManager.TriggerTeleportStart(sp.ControllingClient, reg, finalDestination, teleportFlags, logout); |
@@ -716,7 +698,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
716 | 698 | ||
717 | protected virtual bool UpdateAgent(GridRegion reg, GridRegion finalDestination, AgentData agent) | 699 | protected virtual bool UpdateAgent(GridRegion reg, GridRegion finalDestination, AgentData agent) |
718 | { | 700 | { |
719 | return m_scene.SimulationService.UpdateAgent(finalDestination, agent); | 701 | return Scene.SimulationService.UpdateAgent(finalDestination, agent); |
720 | } | 702 | } |
721 | 703 | ||
722 | protected virtual void SetCallbackURL(AgentData agent, RegionInfo region) | 704 | protected virtual void SetCallbackURL(AgentData agent, RegionInfo region) |
@@ -779,7 +761,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
779 | /// <param name="position"></param> | 761 | /// <param name="position"></param> |
780 | public virtual void RequestTeleportLandmark(IClientAPI remoteClient, AssetLandmark lm) | 762 | public virtual void RequestTeleportLandmark(IClientAPI remoteClient, AssetLandmark lm) |
781 | { | 763 | { |
782 | GridRegion info = m_scene.GridService.GetRegionByUUID(UUID.Zero, lm.RegionID); | 764 | GridRegion info = Scene.GridService.GetRegionByUUID(UUID.Zero, lm.RegionID); |
783 | 765 | ||
784 | if (info == null) | 766 | if (info == null) |
785 | { | 767 | { |
@@ -800,12 +782,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
800 | m_log.DebugFormat( | 782 | m_log.DebugFormat( |
801 | "[ENTITY TRANSFER MODULE]: Request to teleport {0} {1} home", client.Name, client.AgentId); | 783 | "[ENTITY TRANSFER MODULE]: Request to teleport {0} {1} home", client.Name, client.AgentId); |
802 | 784 | ||
803 | //OpenSim.Services.Interfaces.PresenceInfo pinfo = m_scene.PresenceService.GetAgent(client.SessionId); | 785 | //OpenSim.Services.Interfaces.PresenceInfo pinfo = Scene.PresenceService.GetAgent(client.SessionId); |
804 | GridUserInfo uinfo = m_scene.GridUserService.GetGridUserInfo(client.AgentId.ToString()); | 786 | GridUserInfo uinfo = Scene.GridUserService.GetGridUserInfo(client.AgentId.ToString()); |
805 | 787 | ||
806 | if (uinfo != null) | 788 | if (uinfo != null) |
807 | { | 789 | { |
808 | GridRegion regionInfo = m_scene.GridService.GetRegionByUUID(UUID.Zero, uinfo.HomeRegionID); | 790 | GridRegion regionInfo = Scene.GridService.GetRegionByUUID(UUID.Zero, uinfo.HomeRegionID); |
809 | if (regionInfo == null) | 791 | if (regionInfo == null) |
810 | { | 792 | { |
811 | // can't find the Home region: Tell viewer and abort | 793 | // can't find the Home region: Tell viewer and abort |
@@ -1114,7 +1096,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1114 | 1096 | ||
1115 | try | 1097 | try |
1116 | { | 1098 | { |
1117 | SetInTransit(agent.UUID); | 1099 | m_entityTransferStateMachine.SetInTransit(agent.UUID); |
1118 | 1100 | ||
1119 | ulong neighbourHandle = Utils.UIntsToLong((uint)(neighbourx * Constants.RegionSize), (uint)(neighboury * Constants.RegionSize)); | 1101 | ulong neighbourHandle = Utils.UIntsToLong((uint)(neighbourx * Constants.RegionSize), (uint)(neighboury * Constants.RegionSize)); |
1120 | 1102 | ||
@@ -1144,16 +1126,16 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1144 | cAgent.CallbackURI = String.Empty; | 1126 | cAgent.CallbackURI = String.Empty; |
1145 | 1127 | ||
1146 | // Beyond this point, extra cleanup is needed beyond removing transit state | 1128 | // Beyond this point, extra cleanup is needed beyond removing transit state |
1147 | UpdateInTransit(agent.UUID, AgentTransferState.Transferring); | 1129 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.Transferring); |
1148 | 1130 | ||
1149 | if (!m_scene.SimulationService.UpdateAgent(neighbourRegion, cAgent)) | 1131 | if (!m_scene.SimulationService.UpdateAgent(neighbourRegion, cAgent)) |
1150 | { | 1132 | { |
1151 | // region doesn't take it | 1133 | // region doesn't take it |
1152 | UpdateInTransit(agent.UUID, AgentTransferState.CleaningUp); | 1134 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.CleaningUp); |
1153 | 1135 | ||
1154 | ReInstantiateScripts(agent); | 1136 | ReInstantiateScripts(agent); |
1155 | agent.AddToPhysicalScene(isFlying); | 1137 | agent.AddToPhysicalScene(isFlying); |
1156 | ResetFromTransit(agent.UUID); | 1138 | m_entityTransferStateMachine.ResetFromTransit(agent.UUID); |
1157 | 1139 | ||
1158 | return agent; | 1140 | return agent; |
1159 | } | 1141 | } |
@@ -1191,16 +1173,16 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1191 | } | 1173 | } |
1192 | 1174 | ||
1193 | // SUCCESS! | 1175 | // SUCCESS! |
1194 | UpdateInTransit(agent.UUID, AgentTransferState.ReceivedAtDestination); | 1176 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.ReceivedAtDestination); |
1195 | 1177 | ||
1196 | // Unlike a teleport, here we do not wait for the destination region to confirm the receipt. | 1178 | // Unlike a teleport, here we do not wait for the destination region to confirm the receipt. |
1197 | UpdateInTransit(agent.UUID, AgentTransferState.CleaningUp); | 1179 | m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.CleaningUp); |
1198 | 1180 | ||
1199 | agent.MakeChildAgent(); | 1181 | agent.MakeChildAgent(); |
1200 | 1182 | ||
1201 | // FIXME: Possibly this should occur lower down after other commands to close other agents, | 1183 | // FIXME: Possibly this should occur lower down after other commands to close other agents, |
1202 | // but not sure yet what the side effects would be. | 1184 | // but not sure yet what the side effects would be. |
1203 | ResetFromTransit(agent.UUID); | 1185 | m_entityTransferStateMachine.ResetFromTransit(agent.UUID); |
1204 | 1186 | ||
1205 | // now we have a child agent in this region. Request all interesting data about other (root) agents | 1187 | // now we have a child agent in this region. Request all interesting data about other (root) agents |
1206 | agent.SendOtherAgentsAvatarDataToMe(); | 1188 | agent.SendOtherAgentsAvatarDataToMe(); |
@@ -1675,41 +1657,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1675 | 1657 | ||
1676 | #endregion | 1658 | #endregion |
1677 | 1659 | ||
1678 | |||
1679 | #region Agent Arrived | 1660 | #region Agent Arrived |
1661 | |||
1680 | public void AgentArrivedAtDestination(UUID id) | 1662 | public void AgentArrivedAtDestination(UUID id) |
1681 | { | 1663 | { |
1682 | lock (m_agentsInTransit) | 1664 | m_entityTransferStateMachine.SetAgentArrivedAtDestination(id); |
1683 | { | ||
1684 | if (!m_agentsInTransit.ContainsKey(id)) | ||
1685 | { | ||
1686 | m_log.WarnFormat( | ||
1687 | "[ENTITY TRANSFER MODULE]: Region {0} received notification of arrival in destination scene of agent {1} but no teleport request is active", | ||
1688 | m_scene.RegionInfo.RegionName, id); | ||
1689 | |||
1690 | return; | ||
1691 | } | ||
1692 | |||
1693 | AgentTransferState currentState = m_agentsInTransit[id]; | ||
1694 | |||
1695 | if (currentState == AgentTransferState.ReceivedAtDestination) | ||
1696 | { | ||
1697 | // An anomoly but don't make this an outright failure - destination region could be overzealous in sending notification. | ||
1698 | m_log.WarnFormat( | ||
1699 | "[ENTITY TRANSFER MODULE]: Region {0} received notification of arrival in destination scene of agent {1} but notification has already previously been received", | ||
1700 | m_scene.RegionInfo.RegionName, id); | ||
1701 | } | ||
1702 | else if (currentState != AgentTransferState.Transferring) | ||
1703 | { | ||
1704 | m_log.ErrorFormat( | ||
1705 | "[ENTITY TRANSFER MODULE]: Region {0} received notification of arrival in destination scene of agent {1} but agent is in transfer state {2}", | ||
1706 | m_scene.RegionInfo.RegionName, id, currentState); | ||
1707 | |||
1708 | return; | ||
1709 | } | ||
1710 | |||
1711 | m_agentsInTransit[id] = AgentTransferState.ReceivedAtDestination; | ||
1712 | } | ||
1713 | } | 1665 | } |
1714 | 1666 | ||
1715 | #endregion | 1667 | #endregion |
@@ -1979,8 +1931,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1979 | //// And the new channel... | 1931 | //// And the new channel... |
1980 | //if (m_interregionCommsOut != null) | 1932 | //if (m_interregionCommsOut != null) |
1981 | // successYN = m_interregionCommsOut.SendCreateObject(newRegionHandle, grp, true); | 1933 | // successYN = m_interregionCommsOut.SendCreateObject(newRegionHandle, grp, true); |
1982 | if (m_scene.SimulationService != null) | 1934 | if (Scene.SimulationService != null) |
1983 | successYN = m_scene.SimulationService.CreateObject(destination, newPosition, grp, true); | 1935 | successYN = Scene.SimulationService.CreateObject(destination, newPosition, grp, true); |
1984 | 1936 | ||
1985 | if (successYN) | 1937 | if (successYN) |
1986 | { | 1938 | { |
@@ -2051,139 +2003,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
2051 | 2003 | ||
2052 | #region Misc | 2004 | #region Misc |
2053 | 2005 | ||
2054 | private bool WaitForCallback(UUID id) | ||
2055 | { | ||
2056 | lock (m_agentsInTransit) | ||
2057 | { | ||
2058 | if (!IsInTransit(id)) | ||
2059 | throw new Exception( | ||
2060 | string.Format( | ||
2061 | "Asked to wait for destination callback for agent with ID {0} but it is not in transit")); | ||
2062 | |||
2063 | AgentTransferState currentState = m_agentsInTransit[id]; | ||
2064 | |||
2065 | if (currentState != AgentTransferState.Transferring && currentState != AgentTransferState.ReceivedAtDestination) | ||
2066 | throw new Exception( | ||
2067 | string.Format( | ||
2068 | "Asked to wait for destination callback for agent with ID {0} but it is in state {1}", | ||
2069 | currentState)); | ||
2070 | } | ||
2071 | |||
2072 | int count = 200; | ||
2073 | |||
2074 | // There should be no race condition here since no other code should be removing the agent transfer or | ||
2075 | // changing the state to another other than Transferring => ReceivedAtDestination. | ||
2076 | while (m_agentsInTransit[id] != AgentTransferState.ReceivedAtDestination && count-- > 0) | ||
2077 | { | ||
2078 | // m_log.Debug(" >>> Waiting... " + count); | ||
2079 | Thread.Sleep(100); | ||
2080 | } | ||
2081 | |||
2082 | return count > 0; | ||
2083 | } | ||
2084 | |||
2085 | /// <summary> | ||
2086 | /// Set that an agent is in transit. | ||
2087 | /// </summary> | ||
2088 | /// <param name='id'>The ID of the agent being teleported</param> | ||
2089 | /// <returns>true if the agent was not already in transit, false if it was</returns> | ||
2090 | private bool SetInTransit(UUID id) | ||
2091 | { | ||
2092 | lock (m_agentsInTransit) | ||
2093 | { | ||
2094 | if (!m_agentsInTransit.ContainsKey(id)) | ||
2095 | { | ||
2096 | m_agentsInTransit[id] = AgentTransferState.Preparing; | ||
2097 | return true; | ||
2098 | } | ||
2099 | } | ||
2100 | |||
2101 | return false; | ||
2102 | } | ||
2103 | |||
2104 | /// <summary> | ||
2105 | /// Updates the state of an agent that is already in transit. | ||
2106 | /// </summary> | ||
2107 | /// <param name='id'></param> | ||
2108 | /// <param name='newState'></param> | ||
2109 | /// <returns></returns> | ||
2110 | /// <exception cref='Exception'>Illegal transitions will throw an Exception</exception> | ||
2111 | private void UpdateInTransit(UUID id, AgentTransferState newState) | ||
2112 | { | ||
2113 | lock (m_agentsInTransit) | ||
2114 | { | ||
2115 | // Illegal to try and update an agent that's not actually in transit. | ||
2116 | if (!m_agentsInTransit.ContainsKey(id)) | ||
2117 | throw new Exception(string.Format("Agent with ID {0} is not registered as in transit", id)); | ||
2118 | |||
2119 | AgentTransferState oldState = m_agentsInTransit[id]; | ||
2120 | |||
2121 | bool transitionOkay = false; | ||
2122 | |||
2123 | if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp) | ||
2124 | transitionOkay = true; | ||
2125 | else if (newState == AgentTransferState.Transferring && oldState == AgentTransferState.Preparing) | ||
2126 | transitionOkay = true; | ||
2127 | else if (newState == AgentTransferState.ReceivedAtDestination && oldState == AgentTransferState.Transferring) | ||
2128 | transitionOkay = true; | ||
2129 | |||
2130 | if (transitionOkay) | ||
2131 | m_agentsInTransit[id] = newState; | ||
2132 | else | ||
2133 | throw new Exception( | ||
2134 | string.Format( | ||
2135 | "Agent with ID {0} is not allowed to move from old transit state {1} to new state {2}", | ||
2136 | id, oldState, newState)); | ||
2137 | } | ||
2138 | } | ||
2139 | |||
2140 | public bool IsInTransit(UUID id) | 2006 | public bool IsInTransit(UUID id) |
2141 | { | 2007 | { |
2142 | lock (m_agentsInTransit) | 2008 | return m_entityTransferStateMachine.IsInTransit(id); |
2143 | return m_agentsInTransit.ContainsKey(id); | ||
2144 | } | ||
2145 | |||
2146 | /// <summary> | ||
2147 | /// Removes an agent from the transit state machine. | ||
2148 | /// </summary> | ||
2149 | /// <param name='id'></param> | ||
2150 | /// <returns>true if the agent was flagged as being teleported when this method was called, false otherwise</returns> | ||
2151 | private bool ResetFromTransit(UUID id) | ||
2152 | { | ||
2153 | lock (m_agentsInTransit) | ||
2154 | { | ||
2155 | if (m_agentsInTransit.ContainsKey(id)) | ||
2156 | { | ||
2157 | AgentTransferState state = m_agentsInTransit[id]; | ||
2158 | |||
2159 | if (state == AgentTransferState.Transferring || state == AgentTransferState.ReceivedAtDestination) | ||
2160 | { | ||
2161 | // FIXME: For now, we allow exit from any state since a thrown exception in teleport is now guranteed | ||
2162 | // to be handled properly - ResetFromTransit() could be invoked at any step along the process | ||
2163 | m_log.WarnFormat( | ||
2164 | "[ENTITY TRANSFER MODULE]: Agent with ID {0} should not exit directly from state {1}, should go to {2} state first", | ||
2165 | id, state, AgentTransferState.CleaningUp); | ||
2166 | |||
2167 | // throw new Exception( | ||
2168 | // "Agent with ID {0} cannot exit directly from state {1}, it must go to {2} state first", | ||
2169 | // state, AgentTransferState.CleaningUp); | ||
2170 | } | ||
2171 | |||
2172 | m_agentsInTransit.Remove(id); | ||
2173 | |||
2174 | m_log.DebugFormat( | ||
2175 | "[ENTITY TRANSFER MODULE]: Agent {0} cleared from transit in {1}", | ||
2176 | id, m_scene.RegionInfo.RegionName); | ||
2177 | |||
2178 | return true; | ||
2179 | } | ||
2180 | } | ||
2181 | |||
2182 | m_log.WarnFormat( | ||
2183 | "[ENTITY TRANSFER MODULE]: Agent {0} requested to clear from transit in {1} but was already cleared.", | ||
2184 | id, m_scene.RegionInfo.RegionName); | ||
2185 | |||
2186 | return false; | ||
2187 | } | 2009 | } |
2188 | 2010 | ||
2189 | protected void ReInstantiateScripts(ScenePresence sp) | 2011 | protected void ReInstantiateScripts(ScenePresence sp) |
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs new file mode 100644 index 0000000..d0cab49 --- /dev/null +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs | |||
@@ -0,0 +1,269 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Net; | ||
31 | using System.Reflection; | ||
32 | using System.Threading; | ||
33 | using OpenMetaverse; | ||
34 | using log4net; | ||
35 | using Nini.Config; | ||
36 | using OpenSim.Framework; | ||
37 | using OpenSim.Framework.Capabilities; | ||
38 | using OpenSim.Framework.Client; | ||
39 | using OpenSim.Region.Framework.Interfaces; | ||
40 | using OpenSim.Region.Framework.Scenes; | ||
41 | using OpenSim.Region.Physics.Manager; | ||
42 | using OpenSim.Services.Interfaces; | ||
43 | using GridRegion = OpenSim.Services.Interfaces.GridRegion; | ||
44 | |||
45 | namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | ||
46 | { | ||
47 | /// <summary> | ||
48 | /// The possible states that an agent can be in when its being transferred between regions. | ||
49 | /// </summary> | ||
50 | /// <remarks> | ||
51 | /// This is a state machine. | ||
52 | /// | ||
53 | /// [Entry] => Preparing | ||
54 | /// Preparing => { Transferring || CleaningUp || [Exit] } | ||
55 | /// Transferring => { ReceivedAtDestination || CleaningUp } | ||
56 | /// ReceivedAtDestination => CleaningUp | ||
57 | /// CleaningUp => [Exit] | ||
58 | /// | ||
59 | /// In other words, agents normally travel throwing Preparing => Transferring => ReceivedAtDestination => CleaningUp | ||
60 | /// However, any state can transition to CleaningUp if the teleport has failed. | ||
61 | /// </remarks> | ||
62 | enum AgentTransferState | ||
63 | { | ||
64 | Preparing, // The agent is being prepared for transfer | ||
65 | 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 | ||
67 | CleaningUp // The agent is being changed to child/removed after a transfer | ||
68 | } | ||
69 | |||
70 | /// <summary> | ||
71 | /// Records the state of entities when they are in transfer within or between regions (cross or teleport). | ||
72 | /// </summary> | ||
73 | public class EntityTransferStateMachine | ||
74 | { | ||
75 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
76 | |||
77 | /// <summary> | ||
78 | /// If true then on a teleport, the source region waits for a callback from the destination region. If | ||
79 | /// a callback fails to arrive within a set time then the user is pulled back into the source region. | ||
80 | /// </summary> | ||
81 | public bool EnableWaitForAgentArrivedAtDestination { get; set; } | ||
82 | |||
83 | private EntityTransferModule m_mod; | ||
84 | |||
85 | private Dictionary<UUID, AgentTransferState> m_agentsInTransit = new Dictionary<UUID, AgentTransferState>(); | ||
86 | |||
87 | public EntityTransferStateMachine(EntityTransferModule module) | ||
88 | { | ||
89 | m_mod = module; | ||
90 | } | ||
91 | |||
92 | /// <summary> | ||
93 | /// Set that an agent is in transit. | ||
94 | /// </summary> | ||
95 | /// <param name='id'>The ID of the agent being teleported</param> | ||
96 | /// <returns>true if the agent was not already in transit, false if it was</returns> | ||
97 | internal bool SetInTransit(UUID id) | ||
98 | { | ||
99 | lock (m_agentsInTransit) | ||
100 | { | ||
101 | if (!m_agentsInTransit.ContainsKey(id)) | ||
102 | { | ||
103 | m_agentsInTransit[id] = AgentTransferState.Preparing; | ||
104 | return true; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | return false; | ||
109 | } | ||
110 | |||
111 | /// <summary> | ||
112 | /// Updates the state of an agent that is already in transit. | ||
113 | /// </summary> | ||
114 | /// <param name='id'></param> | ||
115 | /// <param name='newState'></param> | ||
116 | /// <returns></returns> | ||
117 | /// <exception cref='Exception'>Illegal transitions will throw an Exception</exception> | ||
118 | internal void UpdateInTransit(UUID id, AgentTransferState newState) | ||
119 | { | ||
120 | lock (m_agentsInTransit) | ||
121 | { | ||
122 | // Illegal to try and update an agent that's not actually in transit. | ||
123 | if (!m_agentsInTransit.ContainsKey(id)) | ||
124 | throw new Exception( | ||
125 | string.Format( | ||
126 | "Agent with ID {0} is not registered as in transit in {1}", | ||
127 | id, m_mod.Scene.RegionInfo.RegionName)); | ||
128 | |||
129 | AgentTransferState oldState = m_agentsInTransit[id]; | ||
130 | |||
131 | bool transitionOkay = false; | ||
132 | |||
133 | if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp) | ||
134 | transitionOkay = true; | ||
135 | else if (newState == AgentTransferState.Transferring && oldState == AgentTransferState.Preparing) | ||
136 | transitionOkay = true; | ||
137 | else if (newState == AgentTransferState.ReceivedAtDestination && oldState == AgentTransferState.Transferring) | ||
138 | transitionOkay = true; | ||
139 | |||
140 | if (transitionOkay) | ||
141 | m_agentsInTransit[id] = newState; | ||
142 | else | ||
143 | throw new Exception( | ||
144 | string.Format( | ||
145 | "Agent with ID {0} is not allowed to move from old transit state {1} to new state {2} in {3}", | ||
146 | id, oldState, newState, m_mod.Scene.RegionInfo.RegionName)); | ||
147 | } | ||
148 | } | ||
149 | |||
150 | internal bool IsInTransit(UUID id) | ||
151 | { | ||
152 | lock (m_agentsInTransit) | ||
153 | return m_agentsInTransit.ContainsKey(id); | ||
154 | } | ||
155 | |||
156 | /// <summary> | ||
157 | /// Removes an agent from the transit state machine. | ||
158 | /// </summary> | ||
159 | /// <param name='id'></param> | ||
160 | /// <returns>true if the agent was flagged as being teleported when this method was called, false otherwise</returns> | ||
161 | internal bool ResetFromTransit(UUID id) | ||
162 | { | ||
163 | lock (m_agentsInTransit) | ||
164 | { | ||
165 | if (m_agentsInTransit.ContainsKey(id)) | ||
166 | { | ||
167 | AgentTransferState state = m_agentsInTransit[id]; | ||
168 | |||
169 | if (state == AgentTransferState.Transferring || state == AgentTransferState.ReceivedAtDestination) | ||
170 | { | ||
171 | // FIXME: For now, we allow exit from any state since a thrown exception in teleport is now guranteed | ||
172 | // to be handled properly - ResetFromTransit() could be invoked at any step along the process | ||
173 | m_log.WarnFormat( | ||
174 | "[ENTITY TRANSFER STATE MACHINE]: Agent with ID {0} should not exit directly from state {1}, should go to {2} state first in {3}", | ||
175 | id, state, AgentTransferState.CleaningUp, m_mod.Scene.RegionInfo.RegionName); | ||
176 | |||
177 | // throw new Exception( | ||
178 | // "Agent with ID {0} cannot exit directly from state {1}, it must go to {2} state first", | ||
179 | // state, AgentTransferState.CleaningUp); | ||
180 | } | ||
181 | |||
182 | m_agentsInTransit.Remove(id); | ||
183 | |||
184 | m_log.DebugFormat( | ||
185 | "[ENTITY TRANSFER STATE MACHINE]: Agent {0} cleared from transit in {1}", | ||
186 | id, m_mod.Scene.RegionInfo.RegionName); | ||
187 | |||
188 | return true; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | m_log.WarnFormat( | ||
193 | "[ENTITY TRANSFER STATE MACHINE]: Agent {0} requested to clear from transit in {1} but was already cleared", | ||
194 | id, m_mod.Scene.RegionInfo.RegionName); | ||
195 | |||
196 | return false; | ||
197 | } | ||
198 | |||
199 | internal bool WaitForAgentArrivedAtDestination(UUID id) | ||
200 | { | ||
201 | if (!m_mod.WaitForAgentArrivedAtDestination) | ||
202 | return true; | ||
203 | |||
204 | lock (m_agentsInTransit) | ||
205 | { | ||
206 | if (!IsInTransit(id)) | ||
207 | throw new Exception( | ||
208 | string.Format( | ||
209 | "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)); | ||
211 | |||
212 | AgentTransferState currentState = m_agentsInTransit[id]; | ||
213 | |||
214 | if (currentState != AgentTransferState.Transferring && currentState != AgentTransferState.ReceivedAtDestination) | ||
215 | throw new Exception( | ||
216 | string.Format( | ||
217 | "Asked to wait for destination callback for agent with ID {0} in {1} but agent is in state {2}", | ||
218 | id, m_mod.Scene.RegionInfo.RegionName, currentState)); | ||
219 | } | ||
220 | |||
221 | int count = 200; | ||
222 | |||
223 | // 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. | ||
225 | while (m_agentsInTransit[id] != AgentTransferState.ReceivedAtDestination && count-- > 0) | ||
226 | { | ||
227 | // m_log.Debug(" >>> Waiting... " + count); | ||
228 | Thread.Sleep(100); | ||
229 | } | ||
230 | |||
231 | return count > 0; | ||
232 | } | ||
233 | |||
234 | internal void SetAgentArrivedAtDestination(UUID id) | ||
235 | { | ||
236 | lock (m_agentsInTransit) | ||
237 | { | ||
238 | if (!m_agentsInTransit.ContainsKey(id)) | ||
239 | { | ||
240 | m_log.WarnFormat( | ||
241 | "[ENTITY TRANSFER STATE MACHINE]: Region {0} received notification of arrival in destination of agent {1} but no teleport request is active", | ||
242 | m_mod.Scene.RegionInfo.RegionName, id); | ||
243 | |||
244 | return; | ||
245 | } | ||
246 | |||
247 | AgentTransferState currentState = m_agentsInTransit[id]; | ||
248 | |||
249 | if (currentState == AgentTransferState.ReceivedAtDestination) | ||
250 | { | ||
251 | // An anomoly but don't make this an outright failure - destination region could be overzealous in sending notification. | ||
252 | m_log.WarnFormat( | ||
253 | "[ENTITY TRANSFER STATE MACHINE]: Region {0} received notification of arrival in destination of agent {1} but notification has already previously been received", | ||
254 | m_mod.Scene.RegionInfo.RegionName, id); | ||
255 | } | ||
256 | else if (currentState != AgentTransferState.Transferring) | ||
257 | { | ||
258 | m_log.ErrorFormat( | ||
259 | "[ENTITY TRANSFER STATE MACHINE]: Region {0} received notification of arrival in destination of agent {1} but agent is in state {2}", | ||
260 | m_mod.Scene.RegionInfo.RegionName, id, currentState); | ||
261 | |||
262 | return; | ||
263 | } | ||
264 | |||
265 | m_agentsInTransit[id] = AgentTransferState.ReceivedAtDestination; | ||
266 | } | ||
267 | } | ||
268 | } | ||
269 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs index 0fdd457..43a72e2 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs | |||
@@ -117,7 +117,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
117 | 117 | ||
118 | protected override GridRegion GetFinalDestination(GridRegion region) | 118 | protected override GridRegion GetFinalDestination(GridRegion region) |
119 | { | 119 | { |
120 | int flags = m_scene.GridService.GetRegionFlags(m_scene.RegionInfo.ScopeID, region.RegionID); | 120 | int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, region.RegionID); |
121 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: region {0} flags: {1}", region.RegionID, flags); | 121 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: region {0} flags: {1}", region.RegionID, flags); |
122 | 122 | ||
123 | if ((flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0) | 123 | if ((flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0) |
@@ -139,7 +139,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
139 | if (base.NeedsClosing(drawdist, oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) | 139 | if (base.NeedsClosing(drawdist, oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) |
140 | return true; | 140 | return true; |
141 | 141 | ||
142 | int flags = m_scene.GridService.GetRegionFlags(m_scene.RegionInfo.ScopeID, reg.RegionID); | 142 | int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, reg.RegionID); |
143 | if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0) | 143 | if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0) |
144 | return true; | 144 | return true; |
145 | 145 | ||
@@ -152,7 +152,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
152 | if (logout) | 152 | if (logout) |
153 | { | 153 | { |
154 | // Log them out of this grid | 154 | // Log them out of this grid |
155 | m_scene.PresenceService.LogoutAgent(sp.ControllingClient.SessionId); | 155 | Scene.PresenceService.LogoutAgent(sp.ControllingClient.SessionId); |
156 | } | 156 | } |
157 | } | 157 | } |
158 | 158 | ||
@@ -161,7 +161,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
161 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: CreateAgent {0} {1}", reg.ServerURI, finalDestination.ServerURI); | 161 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: CreateAgent {0} {1}", reg.ServerURI, finalDestination.ServerURI); |
162 | reason = string.Empty; | 162 | reason = string.Empty; |
163 | logout = false; | 163 | logout = false; |
164 | int flags = m_scene.GridService.GetRegionFlags(m_scene.RegionInfo.ScopeID, reg.RegionID); | 164 | int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, reg.RegionID); |
165 | if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0) | 165 | if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0) |
166 | { | 166 | { |
167 | // this user is going to another grid | 167 | // this user is going to another grid |
@@ -201,7 +201,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
201 | "[ENTITY TRANSFER MODULE]: Request to teleport {0} {1} home", client.Name, client.AgentId); | 201 | "[ENTITY TRANSFER MODULE]: Request to teleport {0} {1} home", client.Name, client.AgentId); |
202 | 202 | ||
203 | // Let's find out if this is a foreign user or a local user | 203 | // Let's find out if this is a foreign user or a local user |
204 | IUserManagement uMan = m_scene.RequestModuleInterface<IUserManagement>(); | 204 | IUserManagement uMan = Scene.RequestModuleInterface<IUserManagement>(); |
205 | if (uMan != null && uMan.IsLocalGridUser(id)) | 205 | if (uMan != null && uMan.IsLocalGridUser(id)) |
206 | { | 206 | { |
207 | // local grid user | 207 | // local grid user |
@@ -265,7 +265,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
265 | return; | 265 | return; |
266 | } | 266 | } |
267 | 267 | ||
268 | GridRegion info = m_scene.GridService.GetRegionByUUID(UUID.Zero, lm.RegionID); | 268 | GridRegion info = Scene.GridService.GetRegionByUUID(UUID.Zero, lm.RegionID); |
269 | 269 | ||
270 | // Local region? | 270 | // Local region? |
271 | if (info != null) | 271 | if (info != null) |
@@ -335,8 +335,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
335 | } | 335 | } |
336 | 336 | ||
337 | // Let's find out if this is a foreign user or a local user | 337 | // Let's find out if this is a foreign user or a local user |
338 | IUserManagement uMan = m_scene.RequestModuleInterface<IUserManagement>(); | 338 | IUserManagement uMan = Scene.RequestModuleInterface<IUserManagement>(); |
339 | // UserAccount account = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, obj.AgentId); | 339 | // UserAccount account = Scene.UserAccountService.GetUserAccount(Scene.RegionInfo.ScopeID, obj.AgentId); |
340 | if (uMan != null && uMan.IsLocalGridUser(obj.AgentId)) | 340 | if (uMan != null && uMan.IsLocalGridUser(obj.AgentId)) |
341 | { | 341 | { |
342 | // local grid user | 342 | // local grid user |