From debc266e751df9e463f9cfe29f8ee9fb935f0146 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 22 Mar 2013 01:00:13 +0000 Subject: [PATCH] At strategic points in the teleport process, if the client has simultaneously logged out then do not continue. This aims to reduce any side effects if the process tries to complete after the client has logged back in (e.g. it was delayed due to a slow destination region response). This introduces a new Aborting entity transfer state which signals that the teleport should be stopped but no compensating actions performed. --- .../EntityTransfer/EntityTransferModule.cs | 59 ++++++++++++++++++- .../EntityTransferStateMachine.cs | 20 ++++--- 2 files changed, 69 insertions(+), 10 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index 06bb1aa3dc..e041bf9871 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -165,6 +165,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer if (!DisableInterRegionTeleportCancellation) client.OnTeleportCancel += OnClientCancelTeleport; + + client.OnConnectionClosed += OnConnectionClosed; } public virtual void Close() {} @@ -183,6 +185,18 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer #region Agent Teleports + private void OnConnectionClosed(IClientAPI client) + { + if (client.IsLoggingOut) + { + m_entityTransferStateMachine.UpdateInTransit(client.AgentId, AgentTransferState.Aborting); + + m_log.DebugFormat( + "[ENTITY TRANSFER MODULE]: Aborted teleport request from {0} in {1} due to simultaneous logout", + client.Name, Scene.Name); + } + } + private void OnClientCancelTeleport(IClientAPI client) { m_entityTransferStateMachine.UpdateInTransit(client.AgentId, AgentTransferState.Cancelling); @@ -573,6 +587,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer return; } + Thread.Sleep(30000); + if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling) { m_log.DebugFormat( @@ -581,6 +597,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer return; } + else if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) + { + m_log.DebugFormat( + "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after CreateAgent due to previous client close.", + sp.Name, finalDestination.RegionName, sp.Scene.Name); + + return; + } // Past this point we have to attempt clean up if the teleport fails, so update transfer state. m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring); @@ -640,12 +664,32 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer //sp.ControllingClient.SendTeleportProgress(teleportFlags, "Updating agent..."); + // We will check for an abort before UpdateAgent since UpdateAgent will require an active viewer to + // establish th econnection to the destination which makes it return true. + if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) + { + m_log.DebugFormat( + "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} before UpdateAgent", + sp.Name, finalDestination.RegionName, sp.Scene.Name); + + return; + } + // A common teleport failure occurs when we can send CreateAgent to the // destination region but the viewer cannot establish the connection (e.g. due to network issues between // the viewer and the destination). In this case, UpdateAgent timesout after 10 seconds, although then // there's a further 10 second wait whilst we attempt to tell the destination to delete the agent in Fail(). if (!UpdateAgent(reg, finalDestination, agent, sp)) { + if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) + { + m_log.DebugFormat( + "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after UpdateAgent due to previous client close.", + sp.Name, finalDestination.RegionName, sp.Scene.Name); + + return; + } + m_log.WarnFormat( "[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1} from {2}. Keeping avatar in source region.", sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName); @@ -660,7 +704,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after UpdateAgent on client request", sp.Name, finalDestination.RegionName, sp.Scene.Name); - CleanupAbortedInterRegionTeleport(sp, finalDestination); + CleanupFailedInterRegionTeleport(sp, finalDestination); return; } @@ -689,6 +733,15 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer // that the client contacted the destination before we close things here. if (!m_entityTransferStateMachine.WaitForAgentArrivedAtDestination(sp.UUID)) { + if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) + { + m_log.DebugFormat( + "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after WaitForAgentArrivedAtDestination due to previous client close.", + sp.Name, finalDestination.RegionName, sp.Scene.Name); + + return; + } + m_log.WarnFormat( "[ENTITY TRANSFER MODULE]: Teleport of {0} to {1} from {2} failed due to no callback from destination region. Returning avatar to source region.", sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName); @@ -755,7 +808,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer /// /// /// - protected virtual void CleanupAbortedInterRegionTeleport(ScenePresence sp, GridRegion finalDestination) + protected virtual void CleanupFailedInterRegionTeleport(ScenePresence sp, GridRegion finalDestination) { m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); @@ -777,7 +830,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer /// Human readable reason for teleport failure. Will be sent to client. protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout, string reason) { - CleanupAbortedInterRegionTeleport(sp, finalDestination); + CleanupFailedInterRegionTeleport(sp, finalDestination); sp.ControllingClient.SendTeleportFailed( string.Format( diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs index 7314727cda..fc029163b6 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs @@ -51,11 +51,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer /// This is a state machine. /// /// [Entry] => Preparing - /// Preparing => { Transferring || Cancelling || CleaningUp || [Exit] } - /// Transferring => { ReceivedAtDestination || Cancelling || CleaningUp } - /// Cancelling => CleaningUp - /// ReceivedAtDestination => CleaningUp + /// Preparing => { Transferring || Cancelling || CleaningUp || Aborting || [Exit] } + /// Transferring => { ReceivedAtDestination || Cancelling || CleaningUp || Aborting } + /// Cancelling => CleaningUp || Aborting + /// ReceivedAtDestination => CleaningUp || Aborting /// CleaningUp => [Exit] + /// Aborting => [Exit] /// /// In other words, agents normally travel throwing Preparing => Transferring => ReceivedAtDestination => CleaningUp /// However, any state can transition to CleaningUp if the teleport has failed. @@ -66,7 +67,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer Transferring, // The agent is in the process of being transferred to a destination ReceivedAtDestination, // The destination has notified us that the agent has been successfully received CleaningUp, // The agent is being changed to child/removed after a transfer - Cancelling // The user has cancelled the teleport but we have yet to act upon this. + Cancelling, // The user has cancelled the teleport but we have yet to act upon this. + Aborting // The transfer is aborting. Unlike Cancelling, no compensating actions should be performed } /// @@ -134,7 +136,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer // Illegal to try and update an agent that's not actually in transit. if (!m_agentsInTransit.ContainsKey(id)) { - if (newState != AgentTransferState.Cancelling) + if (newState != AgentTransferState.Cancelling && newState != AgentTransferState.Aborting) failureMessage = string.Format( "Agent with ID {0} is not registered as in transit in {1}", id, m_mod.Scene.RegionInfo.RegionName); @@ -145,7 +147,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer { oldState = m_agentsInTransit[id]; - if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp) + if (newState == AgentTransferState.Aborting) + { + transitionOkay = true; + } + else if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp) { transitionOkay = true; }