clockwerk-opensim – 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 OpenSim.Framework;
34 using OpenSim.Framework.Capabilities;
35 using OpenSim.Framework.Client;
36 using OpenSim.Framework.Monitoring;
37 using OpenSim.Region.Framework.Interfaces;
38 using OpenSim.Region.Framework.Scenes;
39 using OpenSim.Region.Physics.Manager;
40 using OpenSim.Services.Interfaces;
41  
42 using GridRegion = OpenSim.Services.Interfaces.GridRegion;
43  
44 using OpenMetaverse;
45 using log4net;
46 using Nini.Config;
47 using Mono.Addins;
48  
49 namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
50 {
51 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "EntityTransferModule")]
52 public class EntityTransferModule : INonSharedRegionModule, IEntityTransferModule
53 {
54 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
55 private static readonly string LogHeader = "[ENTITY TRANSFER MODULE]";
56  
57 public const int DefaultMaxTransferDistance = 4095;
58 public const bool WaitForAgentArrivedAtDestinationDefault = true;
59  
60 public string OutgoingTransferVersionName { get; set; }
61  
62 /// <summary>
63 /// Determine the maximum entity transfer version we will use for teleports.
64 /// </summary>
65 public float MaxOutgoingTransferVersion { get; set; }
66  
67 /// <summary>
68 /// The maximum distance, in standard region units (256m) that an agent is allowed to transfer.
69 /// </summary>
70 public int MaxTransferDistance { get; set; }
71  
72 /// <summary>
73 /// If true then on a teleport, the source region waits for a callback from the destination region. If
74 /// a callback fails to arrive within a set time then the user is pulled back into the source region.
75 /// </summary>
76 public bool WaitForAgentArrivedAtDestination { get; set; }
77  
78 /// <summary>
79 /// If true then we ask the viewer to disable teleport cancellation and ignore teleport requests.
80 /// </summary>
81 /// <remarks>
82 /// This is useful in situations where teleport is very likely to always succeed and we want to avoid a
83 /// situation where avatars can be come 'stuck' due to a failed teleport cancellation. Unfortunately, the
84 /// nature of the teleport protocol makes it extremely difficult (maybe impossible) to make teleport
85 /// cancellation consistently suceed.
86 /// </remarks>
87 public bool DisableInterRegionTeleportCancellation { get; set; }
88  
89 /// <summary>
90 /// Number of times inter-region teleport was attempted.
91 /// </summary>
92 private Stat m_interRegionTeleportAttempts;
93  
94 /// <summary>
95 /// Number of times inter-region teleport was aborted (due to simultaneous client logout).
96 /// </summary>
97 private Stat m_interRegionTeleportAborts;
98  
99 /// <summary>
100 /// Number of times inter-region teleport was successfully cancelled by the client.
101 /// </summary>
102 private Stat m_interRegionTeleportCancels;
103  
104 /// <summary>
105 /// Number of times inter-region teleport failed due to server/client/network problems (e.g. viewer failed to
106 /// connect with destination region).
107 /// </summary>
108 /// <remarks>
109 /// This is not necessarily a problem for this simulator - in open-grid/hg conditions, viewer connectivity to
110 /// destination simulator is unknown.
111 /// </remarks>
112 private Stat m_interRegionTeleportFailures;
113  
114 protected string m_ThisHomeURI;
115 protected string m_GatekeeperURI;
116  
117 protected bool m_Enabled = false;
118  
119 public Scene Scene { get; private set; }
120  
121 /// <summary>
122 /// Handles recording and manipulation of state for entities that are in transfer within or between regions
123 /// (cross or teleport).
124 /// </summary>
125 private EntityTransferStateMachine m_entityTransferStateMachine;
126  
127 // For performance, we keed a cached of banned regions so we don't keep going
128 // to the grid service.
129 private class BannedRegionCache
130 {
131 private ExpiringCache<UUID, ExpiringCache<ulong, DateTime>> m_bannedRegions =
132 new ExpiringCache<UUID, ExpiringCache<ulong, DateTime>>();
133 ExpiringCache<ulong, DateTime> m_idCache;
134 DateTime m_banUntil;
135 public BannedRegionCache()
136 {
137 }
138 // Return 'true' if there is a valid ban entry for this agent in this region
139 public bool IfBanned(ulong pRegionHandle, UUID pAgentID)
140 {
141 bool ret = false;
142 if (m_bannedRegions.TryGetValue(pAgentID, out m_idCache))
143 {
144 if (m_idCache.TryGetValue(pRegionHandle, out m_banUntil))
145 {
146 if (DateTime.Now < m_banUntil)
147 {
148 ret = true;
149 }
150 }
151 }
152 return ret;
153 }
154 // Add this agent in this region as a banned person
155 public void Add(ulong pRegionHandle, UUID pAgentID)
156 {
157 if (!m_bannedRegions.TryGetValue(pAgentID, out m_idCache))
158 {
159 m_idCache = new ExpiringCache<ulong, DateTime>();
160 m_bannedRegions.Add(pAgentID, m_idCache, TimeSpan.FromSeconds(45));
161 }
162 m_idCache.Add(pRegionHandle, DateTime.Now + TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15));
163 }
164 // Remove the agent from the region's banned list
165 public void Remove(ulong pRegionHandle, UUID pAgentID)
166 {
167 if (m_bannedRegions.TryGetValue(pAgentID, out m_idCache))
168 {
169 m_idCache.Remove(pRegionHandle);
170 }
171 }
172 }
173 private BannedRegionCache m_bannedRegionCache = new BannedRegionCache();
174  
175 private IEventQueue m_eqModule;
176 private IRegionCombinerModule m_regionCombinerModule;
177  
178 #region ISharedRegionModule
179  
180 public Type ReplaceableInterface
181 {
182 get { return null; }
183 }
184  
185 public virtual string Name
186 {
187 get { return "BasicEntityTransferModule"; }
188 }
189  
190 public virtual void Initialise(IConfigSource source)
191 {
192 IConfig moduleConfig = source.Configs["Modules"];
193 if (moduleConfig != null)
194 {
195 string name = moduleConfig.GetString("EntityTransferModule", "");
196 if (name == Name)
197 {
198 InitialiseCommon(source);
199 m_log.DebugFormat("[ENTITY TRANSFER MODULE]: {0} enabled.", Name);
200 }
201 }
202 }
203  
204 /// <summary>
205 /// Initialize config common for this module and any descendents.
206 /// </summary>
207 /// <param name="source"></param>
208 protected virtual void InitialiseCommon(IConfigSource source)
209 {
210 string transferVersionName = "SIMULATION";
211 float maxTransferVersion = 0.3f;
212  
213 IConfig hypergridConfig = source.Configs["Hypergrid"];
214 if (hypergridConfig != null)
215 {
216 m_ThisHomeURI = hypergridConfig.GetString("HomeURI", string.Empty);
217 if (m_ThisHomeURI != string.Empty && !m_ThisHomeURI.EndsWith("/"))
218 m_ThisHomeURI += '/';
219  
220 m_GatekeeperURI = hypergridConfig.GetString("GatekeeperURI", string.Empty);
221 if (m_GatekeeperURI != string.Empty && !m_GatekeeperURI.EndsWith("/"))
222 m_GatekeeperURI += '/';
223 }
224  
225 IConfig transferConfig = source.Configs["EntityTransfer"];
226 if (transferConfig != null)
227 {
228 string rawVersion
229 = transferConfig.GetString(
230 "MaxOutgoingTransferVersion",
231 string.Format("{0}/{1}", transferVersionName, maxTransferVersion));
232  
233 string[] rawVersionComponents = rawVersion.Split(new char[] { '/' });
234  
235 bool versionValid = false;
236  
237 if (rawVersionComponents.Length >= 2)
238 versionValid = float.TryParse(rawVersionComponents[1], out maxTransferVersion);
239  
240 if (!versionValid)
241 {
242 m_log.ErrorFormat(
243 "[ENTITY TRANSFER MODULE]: MaxOutgoingTransferVersion {0} is invalid, using {1}",
244 rawVersion, string.Format("{0}/{1}", transferVersionName, maxTransferVersion));
245 }
246 else
247 {
248 transferVersionName = rawVersionComponents[0];
249  
250 m_log.InfoFormat(
251 "[ENTITY TRANSFER MODULE]: MaxOutgoingTransferVersion set to {0}",
252 string.Format("{0}/{1}", transferVersionName, maxTransferVersion));
253 }
254  
255 DisableInterRegionTeleportCancellation
256 = transferConfig.GetBoolean("DisableInterRegionTeleportCancellation", false);
257  
258 WaitForAgentArrivedAtDestination
259 = transferConfig.GetBoolean("wait_for_callback", WaitForAgentArrivedAtDestinationDefault);
260  
261 MaxTransferDistance = transferConfig.GetInt("max_distance", DefaultMaxTransferDistance);
262 }
263 else
264 {
265 MaxTransferDistance = DefaultMaxTransferDistance;
266 }
267  
268 OutgoingTransferVersionName = transferVersionName;
269 MaxOutgoingTransferVersion = maxTransferVersion;
270  
271 m_entityTransferStateMachine = new EntityTransferStateMachine(this);
272  
273 m_Enabled = true;
274 }
275  
276 public virtual void PostInitialise()
277 {
278 }
279  
280 public virtual void AddRegion(Scene scene)
281 {
282 if (!m_Enabled)
283 return;
284  
285 Scene = scene;
286  
287 m_interRegionTeleportAttempts =
288 new Stat(
289 "InterRegionTeleportAttempts",
290 "Number of inter-region teleports attempted.",
291 "This does not count attempts which failed due to pre-conditions (e.g. target simulator refused access).\n"
292 + "You can get successfully teleports by subtracting aborts, cancels and teleport failures from this figure.",
293 "",
294 "entitytransfer",
295 Scene.Name,
296 StatType.Push,
297 null,
298 StatVerbosity.Debug);
299  
300 m_interRegionTeleportAborts =
301 new Stat(
302 "InterRegionTeleportAborts",
303 "Number of inter-region teleports aborted due to client actions.",
304 "The chief action is simultaneous logout whilst teleporting.",
305 "",
306 "entitytransfer",
307 Scene.Name,
308 StatType.Push,
309 null,
310 StatVerbosity.Debug);
311  
312 m_interRegionTeleportCancels =
313 new Stat(
314 "InterRegionTeleportCancels",
315 "Number of inter-region teleports cancelled by the client.",
316 null,
317 "",
318 "entitytransfer",
319 Scene.Name,
320 StatType.Push,
321 null,
322 StatVerbosity.Debug);
323  
324 m_interRegionTeleportFailures =
325 new Stat(
326 "InterRegionTeleportFailures",
327 "Number of inter-region teleports that failed due to server/client/network issues.",
328 "This number may not be very helpful in open-grid/hg situations as the network connectivity/quality of destinations is uncontrollable.",
329 "",
330 "entitytransfer",
331 Scene.Name,
332 StatType.Push,
333 null,
334 StatVerbosity.Debug);
335  
336 StatsManager.RegisterStat(m_interRegionTeleportAttempts);
337 StatsManager.RegisterStat(m_interRegionTeleportAborts);
338 StatsManager.RegisterStat(m_interRegionTeleportCancels);
339 StatsManager.RegisterStat(m_interRegionTeleportFailures);
340  
341 scene.RegisterModuleInterface<IEntityTransferModule>(this);
342 scene.EventManager.OnNewClient += OnNewClient;
343 }
344  
345 protected virtual void OnNewClient(IClientAPI client)
346 {
347 client.OnTeleportHomeRequest += TriggerTeleportHome;
348 client.OnTeleportLandmarkRequest += RequestTeleportLandmark;
349  
350 if (!DisableInterRegionTeleportCancellation)
351 client.OnTeleportCancel += OnClientCancelTeleport;
352  
353 client.OnConnectionClosed += OnConnectionClosed;
354 }
355  
356 public virtual void Close() {}
357  
358 public virtual void RemoveRegion(Scene scene)
359 {
360 if (m_Enabled)
361 {
362 StatsManager.DeregisterStat(m_interRegionTeleportAttempts);
363 StatsManager.DeregisterStat(m_interRegionTeleportAborts);
364 StatsManager.DeregisterStat(m_interRegionTeleportCancels);
365 StatsManager.DeregisterStat(m_interRegionTeleportFailures);
366 }
367 }
368  
369 public virtual void RegionLoaded(Scene scene)
370 {
371 if (!m_Enabled)
372 return;
373  
374 m_eqModule = Scene.RequestModuleInterface<IEventQueue>();
375 m_regionCombinerModule = Scene.RequestModuleInterface<IRegionCombinerModule>();
376 }
377  
378 #endregion
379  
380 #region Agent Teleports
381  
382 private void OnConnectionClosed(IClientAPI client)
383 {
384 if (client.IsLoggingOut && m_entityTransferStateMachine.UpdateInTransit(client.AgentId, AgentTransferState.Aborting))
385 {
386 m_log.DebugFormat(
387 "[ENTITY TRANSFER MODULE]: Aborted teleport request from {0} in {1} due to simultaneous logout",
388 client.Name, Scene.Name);
389 }
390 }
391  
392 private void OnClientCancelTeleport(IClientAPI client)
393 {
394 m_entityTransferStateMachine.UpdateInTransit(client.AgentId, AgentTransferState.Cancelling);
395  
396 m_log.DebugFormat(
397 "[ENTITY TRANSFER MODULE]: Received teleport cancel request from {0} in {1}", client.Name, Scene.Name);
398 }
399  
400 // Attempt to teleport the ScenePresence to the specified position in the specified region (spec'ed by its handle).
401 public void Teleport(ScenePresence sp, ulong regionHandle, Vector3 position, Vector3 lookAt, uint teleportFlags)
402 {
403 if (sp.Scene.Permissions.IsGridGod(sp.UUID))
404 {
405 // This user will be a God in the destination scene, too
406 teleportFlags |= (uint)TeleportFlags.Godlike;
407 }
408  
409 if (!sp.Scene.Permissions.CanTeleport(sp.UUID))
410 return;
411  
412 string destinationRegionName = "(not found)";
413  
414 // Record that this agent is in transit so that we can prevent simultaneous requests and do later detection
415 // of whether the destination region completes the teleport.
416 if (!m_entityTransferStateMachine.SetInTransit(sp.UUID))
417 {
418 m_log.DebugFormat(
419 "[ENTITY TRANSFER MODULE]: Ignoring teleport request of {0} {1} to {2}@{3} - agent is already in transit.",
420 sp.Name, sp.UUID, position, regionHandle);
421  
422 sp.ControllingClient.SendTeleportFailed("Previous teleport process incomplete. Please retry shortly.");
423  
424 return;
425 }
426  
427 try
428 {
429 // Reset animations; the viewer does that in teleports.
430 sp.Animator.ResetAnimations();
431  
432 if (regionHandle == sp.Scene.RegionInfo.RegionHandle)
433 {
434 destinationRegionName = sp.Scene.RegionInfo.RegionName;
435  
436 TeleportAgentWithinRegion(sp, position, lookAt, teleportFlags);
437 }
438 else // Another region possibly in another simulator
439 {
440 GridRegion finalDestination = null;
441 try
442 {
443 TeleportAgentToDifferentRegion(
444 sp, regionHandle, position, lookAt, teleportFlags, out finalDestination);
445 }
446 finally
447 {
448 if (finalDestination != null)
449 destinationRegionName = finalDestination.RegionName;
450 }
451 }
452 }
453 catch (Exception e)
454 {
455 m_log.ErrorFormat(
456 "[ENTITY TRANSFER MODULE]: Exception on teleport of {0} from {1}@{2} to {3}@{4}: {5}{6}",
457 sp.Name, sp.AbsolutePosition, sp.Scene.RegionInfo.RegionName, position, destinationRegionName,
458 e.Message, e.StackTrace);
459  
460 sp.ControllingClient.SendTeleportFailed("Internal error");
461 }
462 finally
463 {
464 m_entityTransferStateMachine.ResetFromTransit(sp.UUID);
465 }
466 }
467  
468 /// <summary>
469 /// Teleports the agent within its current region.
470 /// </summary>
471 /// <param name="sp"></param>
472 /// <param name="position"></param>
473 /// <param name="lookAt"></param>
474 /// <param name="teleportFlags"></param>
475 private void TeleportAgentWithinRegion(ScenePresence sp, Vector3 position, Vector3 lookAt, uint teleportFlags)
476 {
477 m_log.DebugFormat(
478 "[ENTITY TRANSFER MODULE]: Teleport for {0} to {1} within {2}",
479 sp.Name, position, sp.Scene.RegionInfo.RegionName);
480  
481 // Teleport within the same region
482 if (!sp.Scene.PositionIsInCurrentRegion(position) || position.Z < 0)
483 {
484 Vector3 emergencyPos = new Vector3(128, 128, 128);
485  
486 m_log.WarnFormat(
487 "[ENTITY TRANSFER MODULE]: RequestTeleportToLocation() was given an illegal position of {0} for avatar {1}, {2} in {3}. Substituting {4}",
488 position, sp.Name, sp.UUID, Scene.Name, emergencyPos);
489  
490 position = emergencyPos;
491 }
492  
493 // TODO: Get proper AVG Height
494 float localAVHeight = 1.56f;
495 float posZLimit = 22;
496  
497 // TODO: Check other Scene HeightField
498 posZLimit = (float)sp.Scene.Heightmap[(int)position.X, (int)position.Y];
499  
500 float newPosZ = posZLimit + localAVHeight;
501 if (posZLimit >= (position.Z - (localAVHeight / 2)) && !(Single.IsInfinity(newPosZ) || Single.IsNaN(newPosZ)))
502 {
503 position.Z = newPosZ;
504 }
505  
506 if (sp.Flying)
507 teleportFlags |= (uint)TeleportFlags.IsFlying;
508  
509 m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring);
510  
511 sp.ControllingClient.SendTeleportStart(teleportFlags);
512  
513 sp.ControllingClient.SendLocalTeleport(position, lookAt, teleportFlags);
514 sp.TeleportFlags = (Constants.TeleportFlags)teleportFlags;
515 sp.Velocity = Vector3.Zero;
516 sp.Teleport(position);
517  
518 m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.ReceivedAtDestination);
519  
520 foreach (SceneObjectGroup grp in sp.GetAttachments())
521 {
522 sp.Scene.EventManager.TriggerOnScriptChangedEvent(grp.LocalId, (uint)Changed.TELEPORT);
523 }
524  
525 m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp);
526 }
527  
528 /// <summary>
529 /// Teleports the agent to a different region.
530 /// </summary>
531 /// <param name='sp'></param>
532 /// <param name='regionHandle'>/param>
533 /// <param name='position'></param>
534 /// <param name='lookAt'></param>
535 /// <param name='teleportFlags'></param>
536 /// <param name='finalDestination'></param>
537 private void TeleportAgentToDifferentRegion(
538 ScenePresence sp, ulong regionHandle, Vector3 position,
539 Vector3 lookAt, uint teleportFlags, out GridRegion finalDestination)
540 {
541 // Get destination region taking into account that the address could be an offset
542 // region inside a varregion.
543 GridRegion reg = GetTeleportDestinationRegion(sp.Scene.GridService, sp.Scene.RegionInfo.ScopeID, regionHandle, ref position);
544  
545 if (reg != null)
546 {
547 string homeURI = Scene.GetAgentHomeURI(sp.ControllingClient.AgentId);
548  
549 string message;
550 finalDestination = GetFinalDestination(reg, sp.ControllingClient.AgentId, homeURI, out message);
551  
552 if (finalDestination == null)
553 {
554 m_log.WarnFormat( "{0} Final destination is having problems. Unable to teleport {1} {2}: {3}",
555 LogHeader, sp.Name, sp.UUID, message);
556  
557 sp.ControllingClient.SendTeleportFailed(message);
558 return;
559 }
560  
561 // Check that these are not the same coordinates
562 if (finalDestination.RegionLocX == sp.Scene.RegionInfo.RegionLocX &&
563 finalDestination.RegionLocY == sp.Scene.RegionInfo.RegionLocY)
564 {
565 // Can't do. Viewer crashes
566 sp.ControllingClient.SendTeleportFailed("Space warp! You would crash. Move to a different region and try again.");
567 return;
568 }
569  
570 // Validate assorted conditions
571 string reason = string.Empty;
572 if (!ValidateGenericConditions(sp, reg, finalDestination, teleportFlags, out reason))
573 {
574 sp.ControllingClient.SendTeleportFailed(reason);
575 return;
576 }
577  
578 if (message != null)
579 sp.ControllingClient.SendAgentAlertMessage(message, true);
580  
581 //
582 // This is it
583 //
584 DoTeleportInternal(sp, reg, finalDestination, position, lookAt, teleportFlags);
585 //
586 //
587 //
588 }
589 else
590 {
591 finalDestination = null;
592  
593 // TP to a place that doesn't exist (anymore)
594 // Inform the viewer about that
595 sp.ControllingClient.SendTeleportFailed("The region you tried to teleport to doesn't exist anymore");
596  
597 // and set the map-tile to '(Offline)'
598 uint regX, regY;
599 Util.RegionHandleToRegionLoc(regionHandle, out regX, out regY);
600  
601 MapBlockData block = new MapBlockData();
602 block.X = (ushort)regX;
603 block.Y = (ushort)regY;
604 block.Access = (byte)SimAccess.Down;
605  
606 List<MapBlockData> blocks = new List<MapBlockData>();
607 blocks.Add(block);
608 sp.ControllingClient.SendMapBlock(blocks, 0);
609 }
610 }
611  
612 // The teleport address could be an address in a subregion of a larger varregion.
613 // Find the real base region and adjust the teleport location to account for the
614 // larger region.
615 private GridRegion GetTeleportDestinationRegion(IGridService gridService, UUID scope, ulong regionHandle, ref Vector3 position)
616 {
617 uint x = 0, y = 0;
618 Util.RegionHandleToWorldLoc(regionHandle, out x, out y);
619  
620 // Compute the world location we're teleporting to
621 double worldX = (double)x + position.X;
622 double worldY = (double)y + position.Y;
623  
624 // Find the region that contains the position
625 GridRegion reg = GetRegionContainingWorldLocation(gridService, scope, worldX, worldY);
626  
627 if (reg != null)
628 {
629 // modify the position for the offset into the actual region returned
630 position.X += x - reg.RegionLocX;
631 position.Y += y - reg.RegionLocY;
632 }
633  
634 return reg;
635 }
636  
637 // Nothing to validate here
638 protected virtual bool ValidateGenericConditions(ScenePresence sp, GridRegion reg, GridRegion finalDestination, uint teleportFlags, out string reason)
639 {
640 reason = String.Empty;
641 return true;
642 }
643  
644 /// <summary>
645 /// Determines whether this instance is within the max transfer distance.
646 /// </summary>
647 /// <param name="sourceRegion"></param>
648 /// <param name="destRegion"></param>
649 /// <returns>
650 /// <c>true</c> if this instance is within max transfer distance; otherwise, <c>false</c>.
651 /// </returns>
652 private bool IsWithinMaxTeleportDistance(RegionInfo sourceRegion, GridRegion destRegion)
653 {
654 if(MaxTransferDistance == 0)
655 return true;
656  
657 // m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Source co-ords are x={0} y={1}", curRegionX, curRegionY);
658 //
659 // m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Final dest is x={0} y={1} {2}@{3}",
660 // destRegionX, destRegionY, finalDestination.RegionID, finalDestination.ServerURI);
661  
662 // Insanely, RegionLoc on RegionInfo is the 256m map co-ord whilst GridRegion.RegionLoc is the raw meters position.
663 return Math.Abs(sourceRegion.RegionLocX - destRegion.RegionCoordX) <= MaxTransferDistance
664 && Math.Abs(sourceRegion.RegionLocY - destRegion.RegionCoordY) <= MaxTransferDistance;
665 }
666  
667 /// <summary>
668 /// Wraps DoTeleportInternal() and manages the transfer state.
669 /// </summary>
670 public void DoTeleport(
671 ScenePresence sp, GridRegion reg, GridRegion finalDestination,
672 Vector3 position, Vector3 lookAt, uint teleportFlags)
673 {
674 // Record that this agent is in transit so that we can prevent simultaneous requests and do later detection
675 // of whether the destination region completes the teleport.
676 if (!m_entityTransferStateMachine.SetInTransit(sp.UUID))
677 {
678 m_log.DebugFormat(
679 "[ENTITY TRANSFER MODULE]: Ignoring teleport request of {0} {1} to {2} ({3}) {4}/{5} - agent is already in transit.",
680 sp.Name, sp.UUID, reg.ServerURI, finalDestination.ServerURI, finalDestination.RegionName, position);
681 sp.ControllingClient.SendTeleportFailed("Agent is already in transit.");
682 return;
683 }
684  
685 try
686 {
687 DoTeleportInternal(sp, reg, finalDestination, position, lookAt, teleportFlags);
688 }
689 catch (Exception e)
690 {
691 m_log.ErrorFormat(
692 "[ENTITY TRANSFER MODULE]: Exception on teleport of {0} from {1}@{2} to {3}@{4}: {5}{6}",
693 sp.Name, sp.AbsolutePosition, sp.Scene.RegionInfo.RegionName, position, finalDestination.RegionName,
694 e.Message, e.StackTrace);
695  
696 sp.ControllingClient.SendTeleportFailed("Internal error");
697 }
698 finally
699 {
700 m_entityTransferStateMachine.ResetFromTransit(sp.UUID);
701 }
702 }
703  
704 /// <summary>
705 /// Teleports the agent to another region.
706 /// This method doesn't manage the transfer state; the caller must do that.
707 /// </summary>
708 private void DoTeleportInternal(
709 ScenePresence sp, GridRegion reg, GridRegion finalDestination,
710 Vector3 position, Vector3 lookAt, uint teleportFlags)
711 {
712 if (reg == null || finalDestination == null)
713 {
714 sp.ControllingClient.SendTeleportFailed("Unable to locate destination");
715 return;
716 }
717  
718 string homeURI = Scene.GetAgentHomeURI(sp.ControllingClient.AgentId);
719  
720 m_log.DebugFormat(
721 "[ENTITY TRANSFER MODULE]: Teleporting {0} {1} from {2} to {3} ({4}) {5}/{6}",
722 sp.Name, sp.UUID, sp.Scene.RegionInfo.RegionName,
723 reg.ServerURI, finalDestination.ServerURI, finalDestination.RegionName, position);
724  
725 RegionInfo sourceRegion = sp.Scene.RegionInfo;
726  
727 if (!IsWithinMaxTeleportDistance(sourceRegion, finalDestination))
728 {
729 sp.ControllingClient.SendTeleportFailed(
730 string.Format(
731 "Can't teleport to {0} ({1},{2}) from {3} ({4},{5}), destination is more than {6} regions way",
732 finalDestination.RegionName, finalDestination.RegionCoordX, finalDestination.RegionCoordY,
733 sourceRegion.RegionName, sourceRegion.RegionLocX, sourceRegion.RegionLocY,
734 MaxTransferDistance));
735  
736 return;
737 }
738  
739 uint newRegionX, newRegionY, oldRegionX, oldRegionY;
740 Util.RegionHandleToRegionLoc(reg.RegionHandle, out newRegionX, out newRegionY);
741 Util.RegionHandleToRegionLoc(sp.Scene.RegionInfo.RegionHandle, out oldRegionX, out oldRegionY);
742  
743 ulong destinationHandle = finalDestination.RegionHandle;
744  
745 // Let's do DNS resolution only once in this process, please!
746 // This may be a costly operation. The reg.ExternalEndPoint field is not a passive field,
747 // it's actually doing a lot of work.
748 IPEndPoint endPoint = finalDestination.ExternalEndPoint;
749 if (endPoint == null || endPoint.Address == null)
750 {
751 sp.ControllingClient.SendTeleportFailed("Remote Region appears to be down");
752  
753 return;
754 }
755  
756 if (!sp.ValidateAttachments())
757 m_log.DebugFormat(
758 "[ENTITY TRANSFER MODULE]: Failed validation of all attachments for teleport of {0} from {1} to {2}. Continuing.",
759 sp.Name, sp.Scene.Name, finalDestination.RegionName);
760  
761 string reason;
762 string version;
763 string myversion = string.Format("{0}/{1}", OutgoingTransferVersionName, MaxOutgoingTransferVersion);
764 if (!Scene.SimulationService.QueryAccess(
765 finalDestination, sp.ControllingClient.AgentId, homeURI, true, position, myversion, out version, out reason))
766 {
767 sp.ControllingClient.SendTeleportFailed(reason);
768  
769 m_log.DebugFormat(
770 "[ENTITY TRANSFER MODULE]: {0} was stopped from teleporting from {1} to {2} because: {3}",
771 sp.Name, sp.Scene.Name, finalDestination.RegionName, reason);
772  
773 return;
774 }
775  
776 // Before this point, teleport 'failure' is due to checkable pre-conditions such as whether the target
777 // simulator can be found and is explicitly prepared to allow access. Therefore, we will not count these
778 // as server attempts.
779 m_interRegionTeleportAttempts.Value++;
780  
781 m_log.DebugFormat(
782 "[ENTITY TRANSFER MODULE]: {0} max transfer version is {1}/{2}, {3} max version is {4}",
783 sp.Scene.Name, OutgoingTransferVersionName, MaxOutgoingTransferVersion, finalDestination.RegionName, version);
784  
785 // Fixing a bug where teleporting while sitting results in the avatar ending up removed from
786 // both regions
787 if (sp.ParentID != (uint)0)
788 sp.StandUp();
789 else if (sp.Flying)
790 teleportFlags |= (uint)TeleportFlags.IsFlying;
791  
792 if (DisableInterRegionTeleportCancellation)
793 teleportFlags |= (uint)TeleportFlags.DisableCancel;
794  
795 // At least on LL 3.3.4, this is not strictly necessary - a teleport will succeed without sending this to
796 // the viewer. However, it might mean that the viewer does not see the black teleport screen (untested).
797 sp.ControllingClient.SendTeleportStart(teleportFlags);
798  
799 // the avatar.Close below will clear the child region list. We need this below for (possibly)
800 // closing the child agents, so save it here (we need a copy as it is Clear()-ed).
801 //List<ulong> childRegions = avatar.KnownRegionHandles;
802 // Compared to ScenePresence.CrossToNewRegion(), there's no obvious code to handle a teleport
803 // failure at this point (unlike a border crossing failure). So perhaps this can never fail
804 // once we reach here...
805 //avatar.Scene.RemoveCapsHandler(avatar.UUID);
806  
807 string capsPath = String.Empty;
808  
809 AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode);
810 AgentCircuitData agentCircuit = sp.ControllingClient.RequestClientInfo();
811 agentCircuit.startpos = position;
812 agentCircuit.child = true;
813 agentCircuit.Appearance = sp.Appearance;
814 if (currentAgentCircuit != null)
815 {
816 agentCircuit.ServiceURLs = currentAgentCircuit.ServiceURLs;
817 agentCircuit.IPAddress = currentAgentCircuit.IPAddress;
818 agentCircuit.Viewer = currentAgentCircuit.Viewer;
819 agentCircuit.Channel = currentAgentCircuit.Channel;
820 agentCircuit.Mac = currentAgentCircuit.Mac;
821 agentCircuit.Id0 = currentAgentCircuit.Id0;
822 }
823  
824 // if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY))
825 float dist = (float)Math.Max(sp.Scene.DefaultDrawDistance,
826 (float)Math.Max(sp.Scene.RegionInfo.RegionSizeX, sp.Scene.RegionInfo.RegionSizeY));
827 if (NeedsNewAgent(dist, oldRegionX, newRegionX, oldRegionY, newRegionY))
828 {
829 // brand new agent, let's create a new caps seed
830 agentCircuit.CapsPath = CapsUtil.GetRandomCapsObjectPath();
831 }
832  
833 // We're going to fallback to V1 if the destination gives us anything smaller than 0.2 or we're forcing
834 // use of the earlier protocol
835 float versionNumber = 0.1f;
836 string[] versionComponents = version.Split(new char[] { '/' });
837 if (versionComponents.Length >= 2)
838 float.TryParse(versionComponents[1], out versionNumber);
839  
840 if (versionNumber >= 0.2f && MaxOutgoingTransferVersion >= versionNumber)
841 TransferAgent_V2(sp, agentCircuit, reg, finalDestination, endPoint, teleportFlags, oldRegionX, newRegionX, oldRegionY, newRegionY, version, out reason);
842 else
843 TransferAgent_V1(sp, agentCircuit, reg, finalDestination, endPoint, teleportFlags, oldRegionX, newRegionX, oldRegionY, newRegionY, version, out reason);
844 }
845  
846 private void TransferAgent_V1(ScenePresence sp, AgentCircuitData agentCircuit, GridRegion reg, GridRegion finalDestination,
847 IPEndPoint endPoint, uint teleportFlags, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, string version, out string reason)
848 {
849 ulong destinationHandle = finalDestination.RegionHandle;
850 AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode);
851  
852 m_log.DebugFormat(
853 "[ENTITY TRANSFER MODULE]: Using TP V1 for {0} going from {1} to {2}",
854 sp.Name, Scene.Name, finalDestination.RegionName);
855  
856 // Let's create an agent there if one doesn't exist yet.
857 // NOTE: logout will always be false for a non-HG teleport.
858 bool logout = false;
859 if (!CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout))
860 {
861 m_interRegionTeleportFailures.Value++;
862  
863 m_log.DebugFormat(
864 "[ENTITY TRANSFER MODULE]: Teleport of {0} from {1} to {2} was refused because {3}",
865 sp.Name, sp.Scene.RegionInfo.RegionName, finalDestination.RegionName, reason);
866  
867 sp.ControllingClient.SendTeleportFailed(reason);
868  
869 return;
870 }
871  
872 if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling)
873 {
874 m_interRegionTeleportCancels.Value++;
875  
876 m_log.DebugFormat(
877 "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request",
878 sp.Name, finalDestination.RegionName, sp.Scene.Name);
879  
880 return;
881 }
882 else if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
883 {
884 m_interRegionTeleportAborts.Value++;
885  
886 m_log.DebugFormat(
887 "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after CreateAgent due to previous client close.",
888 sp.Name, finalDestination.RegionName, sp.Scene.Name);
889  
890 return;
891 }
892  
893 // Past this point we have to attempt clean up if the teleport fails, so update transfer state.
894 m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring);
895  
896 // OK, it got this agent. Let's close some child agents
897 sp.CloseChildAgents(newRegionX, newRegionY);
898  
899 IClientIPEndpoint ipepClient;
900 string capsPath = String.Empty;
901 float dist = (float)Math.Max(sp.Scene.DefaultDrawDistance,
902 (float)Math.Max(sp.Scene.RegionInfo.RegionSizeX, sp.Scene.RegionInfo.RegionSizeY));
903 if (NeedsNewAgent(dist, oldRegionX, newRegionX, oldRegionY, newRegionY))
904 {
905 m_log.DebugFormat(
906 "[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for incoming agent {3} from {4}",
907 finalDestination.RegionName, newRegionX, newRegionY, sp.Name, Scene.Name);
908  
909 //sp.ControllingClient.SendTeleportProgress(teleportFlags, "Creating agent...");
910 #region IP Translation for NAT
911 // Uses ipepClient above
912 if (sp.ClientView.TryGet(out ipepClient))
913 {
914 endPoint.Address = NetworkUtil.GetIPFor(ipepClient.EndPoint, endPoint.Address);
915 }
916 #endregion
917 capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath);
918  
919 if (m_eqModule != null)
920 {
921 // The EnableSimulator message makes the client establish a connection with the destination
922 // simulator by sending the initial UseCircuitCode UDP packet to the destination containing the
923 // correct circuit code.
924 m_eqModule.EnableSimulator(destinationHandle, endPoint, sp.UUID,
925 finalDestination.RegionSizeX, finalDestination.RegionSizeY);
926 m_log.DebugFormat("{0} Sent EnableSimulator. regName={1}, size=<{2},{3}>", LogHeader,
927 finalDestination.RegionName, finalDestination.RegionSizeX, finalDestination.RegionSizeY);
928  
929 // XXX: Is this wait necessary? We will always end up waiting on UpdateAgent for the destination
930 // simulator to confirm that it has established communication with the viewer.
931 Thread.Sleep(200);
932  
933 // At least on LL 3.3.4 for teleports between different regions on the same simulator this appears
934 // unnecessary - teleport will succeed and SEED caps will be requested without it (though possibly
935 // only on TeleportFinish). This is untested for region teleport between different simulators
936 // though this probably also works.
937 m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath, finalDestination.RegionHandle,
938 finalDestination.RegionSizeX, finalDestination.RegionSizeY);
939 }
940 else
941 {
942 // XXX: This is a little misleading since we're information the client of its avatar destination,
943 // which may or may not be a neighbour region of the source region. This path is probably little
944 // used anyway (with EQ being the one used). But it is currently being used for test code.
945 sp.ControllingClient.InformClientOfNeighbour(destinationHandle, endPoint);
946 }
947 }
948 else
949 {
950 agentCircuit.CapsPath = sp.Scene.CapsModule.GetChildSeed(sp.UUID, reg.RegionHandle);
951 capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath);
952 }
953  
954 // Let's send a full update of the agent. This is a synchronous call.
955 AgentData agent = new AgentData();
956 sp.CopyTo(agent);
957 agent.Position = agentCircuit.startpos;
958 SetCallbackURL(agent, sp.Scene.RegionInfo);
959  
960  
961 // We will check for an abort before UpdateAgent since UpdateAgent will require an active viewer to
962 // establish th econnection to the destination which makes it return true.
963 if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
964 {
965 m_interRegionTeleportAborts.Value++;
966  
967 m_log.DebugFormat(
968 "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} before UpdateAgent",
969 sp.Name, finalDestination.RegionName, sp.Scene.Name);
970  
971 return;
972 }
973  
974 // A common teleport failure occurs when we can send CreateAgent to the
975 // destination region but the viewer cannot establish the connection (e.g. due to network issues between
976 // the viewer and the destination). In this case, UpdateAgent timesout after 10 seconds, although then
977 // there's a further 10 second wait whilst we attempt to tell the destination to delete the agent in Fail().
978 if (!UpdateAgent(reg, finalDestination, agent, sp))
979 {
980 if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
981 {
982 m_interRegionTeleportAborts.Value++;
983  
984 m_log.DebugFormat(
985 "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after UpdateAgent due to previous client close.",
986 sp.Name, finalDestination.RegionName, sp.Scene.Name);
987  
988 return;
989 }
990  
991 m_log.WarnFormat(
992 "[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1}. Keeping avatar in {2}",
993 sp.Name, finalDestination.RegionName, sp.Scene.Name);
994  
995 Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Connection between viewer and destination region could not be established.");
996 return;
997 }
998  
999 if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling)
1000 {
1001 m_interRegionTeleportCancels.Value++;
1002  
1003 m_log.DebugFormat(
1004 "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after UpdateAgent on client request",
1005 sp.Name, finalDestination.RegionName, sp.Scene.Name);
1006  
1007 CleanupFailedInterRegionTeleport(sp, currentAgentCircuit.SessionID.ToString(), finalDestination);
1008  
1009 return;
1010 }
1011  
1012 m_log.DebugFormat(
1013 "[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}",
1014 capsPath, sp.Scene.RegionInfo.RegionName, sp.Name);
1015  
1016 // We need to set this here to avoid an unlikely race condition when teleporting to a neighbour simulator,
1017 // where that neighbour simulator could otherwise request a child agent create on the source which then
1018 // closes our existing agent which is still signalled as root.
1019 sp.IsChildAgent = true;
1020  
1021 // OK, send TPFinish to the client, so that it starts the process of contacting the destination region
1022 if (m_eqModule != null)
1023 {
1024 m_eqModule.TeleportFinishEvent(destinationHandle, 13, endPoint, 0, teleportFlags, capsPath, sp.UUID,
1025 finalDestination.RegionSizeX, finalDestination.RegionSizeY);
1026 }
1027 else
1028 {
1029 sp.ControllingClient.SendRegionTeleport(destinationHandle, 13, endPoint, 4,
1030 teleportFlags, capsPath);
1031 }
1032  
1033 // TeleportFinish makes the client send CompleteMovementIntoRegion (at the destination), which
1034 // trigers a whole shebang of things there, including MakeRoot. So let's wait for confirmation
1035 // that the client contacted the destination before we close things here.
1036 if (!m_entityTransferStateMachine.WaitForAgentArrivedAtDestination(sp.UUID))
1037 {
1038 if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
1039 {
1040 m_interRegionTeleportAborts.Value++;
1041  
1042 m_log.DebugFormat(
1043 "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after WaitForAgentArrivedAtDestination due to previous client close.",
1044 sp.Name, finalDestination.RegionName, sp.Scene.Name);
1045  
1046 return;
1047 }
1048  
1049 m_log.WarnFormat(
1050 "[ENTITY TRANSFER MODULE]: Teleport of {0} to {1} from {2} failed due to no callback from destination region. Returning avatar to source region.",
1051 sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName);
1052  
1053 Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Destination region did not signal teleport completion.");
1054  
1055 return;
1056 }
1057  
1058 m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp);
1059  
1060 // For backwards compatibility
1061 if (version == "Unknown" || version == string.Empty)
1062 {
1063 // CrossAttachmentsIntoNewRegion is a synchronous call. We shouldn't need to wait after it
1064 m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Old simulator, sending attachments one by one...");
1065 CrossAttachmentsIntoNewRegion(finalDestination, sp, true);
1066 }
1067  
1068 // May need to logout or other cleanup
1069 AgentHasMovedAway(sp, logout);
1070  
1071 // Well, this is it. The agent is over there.
1072 KillEntity(sp.Scene, sp.LocalId);
1073  
1074 // Now let's make it officially a child agent
1075 sp.MakeChildAgent();
1076  
1077 // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone
1078  
1079 if (NeedsClosing(sp.Scene.DefaultDrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg))
1080 {
1081 if (!sp.Scene.IncomingPreCloseClient(sp))
1082 return;
1083  
1084 // We need to delay here because Imprudence viewers, unlike v1 or v3, have a short (<200ms, <500ms) delay before
1085 // they regard the new region as the current region after receiving the AgentMovementComplete
1086 // response. If close is sent before then, it will cause the viewer to quit instead.
1087 //
1088 // This sleep can be increased if necessary. However, whilst it's active,
1089 // an agent cannot teleport back to this region if it has teleported away.
1090 Thread.Sleep(2000);
1091  
1092 sp.Scene.CloseAgent(sp.UUID, false);
1093 }
1094 else
1095 {
1096 // now we have a child agent in this region.
1097 sp.Reset();
1098 }
1099 }
1100  
1101 private void TransferAgent_V2(ScenePresence sp, AgentCircuitData agentCircuit, GridRegion reg, GridRegion finalDestination,
1102 IPEndPoint endPoint, uint teleportFlags, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, string version, out string reason)
1103 {
1104 ulong destinationHandle = finalDestination.RegionHandle;
1105 AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode);
1106  
1107 // Let's create an agent there if one doesn't exist yet.
1108 // NOTE: logout will always be false for a non-HG teleport.
1109 bool logout = false;
1110 if (!CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout))
1111 {
1112 m_interRegionTeleportFailures.Value++;
1113  
1114 m_log.DebugFormat(
1115 "[ENTITY TRANSFER MODULE]: Teleport of {0} from {1} to {2} was refused because {3}",
1116 sp.Name, sp.Scene.RegionInfo.RegionName, finalDestination.RegionName, reason);
1117  
1118 sp.ControllingClient.SendTeleportFailed(reason);
1119  
1120 return;
1121 }
1122  
1123 if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Cancelling)
1124 {
1125 m_interRegionTeleportCancels.Value++;
1126  
1127 m_log.DebugFormat(
1128 "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request",
1129 sp.Name, finalDestination.RegionName, sp.Scene.Name);
1130  
1131 return;
1132 }
1133 else if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
1134 {
1135 m_interRegionTeleportAborts.Value++;
1136  
1137 m_log.DebugFormat(
1138 "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after CreateAgent due to previous client close.",
1139 sp.Name, finalDestination.RegionName, sp.Scene.Name);
1140  
1141 return;
1142 }
1143  
1144 // Past this point we have to attempt clean up if the teleport fails, so update transfer state.
1145 m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring);
1146  
1147 IClientIPEndpoint ipepClient;
1148 string capsPath = String.Empty;
1149 float dist = (float)Math.Max(sp.Scene.DefaultDrawDistance,
1150 (float)Math.Max(sp.Scene.RegionInfo.RegionSizeX, sp.Scene.RegionInfo.RegionSizeY));
1151 if (NeedsNewAgent(dist, oldRegionX, newRegionX, oldRegionY, newRegionY))
1152 {
1153 m_log.DebugFormat(
1154 "[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for agent {3} from {4}",
1155 finalDestination.RegionName, newRegionX, newRegionY, sp.Name, Scene.Name);
1156  
1157 //sp.ControllingClient.SendTeleportProgress(teleportFlags, "Creating agent...");
1158 #region IP Translation for NAT
1159 // Uses ipepClient above
1160 if (sp.ClientView.TryGet(out ipepClient))
1161 {
1162 endPoint.Address = NetworkUtil.GetIPFor(ipepClient.EndPoint, endPoint.Address);
1163 }
1164 #endregion
1165 capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath);
1166 }
1167 else
1168 {
1169 agentCircuit.CapsPath = sp.Scene.CapsModule.GetChildSeed(sp.UUID, reg.RegionHandle);
1170 capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath);
1171 }
1172  
1173 // We need to set this here to avoid an unlikely race condition when teleporting to a neighbour simulator,
1174 // where that neighbour simulator could otherwise request a child agent create on the source which then
1175 // closes our existing agent which is still signalled as root.
1176 //sp.IsChildAgent = true;
1177  
1178 // New protocol: send TP Finish directly, without prior ES or EAC. That's what happens in the Linden grid
1179 if (m_eqModule != null)
1180 m_eqModule.TeleportFinishEvent(destinationHandle, 13, endPoint, 0, teleportFlags, capsPath, sp.UUID,
1181 finalDestination.RegionSizeX, finalDestination.RegionSizeY);
1182 else
1183 sp.ControllingClient.SendRegionTeleport(destinationHandle, 13, endPoint, 4,
1184 teleportFlags, capsPath);
1185  
1186 m_log.DebugFormat(
1187 "[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}",
1188 capsPath, sp.Scene.RegionInfo.RegionName, sp.Name);
1189  
1190 // Let's send a full update of the agent.
1191 AgentData agent = new AgentData();
1192 sp.CopyTo(agent);
1193 agent.Position = agentCircuit.startpos;
1194 agent.SenderWantsToWaitForRoot = true;
1195 //SetCallbackURL(agent, sp.Scene.RegionInfo);
1196  
1197 // Reset the do not close flag. This must be done before the destination opens child connections (here
1198 // triggered by UpdateAgent) to avoid race conditions. However, we also want to reset it as late as possible
1199 // to avoid a situation where an unexpectedly early call to Scene.NewUserConnection() wrongly results
1200 // in no close.
1201 sp.DoNotCloseAfterTeleport = false;
1202  
1203 // Send the Update. If this returns true, we know the client has contacted the destination
1204 // via CompleteMovementIntoRegion, so we can let go.
1205 // If it returns false, something went wrong, and we need to abort.
1206 if (!UpdateAgent(reg, finalDestination, agent, sp))
1207 {
1208 if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting)
1209 {
1210 m_interRegionTeleportAborts.Value++;
1211  
1212 m_log.DebugFormat(
1213 "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after UpdateAgent due to previous client close.",
1214 sp.Name, finalDestination.RegionName, sp.Scene.Name);
1215  
1216 return;
1217 }
1218  
1219 m_log.WarnFormat(
1220 "[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1}. Keeping avatar in {2}",
1221 sp.Name, finalDestination.RegionName, sp.Scene.Name);
1222  
1223 Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Connection between viewer and destination region could not be established.");
1224 return;
1225 }
1226  
1227 m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp);
1228  
1229 // Need to signal neighbours whether child agents may need closing irrespective of whether this
1230 // one needed closing. We also need to close child agents as quickly as possible to avoid complicated
1231 // race conditions with rapid agent releporting (e.g. from A1 to a non-neighbour B, back
1232 // to a neighbour A2 then off to a non-neighbour C). Closing child agents any later requires complex
1233 // distributed checks to avoid problems in rapid reteleporting scenarios and where child agents are
1234 // abandoned without proper close by viewer but then re-used by an incoming connection.
1235 sp.CloseChildAgents(newRegionX, newRegionY);
1236  
1237 // May need to logout or other cleanup
1238 AgentHasMovedAway(sp, logout);
1239  
1240 // Well, this is it. The agent is over there.
1241 KillEntity(sp.Scene, sp.LocalId);
1242  
1243 // Now let's make it officially a child agent
1244 sp.MakeChildAgent();
1245  
1246 // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone
1247 if (NeedsClosing(sp.Scene.DefaultDrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg))
1248 {
1249 if (!sp.Scene.IncomingPreCloseClient(sp))
1250 return;
1251  
1252 // RED ALERT!!!!
1253 // PLEASE DO NOT DECREASE THIS WAIT TIME UNDER ANY CIRCUMSTANCES.
1254 // THE VIEWERS SEEM TO NEED SOME TIME AFTER RECEIVING MoveAgentIntoRegion
1255 // BEFORE THEY SETTLE IN THE NEW REGION.
1256 // DECREASING THE WAIT TIME HERE WILL EITHER RESULT IN A VIEWER CRASH OR
1257 // IN THE AVIE BEING PLACED IN INFINITY FOR A COUPLE OF SECONDS.
1258 Thread.Sleep(15000);
1259  
1260 // OK, it got this agent. Let's close everything
1261 // If we shouldn't close the agent due to some other region renewing the connection
1262 // then this will be handled in IncomingCloseAgent under lock conditions
1263 m_log.DebugFormat(
1264 "[ENTITY TRANSFER MODULE]: Closing agent {0} in {1} after teleport", sp.Name, Scene.Name);
1265  
1266 sp.Scene.CloseAgent(sp.UUID, false);
1267 }
1268 else
1269 {
1270 // now we have a child agent in this region.
1271 sp.Reset();
1272 }
1273 }
1274  
1275 /// <summary>
1276 /// Clean up an inter-region teleport that did not complete, either because of simulator failure or cancellation.
1277 /// </summary>
1278 /// <remarks>
1279 /// All operations here must be idempotent so that we can call this method at any point in the teleport process
1280 /// up until we send the TeleportFinish event quene event to the viewer.
1281 /// <remarks>
1282 /// <param name='sp'> </param>
1283 /// <param name='finalDestination'></param>
1284 protected virtual void CleanupFailedInterRegionTeleport(ScenePresence sp, string auth_token, GridRegion finalDestination)
1285 {
1286 m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp);
1287  
1288 if (sp.IsChildAgent) // We had set it to child before attempted TP (V1)
1289 {
1290 sp.IsChildAgent = false;
1291 ReInstantiateScripts(sp);
1292  
1293 EnableChildAgents(sp);
1294 }
1295 // Finally, kill the agent we just created at the destination.
1296 // XXX: Possibly this should be done asynchronously.
1297 Scene.SimulationService.CloseAgent(finalDestination, sp.UUID, auth_token);
1298 }
1299  
1300 /// <summary>
1301 /// Signal that the inter-region teleport failed and perform cleanup.
1302 /// </summary>
1303 /// <param name='sp'></param>
1304 /// <param name='finalDestination'></param>
1305 /// <param name='logout'></param>
1306 /// <param name='reason'>Human readable reason for teleport failure. Will be sent to client.</param>
1307 protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout, string auth_code, string reason)
1308 {
1309 CleanupFailedInterRegionTeleport(sp, auth_code, finalDestination);
1310  
1311 m_interRegionTeleportFailures.Value++;
1312  
1313 sp.ControllingClient.SendTeleportFailed(
1314 string.Format(
1315 "Problems connecting to destination {0}, reason: {1}", finalDestination.RegionName, reason));
1316  
1317 sp.Scene.EventManager.TriggerTeleportFail(sp.ControllingClient, logout);
1318 }
1319  
1320 protected virtual bool CreateAgent(ScenePresence sp, GridRegion reg, GridRegion finalDestination, AgentCircuitData agentCircuit, uint teleportFlags, out string reason, out bool logout)
1321 {
1322 GridRegion source = new GridRegion(Scene.RegionInfo);
1323 source.RawServerURI = m_GatekeeperURI;
1324  
1325 logout = false;
1326 bool success = Scene.SimulationService.CreateAgent(source, finalDestination, agentCircuit, teleportFlags, out reason);
1327  
1328 if (success)
1329 sp.Scene.EventManager.TriggerTeleportStart(sp.ControllingClient, reg, finalDestination, teleportFlags, logout);
1330  
1331 return success;
1332 }
1333  
1334 protected virtual bool UpdateAgent(GridRegion reg, GridRegion finalDestination, AgentData agent, ScenePresence sp)
1335 {
1336 return Scene.SimulationService.UpdateAgent(finalDestination, agent);
1337 }
1338  
1339 protected virtual void SetCallbackURL(AgentData agent, RegionInfo region)
1340 {
1341 agent.CallbackURI = region.ServerURI + "agent/" + agent.AgentID.ToString() + "/" + region.RegionID.ToString() + "/release/";
1342  
1343 m_log.DebugFormat(
1344 "[ENTITY TRANSFER MODULE]: Set release callback URL to {0} in {1}",
1345 agent.CallbackURI, region.RegionName);
1346 }
1347  
1348 /// <summary>
1349 /// Clean up operations once an agent has moved away through cross or teleport.
1350 /// </summary>
1351 /// <param name='sp'></param>
1352 /// <param name='logout'></param>
1353 protected virtual void AgentHasMovedAway(ScenePresence sp, bool logout)
1354 {
1355 if (sp.Scene.AttachmentsModule != null)
1356 sp.Scene.AttachmentsModule.DeleteAttachmentsFromScene(sp, true);
1357 }
1358  
1359 protected void KillEntity(Scene scene, uint localID)
1360 {
1361 scene.SendKillObject(new List<uint> { localID });
1362 }
1363  
1364 protected virtual GridRegion GetFinalDestination(GridRegion region, UUID agentID, string agentHomeURI, out string message)
1365 {
1366 message = null;
1367 return region;
1368 }
1369  
1370 // This returns 'true' if the new region already has a child agent for our
1371 // incoming agent. The implication is that, if 'false', we have to create the
1372 // child and then teleport into the region.
1373 protected virtual bool NeedsNewAgent(float drawdist, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY)
1374 {
1375 if (m_regionCombinerModule != null && m_regionCombinerModule.IsRootForMegaregion(Scene.RegionInfo.RegionID))
1376 {
1377 Vector2 swCorner, neCorner;
1378 GetMegaregionViewRange(out swCorner, out neCorner);
1379  
1380 m_log.DebugFormat(
1381 "[ENTITY TRANSFER MODULE]: Megaregion view of {0} is from {1} to {2} with new agent check for {3},{4}",
1382 Scene.Name, swCorner, neCorner, newRegionX, newRegionY);
1383  
1384 return !(newRegionX >= swCorner.X && newRegionX <= neCorner.X && newRegionY >= swCorner.Y && newRegionY <= neCorner.Y);
1385 }
1386 else
1387 {
1388 return Util.IsOutsideView(drawdist, oldRegionX, newRegionX, oldRegionY, newRegionY);
1389 }
1390 }
1391  
1392 protected virtual bool NeedsClosing(float drawdist, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, GridRegion reg)
1393 {
1394 return Util.IsOutsideView(drawdist, oldRegionX, newRegionX, oldRegionY, newRegionY);
1395 }
1396  
1397 #endregion
1398  
1399 #region Landmark Teleport
1400 /// <summary>
1401 /// Tries to teleport agent to landmark.
1402 /// </summary>
1403 /// <param name="remoteClient"></param>
1404 /// <param name="regionHandle"></param>
1405 /// <param name="position"></param>
1406 public virtual void RequestTeleportLandmark(IClientAPI remoteClient, AssetLandmark lm)
1407 {
1408 GridRegion info = Scene.GridService.GetRegionByUUID(UUID.Zero, lm.RegionID);
1409  
1410 if (info == null)
1411 {
1412 // can't find the region: Tell viewer and abort
1413 remoteClient.SendTeleportFailed("The teleport destination could not be found.");
1414 return;
1415 }
1416 ((Scene)(remoteClient.Scene)).RequestTeleportLocation(remoteClient, info.RegionHandle, lm.Position,
1417 Vector3.Zero, (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaLandmark));
1418 }
1419  
1420 #endregion
1421  
1422 #region Teleport Home
1423  
1424 public virtual void TriggerTeleportHome(UUID id, IClientAPI client)
1425 {
1426 TeleportHome(id, client);
1427 }
1428  
1429 public virtual bool TeleportHome(UUID id, IClientAPI client)
1430 {
1431 m_log.DebugFormat(
1432 "[ENTITY TRANSFER MODULE]: Request to teleport {0} {1} home", client.Name, client.AgentId);
1433  
1434 //OpenSim.Services.Interfaces.PresenceInfo pinfo = Scene.PresenceService.GetAgent(client.SessionId);
1435 GridUserInfo uinfo = Scene.GridUserService.GetGridUserInfo(client.AgentId.ToString());
1436  
1437 if (uinfo != null)
1438 {
1439 if (uinfo.HomeRegionID == UUID.Zero)
1440 {
1441 // can't find the Home region: Tell viewer and abort
1442 m_log.ErrorFormat("{0} No grid user info found for {1} {2}. Cannot send home.",
1443 LogHeader, client.Name, client.AgentId);
1444 client.SendTeleportFailed("You don't have a home position set.");
1445 return false;
1446 }
1447 GridRegion regionInfo = Scene.GridService.GetRegionByUUID(UUID.Zero, uinfo.HomeRegionID);
1448 if (regionInfo == null)
1449 {
1450 // can't find the Home region: Tell viewer and abort
1451 client.SendTeleportFailed("Your home region could not be found.");
1452 return false;
1453 }
1454  
1455 m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Home region of {0} is {1} ({2}-{3})",
1456 client.Name, regionInfo.RegionName, regionInfo.RegionCoordX, regionInfo.RegionCoordY);
1457  
1458 // a little eekie that this goes back to Scene and with a forced cast, will fix that at some point...
1459 ((Scene)(client.Scene)).RequestTeleportLocation(
1460 client, regionInfo.RegionHandle, uinfo.HomePosition, uinfo.HomeLookAt,
1461 (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaHome));
1462 return true;
1463 }
1464 else
1465 {
1466 // can't find the Home region: Tell viewer and abort
1467 client.SendTeleportFailed("Your home region could not be found.");
1468 }
1469 return false;
1470 }
1471  
1472 #endregion
1473  
1474  
1475 #region Agent Crossings
1476  
1477 // Given a position relative to the current region (which has previously been tested to
1478 // see that it is actually outside the current region), find the new region that the
1479 // point is actually in.
1480 // Returns the coordinates and information of the new region or 'null' of it doesn't exist.
1481 public GridRegion GetDestination(Scene scene, UUID agentID, Vector3 pos,
1482 out string version, out Vector3 newpos, out string failureReason)
1483 {
1484 version = String.Empty;
1485 newpos = pos;
1486 failureReason = string.Empty;
1487 string homeURI = scene.GetAgentHomeURI(agentID);
1488  
1489 // m_log.DebugFormat(
1490 // "[ENTITY TRANSFER MODULE]: Crossing agent {0} at pos {1} in {2}", agent.Name, pos, scene.Name);
1491  
1492 // Compute world location of the object's position
1493 double presenceWorldX = (double)scene.RegionInfo.WorldLocX + pos.X;
1494 double presenceWorldY = (double)scene.RegionInfo.WorldLocY + pos.Y;
1495  
1496 // Call the grid service to lookup the region containing the new position.
1497 GridRegion neighbourRegion = GetRegionContainingWorldLocation(scene.GridService, scene.RegionInfo.ScopeID,
1498 presenceWorldX, presenceWorldY,
1499 Math.Max(scene.RegionInfo.RegionSizeX, scene.RegionInfo.RegionSizeY));
1500  
1501 if (neighbourRegion != null)
1502 {
1503 // Compute the entity's position relative to the new region
1504 newpos = new Vector3((float)(presenceWorldX - (double)neighbourRegion.RegionLocX),
1505 (float)(presenceWorldY - (double)neighbourRegion.RegionLocY),
1506 pos.Z);
1507  
1508 if (m_bannedRegionCache.IfBanned(neighbourRegion.RegionHandle, agentID))
1509 {
1510 failureReason = "Cannot region cross into banned parcel";
1511 neighbourRegion = null;
1512 }
1513 else
1514 {
1515 // If not banned, make sure this agent is not in the list.
1516 m_bannedRegionCache.Remove(neighbourRegion.RegionHandle, agentID);
1517 }
1518  
1519 // Check to see if we have access to the target region.
1520 string myversion = string.Format("{0}/{1}", OutgoingTransferVersionName, MaxOutgoingTransferVersion);
1521 if (neighbourRegion != null
1522 && !scene.SimulationService.QueryAccess(neighbourRegion, agentID, homeURI, false, newpos, myversion, out version, out failureReason))
1523 {
1524 // remember banned
1525 m_bannedRegionCache.Add(neighbourRegion.RegionHandle, agentID);
1526 neighbourRegion = null;
1527 }
1528 }
1529 else
1530 {
1531 // The destination region just doesn't exist
1532 failureReason = "Cannot cross into non-existent region";
1533 }
1534  
1535 if (neighbourRegion == null)
1536 m_log.DebugFormat("{0} GetDestination: region not found. Old region name={1} at <{2},{3}> of size <{4},{5}>. Old pos={6}",
1537 LogHeader, scene.RegionInfo.RegionName,
1538 scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY,
1539 scene.RegionInfo.RegionSizeX, scene.RegionInfo.RegionSizeY,
1540 pos);
1541 else
1542 m_log.DebugFormat("{0} GetDestination: new region={1} at <{2},{3}> of size <{4},{5}>, newpos=<{6},{7}>",
1543 LogHeader, neighbourRegion.RegionName,
1544 neighbourRegion.RegionLocX, neighbourRegion.RegionLocY, neighbourRegion.RegionSizeX, neighbourRegion.RegionSizeY,
1545 newpos.X, newpos.Y);
1546  
1547 return neighbourRegion;
1548 }
1549  
1550 public bool Cross(ScenePresence agent, bool isFlying)
1551 {
1552 Vector3 newpos;
1553 string version;
1554 string failureReason;
1555  
1556 GridRegion neighbourRegion = GetDestination(agent.Scene, agent.UUID, agent.AbsolutePosition,
1557 out version, out newpos, out failureReason);
1558 if (neighbourRegion == null)
1559 {
1560 agent.ControllingClient.SendAlertMessage(failureReason);
1561 return false;
1562 }
1563  
1564 agent.IsInTransit = true;
1565  
1566 CrossAgentToNewRegionDelegate d = CrossAgentToNewRegionAsync;
1567 d.BeginInvoke(agent, newpos, neighbourRegion, isFlying, version, CrossAgentToNewRegionCompleted, d);
1568  
1569 Scene.EventManager.TriggerCrossAgentToNewRegion(agent, isFlying, neighbourRegion);
1570  
1571 return true;
1572 }
1573  
1574  
1575 public delegate void InformClientToInitiateTeleportToLocationDelegate(ScenePresence agent, uint regionX, uint regionY,
1576 Vector3 position,
1577 Scene initiatingScene);
1578  
1579 private void InformClientToInitiateTeleportToLocation(ScenePresence agent, uint regionX, uint regionY, Vector3 position, Scene initiatingScene)
1580 {
1581  
1582 // This assumes that we know what our neighbours are.
1583  
1584 InformClientToInitiateTeleportToLocationDelegate d = InformClientToInitiateTeleportToLocationAsync;
1585 d.BeginInvoke(agent, regionX, regionY, position, initiatingScene,
1586 InformClientToInitiateTeleportToLocationCompleted,
1587 d);
1588 }
1589  
1590 public void InformClientToInitiateTeleportToLocationAsync(ScenePresence agent, uint regionX, uint regionY, Vector3 position,
1591 Scene initiatingScene)
1592 {
1593 Thread.Sleep(10000);
1594  
1595 m_log.DebugFormat(
1596 "[ENTITY TRANSFER MODULE]: Auto-reteleporting {0} to correct megaregion location {1},{2},{3} from {4}",
1597 agent.Name, regionX, regionY, position, initiatingScene.Name);
1598  
1599 agent.Scene.RequestTeleportLocation(
1600 agent.ControllingClient,
1601 Util.RegionLocToHandle(regionX, regionY),
1602 position,
1603 agent.Lookat,
1604 (uint)Constants.TeleportFlags.ViaLocation);
1605  
1606 /*
1607 IMessageTransferModule im = initiatingScene.RequestModuleInterface<IMessageTransferModule>();
1608 if (im != null)
1609 {
1610 UUID gotoLocation = Util.BuildFakeParcelID(
1611 Util.RegionLocToHandle(regionX, regionY),
1612 (uint)(int)position.X,
1613 (uint)(int)position.Y,
1614 (uint)(int)position.Z);
1615  
1616 GridInstantMessage m
1617 = new GridInstantMessage(
1618 initiatingScene,
1619 UUID.Zero,
1620 "Region",
1621 agent.UUID,
1622 (byte)InstantMessageDialog.GodLikeRequestTeleport,
1623 false,
1624 "",
1625 gotoLocation,
1626 false,
1627 new Vector3(127, 0, 0),
1628 new Byte[0],
1629 false);
1630  
1631 im.SendInstantMessage(m, delegate(bool success)
1632 {
1633 m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Client Initiating Teleport sending IM success = {0}", success);
1634 });
1635  
1636 }
1637 */
1638 }
1639  
1640 private void InformClientToInitiateTeleportToLocationCompleted(IAsyncResult iar)
1641 {
1642 InformClientToInitiateTeleportToLocationDelegate icon =
1643 (InformClientToInitiateTeleportToLocationDelegate)iar.AsyncState;
1644 icon.EndInvoke(iar);
1645 }
1646  
1647 public bool CrossAgentToNewRegionPrep(ScenePresence agent, GridRegion neighbourRegion)
1648 {
1649 if (neighbourRegion == null)
1650 return false;
1651  
1652 m_entityTransferStateMachine.SetInTransit(agent.UUID);
1653  
1654 agent.RemoveFromPhysicalScene();
1655  
1656 return true;
1657 }
1658  
1659 /// <summary>
1660 /// This Closes child agents on neighbouring regions
1661 /// Calls an asynchronous method to do so.. so it doesn't lag the sim.
1662 /// </summary>
1663 public ScenePresence CrossAgentToNewRegionAsync(
1664 ScenePresence agent, Vector3 pos, GridRegion neighbourRegion,
1665 bool isFlying, string version)
1666 {
1667 try
1668 {
1669 m_log.DebugFormat("{0}: CrossAgentToNewRegionAsync: new region={1} at <{2},{3}>. newpos={4}",
1670 LogHeader, neighbourRegion.RegionName, neighbourRegion.RegionLocX, neighbourRegion.RegionLocY, pos);
1671  
1672 if (!CrossAgentToNewRegionPrep(agent, neighbourRegion))
1673 {
1674 m_log.DebugFormat("{0}: CrossAgentToNewRegionAsync: prep failed. Resetting transfer state", LogHeader);
1675 m_entityTransferStateMachine.ResetFromTransit(agent.UUID);
1676 }
1677  
1678 if (!CrossAgentIntoNewRegionMain(agent, pos, neighbourRegion, isFlying))
1679 {
1680 m_log.DebugFormat("{0}: CrossAgentToNewRegionAsync: cross main failed. Resetting transfer state", LogHeader);
1681 m_entityTransferStateMachine.ResetFromTransit(agent.UUID);
1682 }
1683  
1684 CrossAgentToNewRegionPost(agent, pos, neighbourRegion, isFlying, version);
1685 }
1686 catch (Exception e)
1687 {
1688 m_log.Error(string.Format("{0}: CrossAgentToNewRegionAsync: failed with exception ", LogHeader), e);
1689 }
1690  
1691 return agent;
1692 }
1693  
1694 public bool CrossAgentIntoNewRegionMain(ScenePresence agent, Vector3 pos, GridRegion neighbourRegion, bool isFlying)
1695 {
1696 try
1697 {
1698 AgentData cAgent = new AgentData();
1699 agent.CopyTo(cAgent);
1700 cAgent.Position = pos + agent.Velocity;
1701 if (isFlying)
1702 cAgent.ControlFlags |= (uint)AgentManager.ControlFlags.AGENT_CONTROL_FLY;
1703  
1704 // We don't need the callback anymnore
1705 cAgent.CallbackURI = String.Empty;
1706  
1707 // Beyond this point, extra cleanup is needed beyond removing transit state
1708 m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.Transferring);
1709  
1710 if (!agent.Scene.SimulationService.UpdateAgent(neighbourRegion, cAgent))
1711 {
1712 // region doesn't take it
1713 m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.CleaningUp);
1714  
1715 m_log.WarnFormat(
1716 "[ENTITY TRANSFER MODULE]: Region {0} would not accept update for agent {1} on cross attempt. Returning to original region.",
1717 neighbourRegion.RegionName, agent.Name);
1718  
1719 ReInstantiateScripts(agent);
1720 agent.AddToPhysicalScene(isFlying);
1721  
1722 return false;
1723 }
1724  
1725 }
1726 catch (Exception e)
1727 {
1728 m_log.ErrorFormat(
1729 "[ENTITY TRANSFER MODULE]: Problem crossing user {0} to new region {1} from {2}. Exception {3}{4}",
1730 agent.Name, neighbourRegion.RegionName, agent.Scene.RegionInfo.RegionName, e.Message, e.StackTrace);
1731  
1732 // TODO: Might be worth attempting other restoration here such as reinstantiation of scripts, etc.
1733 return false;
1734 }
1735  
1736 return true;
1737 }
1738  
1739 public void CrossAgentToNewRegionPost(ScenePresence agent, Vector3 pos, GridRegion neighbourRegion,
1740 bool isFlying, string version)
1741 {
1742 agent.ControllingClient.RequestClientInfo();
1743  
1744 string agentcaps;
1745 if (!agent.KnownRegions.TryGetValue(neighbourRegion.RegionHandle, out agentcaps))
1746 {
1747 m_log.ErrorFormat("[ENTITY TRANSFER MODULE]: No ENTITY TRANSFER MODULE information for region handle {0}, exiting CrossToNewRegion.",
1748 neighbourRegion.RegionHandle);
1749 return;
1750 }
1751  
1752 // No turning back
1753 agent.IsChildAgent = true;
1754  
1755 string capsPath = neighbourRegion.ServerURI + CapsUtil.GetCapsSeedPath(agentcaps);
1756  
1757 m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} to client {1}", capsPath, agent.UUID);
1758  
1759 Vector3 vel2 = new Vector3(agent.Velocity.X, agent.Velocity.Y, 0);
1760  
1761 if (m_eqModule != null)
1762 {
1763 m_eqModule.CrossRegion(
1764 neighbourRegion.RegionHandle, pos + agent.Velocity, vel2 /* agent.Velocity */,
1765 neighbourRegion.ExternalEndPoint,
1766 capsPath, agent.UUID, agent.ControllingClient.SessionId,
1767 neighbourRegion.RegionSizeX, neighbourRegion.RegionSizeY);
1768 }
1769 else
1770 {
1771 m_log.ErrorFormat("{0} Using old CrossRegion packet. Varregion will not work!!", LogHeader);
1772 agent.ControllingClient.CrossRegion(neighbourRegion.RegionHandle, pos + agent.Velocity, agent.Velocity, neighbourRegion.ExternalEndPoint,
1773 capsPath);
1774 }
1775  
1776 // SUCCESS!
1777 m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.ReceivedAtDestination);
1778  
1779 // Unlike a teleport, here we do not wait for the destination region to confirm the receipt.
1780 m_entityTransferStateMachine.UpdateInTransit(agent.UUID, AgentTransferState.CleaningUp);
1781  
1782 agent.MakeChildAgent();
1783  
1784 // FIXME: Possibly this should occur lower down after other commands to close other agents,
1785 // but not sure yet what the side effects would be.
1786 m_entityTransferStateMachine.ResetFromTransit(agent.UUID);
1787  
1788 // now we have a child agent in this region. Request all interesting data about other (root) agents
1789 agent.SendOtherAgentsAvatarDataToClient();
1790 agent.SendOtherAgentsAppearanceToClient();
1791  
1792 // Backwards compatibility. Best effort
1793 if (version == "Unknown" || version == string.Empty)
1794 {
1795 m_log.DebugFormat("[ENTITY TRANSFER MODULE]: neighbor with old version, passing attachments one by one...");
1796 Thread.Sleep(3000); // wait a little now that we're not waiting for the callback
1797 CrossAttachmentsIntoNewRegion(neighbourRegion, agent, true);
1798 }
1799  
1800 // Next, let's close the child agent connections that are too far away.
1801 uint neighbourx;
1802 uint neighboury;
1803 Util.RegionHandleToRegionLoc(neighbourRegion.RegionHandle, out neighbourx, out neighboury);
1804  
1805 agent.CloseChildAgents(neighbourx, neighboury);
1806  
1807 AgentHasMovedAway(agent, false);
1808  
1809 // the user may change their profile information in other region,
1810 // so the userinfo in UserProfileCache is not reliable any more, delete it
1811 // REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE!
1812 // if (agent.Scene.NeedSceneCacheClear(agent.UUID))
1813 // {
1814 // m_log.DebugFormat(
1815 // "[ENTITY TRANSFER MODULE]: User {0} is going to another region", agent.UUID);
1816 // }
1817  
1818 //m_log.Debug("AFTER CROSS");
1819 //Scene.DumpChildrenSeeds(UUID);
1820 //DumpKnownRegions();
1821  
1822 return;
1823 }
1824  
1825 private void CrossAgentToNewRegionCompleted(IAsyncResult iar)
1826 {
1827 CrossAgentToNewRegionDelegate icon = (CrossAgentToNewRegionDelegate)iar.AsyncState;
1828 ScenePresence agent = icon.EndInvoke(iar);
1829  
1830 //// If the cross was successful, this agent is a child agent
1831 //if (agent.IsChildAgent)
1832 // agent.Reset();
1833 //else // Not successful
1834 // agent.RestoreInCurrentScene();
1835  
1836 // In any case
1837 agent.IsInTransit = false;
1838  
1839 m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Crossing agent {0} {1} completed.", agent.Firstname, agent.Lastname);
1840 }
1841  
1842 #endregion
1843  
1844 #region Enable Child Agent
1845  
1846 /// <summary>
1847 /// This informs a single neighbouring region about agent "avatar".
1848 /// Calls an asynchronous method to do so.. so it doesn't lag the sim.
1849 /// </summary>
1850 /// <param name="sp"></param>
1851 /// <param name="region"></param>
1852 public void EnableChildAgent(ScenePresence sp, GridRegion region)
1853 {
1854 m_log.DebugFormat("[ENTITY TRANSFER]: Enabling child agent in new neighbour {0}", region.RegionName);
1855  
1856 AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode);
1857 AgentCircuitData agent = sp.ControllingClient.RequestClientInfo();
1858 agent.BaseFolder = UUID.Zero;
1859 agent.InventoryFolder = UUID.Zero;
1860 agent.startpos = new Vector3(128, 128, 70);
1861 agent.child = true;
1862 agent.Appearance = sp.Appearance;
1863 agent.CapsPath = CapsUtil.GetRandomCapsObjectPath();
1864  
1865 agent.ChildrenCapSeeds = new Dictionary<ulong, string>(sp.Scene.CapsModule.GetChildrenSeeds(sp.UUID));
1866 //m_log.DebugFormat("[XXX] Seeds 1 {0}", agent.ChildrenCapSeeds.Count);
1867  
1868 if (!agent.ChildrenCapSeeds.ContainsKey(sp.Scene.RegionInfo.RegionHandle))
1869 agent.ChildrenCapSeeds.Add(sp.Scene.RegionInfo.RegionHandle, sp.ControllingClient.RequestClientInfo().CapsPath);
1870 //m_log.DebugFormat("[XXX] Seeds 2 {0}", agent.ChildrenCapSeeds.Count);
1871  
1872 sp.AddNeighbourRegion(region.RegionHandle, agent.CapsPath);
1873 //foreach (ulong h in agent.ChildrenCapSeeds.Keys)
1874 // m_log.DebugFormat("[XXX] --> {0}", h);
1875 //m_log.DebugFormat("[XXX] Adding {0}", region.RegionHandle);
1876 if (agent.ChildrenCapSeeds.ContainsKey(region.RegionHandle))
1877 {
1878 m_log.WarnFormat(
1879 "[ENTITY TRANSFER]: Overwriting caps seed {0} with {1} for region {2} (handle {3}) for {4} in {5}",
1880 agent.ChildrenCapSeeds[region.RegionHandle], agent.CapsPath,
1881 region.RegionName, region.RegionHandle, sp.Name, Scene.Name);
1882 }
1883  
1884 agent.ChildrenCapSeeds[region.RegionHandle] = agent.CapsPath;
1885  
1886 if (sp.Scene.CapsModule != null)
1887 {
1888 sp.Scene.CapsModule.SetChildrenSeed(sp.UUID, agent.ChildrenCapSeeds);
1889 }
1890  
1891 if (currentAgentCircuit != null)
1892 {
1893 agent.ServiceURLs = currentAgentCircuit.ServiceURLs;
1894 agent.IPAddress = currentAgentCircuit.IPAddress;
1895 agent.Viewer = currentAgentCircuit.Viewer;
1896 agent.Channel = currentAgentCircuit.Channel;
1897 agent.Mac = currentAgentCircuit.Mac;
1898 agent.Id0 = currentAgentCircuit.Id0;
1899 }
1900  
1901 IPEndPoint external = region.ExternalEndPoint;
1902 if (external != null)
1903 {
1904 InformClientOfNeighbourDelegate d = InformClientOfNeighbourAsync;
1905 d.BeginInvoke(sp, agent, region, external, true,
1906 InformClientOfNeighbourCompleted,
1907 d);
1908 }
1909 }
1910 #endregion
1911  
1912 #region Enable Child Agents
1913  
1914 private delegate void InformClientOfNeighbourDelegate(
1915 ScenePresence avatar, AgentCircuitData a, GridRegion reg, IPEndPoint endPoint, bool newAgent);
1916  
1917 /// <summary>
1918 /// This informs all neighbouring regions about agent "avatar".
1919 /// </summary>
1920 /// <param name="sp"></param>
1921 public void EnableChildAgents(ScenePresence sp)
1922 {
1923 List<GridRegion> neighbours = new List<GridRegion>();
1924 RegionInfo m_regionInfo = sp.Scene.RegionInfo;
1925  
1926 if (m_regionInfo != null)
1927 {
1928 neighbours = RequestNeighbours(sp, m_regionInfo.RegionLocX, m_regionInfo.RegionLocY);
1929 }
1930 else
1931 {
1932 m_log.Debug("[ENTITY TRANSFER MODULE]: m_regionInfo was null in EnableChildAgents, is this a NPC?");
1933 }
1934  
1935 /// We need to find the difference between the new regions where there are no child agents
1936 /// and the regions where there are already child agents. We only send notification to the former.
1937 List<ulong> neighbourHandles = NeighbourHandles(neighbours); // on this region
1938 neighbourHandles.Add(sp.Scene.RegionInfo.RegionHandle); // add this region too
1939 List<ulong> previousRegionNeighbourHandles;
1940  
1941 if (sp.Scene.CapsModule != null)
1942 {
1943 previousRegionNeighbourHandles =
1944 new List<ulong>(sp.Scene.CapsModule.GetChildrenSeeds(sp.UUID).Keys);
1945 }
1946 else
1947 {
1948 previousRegionNeighbourHandles = new List<ulong>();
1949 }
1950  
1951 List<ulong> newRegions = NewNeighbours(neighbourHandles, previousRegionNeighbourHandles);
1952 List<ulong> oldRegions = OldNeighbours(neighbourHandles, previousRegionNeighbourHandles);
1953  
1954 // Dump("Current Neighbors", neighbourHandles);
1955 // Dump("Previous Neighbours", previousRegionNeighbourHandles);
1956 // Dump("New Neighbours", newRegions);
1957 // Dump("Old Neighbours", oldRegions);
1958  
1959 /// Update the scene presence's known regions here on this region
1960 sp.DropOldNeighbours(oldRegions);
1961  
1962 /// Collect as many seeds as possible
1963 Dictionary<ulong, string> seeds;
1964 if (sp.Scene.CapsModule != null)
1965 seeds = new Dictionary<ulong, string>(sp.Scene.CapsModule.GetChildrenSeeds(sp.UUID));
1966 else
1967 seeds = new Dictionary<ulong, string>();
1968  
1969 //m_log.Debug(" !!! No. of seeds: " + seeds.Count);
1970 if (!seeds.ContainsKey(sp.Scene.RegionInfo.RegionHandle))
1971 seeds.Add(sp.Scene.RegionInfo.RegionHandle, sp.ControllingClient.RequestClientInfo().CapsPath);
1972  
1973 /// Create the necessary child agents
1974 List<AgentCircuitData> cagents = new List<AgentCircuitData>();
1975 foreach (GridRegion neighbour in neighbours)
1976 {
1977 if (neighbour.RegionHandle != sp.Scene.RegionInfo.RegionHandle)
1978 {
1979 AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode);
1980 AgentCircuitData agent = sp.ControllingClient.RequestClientInfo();
1981 agent.BaseFolder = UUID.Zero;
1982 agent.InventoryFolder = UUID.Zero;
1983 agent.startpos = sp.AbsolutePosition + CalculateOffset(sp, neighbour);
1984 agent.child = true;
1985 agent.Appearance = sp.Appearance;
1986 if (currentAgentCircuit != null)
1987 {
1988 agent.ServiceURLs = currentAgentCircuit.ServiceURLs;
1989 agent.IPAddress = currentAgentCircuit.IPAddress;
1990 agent.Viewer = currentAgentCircuit.Viewer;
1991 agent.Channel = currentAgentCircuit.Channel;
1992 agent.Mac = currentAgentCircuit.Mac;
1993 agent.Id0 = currentAgentCircuit.Id0;
1994 }
1995  
1996 if (newRegions.Contains(neighbour.RegionHandle))
1997 {
1998 agent.CapsPath = CapsUtil.GetRandomCapsObjectPath();
1999 sp.AddNeighbourRegion(neighbour.RegionHandle, agent.CapsPath);
2000 seeds.Add(neighbour.RegionHandle, agent.CapsPath);
2001 }
2002 else
2003 {
2004 agent.CapsPath = sp.Scene.CapsModule.GetChildSeed(sp.UUID, neighbour.RegionHandle);
2005 }
2006  
2007 cagents.Add(agent);
2008 }
2009 }
2010  
2011 /// Update all child agent with everyone's seeds
2012 foreach (AgentCircuitData a in cagents)
2013 {
2014 a.ChildrenCapSeeds = new Dictionary<ulong, string>(seeds);
2015 }
2016  
2017 if (sp.Scene.CapsModule != null)
2018 {
2019 sp.Scene.CapsModule.SetChildrenSeed(sp.UUID, seeds);
2020 }
2021 sp.KnownRegions = seeds;
2022 //avatar.Scene.DumpChildrenSeeds(avatar.UUID);
2023 //avatar.DumpKnownRegions();
2024  
2025 bool newAgent = false;
2026 int count = 0;
2027 foreach (GridRegion neighbour in neighbours)
2028 {
2029 //m_log.WarnFormat("--> Going to send child agent to {0}", neighbour.RegionName);
2030 // Don't do it if there's already an agent in that region
2031 if (newRegions.Contains(neighbour.RegionHandle))
2032 newAgent = true;
2033 else
2034 newAgent = false;
2035 // continue;
2036  
2037 if (neighbour.RegionHandle != sp.Scene.RegionInfo.RegionHandle)
2038 {
2039 try
2040 {
2041 // Let's put this back at sync, so that it doesn't clog
2042 // the network, especially for regions in the same physical server.
2043 // We're really not in a hurry here.
2044 InformClientOfNeighbourAsync(sp, cagents[count], neighbour, neighbour.ExternalEndPoint, newAgent);
2045 //InformClientOfNeighbourDelegate d = InformClientOfNeighbourAsync;
2046 //d.BeginInvoke(sp, cagents[count], neighbour, neighbour.ExternalEndPoint, newAgent,
2047 // InformClientOfNeighbourCompleted,
2048 // d);
2049 }
2050  
2051 catch (ArgumentOutOfRangeException)
2052 {
2053 m_log.ErrorFormat(
2054 "[ENTITY TRANSFER MODULE]: Neighbour Regions response included the current region in the neighbour list. The following region will not display to the client: {0} for region {1} ({2}, {3}).",
2055 neighbour.ExternalHostName,
2056 neighbour.RegionHandle,
2057 neighbour.RegionLocX,
2058 neighbour.RegionLocY);
2059 }
2060 catch (Exception e)
2061 {
2062 m_log.ErrorFormat(
2063 "[ENTITY TRANSFER MODULE]: Could not resolve external hostname {0} for region {1} ({2}, {3}). {4}",
2064 neighbour.ExternalHostName,
2065 neighbour.RegionHandle,
2066 neighbour.RegionLocX,
2067 neighbour.RegionLocY,
2068 e);
2069  
2070 // FIXME: Okay, even though we've failed, we're still going to throw the exception on,
2071 // since I don't know what will happen if we just let the client continue
2072  
2073 // XXX: Well, decided to swallow the exception instead for now. Let us see how that goes.
2074 // throw e;
2075  
2076 }
2077 }
2078 count++;
2079 }
2080 }
2081  
2082 // Computes the difference between two region bases.
2083 // Returns a vector of world coordinates (meters) from base of first region to the second.
2084 // The first region is the home region of the passed scene presence.
2085 Vector3 CalculateOffset(ScenePresence sp, GridRegion neighbour)
2086 {
2087 /*
2088 int rRegionX = (int)sp.Scene.RegionInfo.LegacyRegionLocX;
2089 int rRegionY = (int)sp.Scene.RegionInfo.LegacyRegionLocY;
2090 int tRegionX = neighbour.RegionLocX / (int)Constants.RegionSize;
2091 int tRegionY = neighbour.RegionLocY / (int)Constants.RegionSize;
2092 int shiftx = (rRegionX - tRegionX) * (int)Constants.RegionSize;
2093 int shifty = (rRegionY - tRegionY) * (int)Constants.RegionSize;
2094 return new Vector3(shiftx, shifty, 0f);
2095 */
2096 return new Vector3( sp.Scene.RegionInfo.WorldLocX - neighbour.RegionLocX,
2097 sp.Scene.RegionInfo.WorldLocY - neighbour.RegionLocY,
2098 0f);
2099 }
2100  
2101 public GridRegion GetRegionContainingWorldLocation(IGridService pGridService, UUID pScopeID, double px, double py)
2102 {
2103 // Since we don't know how big the regions could be, we have to search a very large area
2104 // to find possible regions.
2105 return GetRegionContainingWorldLocation(pGridService, pScopeID, px, py, Constants.MaximumRegionSize);
2106 }
2107  
2108 #region NotFoundLocationCache class
2109 // A collection of not found locations to make future lookups 'not found' lookups quick.
2110 // A simple expiring cache that keeps not found locations for some number of seconds.
2111 // A 'not found' location is presumed to be anywhere in the minimum sized region that
2112 // contains that point. A conservitive estimate.
2113 private class NotFoundLocationCache
2114 {
2115 private struct NotFoundLocation
2116 {
2117 public double minX, maxX, minY, maxY;
2118 public DateTime expireTime;
2119 }
2120 private List<NotFoundLocation> m_notFoundLocations = new List<NotFoundLocation>();
2121 public NotFoundLocationCache()
2122 {
2123 }
2124 // Add an area to the list of 'not found' places. The area is the snapped region
2125 // area around the added point.
2126 public void Add(double pX, double pY)
2127 {
2128 lock (m_notFoundLocations)
2129 {
2130 if (!LockedContains(pX, pY))
2131 {
2132 NotFoundLocation nfl = new NotFoundLocation();
2133 // A not found location is not found for at least a whole region sized area
2134 nfl.minX = pX - (pX % (double)Constants.RegionSize);
2135 nfl.minY = pY - (pY % (double)Constants.RegionSize);
2136 nfl.maxX = nfl.minX + (double)Constants.RegionSize;
2137 nfl.maxY = nfl.minY + (double)Constants.RegionSize;
2138 nfl.expireTime = DateTime.Now + TimeSpan.FromSeconds(30);
2139 m_notFoundLocations.Add(nfl);
2140 }
2141 }
2142  
2143 }
2144 // Test to see of this point is in any of the 'not found' areas.
2145 // Return 'true' if the point is found inside the 'not found' areas.
2146 public bool Contains(double pX, double pY)
2147 {
2148 bool ret = false;
2149 lock (m_notFoundLocations)
2150 ret = LockedContains(pX, pY);
2151 return ret;
2152 }
2153 private bool LockedContains(double pX, double pY)
2154 {
2155 bool ret = false;
2156 this.DoExpiration();
2157 foreach (NotFoundLocation nfl in m_notFoundLocations)
2158 {
2159 if (pX >= nfl.minX && pX < nfl.maxX && pY >= nfl.minY && pY < nfl.maxY)
2160 {
2161 ret = true;
2162 break;
2163 }
2164 }
2165 return ret;
2166 }
2167 private void DoExpiration()
2168 {
2169 List<NotFoundLocation> m_toRemove = null;
2170 DateTime now = DateTime.Now;
2171 foreach (NotFoundLocation nfl in m_notFoundLocations)
2172 {
2173 if (nfl.expireTime < now)
2174 {
2175 if (m_toRemove == null)
2176 m_toRemove = new List<NotFoundLocation>();
2177 m_toRemove.Add(nfl);
2178 }
2179 }
2180 if (m_toRemove != null)
2181 {
2182 foreach (NotFoundLocation nfl in m_toRemove)
2183 m_notFoundLocations.Remove(nfl);
2184 m_toRemove.Clear();
2185 }
2186 }
2187 }
2188 #endregion // NotFoundLocationCache class
2189 private NotFoundLocationCache m_notFoundLocationCache = new NotFoundLocationCache();
2190  
2191 // Given a world position (fractional meter coordinate), get the GridRegion info for
2192 // the region containing that point.
2193 // Someday this should be a method on GridService.
2194 // 'pSizeHint' is the size of the source region but since the destination point can be anywhere
2195 // the size of the target region is unknown thus the search area might have to be very large.
2196 // Return 'null' if no such region exists.
2197 public GridRegion GetRegionContainingWorldLocation(IGridService pGridService, UUID pScopeID,
2198 double px, double py, uint pSizeHint)
2199 {
2200 m_log.DebugFormat("{0} GetRegionContainingWorldLocation: call, XY=<{1},{2}>", LogHeader, px, py);
2201 GridRegion ret = null;
2202 const double fudge = 2.0;
2203  
2204 // One problem with this routine is negative results. That is, this can be called lots of times
2205 // for regions that don't exist. m_notFoundLocationCache remembers 'not found' results so they
2206 // will be quick 'not found's next time.
2207 // NotFoundLocationCache is an expiring cache so it will eventually forget about 'not found' and
2208 // thus re-ask the GridService about the location.
2209 if (m_notFoundLocationCache.Contains(px, py))
2210 {
2211 m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Not found via cache. loc=<{1},{2}>", LogHeader, px, py);
2212 return null;
2213 }
2214  
2215 // As an optimization, since most regions will be legacy sized regions (256x256), first try to get
2216 // the region at the appropriate legacy region location.
2217 uint possibleX = (uint)Math.Floor(px);
2218 possibleX -= possibleX % Constants.RegionSize;
2219 uint possibleY = (uint)Math.Floor(py);
2220 possibleY -= possibleY % Constants.RegionSize;
2221 ret = pGridService.GetRegionByPosition(pScopeID, (int)possibleX, (int)possibleY);
2222 if (ret != null)
2223 {
2224 m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Found region using legacy size. rloc=<{1},{2}>. Rname={3}",
2225 LogHeader, possibleX, possibleY, ret.RegionName);
2226 }
2227  
2228 if (ret == null)
2229 {
2230 // If the simple lookup failed, search the larger area for a region that contains this point
2231 double range = (double)pSizeHint + fudge;
2232 while (ret == null && range <= (Constants.MaximumRegionSize + Constants.RegionSize))
2233 {
2234 // Get from the grid service a list of regions that might contain this point.
2235 // The region origin will be in the zero direction so only subtract the range.
2236 List<GridRegion> possibleRegions = pGridService.GetRegionRange(pScopeID,
2237 (int)(px - range), (int)(px),
2238 (int)(py - range), (int)(py));
2239 m_log.DebugFormat("{0} GetRegionContainingWorldLocation: possibleRegions cnt={1}, range={2}",
2240 LogHeader, possibleRegions.Count, range);
2241 if (possibleRegions != null && possibleRegions.Count > 0)
2242 {
2243 // If we found some regions, check to see if the point is within
2244 foreach (GridRegion gr in possibleRegions)
2245 {
2246 m_log.DebugFormat("{0} GetRegionContainingWorldLocation: possibleRegion nm={1}, regionLoc=<{2},{3}>, regionSize=<{4},{5}>",
2247 LogHeader, gr.RegionName, gr.RegionLocX, gr.RegionLocY, gr.RegionSizeX, gr.RegionSizeY);
2248 if (px >= (double)gr.RegionLocX && px < (double)(gr.RegionLocX + gr.RegionSizeX)
2249 && py >= (double)gr.RegionLocY && py < (double)(gr.RegionLocY + gr.RegionSizeY))
2250 {
2251 // Found a region that contains the point
2252 ret = gr;
2253 m_log.DebugFormat("{0} GetRegionContainingWorldLocation: found. RegionName={1}", LogHeader, ret.RegionName);
2254 break;
2255 }
2256 }
2257 }
2258 // Larger search area for next time around if not found
2259 range *= 2;
2260 }
2261 }
2262  
2263 if (ret == null)
2264 {
2265 // remember this location was not found so we can quickly not find it next time
2266 m_notFoundLocationCache.Add(px, py);
2267 m_log.DebugFormat("{0} GetRegionContainingWorldLocation: Not found. Remembering loc=<{1},{2}>", LogHeader, px, py);
2268 }
2269  
2270 return ret;
2271 }
2272  
2273 private void InformClientOfNeighbourCompleted(IAsyncResult iar)
2274 {
2275 InformClientOfNeighbourDelegate icon = (InformClientOfNeighbourDelegate)iar.AsyncState;
2276 icon.EndInvoke(iar);
2277 //m_log.WarnFormat(" --> InformClientOfNeighbourCompleted");
2278 }
2279  
2280 /// <summary>
2281 /// Async component for informing client of which neighbours exist
2282 /// </summary>
2283 /// <remarks>
2284 /// This needs to run asynchronously, as a network timeout may block the thread for a long while
2285 /// </remarks>
2286 /// <param name="remoteClient"></param>
2287 /// <param name="a"></param>
2288 /// <param name="regionHandle"></param>
2289 /// <param name="endPoint"></param>
2290 private void InformClientOfNeighbourAsync(ScenePresence sp, AgentCircuitData a, GridRegion reg,
2291 IPEndPoint endPoint, bool newAgent)
2292 {
2293 // Let's wait just a little to give time to originating regions to catch up with closing child agents
2294 // after a cross here
2295 Thread.Sleep(500);
2296  
2297 Scene scene = sp.Scene;
2298  
2299 m_log.DebugFormat(
2300 "[ENTITY TRANSFER MODULE]: Informing {0} {1} about neighbour {2} {3} at ({4},{5})",
2301 sp.Name, sp.UUID, reg.RegionName, endPoint, reg.RegionCoordX, reg.RegionCoordY);
2302  
2303 string capsPath = reg.ServerURI + CapsUtil.GetCapsSeedPath(a.CapsPath);
2304  
2305 string reason = String.Empty;
2306  
2307 bool regionAccepted = scene.SimulationService.CreateAgent(null, reg, a, (uint)TeleportFlags.Default, out reason);
2308  
2309 if (regionAccepted && newAgent)
2310 {
2311 if (m_eqModule != null)
2312 {
2313 #region IP Translation for NAT
2314 IClientIPEndpoint ipepClient;
2315 if (sp.ClientView.TryGet(out ipepClient))
2316 {
2317 endPoint.Address = NetworkUtil.GetIPFor(ipepClient.EndPoint, endPoint.Address);
2318 }
2319 #endregion
2320  
2321 m_log.DebugFormat("{0} {1} is sending {2} EnableSimulator for neighbour region {3}(loc=<{4},{5}>,siz=<{6},{7}>) " +
2322 "and EstablishAgentCommunication with seed cap {8}", LogHeader,
2323 scene.RegionInfo.RegionName, sp.Name,
2324 reg.RegionName, reg.RegionLocX, reg.RegionLocY, reg.RegionSizeX, reg.RegionSizeY , capsPath);
2325  
2326 m_eqModule.EnableSimulator(reg.RegionHandle, endPoint, sp.UUID, reg.RegionSizeX, reg.RegionSizeY);
2327 m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath, reg.RegionHandle, reg.RegionSizeX, reg.RegionSizeY);
2328 }
2329 else
2330 {
2331 sp.ControllingClient.InformClientOfNeighbour(reg.RegionHandle, endPoint);
2332 // TODO: make Event Queue disablable!
2333 }
2334  
2335 m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Completed inform {0} {1} about neighbour {2}", sp.Name, sp.UUID, endPoint);
2336 }
2337  
2338 if (!regionAccepted)
2339 m_log.WarnFormat(
2340 "[ENTITY TRANSFER MODULE]: Region {0} did not accept {1} {2}: {3}",
2341 reg.RegionName, sp.Name, sp.UUID, reason);
2342 }
2343  
2344 /// <summary>
2345 /// Gets the range considered in view of this megaregion (assuming this is a megaregion).
2346 /// </summary>
2347 /// <remarks>Expressed in 256m units</remarks>
2348 /// <param name='swCorner'></param>
2349 /// <param name='neCorner'></param>
2350 private void GetMegaregionViewRange(out Vector2 swCorner, out Vector2 neCorner)
2351 {
2352 Vector2 extent = Vector2.Zero;
2353  
2354 if (m_regionCombinerModule != null)
2355 {
2356 Vector2 megaRegionSize = m_regionCombinerModule.GetSizeOfMegaregion(Scene.RegionInfo.RegionID);
2357 extent.X = (float)Util.WorldToRegionLoc((uint)megaRegionSize.X);
2358 extent.Y = (float)Util.WorldToRegionLoc((uint)megaRegionSize.Y);
2359 }
2360  
2361 swCorner.X = Scene.RegionInfo.RegionLocX - 1;
2362 swCorner.Y = Scene.RegionInfo.RegionLocY - 1;
2363 neCorner.X = Scene.RegionInfo.RegionLocX + extent.X;
2364 neCorner.Y = Scene.RegionInfo.RegionLocY + extent.Y;
2365 }
2366  
2367 /// <summary>
2368 /// Return the list of regions that are considered to be neighbours to the given scene.
2369 /// </summary>
2370 /// <param name="pScene"></param>
2371 /// <param name="pRegionLocX"></param>
2372 /// <param name="pRegionLocY"></param>
2373 /// <returns></returns>
2374 protected List<GridRegion> RequestNeighbours(ScenePresence avatar, uint pRegionLocX, uint pRegionLocY)
2375 {
2376 Scene pScene = avatar.Scene;
2377 RegionInfo m_regionInfo = pScene.RegionInfo;
2378  
2379 // Leaving this as a "megaregions" computation vs "non-megaregions" computation; it isn't
2380 // clear what should be done with a "far view" given that megaregions already extended the
2381 // view to include everything in the megaregion
2382 if (m_regionCombinerModule == null || !m_regionCombinerModule.IsRootForMegaregion(Scene.RegionInfo.RegionID))
2383 {
2384 // The area to check is as big as the current region.
2385 // We presume all adjacent regions are the same size as this region.
2386 uint dd = Math.Max((uint)avatar.Scene.DefaultDrawDistance,
2387 Math.Max(Scene.RegionInfo.RegionSizeX, Scene.RegionInfo.RegionSizeY));
2388  
2389 uint startX = Util.RegionToWorldLoc(pRegionLocX) - dd + Constants.RegionSize/2;
2390 uint startY = Util.RegionToWorldLoc(pRegionLocY) - dd + Constants.RegionSize/2;
2391  
2392 uint endX = Util.RegionToWorldLoc(pRegionLocX) + dd + Constants.RegionSize/2;
2393 uint endY = Util.RegionToWorldLoc(pRegionLocY) + dd + Constants.RegionSize/2;
2394  
2395 List<GridRegion> neighbours =
2396 avatar.Scene.GridService.GetRegionRange(m_regionInfo.ScopeID, (int)startX, (int)endX, (int)startY, (int)endY);
2397  
2398 neighbours.RemoveAll(delegate(GridRegion r) { return r.RegionID == m_regionInfo.RegionID; });
2399 return neighbours;
2400 }
2401 else
2402 {
2403 Vector2 swCorner, neCorner;
2404 GetMegaregionViewRange(out swCorner, out neCorner);
2405  
2406 List<GridRegion> neighbours
2407 = pScene.GridService.GetRegionRange(
2408 m_regionInfo.ScopeID,
2409 (int)Util.RegionToWorldLoc((uint)swCorner.X), (int)Util.RegionToWorldLoc((uint)neCorner.X),
2410 (int)Util.RegionToWorldLoc((uint)swCorner.Y), (int)Util.RegionToWorldLoc((uint)neCorner.Y) );
2411  
2412 neighbours.RemoveAll(delegate(GridRegion r) { return r.RegionID == m_regionInfo.RegionID; });
2413  
2414 return neighbours;
2415 }
2416 }
2417  
2418 private List<ulong> NewNeighbours(List<ulong> currentNeighbours, List<ulong> previousNeighbours)
2419 {
2420 return currentNeighbours.FindAll(delegate(ulong handle) { return !previousNeighbours.Contains(handle); });
2421 }
2422  
2423 // private List<ulong> CommonNeighbours(List<ulong> currentNeighbours, List<ulong> previousNeighbours)
2424 // {
2425 // return currentNeighbours.FindAll(delegate(ulong handle) { return previousNeighbours.Contains(handle); });
2426 // }
2427  
2428 private List<ulong> OldNeighbours(List<ulong> currentNeighbours, List<ulong> previousNeighbours)
2429 {
2430 return previousNeighbours.FindAll(delegate(ulong handle) { return !currentNeighbours.Contains(handle); });
2431 }
2432  
2433 private List<ulong> NeighbourHandles(List<GridRegion> neighbours)
2434 {
2435 List<ulong> handles = new List<ulong>();
2436 foreach (GridRegion reg in neighbours)
2437 {
2438 handles.Add(reg.RegionHandle);
2439 }
2440 return handles;
2441 }
2442  
2443 // private void Dump(string msg, List<ulong> handles)
2444 // {
2445 // m_log.InfoFormat("-------------- HANDLE DUMP ({0}) ---------", msg);
2446 // foreach (ulong handle in handles)
2447 // {
2448 // uint x, y;
2449 // Utils.LongToUInts(handle, out x, out y);
2450 // x = x / Constants.RegionSize;
2451 // y = y / Constants.RegionSize;
2452 // m_log.InfoFormat("({0}, {1})", x, y);
2453 // }
2454 // }
2455  
2456 #endregion
2457  
2458 #region Agent Arrived
2459  
2460 public void AgentArrivedAtDestination(UUID id)
2461 {
2462 m_entityTransferStateMachine.SetAgentArrivedAtDestination(id);
2463 }
2464  
2465 #endregion
2466  
2467 #region Object Transfers
2468  
2469 /// <summary>
2470 /// Move the given scene object into a new region depending on which region its absolute position has moved
2471 /// into.
2472 ///
2473 /// Using the objects new world location, ask the grid service for a the new region and adjust the prim
2474 /// position to be relative to the new region.
2475 /// </summary>
2476 /// <param name="grp">the scene object that we're crossing</param>
2477 /// <param name="attemptedPosition">the attempted out of region position of the scene object. This position is
2478 /// relative to the region the object currently is in.</param>
2479 /// <param name="silent">if 'true', the deletion of the client from the region is not broadcast to the clients</param>
2480 public void Cross(SceneObjectGroup grp, Vector3 attemptedPosition, bool silent)
2481 {
2482 if (grp == null)
2483 return;
2484 if (grp.IsDeleted)
2485 return;
2486  
2487 Scene scene = grp.Scene;
2488 if (scene == null)
2489 return;
2490  
2491 if (grp.RootPart.DIE_AT_EDGE)
2492 {
2493 // We remove the object here
2494 try
2495 {
2496 scene.DeleteSceneObject(grp, false);
2497 }
2498 catch (Exception)
2499 {
2500 m_log.Warn("[DATABASE]: exception when trying to remove the prim that crossed the border.");
2501 }
2502 return;
2503 }
2504  
2505 // Remember the old group position in case the region lookup fails so position can be restored.
2506 Vector3 oldGroupPosition = grp.RootPart.GroupPosition;
2507  
2508 // Compute the absolute position of the object.
2509 double objectWorldLocX = (double)scene.RegionInfo.WorldLocX + attemptedPosition.X;
2510 double objectWorldLocY = (double)scene.RegionInfo.WorldLocY + attemptedPosition.Y;
2511  
2512 // Ask the grid service for the region that contains the passed address
2513 GridRegion destination = GetRegionContainingWorldLocation(scene.GridService, scene.RegionInfo.ScopeID,
2514 objectWorldLocX, objectWorldLocY);
2515  
2516 Vector3 pos = Vector3.Zero;
2517 if (destination != null)
2518 {
2519 // Adjust the object's relative position from the old region (attemptedPosition)
2520 // to be relative to the new region (pos).
2521 pos = new Vector3( (float)(objectWorldLocX - (double)destination.RegionLocX),
2522 (float)(objectWorldLocY - (double)destination.RegionLocY),
2523 attemptedPosition.Z);
2524 }
2525  
2526 if (destination == null || !CrossPrimGroupIntoNewRegion(destination, pos, grp, silent))
2527 {
2528 m_log.InfoFormat("[ENTITY TRANSFER MODULE] cross region transfer failed for object {0}", grp.UUID);
2529  
2530 // We are going to move the object back to the old position so long as the old position
2531 // is in the region
2532 oldGroupPosition.X = Util.Clamp<float>(oldGroupPosition.X, 1.0f, (float)(scene.RegionInfo.RegionSizeX - 1));
2533 oldGroupPosition.Y = Util.Clamp<float>(oldGroupPosition.Y, 1.0f, (float)(scene.RegionInfo.RegionSizeY - 1));
2534 oldGroupPosition.Z = Util.Clamp<float>(oldGroupPosition.Z, 1.0f, Constants.RegionHeight);
2535  
2536 grp.AbsolutePosition = oldGroupPosition;
2537 grp.Velocity = Vector3.Zero;
2538 if (grp.RootPart.PhysActor != null)
2539 grp.RootPart.PhysActor.CrossingFailure();
2540  
2541 if (grp.RootPart.KeyframeMotion != null)
2542 grp.RootPart.KeyframeMotion.CrossingFailure();
2543  
2544 grp.ScheduleGroupForFullUpdate();
2545 }
2546 }
2547  
2548 /// <summary>
2549 /// Move the given scene object into a new region
2550 /// </summary>
2551 /// <param name="newRegionHandle"></param>
2552 /// <param name="grp">Scene Object Group that we're crossing</param>
2553 /// <returns>
2554 /// true if the crossing itself was successful, false on failure
2555 /// FIMXE: we still return true if the crossing object was not successfully deleted from the originating region
2556 /// </returns>
2557 protected bool CrossPrimGroupIntoNewRegion(GridRegion destination, Vector3 newPosition, SceneObjectGroup grp, bool silent)
2558 {
2559 //m_log.Debug(" >>> CrossPrimGroupIntoNewRegion <<<");
2560  
2561 bool successYN = false;
2562 grp.RootPart.ClearUpdateSchedule();
2563 //int primcrossingXMLmethod = 0;
2564  
2565 if (destination != null)
2566 {
2567 //string objectState = grp.GetStateSnapshot();
2568  
2569 //successYN
2570 // = m_sceneGridService.PrimCrossToNeighboringRegion(
2571 // newRegionHandle, grp.UUID, m_serialiser.SaveGroupToXml2(grp), primcrossingXMLmethod);
2572 //if (successYN && (objectState != "") && m_allowScriptCrossings)
2573 //{
2574 // successYN = m_sceneGridService.PrimCrossToNeighboringRegion(
2575 // newRegionHandle, grp.UUID, objectState, 100);
2576 //}
2577  
2578 //// And the new channel...
2579 //if (m_interregionCommsOut != null)
2580 // successYN = m_interregionCommsOut.SendCreateObject(newRegionHandle, grp, true);
2581 if (Scene.SimulationService != null)
2582 successYN = Scene.SimulationService.CreateObject(destination, newPosition, grp, true);
2583  
2584 if (successYN)
2585 {
2586 // We remove the object here
2587 try
2588 {
2589 grp.Scene.DeleteSceneObject(grp, silent);
2590 }
2591 catch (Exception e)
2592 {
2593 m_log.ErrorFormat(
2594 "[ENTITY TRANSFER MODULE]: Exception deleting the old object left behind on a border crossing for {0}, {1}",
2595 grp, e);
2596 }
2597 }
2598 /*
2599 * done on caller ( not in attachments crossing for now)
2600 else
2601 {
2602  
2603 if (!grp.IsDeleted)
2604 {
2605 PhysicsActor pa = grp.RootPart.PhysActor;
2606 if (pa != null)
2607 {
2608 pa.CrossingFailure();
2609 if (grp.RootPart.KeyframeMotion != null)
2610 {
2611 // moved to KeyframeMotion.CrossingFailure
2612 // grp.RootPart.Velocity = Vector3.Zero;
2613 grp.RootPart.KeyframeMotion.CrossingFailure();
2614 // grp.SendGroupRootTerseUpdate();
2615 }
2616 }
2617 }
2618  
2619 m_log.ErrorFormat("[ENTITY TRANSFER MODULE]: Prim crossing failed for {0}", grp);
2620 }
2621 */
2622 }
2623 else
2624 {
2625 m_log.Error("[ENTITY TRANSFER MODULE]: destination was unexpectedly null in Scene.CrossPrimGroupIntoNewRegion()");
2626 }
2627  
2628 return successYN;
2629 }
2630  
2631 /// <summary>
2632 /// Cross the attachments for an avatar into the destination region.
2633 /// </summary>
2634 /// <remarks>
2635 /// This is only invoked for simulators released prior to April 2011. Versions of OpenSimulator since then
2636 /// transfer attachments in one go as part of the ChildAgentDataUpdate data passed in the update agent call.
2637 /// </remarks>
2638 /// <param name='destination'></param>
2639 /// <param name='sp'></param>
2640 /// <param name='silent'></param>
2641 protected void CrossAttachmentsIntoNewRegion(GridRegion destination, ScenePresence sp, bool silent)
2642 {
2643 List<SceneObjectGroup> attachments = sp.GetAttachments();
2644  
2645 // m_log.DebugFormat(
2646 // "[ENTITY TRANSFER MODULE]: Crossing {0} attachments into {1} for {2}",
2647 // m_attachments.Count, destination.RegionName, sp.Name);
2648  
2649 foreach (SceneObjectGroup gobj in attachments)
2650 {
2651 // If the prim group is null then something must have happened to it!
2652 if (gobj != null && !gobj.IsDeleted)
2653 {
2654 SceneObjectGroup clone = (SceneObjectGroup)gobj.CloneForNewScene();
2655 clone.RootPart.GroupPosition = gobj.RootPart.AttachedPos;
2656 clone.IsAttachment = false;
2657  
2658 //gobj.RootPart.LastOwnerID = gobj.GetFromAssetID();
2659 m_log.DebugFormat(
2660 "[ENTITY TRANSFER MODULE]: Sending attachment {0} to region {1}",
2661 clone.UUID, destination.RegionName);
2662  
2663 CrossPrimGroupIntoNewRegion(destination, Vector3.Zero, clone, silent);
2664 }
2665 }
2666  
2667 sp.ClearAttachments();
2668 }
2669  
2670 #endregion
2671  
2672 #region Misc
2673  
2674 public bool IsInTransit(UUID id)
2675 {
2676 return m_entityTransferStateMachine.GetAgentTransferState(id) != null;
2677 }
2678  
2679 protected void ReInstantiateScripts(ScenePresence sp)
2680 {
2681 int i = 0;
2682 if (sp.InTransitScriptStates.Count > 0)
2683 {
2684 List<SceneObjectGroup> attachments = sp.GetAttachments();
2685  
2686 foreach (SceneObjectGroup sog in attachments)
2687 {
2688 if (i < sp.InTransitScriptStates.Count)
2689 {
2690 sog.SetState(sp.InTransitScriptStates[i++], sp.Scene);
2691 sog.CreateScriptInstances(0, false, sp.Scene.DefaultScriptEngine, 0);
2692 sog.ResumeScripts();
2693 }
2694 else
2695 m_log.ErrorFormat(
2696 "[ENTITY TRANSFER MODULE]: InTransitScriptStates.Count={0} smaller than Attachments.Count={1}",
2697 sp.InTransitScriptStates.Count, attachments.Count);
2698 }
2699  
2700 sp.InTransitScriptStates.Clear();
2701 }
2702 }
2703 #endregion
2704  
2705 }
2706 }