OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
EntityTransferStateMachine.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.org/
3  * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the OpenSimulator Project nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 using System;
29 using System.Collections.Generic;
30 using System.Net;
31 using System.Reflection;
32 using System.Threading;
33 using OpenMetaverse;
34 using log4net;
35 using Nini.Config;
36 using OpenSim.Framework;
37 using OpenSim.Framework.Capabilities;
38 using OpenSim.Framework.Client;
39 using OpenSim.Region.Framework.Interfaces;
40 using OpenSim.Region.Framework.Scenes;
41 using OpenSim.Region.PhysicsModules.SharedBase;
42 using OpenSim.Services.Interfaces;
44 
45 namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
46 {
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  Cancelling, // The user has cancelled the teleport but we have yet to act upon this.
71  Aborting // The transfer is aborting. Unlike Cancelling, no compensating actions should be performed
72  }
73 
78  {
79  private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
80  private static readonly string LogHeader = "[ENTITY TRANSFER STATE MACHINE]";
81 
86  public bool EnableWaitForAgentArrivedAtDestination { get; set; }
87 
88  private EntityTransferModule m_mod;
89 
90  private Dictionary<UUID, AgentTransferState> m_agentsInTransit = new Dictionary<UUID, AgentTransferState>();
91 
93  {
94  m_mod = module;
95  }
96 
102  internal bool SetInTransit(UUID id)
103  {
104  m_log.DebugFormat("{0} SetInTransit. agent={1}, newState=Preparing", LogHeader, id);
105  lock (m_agentsInTransit)
106  {
107  if (!m_agentsInTransit.ContainsKey(id))
108  {
109  m_agentsInTransit[id] = AgentTransferState.Preparing;
110  return true;
111  }
112  }
113 
114  return false;
115  }
116 
124  internal bool UpdateInTransit(UUID id, AgentTransferState newState)
125  {
126  m_log.DebugFormat("{0} UpdateInTransit. agent={1}, newState={2}", LogHeader, id, newState);
127 
128  bool transitionOkay = false;
129 
130  // We don't want to throw an exception on cancel since this can come it at any time.
131  bool failIfNotOkay = true;
132 
133  // Should be a failure message if failure is not okay.
134  string failureMessage = null;
135 
136  AgentTransferState? oldState = null;
137 
138  lock (m_agentsInTransit)
139  {
140  // Illegal to try and update an agent that's not actually in transit.
141  if (!m_agentsInTransit.ContainsKey(id))
142  {
143  if (newState != AgentTransferState.Cancelling && newState != AgentTransferState.Aborting)
144  failureMessage = string.Format(
145  "Agent with ID {0} is not registered as in transit in {1}",
146  id, m_mod.Scene.RegionInfo.RegionName);
147  else
148  failIfNotOkay = false;
149  }
150  else
151  {
152  oldState = m_agentsInTransit[id];
153 
154  if (newState == AgentTransferState.Aborting)
155  {
156  transitionOkay = true;
157  }
158  else if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp)
159  {
160  transitionOkay = true;
161  }
162  else if (newState == AgentTransferState.Transferring && oldState == AgentTransferState.Preparing)
163  {
164  transitionOkay = true;
165  }
166  else if (newState == AgentTransferState.ReceivedAtDestination && oldState == AgentTransferState.Transferring)
167  {
168  transitionOkay = true;
169  }
170  else
171  {
172  if (newState == AgentTransferState.Cancelling
173  && (oldState == AgentTransferState.Preparing || oldState == AgentTransferState.Transferring))
174  {
175  transitionOkay = true;
176  }
177  else
178  {
179  failIfNotOkay = false;
180  }
181  }
182 
183  if (!transitionOkay)
184  failureMessage
185  = string.Format(
186  "Agent with ID {0} is not allowed to move from old transit state {1} to new state {2} in {3}",
187  id, oldState, newState, m_mod.Scene.RegionInfo.RegionName);
188  }
189 
190  if (transitionOkay)
191  {
192  m_agentsInTransit[id] = newState;
193 
194 // m_log.DebugFormat(
195 // "[ENTITY TRANSFER STATE MACHINE]: Changed agent with id {0} from state {1} to {2} in {3}",
196 // id, oldState, newState, m_mod.Scene.Name);
197  }
198  else if (failIfNotOkay)
199  {
200  m_log.DebugFormat("{0} UpdateInTransit. Throwing transition failure = {1}", LogHeader, failureMessage);
201  throw new Exception(failureMessage);
202  }
203 // else
204 // {
205 // if (oldState != null)
206 // m_log.DebugFormat(
207 // "[ENTITY TRANSFER STATE MACHINE]: Ignored change of agent with id {0} from state {1} to {2} in {3}",
208 // id, oldState, newState, m_mod.Scene.Name);
209 // else
210 // m_log.DebugFormat(
211 // "[ENTITY TRANSFER STATE MACHINE]: Ignored change of agent with id {0} to state {1} in {2} since agent not in transit",
212 // id, newState, m_mod.Scene.Name);
213 // }
214  }
215 
216  return transitionOkay;
217  }
218 
226  internal AgentTransferState? GetAgentTransferState(UUID id)
227  {
228  lock (m_agentsInTransit)
229  {
230  if (!m_agentsInTransit.ContainsKey(id))
231  return null;
232  else
233  return m_agentsInTransit[id];
234  }
235  }
236 
242  internal bool ResetFromTransit(UUID id)
243  {
244  lock (m_agentsInTransit)
245  {
246  if (m_agentsInTransit.ContainsKey(id))
247  {
248  AgentTransferState state = m_agentsInTransit[id];
249 
250  if (state == AgentTransferState.Transferring || state == AgentTransferState.ReceivedAtDestination)
251  {
252  // FIXME: For now, we allow exit from any state since a thrown exception in teleport is now guranteed
253  // to be handled properly - ResetFromTransit() could be invoked at any step along the process
254  m_log.WarnFormat(
255  "[ENTITY TRANSFER STATE MACHINE]: Agent with ID {0} should not exit directly from state {1}, should go to {2} state first in {3}",
256  id, state, AgentTransferState.CleaningUp, m_mod.Scene.RegionInfo.RegionName);
257 
258 // throw new Exception(
259 // "Agent with ID {0} cannot exit directly from state {1}, it must go to {2} state first",
260 // state, AgentTransferState.CleaningUp);
261  }
262 
263  m_agentsInTransit.Remove(id);
264 
265  m_log.DebugFormat(
266  "[ENTITY TRANSFER STATE MACHINE]: Agent {0} cleared from transit in {1}",
267  id, m_mod.Scene.RegionInfo.RegionName);
268 
269  return true;
270  }
271  }
272 
273  m_log.WarnFormat(
274  "[ENTITY TRANSFER STATE MACHINE]: Agent {0} requested to clear from transit in {1} but was already cleared",
275  id, m_mod.Scene.RegionInfo.RegionName);
276 
277  return false;
278  }
279 
280  internal bool WaitForAgentArrivedAtDestination(UUID id)
281  {
282  if (!m_mod.WaitForAgentArrivedAtDestination)
283  return true;
284 
285  lock (m_agentsInTransit)
286  {
287  AgentTransferState? currentState = GetAgentTransferState(id);
288 
289  if (currentState == null)
290  throw new Exception(
291  string.Format(
292  "Asked to wait for destination callback for agent with ID {0} in {1} but agent is not in transit",
293  id, m_mod.Scene.RegionInfo.RegionName));
294 
295  if (currentState != AgentTransferState.Transferring && currentState != AgentTransferState.ReceivedAtDestination)
296  throw new Exception(
297  string.Format(
298  "Asked to wait for destination callback for agent with ID {0} in {1} but agent is in state {2}",
299  id, m_mod.Scene.RegionInfo.RegionName, currentState));
300  }
301 
302  int count = 400;
303 
304  // There should be no race condition here since no other code should be removing the agent transfer or
305  // changing the state to another other than Transferring => ReceivedAtDestination.
306 
307  while (count-- > 0)
308  {
309  lock (m_agentsInTransit)
310  {
311  if (m_agentsInTransit[id] == AgentTransferState.ReceivedAtDestination)
312  break;
313  }
314 
315 // m_log.Debug(" >>> Waiting... " + count);
316  Thread.Sleep(100);
317  }
318 
319  return count > 0;
320  }
321 
322  internal void SetAgentArrivedAtDestination(UUID id)
323  {
324  lock (m_agentsInTransit)
325  {
326  if (!m_agentsInTransit.ContainsKey(id))
327  {
328  m_log.WarnFormat(
329  "[ENTITY TRANSFER STATE MACHINE]: Region {0} received notification of arrival in destination of agent {1} but no teleport request is active",
330  m_mod.Scene.RegionInfo.RegionName, id);
331 
332  return;
333  }
334 
335  AgentTransferState currentState = m_agentsInTransit[id];
336 
337  if (currentState == AgentTransferState.ReceivedAtDestination)
338  {
339  // An anomoly but don't make this an outright failure - destination region could be overzealous in sending notification.
340  m_log.WarnFormat(
341  "[ENTITY TRANSFER STATE MACHINE]: Region {0} received notification of arrival in destination of agent {1} but notification has already previously been received",
342  m_mod.Scene.RegionInfo.RegionName, id);
343  }
344  else if (currentState != AgentTransferState.Transferring)
345  {
346  m_log.ErrorFormat(
347  "[ENTITY TRANSFER STATE MACHINE]: Region {0} received notification of arrival in destination of agent {1} but agent is in state {2}",
348  m_mod.Scene.RegionInfo.RegionName, id, currentState);
349 
350  return;
351  }
352 
353  m_agentsInTransit[id] = AgentTransferState.ReceivedAtDestination;
354  }
355  }
356  }
357 }
OpenSim.Services.Interfaces.GridRegion GridRegion
AgentTransferState
The possible states that an agent can be in when its being transferred between regions.
Records the state of entities when they are in transfer within or between regions (cross or teleport)...