diff options
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 | ||
47 | namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | 47 | namespace 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; |