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).0.7.4-extended
parent
03983edc3b
commit
adfc8ade9a
|
@ -146,6 +146,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
|
||||
protected virtual void OnNewClient(IClientAPI client)
|
||||
{
|
||||
client.OnTeleportCancel += OnClientCancelTeleport;
|
||||
client.OnTeleportHomeRequest += TeleportHome;
|
||||
client.OnTeleportLandmarkRequest += RequestTeleportLandmark;
|
||||
}
|
||||
|
@ -166,6 +167,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
|
||||
#region Agent Teleports
|
||||
|
||||
private void OnClientCancelTeleport(IClientAPI client)
|
||||
{
|
||||
m_entityTransferStateMachine.UpdateInTransit(client.AgentId, AgentTransferState.Cancelling);
|
||||
|
||||
m_log.DebugFormat(
|
||||
"[ENTITY TRANSFER MODULE]: Received teleport cancel request from {0} in {1}", client.Name, Scene.Name);
|
||||
}
|
||||
|
||||
public void Teleport(ScenePresence sp, ulong regionHandle, Vector3 position, Vector3 lookAt, uint teleportFlags)
|
||||
{
|
||||
if (sp.Scene.Permissions.IsGridGod(sp.UUID))
|
||||
|
@ -550,6 +559,15 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling)
|
||||
{
|
||||
m_log.DebugFormat(
|
||||
"[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request",
|
||||
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);
|
||||
|
||||
|
@ -614,7 +632,16 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
return;
|
||||
}
|
||||
|
||||
sp.ControllingClient.SendTeleportProgress(teleportFlags | (uint)TeleportFlags.DisableCancel, "sending_dest");
|
||||
if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling)
|
||||
{
|
||||
m_log.DebugFormat(
|
||||
"[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);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_log.DebugFormat(
|
||||
"[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}",
|
||||
|
@ -697,14 +724,19 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
// }
|
||||
}
|
||||
|
||||
protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout)
|
||||
/// <summary>
|
||||
/// Clean up an inter-region teleport that did not complete, either because of simulator failure or cancellation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All operations here must be idempotent so that we can call this method at any point in the teleport process
|
||||
/// up until we send the TeleportFinish event quene event to the viewer.
|
||||
/// <remarks>
|
||||
/// <param name='sp'> </param>
|
||||
/// <param name='finalDestination'></param>
|
||||
protected virtual void CleanupAbortedInterRegionTeleport(ScenePresence sp, GridRegion finalDestination)
|
||||
{
|
||||
m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp);
|
||||
|
||||
// Client never contacted destination. Let's restore everything back
|
||||
sp.ControllingClient.SendTeleportFailed("Problems connecting to destination.");
|
||||
|
||||
// Fail. Reset it back
|
||||
sp.IsChildAgent = false;
|
||||
ReInstantiateScripts(sp);
|
||||
|
||||
|
@ -712,7 +744,20 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
|
||||
// Finally, kill the agent we just created at the destination.
|
||||
Scene.SimulationService.CloseAgent(finalDestination, sp.UUID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Signal that the inter-region teleport failed and perform cleanup.
|
||||
/// </summary>
|
||||
/// <param name='sp'></param>
|
||||
/// <param name='finalDestination'></param>
|
||||
/// <param name='logout'></param>
|
||||
protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout)
|
||||
{
|
||||
CleanupAbortedInterRegionTeleport(sp, finalDestination);
|
||||
|
||||
sp.ControllingClient.SendTeleportFailed(
|
||||
string.Format("Problems connecting to destination {0}", finalDestination.RegionName));
|
||||
sp.Scene.EventManager.TriggerTeleportFail(sp.ControllingClient, logout);
|
||||
}
|
||||
|
||||
|
@ -2068,7 +2113,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
|
||||
public bool IsInTransit(UUID id)
|
||||
{
|
||||
return m_entityTransferStateMachine.IsInTransit(id);
|
||||
return m_entityTransferStateMachine.GetAgentTransferState(id) != null;
|
||||
}
|
||||
|
||||
protected void ReInstantiateScripts(ScenePresence sp)
|
||||
|
|
|
@ -51,8 +51,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
/// This is a state machine.
|
||||
///
|
||||
/// [Entry] => Preparing
|
||||
/// Preparing => { Transferring || CleaningUp || [Exit] }
|
||||
/// Transferring => { ReceivedAtDestination || CleaningUp }
|
||||
/// Preparing => { Transferring || Cancelling || CleaningUp || [Exit] }
|
||||
/// Transferring => { ReceivedAtDestination || Cancelling || CleaningUp }
|
||||
/// Cancelling => CleaningUp
|
||||
/// ReceivedAtDestination => CleaningUp
|
||||
/// CleaningUp => [Exit]
|
||||
///
|
||||
|
@ -64,7 +65,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
Preparing, // The agent is being prepared for transfer
|
||||
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
|
||||
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.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -115,42 +117,110 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
/// <param name='newState'></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref='Exception'>Illegal transitions will throw an Exception</exception>
|
||||
internal void UpdateInTransit(UUID id, AgentTransferState newState)
|
||||
internal bool UpdateInTransit(UUID id, AgentTransferState newState)
|
||||
{
|
||||
bool transitionOkay = false;
|
||||
|
||||
// We don't want to throw an exception on cancel since this can come it at any time.
|
||||
bool failIfNotOkay = true;
|
||||
|
||||
// Should be a failure message if failure is not okay.
|
||||
string failureMessage = null;
|
||||
|
||||
AgentTransferState? oldState = null;
|
||||
|
||||
lock (m_agentsInTransit)
|
||||
{
|
||||
// Illegal to try and update an agent that's not actually in transit.
|
||||
if (!m_agentsInTransit.ContainsKey(id))
|
||||
throw new Exception(
|
||||
string.Format(
|
||||
"Agent with ID {0} is not registered as in transit in {1}",
|
||||
id, m_mod.Scene.RegionInfo.RegionName));
|
||||
{
|
||||
if (newState != AgentTransferState.Cancelling)
|
||||
failureMessage = string.Format(
|
||||
"Agent with ID {0} is not registered as in transit in {1}",
|
||||
id, m_mod.Scene.RegionInfo.RegionName);
|
||||
else
|
||||
failIfNotOkay = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
oldState = m_agentsInTransit[id];
|
||||
|
||||
AgentTransferState oldState = m_agentsInTransit[id];
|
||||
if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp)
|
||||
{
|
||||
transitionOkay = true;
|
||||
}
|
||||
else if (newState == AgentTransferState.Transferring && oldState == AgentTransferState.Preparing)
|
||||
{
|
||||
transitionOkay = true;
|
||||
}
|
||||
else if (newState == AgentTransferState.ReceivedAtDestination && oldState == AgentTransferState.Transferring)
|
||||
{
|
||||
transitionOkay = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newState == AgentTransferState.Cancelling
|
||||
&& (oldState == AgentTransferState.Preparing || oldState == AgentTransferState.Transferring))
|
||||
{
|
||||
transitionOkay = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
failIfNotOkay = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool transitionOkay = false;
|
||||
|
||||
if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp)
|
||||
transitionOkay = true;
|
||||
else if (newState == AgentTransferState.Transferring && oldState == AgentTransferState.Preparing)
|
||||
transitionOkay = true;
|
||||
else if (newState == AgentTransferState.ReceivedAtDestination && oldState == AgentTransferState.Transferring)
|
||||
transitionOkay = true;
|
||||
if (!transitionOkay)
|
||||
failureMessage
|
||||
= string.Format(
|
||||
"Agent with ID {0} is not allowed to move from old transit state {1} to new state {2} in {3}",
|
||||
id, oldState, newState, m_mod.Scene.RegionInfo.RegionName);
|
||||
}
|
||||
|
||||
if (transitionOkay)
|
||||
{
|
||||
m_agentsInTransit[id] = newState;
|
||||
else
|
||||
throw new Exception(
|
||||
string.Format(
|
||||
"Agent with ID {0} is not allowed to move from old transit state {1} to new state {2} in {3}",
|
||||
id, oldState, newState, m_mod.Scene.RegionInfo.RegionName));
|
||||
|
||||
// m_log.DebugFormat(
|
||||
// "[ENTITY TRANSFER STATE MACHINE]: Changed agent with id {0} from state {1} to {2} in {3}",
|
||||
// id, oldState, newState, m_mod.Scene.Name);
|
||||
}
|
||||
else if (failIfNotOkay)
|
||||
{
|
||||
throw new Exception(failureMessage);
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// if (oldState != null)
|
||||
// m_log.DebugFormat(
|
||||
// "[ENTITY TRANSFER STATE MACHINE]: Ignored change of agent with id {0} from state {1} to {2} in {3}",
|
||||
// id, oldState, newState, m_mod.Scene.Name);
|
||||
// else
|
||||
// m_log.DebugFormat(
|
||||
// "[ENTITY TRANSFER STATE MACHINE]: Ignored change of agent with id {0} to state {1} in {2} since agent not in transit",
|
||||
// id, newState, m_mod.Scene.Name);
|
||||
// }
|
||||
}
|
||||
|
||||
return transitionOkay;
|
||||
}
|
||||
|
||||
internal bool IsInTransit(UUID id)
|
||||
/// <summary>
|
||||
/// Gets the current agent transfer state.
|
||||
/// </summary>
|
||||
/// <returns>Null if the agent is not in transit</returns>
|
||||
/// <param name='id'>
|
||||
/// Identifier.
|
||||
/// </param>
|
||||
internal AgentTransferState? GetAgentTransferState(UUID id)
|
||||
{
|
||||
lock (m_agentsInTransit)
|
||||
return m_agentsInTransit.ContainsKey(id);
|
||||
{
|
||||
if (!m_agentsInTransit.ContainsKey(id))
|
||||
return null;
|
||||
else
|
||||
return m_agentsInTransit[id];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -203,14 +273,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
|
||||
lock (m_agentsInTransit)
|
||||
{
|
||||
if (!IsInTransit(id))
|
||||
AgentTransferState? currentState = GetAgentTransferState(id);
|
||||
|
||||
if (currentState == null)
|
||||
throw new Exception(
|
||||
string.Format(
|
||||
"Asked to wait for destination callback for agent with ID {0} in {1} but agent is not in transit",
|
||||
id, m_mod.Scene.RegionInfo.RegionName));
|
||||
|
||||
AgentTransferState currentState = m_agentsInTransit[id];
|
||||
|
||||
if (currentState != AgentTransferState.Transferring && currentState != AgentTransferState.ReceivedAtDestination)
|
||||
throw new Exception(
|
||||
string.Format(
|
||||
|
|
Loading…
Reference in New Issue