opensim-development – Blame information for rev 1

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