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.
0.7.4-extended
Justin Clark-Casey (justincc) 2013-03-22 01:00:13 +00:00
parent b87c5140a1
commit debc266e75
2 changed files with 69 additions and 10 deletions

View File

@ -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
/// <remarks>
/// <param name='sp'> </param>
/// <param name='finalDestination'></param>
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
/// <param name='reason'>Human readable reason for teleport failure. Will be sent to client.</param>
protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout, string reason)
{
CleanupAbortedInterRegionTeleport(sp, finalDestination);
CleanupFailedInterRegionTeleport(sp, finalDestination);
sp.ControllingClient.SendTeleportFailed(
string.Format(

View File

@ -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
}
/// <summary>
@ -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;
}