opensim-development – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 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 private static readonly string LogHeader = "[ENTITY TRANSFER STATE MACHINE]";
81  
82 /// <summary>
83 /// If true then on a teleport, the source region waits for a callback from the destination region. If
84 /// a callback fails to arrive within a set time then the user is pulled back into the source region.
85 /// </summary>
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  
92 public EntityTransferStateMachine(EntityTransferModule module)
93 {
94 m_mod = module;
95 }
96  
97 /// <summary>
98 /// Set that an agent is in transit.
99 /// </summary>
100 /// <param name='id'>The ID of the agent being teleported</param>
101 /// <returns>true if the agent was not already in transit, false if it was</returns>
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  
117 /// <summary>
118 /// Updates the state of an agent that is already in transit.
119 /// </summary>
120 /// <param name='id'></param>
121 /// <param name='newState'></param>
122 /// <returns></returns>
123 /// <exception cref='Exception'>Illegal transitions will throw an Exception</exception>
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  
219 /// <summary>
220 /// Gets the current agent transfer state.
221 /// </summary>
222 /// <returns>Null if the agent is not in transit</returns>
223 /// <param name='id'>
224 /// Identifier.
225 /// </param>
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  
237 /// <summary>
238 /// Removes an agent from the transit state machine.
239 /// </summary>
240 /// <param name='id'></param>
241 /// <returns>true if the agent was flagged as being teleported when this method was called, false otherwise</returns>
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 = 200;
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 }