aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-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;