aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorJustin Clark-Casey (justincc)2012-05-25 02:02:53 +0100
committerJustin Clark-Casey (justincc)2012-05-25 02:02:53 +0100
commit40c78b06246d1131e07982dc6a9366666d9ea031 (patch)
tree094bc14aabd790bb97127a6406c17d80a469ceb0
parentIn remote QueryAccess, also receive the actual status (true|false) instead of... (diff)
downloadopensim-SC-40c78b06246d1131e07982dc6a9366666d9ea031.zip
opensim-SC-40c78b06246d1131e07982dc6a9366666d9ea031.tar.gz
opensim-SC-40c78b06246d1131e07982dc6a9366666d9ea031.tar.bz2
opensim-SC-40c78b06246d1131e07982dc6a9366666d9ea031.tar.xz
Stop it being possible for an agent to teleport back to its source region before the source region has finished cleaning up old agent data and structures.
If this is allowed, then the client usually gets forcibly logged out and data structures might be put into bad states. To prevent this, the binary state machine of EMT.m_agentsInTransit is replaced with a 4 state machine (Preparing, Transferring, ReceivedAtDestination, CleaningUp). This is necessary because the source region needs to know when the destination region has received the user but a teleport back cannot happen until the source region has cleaned up. Tested on standalone, grid and with v1 and v3 clients.
-rw-r--r--OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs189
-rw-r--r--OpenSim/Region/Framework/Interfaces/IEntityTransferModule.cs2
-rw-r--r--OpenSim/Region/Framework/Scenes/Scene.cs6
3 files changed, 169 insertions, 28 deletions
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
index b5717cd..ddb621d 100644
--- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
@@ -46,6 +46,29 @@ using Nini.Config;
46 46
47namespace OpenSim.Region.CoreModules.Framework.EntityTransfer 47namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
48{ 48{
49 /// <summary>
50 /// The possible states that an agent can be in when its being transferred between regions.
51 /// </summary>
52 /// <remarks>
53 /// This is a state machine.
54 ///
55 /// [Entry] => Preparing
56 /// Preparing => { Transferring || CleaningUp || [Exit] }
57 /// Transferring => { ReceivedAtDestination || CleaningUp }
58 /// ReceivedAtDestination => CleaningUp
59 /// CleaningUp => [Exit]
60 ///
61 /// In other words, agents normally travel throwing Preparing => Transferring => ReceivedAtDestination => CleaningUp
62 /// However, any state can transition to CleaningUp if the teleport has failed.
63 /// </remarks>
64 enum AgentTransferState
65 {
66 Preparing, // The agent is being prepared for transfer
67 Transferring, // The agent is in the process of being transferred to a destination
68 ReceivedAtDestination, // The destination has notified us that the agent has been successfully received
69 CleaningUp // The agent is being changed to child/removed after a transfer
70 }
71
49 public class EntityTransferModule : INonSharedRegionModule, IEntityTransferModule 72 public class EntityTransferModule : INonSharedRegionModule, IEntityTransferModule
50 { 73 {
51 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 74 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
@@ -68,7 +91,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
68 91
69 protected Scene m_scene; 92 protected Scene m_scene;
70 93
71 protected List<UUID> m_agentsInTransit; 94 private Dictionary<UUID, AgentTransferState> m_agentsInTransit;
72 95
73 private ExpiringCache<UUID, ExpiringCache<ulong, DateTime>> m_bannedRegions = 96 private ExpiringCache<UUID, ExpiringCache<ulong, DateTime>> m_bannedRegions =
74 new ExpiringCache<UUID, ExpiringCache<ulong, DateTime>>(); 97 new ExpiringCache<UUID, ExpiringCache<ulong, DateTime>>();
@@ -120,7 +143,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
120 MaxTransferDistance = DefaultMaxTransferDistance; 143 MaxTransferDistance = DefaultMaxTransferDistance;
121 } 144 }
122 145
123 m_agentsInTransit = new List<UUID>(); 146 m_agentsInTransit = new Dictionary<UUID, AgentTransferState>();
124 m_Enabled = true; 147 m_Enabled = true;
125 } 148 }
126 149
@@ -259,17 +282,22 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
259 position.Z = newPosZ; 282 position.Z = newPosZ;
260 } 283 }
261 284
285 UpdateInTransit(sp.UUID, AgentTransferState.Transferring);
286
262 sp.ControllingClient.SendTeleportStart(teleportFlags); 287 sp.ControllingClient.SendTeleportStart(teleportFlags);
263 288
264 sp.ControllingClient.SendLocalTeleport(position, lookAt, teleportFlags); 289 sp.ControllingClient.SendLocalTeleport(position, lookAt, teleportFlags);
265 sp.Velocity = Vector3.Zero; 290 sp.Velocity = Vector3.Zero;
266 sp.Teleport(position); 291 sp.Teleport(position);
267 292
293 UpdateInTransit(sp.UUID, AgentTransferState.ReceivedAtDestination);
294
268 foreach (SceneObjectGroup grp in sp.GetAttachments()) 295 foreach (SceneObjectGroup grp in sp.GetAttachments())
269 { 296 {
270 sp.Scene.EventManager.TriggerOnScriptChangedEvent(grp.LocalId, (uint)Changed.TELEPORT); 297 sp.Scene.EventManager.TriggerOnScriptChangedEvent(grp.LocalId, (uint)Changed.TELEPORT);
271 } 298 }
272 299
300 UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp);
273 ResetFromTransit(sp.UUID); 301 ResetFromTransit(sp.UUID);
274 } 302 }
275 303
@@ -454,7 +482,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
454 return; 482 return;
455 } 483 }
456 484
457 m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Destination is running version {0}", version); 485 m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Destination is running version {0}", version);
458 486
459 // Fixing a bug where teleporting while sitting results in the avatar ending up removed from 487 // Fixing a bug where teleporting while sitting results in the avatar ending up removed from
460 // both regions 488 // both regions
@@ -508,6 +536,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
508 return; 536 return;
509 } 537 }
510 538
539 // Past this point we have to attempt clean up if the teleport fails, so update transfer state.
540 UpdateInTransit(sp.UUID, AgentTransferState.Transferring);
541
511 // OK, it got this agent. Let's close some child agents 542 // OK, it got this agent. Let's close some child agents
512 sp.CloseChildAgents(newRegionX, newRegionY); 543 sp.CloseChildAgents(newRegionX, newRegionY);
513 544
@@ -598,6 +629,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
598 return; 629 return;
599 } 630 }
600 631
632 UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp);
633
601 // For backwards compatibility 634 // For backwards compatibility
602 if (version == "Unknown" || version == string.Empty) 635 if (version == "Unknown" || version == string.Empty)
603 { 636 {
@@ -624,8 +657,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
624 // We need to delay here because Imprudence viewers, unlike v1 or v3, have a short (<200ms, <500ms) delay before 657 // We need to delay here because Imprudence viewers, unlike v1 or v3, have a short (<200ms, <500ms) delay before
625 // they regard the new region as the current region after receiving the AgentMovementComplete 658 // they regard the new region as the current region after receiving the AgentMovementComplete
626 // response. If close is sent before then, it will cause the viewer to quit instead. 659 // response. If close is sent before then, it will cause the viewer to quit instead.
627 // However, if this delay is longer, then a viewer can teleport back to this region and experience 660 //
628 // a failure because the old ScenePresence has not yet been cleaned up. 661 // This sleep can be increased if necessary. However, whilst it's active,
662 // an agent cannot teleport back to this region if it has teleported away.
629 Thread.Sleep(2000); 663 Thread.Sleep(2000);
630 664
631 sp.Close(); 665 sp.Close();
@@ -644,6 +678,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
644 "[ENTITY TRANSFER MODULE]: User {0} is going to another region, profile cache removed", 678 "[ENTITY TRANSFER MODULE]: User {0} is going to another region, profile cache removed",
645 sp.UUID); 679 sp.UUID);
646 } 680 }
681
682 ResetFromTransit(sp.UUID);
647 } 683 }
648 684
649 protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout) 685 protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout)
@@ -1089,6 +1125,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
1089 Vector3 vel2 = new Vector3(agent.Velocity.X, agent.Velocity.Y, 0); 1125 Vector3 vel2 = new Vector3(agent.Velocity.X, agent.Velocity.Y, 0);
1090 1126
1091 agent.RemoveFromPhysicalScene(); 1127 agent.RemoveFromPhysicalScene();
1128
1092 SetInTransit(agent.UUID); 1129 SetInTransit(agent.UUID);
1093 1130
1094 AgentData cAgent = new AgentData(); 1131 AgentData cAgent = new AgentData();
@@ -1100,6 +1137,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
1100 // We don't need the callback anymnore 1137 // We don't need the callback anymnore
1101 cAgent.CallbackURI = String.Empty; 1138 cAgent.CallbackURI = String.Empty;
1102 1139
1140 // Beyond this point, extra cleanup is needed beyond removing transit state
1141 UpdateInTransit(agent.UUID, AgentTransferState.Transferring);
1142
1103 if (!m_scene.SimulationService.UpdateAgent(neighbourRegion, cAgent)) 1143 if (!m_scene.SimulationService.UpdateAgent(neighbourRegion, cAgent))
1104 { 1144 {
1105 // region doesn't take it 1145 // region doesn't take it
@@ -1141,8 +1181,16 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
1141 capsPath); 1181 capsPath);
1142 } 1182 }
1143 1183
1144 // SUCCESS! 1184 // SUCCESS!
1185 UpdateInTransit(agent.UUID, AgentTransferState.ReceivedAtDestination);
1186
1187 // Unlike a teleport, here we do not wait for the destination region to confirm the receipt.
1188 UpdateInTransit(agent.UUID, AgentTransferState.CleaningUp);
1189
1145 agent.MakeChildAgent(); 1190 agent.MakeChildAgent();
1191
1192 // FIXME: Possibly this should occur lower down after other commands to close other agents,
1193 // but not sure yet what the side effects would be.
1146 ResetFromTransit(agent.UUID); 1194 ResetFromTransit(agent.UUID);
1147 1195
1148 // now we have a child agent in this region. Request all interesting data about other (root) agents 1196 // now we have a child agent in this region. Request all interesting data about other (root) agents
@@ -1622,7 +1670,37 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
1622 #region Agent Arrived 1670 #region Agent Arrived
1623 public void AgentArrivedAtDestination(UUID id) 1671 public void AgentArrivedAtDestination(UUID id)
1624 { 1672 {
1625 ResetFromTransit(id); 1673 lock (m_agentsInTransit)
1674 {
1675 if (!m_agentsInTransit.ContainsKey(id))
1676 {
1677 m_log.WarnFormat(
1678 "[ENTITY TRANSFER MODULE]: Region {0} received notification of arrival in destination scene of agent {1} but no teleport request is active",
1679 m_scene.RegionInfo.RegionName, id);
1680
1681 return;
1682 }
1683
1684 AgentTransferState currentState = m_agentsInTransit[id];
1685
1686 if (currentState == AgentTransferState.ReceivedAtDestination)
1687 {
1688 // An anomoly but don't make this an outright failure - destination region could be overzealous in sending notification.
1689 m_log.WarnFormat(
1690 "[ENTITY TRANSFER MODULE]: Region {0} received notification of arrival in destination scene of agent {1} but notification has already previously been received",
1691 m_scene.RegionInfo.RegionName, id);
1692 }
1693 else if (currentState != AgentTransferState.Transferring)
1694 {
1695 m_log.ErrorFormat(
1696 "[ENTITY TRANSFER MODULE]: Region {0} received notification of arrival in destination scene of agent {1} but agent is in transfer state {2}",
1697 m_scene.RegionInfo.RegionName, id, currentState);
1698
1699 return;
1700 }
1701
1702 m_agentsInTransit[id] = AgentTransferState.ReceivedAtDestination;
1703 }
1626 } 1704 }
1627 1705
1628 #endregion 1706 #endregion
@@ -1964,10 +2042,29 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
1964 2042
1965 #region Misc 2043 #region Misc
1966 2044
1967 protected bool WaitForCallback(UUID id) 2045 private bool WaitForCallback(UUID id)
1968 { 2046 {
2047 lock (m_agentsInTransit)
2048 {
2049 if (!IsInTransit(id))
2050 throw new Exception(
2051 string.Format(
2052 "Asked to wait for destination callback for agent with ID {0} but it is not in transit"));
2053
2054 AgentTransferState currentState = m_agentsInTransit[id];
2055
2056 if (currentState != AgentTransferState.Transferring && currentState != AgentTransferState.ReceivedAtDestination)
2057 throw new Exception(
2058 string.Format(
2059 "Asked to wait for destination callback for agent with ID {0} but it is in state {1}",
2060 currentState));
2061 }
2062
1969 int count = 200; 2063 int count = 200;
1970 while (m_agentsInTransit.Contains(id) && count-- > 0) 2064
2065 // There should be no race condition here since no other code should be removing the agent transfer or
2066 // changing the state to another other than Transferring => ReceivedAtDestination.
2067 while (m_agentsInTransit[id] != AgentTransferState.ReceivedAtDestination && count-- > 0)
1971 { 2068 {
1972// m_log.Debug(" >>> Waiting... " + count); 2069// m_log.Debug(" >>> Waiting... " + count);
1973 Thread.Sleep(100); 2070 Thread.Sleep(100);
@@ -1977,17 +2074,17 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
1977 } 2074 }
1978 2075
1979 /// <summary> 2076 /// <summary>
1980 /// Set that an agent is in the process of being teleported. 2077 /// Set that an agent is in transit.
1981 /// </summary> 2078 /// </summary>
1982 /// <param name='id'>The ID of the agent being teleported</param> 2079 /// <param name='id'>The ID of the agent being teleported</param>
1983 /// <returns>true if the agent was not already in transit, false if it was</returns> 2080 /// <returns>true if the agent was not already in transit, false if it was</returns>
1984 protected bool SetInTransit(UUID id) 2081 private bool SetInTransit(UUID id)
1985 { 2082 {
1986 lock (m_agentsInTransit) 2083 lock (m_agentsInTransit)
1987 { 2084 {
1988 if (!m_agentsInTransit.Contains(id)) 2085 if (!m_agentsInTransit.ContainsKey(id))
1989 { 2086 {
1990 m_agentsInTransit.Add(id); 2087 m_agentsInTransit[id] = AgentTransferState.Preparing;
1991 return true; 2088 return true;
1992 } 2089 }
1993 } 2090 }
@@ -1996,29 +2093,73 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
1996 } 2093 }
1997 2094
1998 /// <summary> 2095 /// <summary>
1999 /// Show whether the given agent is being teleported. 2096 /// Updates the state of an agent that is already in transit.
2000 /// </summary> 2097 /// </summary>
2001 /// <returns>true if the agent is in the process of being teleported, false otherwise.</returns> 2098 /// <param name='id'></param>
2002 /// <param name='id'>The agent ID</para></param> 2099 /// <param name='newState'></param>
2100 /// <returns></returns>
2101 /// <exception cref='Exception'>Illegal transitions will throw an Exception</exception>
2102 private void UpdateInTransit(UUID id, AgentTransferState newState)
2103 {
2104 lock (m_agentsInTransit)
2105 {
2106 // Illegal to try and update an agent that's not actually in transit.
2107 if (!m_agentsInTransit.ContainsKey(id))
2108 throw new Exception(string.Format("Agent with ID {0} is not registered as in transit", id));
2109
2110 AgentTransferState oldState = m_agentsInTransit[id];
2111
2112 bool transitionOkay = false;
2113
2114 if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp)
2115 transitionOkay = true;
2116 else if (newState == AgentTransferState.Transferring && oldState == AgentTransferState.Preparing)
2117 transitionOkay = true;
2118 else if (newState == AgentTransferState.ReceivedAtDestination && oldState == AgentTransferState.Transferring)
2119 transitionOkay = true;
2120
2121 if (transitionOkay)
2122 m_agentsInTransit[id] = newState;
2123 else
2124 throw new Exception(
2125 string.Format(
2126 "Agent with ID {0} is not allowed to move from old transit state {1} to new state {2}",
2127 id, oldState, newState));
2128 }
2129 }
2130
2003 public bool IsInTransit(UUID id) 2131 public bool IsInTransit(UUID id)
2004 { 2132 {
2005 lock (m_agentsInTransit) 2133 lock (m_agentsInTransit)
2006 return m_agentsInTransit.Contains(id); 2134 return m_agentsInTransit.ContainsKey(id);
2007 } 2135 }
2008 2136
2009 /// <summary> 2137 /// <summary>
2010 /// Set that an agent is no longer being teleported. 2138 /// Removes an agent from the transit state machine.
2011 /// </summary> 2139 /// </summary>
2012 /// <returns></returns> 2140 /// <param name='id'></param>
2013 /// <param name='id'> 2141 /// <returns>true if the agent was flagged as being teleported when this method was called, false otherwise</returns>
2014 /// true if the agent was flagged as being teleported when this method was called, false otherwise 2142 private bool ResetFromTransit(UUID id)
2015 /// </param>
2016 protected bool ResetFromTransit(UUID id)
2017 { 2143 {
2018 lock (m_agentsInTransit) 2144 lock (m_agentsInTransit)
2019 { 2145 {
2020 if (m_agentsInTransit.Contains(id)) 2146 if (m_agentsInTransit.ContainsKey(id))
2021 { 2147 {
2148 AgentTransferState state = m_agentsInTransit[id];
2149
2150 if (state == AgentTransferState.Transferring || state == AgentTransferState.ReceivedAtDestination)
2151 {
2152 // FIXME: For now, we allow exit from any state since a thrown exception in teleport is now guranteed
2153 // to be handled properly - ResetFromTransit() could be invoked at any step along the process
2154 m_log.WarnFormat(
2155 "[ENTITY TRANSFER MODULE]: Agent with ID should not exit directly from state {1}, should go to {2} state first",
2156 state, AgentTransferState.CleaningUp);
2157
2158// throw new Exception(
2159// "Agent with ID {0} cannot exit directly from state {1}, it must go to {2} state first",
2160// state, AgentTransferState.CleaningUp);
2161 }
2162
2022 m_agentsInTransit.Remove(id); 2163 m_agentsInTransit.Remove(id);
2023 2164
2024 m_log.DebugFormat( 2165 m_log.DebugFormat(
diff --git a/OpenSim/Region/Framework/Interfaces/IEntityTransferModule.cs b/OpenSim/Region/Framework/Interfaces/IEntityTransferModule.cs
index 75c44d5..69be83e 100644
--- a/OpenSim/Region/Framework/Interfaces/IEntityTransferModule.cs
+++ b/OpenSim/Region/Framework/Interfaces/IEntityTransferModule.cs
@@ -77,8 +77,8 @@ namespace OpenSim.Region.Framework.Interfaces
77 /// <summary> 77 /// <summary>
78 /// Show whether the given agent is being teleported. 78 /// Show whether the given agent is being teleported.
79 /// </summary> 79 /// </summary>
80 /// <returns>true if the agent is in the process of being teleported, false otherwise.</returns>
81 /// <param name='id'>The agent ID</para></param> 80 /// <param name='id'>The agent ID</para></param>
81 /// <returns>true if the agent is in the process of being teleported, false otherwise.</returns>
82 bool IsInTransit(UUID id); 82 bool IsInTransit(UUID id);
83 83
84 bool Cross(ScenePresence agent, bool isFlying); 84 bool Cross(ScenePresence agent, bool isFlying);
diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs
index 755b1e6..98a75e4 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.cs
@@ -5212,10 +5212,10 @@ namespace OpenSim.Region.Framework.Scenes
5212 { 5212 {
5213 if (EntityTransferModule.IsInTransit(agentID)) 5213 if (EntityTransferModule.IsInTransit(agentID))
5214 { 5214 {
5215 reason = "Agent is already in transit on this region"; 5215 reason = "Agent is still in transit from this region";
5216 5216
5217 m_log.DebugFormat( 5217 m_log.WarnFormat(
5218 "[SCENE]: Denying agent {0} entry into {1} since region already has them registered as in transit", 5218 "[SCENE]: Denying agent {0} entry into {1} since region still has them registered as in transit",
5219 agentID, RegionInfo.RegionName); 5219 agentID, RegionInfo.RegionName);
5220 5220
5221 return false; 5221 return false;