/// <summary> /// Try to teleport an agent to a new region. /// </summary> /// <param name="remoteClient"></param> /// <param name="RegionHandle"></param> /// <param name="position"></param> /// <param name="lookAt"></param> /// <param name="flags"></param> public virtual void RequestTeleportToLocation(ScenePresence avatar, ulong regionHandle, Vector3 position, Vector3 lookAt, uint teleportFlags) { if (!avatar.Scene.Permissions.CanTeleport(avatar.UUID)) return; bool destRegionUp = true; IEventQueue eq = avatar.Scene.RequestModuleInterface<IEventQueue>(); // Reset animations; the viewer does that in teleports. avatar.ResetAnimations(); if (regionHandle == m_regionInfo.RegionHandle) { m_log.DebugFormat( "[SCENE COMMUNICATION SERVICE]: RequestTeleportToLocation {0} within {1}", position, m_regionInfo.RegionName); // Teleport within the same region if (IsOutsideRegion(avatar.Scene, position) || position.Z < 0) { Vector3 emergencyPos = new Vector3(128, 128, 128); m_log.WarnFormat( "[SCENE COMMUNICATION SERVICE]: RequestTeleportToLocation() was given an illegal position of {0} for avatar {1}, {2}. Substituting {3}", position, avatar.Name, avatar.UUID, emergencyPos); position = emergencyPos; } // TODO: Get proper AVG Height float localAVHeight = 1.56f; float posZLimit = 22; // TODO: Check other Scene HeightField if (position.X > 0 && position.X <= (int)Constants.RegionSize && position.Y > 0 && position.Y <=(int)Constants.RegionSize) { posZLimit = (float) avatar.Scene.Heightmap[(int) position.X, (int) position.Y]; } float newPosZ = posZLimit + localAVHeight; if (posZLimit >= (position.Z - (localAVHeight / 2)) && !(Single.IsInfinity(newPosZ) || Single.IsNaN(newPosZ))) { position.Z = newPosZ; } // Only send this if the event queue is null if (eq == null) avatar.ControllingClient.SendTeleportLocationStart(); avatar.ControllingClient.SendLocalTeleport(position, lookAt, teleportFlags); avatar.Teleport(position); } else { RegionInfo reg = RequestNeighbouringRegionInfo(regionHandle); if (reg != null) { m_log.DebugFormat( "[SCENE COMMUNICATION SERVICE]: RequestTeleportToLocation to {0} in {1}", position, reg.RegionName); if (eq == null) avatar.ControllingClient.SendTeleportLocationStart(); // Let's do DNS resolution only once in this process, please! // This may be a costly operation. The reg.ExternalEndPoint field is not a passive field, // it's actually doing a lot of work. IPEndPoint endPoint = reg.ExternalEndPoint; if (endPoint.Address == null) { // Couldn't resolve the name. Can't TP, because the viewer wants IP addresses. destRegionUp = false; } if (destRegionUp) { uint newRegionX = (uint)(reg.RegionHandle >> 40); uint newRegionY = (((uint)(reg.RegionHandle)) >> 8); uint oldRegionX = (uint)(m_regionInfo.RegionHandle >> 40); uint oldRegionY = (((uint)(m_regionInfo.RegionHandle)) >> 8); // Fixing a bug where teleporting while sitting results in the avatar ending up removed from // both regions if (avatar.ParentID != (uint)0) avatar.StandUp(); if (!avatar.ValidateAttachments()) { avatar.ControllingClient.SendTeleportFailed("Inconsistent attachment state"); return; } // the avatar.Close below will clear the child region list. We need this below for (possibly) // closing the child agents, so save it here (we need a copy as it is Clear()-ed). //List<ulong> childRegions = new List<ulong>(avatar.GetKnownRegionList()); // Compared to ScenePresence.CrossToNewRegion(), there's no obvious code to handle a teleport // failure at this point (unlike a border crossing failure). So perhaps this can never fail // once we reach here... //avatar.Scene.RemoveCapsHandler(avatar.UUID); string capsPath = String.Empty; AgentCircuitData agentCircuit = avatar.ControllingClient.RequestClientInfo(); agentCircuit.BaseFolder = UUID.Zero; agentCircuit.InventoryFolder = UUID.Zero; agentCircuit.startpos = position; agentCircuit.child = true; if (Util.IsOutsideView(oldRegionX, newRegionX, oldRegionY, newRegionY)) { // brand new agent, let's create a new caps seed agentCircuit.CapsPath = CapsUtil.GetRandomCapsObjectPath(); } string reason = String.Empty; // Let's create an agent there if one doesn't exist yet. //if (!m_commsProvider.InterRegion.InformRegionOfChildAgent(reg.RegionHandle, agentCircuit)) if (!m_interregionCommsOut.SendCreateChildAgent(reg.RegionHandle, agentCircuit, out reason)) { avatar.ControllingClient.SendTeleportFailed(String.Format("Destination is not accepting teleports: {0}", reason)); return; } // OK, it got this agent. Let's close some child agents avatar.CloseChildAgents(newRegionX, newRegionY); if (Util.IsOutsideView(oldRegionX, newRegionX, oldRegionY, newRegionY)) { #region IP Translation for NAT IClientIPEndpoint ipepClient; if (avatar.ClientView.TryGet(out ipepClient)) { capsPath = "http://" + NetworkUtil.GetHostFor(ipepClient.EndPoint, reg.ExternalHostName) + ":" + reg.HttpPort + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath); } else { capsPath = "http://" + reg.ExternalHostName + ":" + reg.HttpPort + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath); } #endregion if (eq != null) { #region IP Translation for NAT // Uses ipepClient above if (avatar.ClientView.TryGet(out ipepClient)) { endPoint.Address = NetworkUtil.GetIPFor(ipepClient.EndPoint, endPoint.Address); } #endregion eq.EnableSimulator(reg.RegionHandle, endPoint, avatar.UUID); // ES makes the client send a UseCircuitCode message to the destination, // which triggers a bunch of things there. // So let's wait Thread.Sleep(2000); eq.EstablishAgentCommunication(avatar.UUID, endPoint, capsPath); } else { avatar.ControllingClient.InformClientOfNeighbour(reg.RegionHandle, endPoint); } } else { agentCircuit.CapsPath = avatar.Scene.CapsModule.GetChildSeed(avatar.UUID, reg.RegionHandle); capsPath = "http://" + reg.ExternalHostName + ":" + reg.HttpPort + "/CAPS/" + agentCircuit.CapsPath + "0000/"; } // Expect avatar crossing is a heavy-duty function at the destination. // That is where MakeRoot is called, which fetches appearance and inventory. // Plus triggers OnMakeRoot, which spawns a series of asynchronous updates. //m_commsProvider.InterRegion.ExpectAvatarCrossing(reg.RegionHandle, avatar.ControllingClient.AgentId, // position, false); //{ // avatar.ControllingClient.SendTeleportFailed("Problem with destination."); // // We should close that agent we just created over at destination... // List<ulong> lst = new List<ulong>(); // lst.Add(reg.RegionHandle); // SendCloseChildAgentAsync(avatar.UUID, lst); // return; //} SetInTransit(avatar.UUID); // Let's send a full update of the agent. This is a synchronous call. AgentData agent = new AgentData(); avatar.CopyTo(agent); agent.Position = position; agent.CallbackURI = "http://" + m_regionInfo.ExternalHostName + ":" + m_regionInfo.HttpPort + "/agent/" + avatar.UUID.ToString() + "/" + avatar.Scene.RegionInfo.RegionHandle.ToString() + "/release/"; m_interregionCommsOut.SendChildAgentUpdate(reg.RegionHandle, agent); m_log.DebugFormat( "[CAPS]: Sending new CAPS seed url {0} to client {1}", capsPath, avatar.UUID); if (eq != null) { eq.TeleportFinishEvent(reg.RegionHandle, 13, endPoint, 4, teleportFlags, capsPath, avatar.UUID); } else { avatar.ControllingClient.SendRegionTeleport(reg.RegionHandle, 13, endPoint, 4, teleportFlags, capsPath); } // TeleportFinish makes the client send CompleteMovementIntoRegion (at the destination), which // trigers a whole shebang of things there, including MakeRoot. So let's wait for confirmation // that the client contacted the destination before we send the attachments and close things here. if (!WaitForCallback(avatar.UUID)) { // Client never contacted destination. Let's restore everything back avatar.ControllingClient.SendTeleportFailed("Problems connecting to destination."); ResetFromTransit(avatar.UUID); // Yikes! We should just have a ref to scene here. avatar.Scene.InformClientOfNeighbours(avatar); // Finally, kill the agent we just created at the destination. m_interregionCommsOut.SendCloseAgent(reg.RegionHandle, avatar.UUID); return; } // Can't go back from here if (KiPrimitive != null) { KiPrimitive(avatar.LocalId); } avatar.MakeChildAgent(); // CrossAttachmentsIntoNewRegion is a synchronous call. We shouldn't need to wait after it avatar.CrossAttachmentsIntoNewRegion(reg.RegionHandle, true); // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone if (Util.IsOutsideView(oldRegionX, newRegionX, oldRegionY, newRegionY)) { Thread.Sleep(5000); avatar.Close(); CloseConnection(avatar.UUID); } else // now we have a child agent in this region. avatar.Reset(); // if (teleport success) // seems to be always success here // the user may change their profile information in other region, // so the userinfo in UserProfileCache is not reliable any more, delete it if (avatar.Scene.NeedSceneCacheClear(avatar.UUID)) { m_commsProvider.UserProfileCacheService.RemoveUser(avatar.UUID); m_log.DebugFormat( "[SCENE COMMUNICATION SERVICE]: User {0} is going to another region, profile cache removed", avatar.UUID); } } else { avatar.ControllingClient.SendTeleportFailed("Remote Region appears to be down"); } } else { // TP to a place that doesn't exist (anymore) // Inform the viewer about that avatar.ControllingClient.SendTeleportFailed("The region you tried to teleport to doesn't exist anymore"); // and set the map-tile to '(Offline)' uint regX, regY; Utils.LongToUInts(regionHandle, out regX, out regY); MapBlockData block = new MapBlockData(); block.X = (ushort)(regX / Constants.RegionSize); block.Y = (ushort)(regY / Constants.RegionSize); block.Access = 254; // == not there List<MapBlockData> blocks = new List<MapBlockData>(); blocks.Add(block); avatar.ControllingClient.SendMapBlock(blocks, 0); } } }