aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim
diff options
context:
space:
mode:
authorJustin Clark-Casey (justincc)2013-03-12 22:16:09 +0000
committerJustin Clark-Casey (justincc)2013-03-12 22:16:09 +0000
commitc43d4b557267547d07f6c90dc7e335ce4f7e07be (patch)
treebaa7c11fae0e9c7e51cb173ba4c8182a8a5515c3 /OpenSim
parentminor: remove mono compiler warning in SceneObjectUndoRedoTests (diff)
downloadopensim-SC-c43d4b557267547d07f6c90dc7e335ce4f7e07be.zip
opensim-SC-c43d4b557267547d07f6c90dc7e335ce4f7e07be.tar.gz
opensim-SC-c43d4b557267547d07f6c90dc7e335ce4f7e07be.tar.bz2
opensim-SC-c43d4b557267547d07f6c90dc7e335ce4f7e07be.tar.xz
Improve teleport cancellation in some circumstances, though cancelling teleports is still not recommended.
Previously, hitting the cancel button on a teleport would cancel on the client side but the request was ignored on the server side. Cancel would still work if the teleport failed in the early stages (e.g. because the destination never replied to early CreateAgent and UpdateAgent messages). But if the teleport still completed after a delay here or later on, the viewer would become confused (usual symptom appears to be avatar being unable to move/reteleport). This commit makes OpenSimulator obey cancellations which are received before it sends the TeleportFinish event queue message and does proper cleanup. But cancellations received after this (which can happen even though the cancel button is removed as this messages comes on a different thread) can still result in a frozen avatar. This looks extremely difficult and impossible to fix. I can replicate the same problem on the Linden Lab grid by hitting cancel immediately after a teleport starts (a teleport which would otherwise quickly succeed).
Diffstat (limited to 'OpenSim')
-rw-r--r--OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs59
-rw-r--r--OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs124
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(