diff options
-rw-r--r-- | OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs | 59 | ||||
-rw-r--r-- | OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs | 124 |
2 files changed, 149 insertions, 34 deletions
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index 01b1668..34f0924 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs | |||
@@ -148,6 +148,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
148 | 148 | ||
149 | protected virtual void OnNewClient(IClientAPI client) | 149 | protected virtual void OnNewClient(IClientAPI client) |
150 | { | 150 | { |
151 | client.OnTeleportCancel += OnClientCancelTeleport; | ||
151 | client.OnTeleportHomeRequest += TeleportHome; | 152 | client.OnTeleportHomeRequest += TeleportHome; |
152 | client.OnTeleportLandmarkRequest += RequestTeleportLandmark; | 153 | client.OnTeleportLandmarkRequest += RequestTeleportLandmark; |
153 | } | 154 | } |
@@ -168,6 +169,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
168 | 169 | ||
169 | #region Agent Teleports | 170 | #region Agent Teleports |
170 | 171 | ||
172 | private void OnClientCancelTeleport(IClientAPI client) | ||
173 | { | ||
174 | m_entityTransferStateMachine.UpdateInTransit(client.AgentId, AgentTransferState.Cancelling); | ||
175 | |||
176 | m_log.DebugFormat( | ||
177 | "[ENTITY TRANSFER MODULE]: Received teleport cancel request from {0} in {1}", client.Name, Scene.Name); | ||
178 | } | ||
179 | |||
171 | public void Teleport(ScenePresence sp, ulong regionHandle, Vector3 position, Vector3 lookAt, uint teleportFlags) | 180 | public void Teleport(ScenePresence sp, ulong regionHandle, Vector3 position, Vector3 lookAt, uint teleportFlags) |
172 | { | 181 | { |
173 | if (sp.Scene.Permissions.IsGridGod(sp.UUID)) | 182 | if (sp.Scene.Permissions.IsGridGod(sp.UUID)) |
@@ -567,6 +576,15 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
567 | return; | 576 | return; |
568 | } | 577 | } |
569 | 578 | ||
579 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling) | ||
580 | { | ||
581 | m_log.DebugFormat( | ||
582 | "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request", | ||
583 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
584 | |||
585 | return; | ||
586 | } | ||
587 | |||
570 | // Past this point we have to attempt clean up if the teleport fails, so update transfer state. | 588 | // Past this point we have to attempt clean up if the teleport fails, so update transfer state. |
571 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring); | 589 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring); |
572 | 590 | ||
@@ -631,7 +649,16 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
631 | return; | 649 | return; |
632 | } | 650 | } |
633 | 651 | ||
634 | sp.ControllingClient.SendTeleportProgress(teleportFlags | (uint)TeleportFlags.DisableCancel, "sending_dest"); | 652 | if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling) |
653 | { | ||
654 | m_log.DebugFormat( | ||
655 | "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after UpdateAgent on client request", | ||
656 | sp.Name, finalDestination.RegionName, sp.Scene.Name); | ||
657 | |||
658 | CleanupAbortedInterRegionTeleport(sp, finalDestination); | ||
659 | |||
660 | return; | ||
661 | } | ||
635 | 662 | ||
636 | m_log.DebugFormat( | 663 | m_log.DebugFormat( |
637 | "[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}", | 664 | "[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}", |
@@ -714,14 +741,19 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
714 | // } | 741 | // } |
715 | } | 742 | } |
716 | 743 | ||
717 | protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout) | 744 | /// <summary> |
745 | /// Clean up an inter-region teleport that did not complete, either because of simulator failure or cancellation. | ||
746 | /// </summary> | ||
747 | /// <remarks> | ||
748 | /// All operations here must be idempotent so that we can call this method at any point in the teleport process | ||
749 | /// up until we send the TeleportFinish event quene event to the viewer. | ||
750 | /// <remarks> | ||
751 | /// <param name='sp'> </param> | ||
752 | /// <param name='finalDestination'></param> | ||
753 | protected virtual void CleanupAbortedInterRegionTeleport(ScenePresence sp, GridRegion finalDestination) | ||
718 | { | 754 | { |
719 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); | 755 | m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); |
720 | 756 | ||
721 | // Client never contacted destination. Let's restore everything back | ||
722 | sp.ControllingClient.SendTeleportFailed("Problems connecting to destination."); | ||
723 | |||
724 | // Fail. Reset it back | ||
725 | sp.IsChildAgent = false; | 757 | sp.IsChildAgent = false; |
726 | ReInstantiateScripts(sp); | 758 | ReInstantiateScripts(sp); |
727 | 759 | ||
@@ -729,7 +761,20 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
729 | 761 | ||
730 | // Finally, kill the agent we just created at the destination. | 762 | // Finally, kill the agent we just created at the destination. |
731 | Scene.SimulationService.CloseAgent(finalDestination, sp.UUID); | 763 | Scene.SimulationService.CloseAgent(finalDestination, sp.UUID); |
764 | } | ||
765 | |||
766 | /// <summary> | ||
767 | /// Signal that the inter-region teleport failed and perform cleanup. | ||
768 | /// </summary> | ||
769 | /// <param name='sp'></param> | ||
770 | /// <param name='finalDestination'></param> | ||
771 | /// <param name='logout'></param> | ||
772 | protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout) | ||
773 | { | ||
774 | CleanupAbortedInterRegionTeleport(sp, finalDestination); | ||
732 | 775 | ||
776 | sp.ControllingClient.SendTeleportFailed( | ||
777 | string.Format("Problems connecting to destination {0}", finalDestination.RegionName)); | ||
733 | sp.Scene.EventManager.TriggerTeleportFail(sp.ControllingClient, logout); | 778 | sp.Scene.EventManager.TriggerTeleportFail(sp.ControllingClient, logout); |
734 | } | 779 | } |
735 | 780 | ||
@@ -2097,7 +2142,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
2097 | 2142 | ||
2098 | public bool IsInTransit(UUID id) | 2143 | public bool IsInTransit(UUID id) |
2099 | { | 2144 | { |
2100 | return m_entityTransferStateMachine.IsInTransit(id); | 2145 | return m_entityTransferStateMachine.GetAgentTransferState(id) != null; |
2101 | } | 2146 | } |
2102 | 2147 | ||
2103 | protected void ReInstantiateScripts(ScenePresence sp) | 2148 | protected void ReInstantiateScripts(ScenePresence sp) |
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs index d0cab49..24d81d9 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs | |||
@@ -51,8 +51,9 @@ 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 || [Exit] } |
55 | /// Transferring => { ReceivedAtDestination || CleaningUp } | 55 | /// Transferring => { ReceivedAtDestination || Cancelling || CleaningUp } |
56 | /// Cancelling => CleaningUp | ||
56 | /// ReceivedAtDestination => CleaningUp | 57 | /// ReceivedAtDestination => CleaningUp |
57 | /// CleaningUp => [Exit] | 58 | /// CleaningUp => [Exit] |
58 | /// | 59 | /// |
@@ -64,7 +65,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
64 | Preparing, // The agent is being prepared for transfer | 65 | Preparing, // The agent is being prepared for transfer |
65 | Transferring, // The agent is in the process of being transferred to a destination | 66 | 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 | 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 | CleaningUp, // The agent is being changed to child/removed after a transfer |
69 | Cancelling // The user has cancelled the teleport but we have yet to act upon this. | ||
68 | } | 70 | } |
69 | 71 | ||
70 | /// <summary> | 72 | /// <summary> |
@@ -115,42 +117,110 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
115 | /// <param name='newState'></param> | 117 | /// <param name='newState'></param> |
116 | /// <returns></returns> | 118 | /// <returns></returns> |
117 | /// <exception cref='Exception'>Illegal transitions will throw an Exception</exception> | 119 | /// <exception cref='Exception'>Illegal transitions will throw an Exception</exception> |
118 | internal void UpdateInTransit(UUID id, AgentTransferState newState) | 120 | internal bool UpdateInTransit(UUID id, AgentTransferState newState) |
119 | { | 121 | { |
122 | bool transitionOkay = false; | ||
123 | |||
124 | // We don't want to throw an exception on cancel since this can come it at any time. | ||
125 | bool failIfNotOkay = true; | ||
126 | |||
127 | // Should be a failure message if failure is not okay. | ||
128 | string failureMessage = null; | ||
129 | |||
130 | AgentTransferState? oldState = null; | ||
131 | |||
120 | lock (m_agentsInTransit) | 132 | lock (m_agentsInTransit) |
121 | { | 133 | { |
122 | // Illegal to try and update an agent that's not actually in transit. | 134 | // Illegal to try and update an agent that's not actually in transit. |
123 | if (!m_agentsInTransit.ContainsKey(id)) | 135 | if (!m_agentsInTransit.ContainsKey(id)) |
124 | throw new Exception( | 136 | { |
125 | string.Format( | 137 | if (newState != AgentTransferState.Cancelling) |
126 | "Agent with ID {0} is not registered as in transit in {1}", | 138 | failureMessage = string.Format( |
127 | id, m_mod.Scene.RegionInfo.RegionName)); | 139 | "Agent with ID {0} is not registered as in transit in {1}", |
128 | 140 | id, m_mod.Scene.RegionInfo.RegionName); | |
129 | AgentTransferState oldState = m_agentsInTransit[id]; | 141 | else |
142 | failIfNotOkay = false; | ||
143 | } | ||
144 | else | ||
145 | { | ||
146 | oldState = m_agentsInTransit[id]; | ||
130 | 147 | ||
131 | bool transitionOkay = false; | 148 | if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp) |
149 | { | ||
150 | transitionOkay = true; | ||
151 | } | ||
152 | else if (newState == AgentTransferState.Transferring && oldState == AgentTransferState.Preparing) | ||
153 | { | ||
154 | transitionOkay = true; | ||
155 | } | ||
156 | else if (newState == AgentTransferState.ReceivedAtDestination && oldState == AgentTransferState.Transferring) | ||
157 | { | ||
158 | transitionOkay = true; | ||
159 | } | ||
160 | else | ||
161 | { | ||
162 | if (newState == AgentTransferState.Cancelling | ||
163 | && (oldState == AgentTransferState.Preparing || oldState == AgentTransferState.Transferring)) | ||
164 | { | ||
165 | transitionOkay = true; | ||
166 | } | ||
167 | else | ||
168 | { | ||
169 | failIfNotOkay = false; | ||
170 | } | ||
171 | } | ||
132 | 172 | ||
133 | if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp) | 173 | if (!transitionOkay) |
134 | transitionOkay = true; | 174 | failureMessage |
135 | else if (newState == AgentTransferState.Transferring && oldState == AgentTransferState.Preparing) | 175 | = string.Format( |
136 | transitionOkay = true; | 176 | "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) | 177 | id, oldState, newState, m_mod.Scene.RegionInfo.RegionName); |
138 | transitionOkay = true; | 178 | } |
139 | 179 | ||
140 | if (transitionOkay) | 180 | if (transitionOkay) |
181 | { | ||
141 | m_agentsInTransit[id] = newState; | 182 | m_agentsInTransit[id] = newState; |
142 | else | 183 | |
143 | throw new Exception( | 184 | // m_log.DebugFormat( |
144 | string.Format( | 185 | // "[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}", | 186 | // id, oldState, newState, m_mod.Scene.Name); |
146 | id, oldState, newState, m_mod.Scene.RegionInfo.RegionName)); | 187 | } |
188 | else if (failIfNotOkay) | ||
189 | { | ||
190 | throw new Exception(failureMessage); | ||
191 | } | ||
192 | // else | ||
193 | // { | ||
194 | // if (oldState != null) | ||
195 | // m_log.DebugFormat( | ||
196 | // "[ENTITY TRANSFER STATE MACHINE]: Ignored change of agent with id {0} from state {1} to {2} in {3}", | ||
197 | // id, oldState, newState, m_mod.Scene.Name); | ||
198 | // else | ||
199 | // m_log.DebugFormat( | ||
200 | // "[ENTITY TRANSFER STATE MACHINE]: Ignored change of agent with id {0} to state {1} in {2} since agent not in transit", | ||
201 | // id, newState, m_mod.Scene.Name); | ||
202 | // } | ||
147 | } | 203 | } |
204 | |||
205 | return transitionOkay; | ||
148 | } | 206 | } |
149 | 207 | ||
150 | internal bool IsInTransit(UUID id) | 208 | /// <summary> |
209 | /// Gets the current agent transfer state. | ||
210 | /// </summary> | ||
211 | /// <returns>Null if the agent is not in transit</returns> | ||
212 | /// <param name='id'> | ||
213 | /// Identifier. | ||
214 | /// </param> | ||
215 | internal AgentTransferState? GetAgentTransferState(UUID id) | ||
151 | { | 216 | { |
152 | lock (m_agentsInTransit) | 217 | lock (m_agentsInTransit) |
153 | return m_agentsInTransit.ContainsKey(id); | 218 | { |
219 | if (!m_agentsInTransit.ContainsKey(id)) | ||
220 | return null; | ||
221 | else | ||
222 | return m_agentsInTransit[id]; | ||
223 | } | ||
154 | } | 224 | } |
155 | 225 | ||
156 | /// <summary> | 226 | /// <summary> |
@@ -203,14 +273,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
203 | 273 | ||
204 | lock (m_agentsInTransit) | 274 | lock (m_agentsInTransit) |
205 | { | 275 | { |
206 | if (!IsInTransit(id)) | 276 | AgentTransferState? currentState = GetAgentTransferState(id); |
277 | |||
278 | if (currentState == null) | ||
207 | throw new Exception( | 279 | throw new Exception( |
208 | string.Format( | 280 | string.Format( |
209 | "Asked to wait for destination callback for agent with ID {0} in {1} but agent is not in transit", | 281 | "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)); | 282 | id, m_mod.Scene.RegionInfo.RegionName)); |
211 | 283 | ||
212 | AgentTransferState currentState = m_agentsInTransit[id]; | ||
213 | |||
214 | if (currentState != AgentTransferState.Transferring && currentState != AgentTransferState.ReceivedAtDestination) | 284 | if (currentState != AgentTransferState.Transferring && currentState != AgentTransferState.ReceivedAtDestination) |
215 | throw new Exception( | 285 | throw new Exception( |
216 | string.Format( | 286 | string.Format( |