clockwerk-opensim-stable – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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.Physics.Manager;
42 using OpenSim.Services.Interfaces;
43 using GridRegion = OpenSim.Services.Interfaces.GridRegion;
44  
45 namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
46 {
47 /// <summary>
48 /// The possible states that an agent can be in when its being transferred between regions.
49 /// </summary>
50 /// <remarks>
51 /// This is a state machine.
52 ///
53 /// [Entry] => Preparing
54 /// Preparing => { Transferring || Cancelling || CleaningUp || Aborting || [Exit] }
55 /// Transferring => { ReceivedAtDestination || Cancelling || CleaningUp || Aborting }
56 /// Cancelling => CleaningUp || Aborting
57 /// ReceivedAtDestination => CleaningUp || Aborting
58 /// CleaningUp => [Exit]
59 /// Aborting => [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 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  
74 /// <summary>
75 /// Records the state of entities when they are in transfer within or between regions (cross or teleport).
76 /// </summary>
77 public class EntityTransferStateMachine
78 {
79 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
80  
81 /// <summary>
82 /// If true then on a teleport, the source region waits for a callback from the destination region. If
83 /// a callback fails to arrive within a set time then the user is pulled back into the source region.
84 /// </summary>
85 public bool EnableWaitForAgentArrivedAtDestination { get; set; }
86  
87 private EntityTransferModule m_mod;
88  
89 private Dictionary<UUID, AgentTransferState> m_agentsInTransit = new Dictionary<UUID, AgentTransferState>();
90  
91 public EntityTransferStateMachine(EntityTransferModule module)
92 {
93 m_mod = module;
94 }
95  
96 /// <summary>
97 /// Set that an agent is in transit.
98 /// </summary>
99 /// <param name='id'>The ID of the agent being teleported</param>
100 /// <returns>true if the agent was not already in transit, false if it was</returns>
101 internal bool SetInTransit(UUID id)
102 {
103 lock (m_agentsInTransit)
104 {
105 if (!m_agentsInTransit.ContainsKey(id))
106 {
107 m_agentsInTransit[id] = AgentTransferState.Preparing;
108 return true;
109 }
110 }
111  
112 return false;
113 }
114  
115 /// <summary>
116 /// Updates the state of an agent that is already in transit.
117 /// </summary>
118 /// <param name='id'></param>
119 /// <param name='newState'></param>
120 /// <returns></returns>
121 /// <exception cref='Exception'>Illegal transitions will throw an Exception</exception>
122 internal bool UpdateInTransit(UUID id, AgentTransferState newState)
123 {
124 bool transitionOkay = false;
125  
126 // We don't want to throw an exception on cancel since this can come it at any time.
127 bool failIfNotOkay = true;
128  
129 // Should be a failure message if failure is not okay.
130 string failureMessage = null;
131  
132 AgentTransferState? oldState = null;
133  
134 lock (m_agentsInTransit)
135 {
136 // Illegal to try and update an agent that's not actually in transit.
137 if (!m_agentsInTransit.ContainsKey(id))
138 {
139 if (newState != AgentTransferState.Cancelling && newState != AgentTransferState.Aborting)
140 failureMessage = string.Format(
141 "Agent with ID {0} is not registered as in transit in {1}",
142 id, m_mod.Scene.RegionInfo.RegionName);
143 else
144 failIfNotOkay = false;
145 }
146 else
147 {
148 oldState = m_agentsInTransit[id];
149  
150 if (newState == AgentTransferState.Aborting)
151 {
152 transitionOkay = true;
153 }
154 else if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp)
155 {
156 transitionOkay = true;
157 }
158 else if (newState == AgentTransferState.Transferring && oldState == AgentTransferState.Preparing)
159 {
160 transitionOkay = true;
161 }
162 else if (newState == AgentTransferState.ReceivedAtDestination && oldState == AgentTransferState.Transferring)
163 {
164 transitionOkay = true;
165 }
166 else
167 {
168 if (newState == AgentTransferState.Cancelling
169 && (oldState == AgentTransferState.Preparing || oldState == AgentTransferState.Transferring))
170 {
171 transitionOkay = true;
172 }
173 else
174 {
175 failIfNotOkay = false;
176 }
177 }
178  
179 if (!transitionOkay)
180 failureMessage
181 = string.Format(
182 "Agent with ID {0} is not allowed to move from old transit state {1} to new state {2} in {3}",
183 id, oldState, newState, m_mod.Scene.RegionInfo.RegionName);
184 }
185  
186 if (transitionOkay)
187 {
188 m_agentsInTransit[id] = newState;
189  
190 // m_log.DebugFormat(
191 // "[ENTITY TRANSFER STATE MACHINE]: Changed agent with id {0} from state {1} to {2} in {3}",
192 // id, oldState, newState, m_mod.Scene.Name);
193 }
194 else if (failIfNotOkay)
195 {
196 throw new Exception(failureMessage);
197 }
198 // else
199 // {
200 // if (oldState != null)
201 // m_log.DebugFormat(
202 // "[ENTITY TRANSFER STATE MACHINE]: Ignored change of agent with id {0} from state {1} to {2} in {3}",
203 // id, oldState, newState, m_mod.Scene.Name);
204 // else
205 // m_log.DebugFormat(
206 // "[ENTITY TRANSFER STATE MACHINE]: Ignored change of agent with id {0} to state {1} in {2} since agent not in transit",
207 // id, newState, m_mod.Scene.Name);
208 // }
209 }
210  
211 return transitionOkay;
212 }
213  
214 /// <summary>
215 /// Gets the current agent transfer state.
216 /// </summary>
217 /// <returns>Null if the agent is not in transit</returns>
218 /// <param name='id'>
219 /// Identifier.
220 /// </param>
221 internal AgentTransferState? GetAgentTransferState(UUID id)
222 {
223 lock (m_agentsInTransit)
224 {
225 if (!m_agentsInTransit.ContainsKey(id))
226 return null;
227 else
228 return m_agentsInTransit[id];
229 }
230 }
231  
232 /// <summary>
233 /// Removes an agent from the transit state machine.
234 /// </summary>
235 /// <param name='id'></param>
236 /// <returns>true if the agent was flagged as being teleported when this method was called, false otherwise</returns>
237 internal bool ResetFromTransit(UUID id)
238 {
239 lock (m_agentsInTransit)
240 {
241 if (m_agentsInTransit.ContainsKey(id))
242 {
243 AgentTransferState state = m_agentsInTransit[id];
244  
245 if (state == AgentTransferState.Transferring || state == AgentTransferState.ReceivedAtDestination)
246 {
247 // FIXME: For now, we allow exit from any state since a thrown exception in teleport is now guranteed
248 // to be handled properly - ResetFromTransit() could be invoked at any step along the process
249 m_log.WarnFormat(
250 "[ENTITY TRANSFER STATE MACHINE]: Agent with ID {0} should not exit directly from state {1}, should go to {2} state first in {3}",
251 id, state, AgentTransferState.CleaningUp, m_mod.Scene.RegionInfo.RegionName);
252  
253 // throw new Exception(
254 // "Agent with ID {0} cannot exit directly from state {1}, it must go to {2} state first",
255 // state, AgentTransferState.CleaningUp);
256 }
257  
258 m_agentsInTransit.Remove(id);
259  
260 m_log.DebugFormat(
261 "[ENTITY TRANSFER STATE MACHINE]: Agent {0} cleared from transit in {1}",
262 id, m_mod.Scene.RegionInfo.RegionName);
263  
264 return true;
265 }
266 }
267  
268 m_log.WarnFormat(
269 "[ENTITY TRANSFER STATE MACHINE]: Agent {0} requested to clear from transit in {1} but was already cleared",
270 id, m_mod.Scene.RegionInfo.RegionName);
271  
272 return false;
273 }
274  
275 internal bool WaitForAgentArrivedAtDestination(UUID id)
276 {
277 if (!m_mod.WaitForAgentArrivedAtDestination)
278 return true;
279  
280 lock (m_agentsInTransit)
281 {
282 AgentTransferState? currentState = GetAgentTransferState(id);
283  
284 if (currentState == null)
285 throw new Exception(
286 string.Format(
287 "Asked to wait for destination callback for agent with ID {0} in {1} but agent is not in transit",
288 id, m_mod.Scene.RegionInfo.RegionName));
289  
290 if (currentState != AgentTransferState.Transferring && currentState != AgentTransferState.ReceivedAtDestination)
291 throw new Exception(
292 string.Format(
293 "Asked to wait for destination callback for agent with ID {0} in {1} but agent is in state {2}",
294 id, m_mod.Scene.RegionInfo.RegionName, currentState));
295 }
296  
297 int count = 200;
298  
299 // There should be no race condition here since no other code should be removing the agent transfer or
300 // changing the state to another other than Transferring => ReceivedAtDestination.
301  
302 while (count-- > 0)
303 {
304 lock (m_agentsInTransit)
305 {
306 if (m_agentsInTransit[id] == AgentTransferState.ReceivedAtDestination)
307 break;
308 }
309  
310 // m_log.Debug(" >>> Waiting... " + count);
311 Thread.Sleep(100);
312 }
313  
314 return count > 0;
315 }
316  
317 internal void SetAgentArrivedAtDestination(UUID id)
318 {
319 lock (m_agentsInTransit)
320 {
321 if (!m_agentsInTransit.ContainsKey(id))
322 {
323 m_log.WarnFormat(
324 "[ENTITY TRANSFER STATE MACHINE]: Region {0} received notification of arrival in destination of agent {1} but no teleport request is active",
325 m_mod.Scene.RegionInfo.RegionName, id);
326  
327 return;
328 }
329  
330 AgentTransferState currentState = m_agentsInTransit[id];
331  
332 if (currentState == AgentTransferState.ReceivedAtDestination)
333 {
334 // An anomoly but don't make this an outright failure - destination region could be overzealous in sending notification.
335 m_log.WarnFormat(
336 "[ENTITY TRANSFER STATE MACHINE]: Region {0} received notification of arrival in destination of agent {1} but notification has already previously been received",
337 m_mod.Scene.RegionInfo.RegionName, id);
338 }
339 else if (currentState != AgentTransferState.Transferring)
340 {
341 m_log.ErrorFormat(
342 "[ENTITY TRANSFER STATE MACHINE]: Region {0} received notification of arrival in destination of agent {1} but agent is in state {2}",
343 m_mod.Scene.RegionInfo.RegionName, id, currentState);
344  
345 return;
346 }
347  
348 m_agentsInTransit[id] = AgentTransferState.ReceivedAtDestination;
349 }
350 }
351 }
352 }