diff --git a/OpenSim/Framework/ChildAgentDataUpdate.cs b/OpenSim/Framework/ChildAgentDataUpdate.cs index 7c5af4c319..e14878cf64 100644 --- a/OpenSim/Framework/ChildAgentDataUpdate.cs +++ b/OpenSim/Framework/ChildAgentDataUpdate.cs @@ -287,7 +287,7 @@ namespace OpenSim.Framework public Vector3 AtAxis; public Vector3 LeftAxis; public Vector3 UpAxis; - public bool ChangedGrid; + public bool SenderWantsToWaitForRoot; public float Far; public float Aspect; @@ -356,8 +356,9 @@ namespace OpenSim.Framework args["left_axis"] = OSD.FromString(LeftAxis.ToString()); args["up_axis"] = OSD.FromString(UpAxis.ToString()); - - args["changed_grid"] = OSD.FromBoolean(ChangedGrid); + //backwards compatibility + args["changed_grid"] = OSD.FromBoolean(SenderWantsToWaitForRoot); + args["wait_for_root"] = OSD.FromBoolean(SenderWantsToWaitForRoot); args["far"] = OSD.FromReal(Far); args["aspect"] = OSD.FromReal(Aspect); @@ -526,8 +527,8 @@ namespace OpenSim.Framework if (args["up_axis"] != null) Vector3.TryParse(args["up_axis"].AsString(), out AtAxis); - if (args["changed_grid"] != null) - ChangedGrid = args["changed_grid"].AsBoolean(); + if (args.ContainsKey("wait_for_root") && args["wait_for_root"] != null) + SenderWantsToWaitForRoot = args["wait_for_root"].AsBoolean(); if (args["far"] != null) Far = (float)(args["far"].AsReal()); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 32282af19a..b0ec88ce40 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -1196,6 +1196,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // UseCircuitCode handling if (packet.Type == PacketType.UseCircuitCode) { + m_log.DebugFormat("[ZZZ]: In the dungeon: UseCircuitCode"); // We need to copy the endpoint so that it doesn't get changed when another thread reuses the // buffer. object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet }; @@ -1204,6 +1205,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP return; } + else if (packet.Type == PacketType.CompleteAgentMovement) + { + // Send ack straight away to let the viewer know that we got it. + SendAckImmediate(endPoint, packet.Header.Sequence); + + m_log.DebugFormat("[ZZZ]: In the dungeon: CompleteAgentMovement"); + // We need to copy the endpoint so that it doesn't get changed when another thread reuses the + // buffer. + object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet }; + + Util.FireAndForget(HandleCompleteMovementIntoRegion, array); + + return; + } // Determine which agent this packet came from IClientAPI client; @@ -1542,6 +1557,56 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } + private void HandleCompleteMovementIntoRegion(object o) + { + IPEndPoint endPoint = null; + IClientAPI client = null; + + try + { + object[] array = (object[])o; + endPoint = (IPEndPoint)array[0]; + CompleteAgentMovementPacket packet = (CompleteAgentMovementPacket)array[1]; + + // Determine which agent this packet came from + int count = 10; + while (!m_scene.TryGetClient(endPoint, out client) && count-- > 0) + { + m_log.Debug("[LLUDPSERVER]: Received a CompleteMovementIntoRegion in " + m_scene.RegionInfo.RegionName + " (not ready yet)"); + Thread.Sleep(200); + } + + if (client == null) + return; + + IncomingPacket incomingPacket1; + + // Inbox insertion + if (UsePools) + { + incomingPacket1 = m_incomingPacketPool.GetObject(); + incomingPacket1.Client = (LLClientView)client; + incomingPacket1.Packet = packet; + } + else + { + incomingPacket1 = new IncomingPacket((LLClientView)client, packet); + } + + packetInbox.Enqueue(incomingPacket1); + } + catch (Exception e) + { + m_log.ErrorFormat( + "[LLUDPSERVER]: CompleteMovementIntoRegion handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}", + endPoint != null ? endPoint.ToString() : "n/a", + client != null ? client.Name : "unknown", + client != null ? client.AgentId.ToString() : "unknown", + e.Message, + e.StackTrace); + } + } + /// /// Send an ack immediately to the given endpoint. /// @@ -1934,6 +1999,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP Packet packet = incomingPacket.Packet; LLClientView client = incomingPacket.Client; + if (packet is CompleteAgentMovementPacket) + m_log.DebugFormat("[ZZZ]: Received CompleteAgentMovementPacket"); + if (client.IsActive) { m_currentIncomingClient = client; diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index 5124aedae4..b0c0b1d370 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -684,7 +684,18 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer agentCircuit.CapsPath = CapsUtil.GetRandomCapsObjectPath(); } - //sp.ControllingClient.SendTeleportProgress(teleportFlags, "Contacting destination..."); + if (version.Equals("SIMULATION/0.2")) + TransferAgent_V2(sp, agentCircuit, reg, finalDestination, endPoint, teleportFlags, oldRegionX, newRegionX, oldRegionY, newRegionY, version, out reason); + else + TransferAgent_V1(sp, agentCircuit, reg, finalDestination, endPoint, teleportFlags, oldRegionX, newRegionX, oldRegionY, newRegionY, version, out reason); + + } + + private void TransferAgent_V1(ScenePresence sp, AgentCircuitData agentCircuit, GridRegion reg, GridRegion finalDestination, + IPEndPoint endPoint, uint teleportFlags, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, string version, out string reason) + { + ulong destinationHandle = finalDestination.RegionHandle; + AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); // Let's create an agent there if one doesn't exist yet. // NOTE: logout will always be false for a non-HG teleport. @@ -707,7 +718,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer m_interRegionTeleportCancels.Value++; m_log.DebugFormat( - "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request", + "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after CreateAgent on client request", sp.Name, finalDestination.RegionName, sp.Scene.Name); return; @@ -725,14 +736,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer // Past this point we have to attempt clean up if the teleport fails, so update transfer state. m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring); - - #region old protocol - - IClientIPEndpoint ipepClient; + + IClientIPEndpoint ipepClient; + string capsPath = String.Empty; if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY)) { m_log.DebugFormat( - "[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for incoming agent {3} from {4}", + "[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for incoming agent {3} from {4}", finalDestination.RegionName, newRegionX, newRegionY, sp.Name, Scene.Name); //sp.ControllingClient.SendTeleportProgress(teleportFlags, "Creating agent..."); @@ -745,30 +755,30 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer #endregion capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath); - //if (m_eqModule != null) - //{ - // // The EnableSimulator message makes the client establish a connection with the destination - // // simulator by sending the initial UseCircuitCode UDP packet to the destination containing the - // // correct circuit code. - // m_eqModule.EnableSimulator(destinationHandle, endPoint, sp.UUID); + if (m_eqModule != null) + { + // The EnableSimulator message makes the client establish a connection with the destination + // simulator by sending the initial UseCircuitCode UDP packet to the destination containing the + // correct circuit code. + m_eqModule.EnableSimulator(destinationHandle, endPoint, sp.UUID); - // // XXX: Is this wait necessary? We will always end up waiting on UpdateAgent for the destination - // // simulator to confirm that it has established communication with the viewer. - // Thread.Sleep(200); + // XXX: Is this wait necessary? We will always end up waiting on UpdateAgent for the destination + // simulator to confirm that it has established communication with the viewer. + Thread.Sleep(200); - // // At least on LL 3.3.4 for teleports between different regions on the same simulator this appears - // // unnecessary - teleport will succeed and SEED caps will be requested without it (though possibly - // // only on TeleportFinish). This is untested for region teleport between different simulators - // // though this probably also works. - // m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath); - //} - //else - //{ - // // XXX: This is a little misleading since we're information the client of its avatar destination, - // // which may or may not be a neighbour region of the source region. This path is probably little - // // used anyway (with EQ being the one used). But it is currently being used for test code. - // sp.ControllingClient.InformClientOfNeighbour(destinationHandle, endPoint); - //} + // At least on LL 3.3.4 for teleports between different regions on the same simulator this appears + // unnecessary - teleport will succeed and SEED caps will be requested without it (though possibly + // only on TeleportFinish). This is untested for region teleport between different simulators + // though this probably also works. + m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath); + } + else + { + // XXX: This is a little misleading since we're information the client of its avatar destination, + // which may or may not be a neighbour region of the source region. This path is probably little + // used anyway (with EQ being the one used). But it is currently being used for test code. + sp.ControllingClient.InformClientOfNeighbour(destinationHandle, endPoint); + } } else { @@ -776,15 +786,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath); } - #endregion old protocol - // Let's send a full update of the agent. This is a synchronous call. AgentData agent = new AgentData(); sp.CopyTo(agent); - agent.Position = position; - //SetCallbackURL(agent, sp.Scene.RegionInfo); + agent.Position = agentCircuit.startpos; + SetCallbackURL(agent, sp.Scene.RegionInfo); - //sp.ControllingClient.SendTeleportProgress(teleportFlags, "Updating agent..."); // We will check for an abort before UpdateAgent since UpdateAgent will require an active viewer to // establish th econnection to the destination which makes it return true. @@ -814,7 +821,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer sp.ControllingClient.SendRegionTeleport(destinationHandle, 13, endPoint, 4, teleportFlags, capsPath); } - + // A common teleport failure occurs when we can send CreateAgent to the // destination region but the viewer cannot establish the connection (e.g. due to network issues between // the viewer and the destination). In this case, UpdateAgent timesout after 10 seconds, although then @@ -835,7 +842,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer m_log.WarnFormat( "[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1} from {2}. Keeping avatar in source region.", sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName); - + Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Connection between viewer and destination region could not be established."); return; } @@ -845,7 +852,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer m_interRegionTeleportCancels.Value++; m_log.DebugFormat( - "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after UpdateAgent on client request", + "[ENTITY TRANSFER MODULE]: Cancelled teleport of {0} to {1} from {2} after UpdateAgent on client request", sp.Name, finalDestination.RegionName, sp.Scene.Name); CleanupFailedInterRegionTeleport(sp, currentAgentCircuit.SessionID.ToString(), finalDestination); @@ -857,30 +864,30 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer "[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}", capsPath, sp.Scene.RegionInfo.RegionName, sp.Name); - //// 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 close things here. - //if (!m_entityTransferStateMachine.WaitForAgentArrivedAtDestination(sp.UUID)) - //{ - // if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) - // { - // m_interRegionTeleportAborts.Value++; + // 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 close things here. + if (!m_entityTransferStateMachine.WaitForAgentArrivedAtDestination(sp.UUID)) + { + if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) + { + m_interRegionTeleportAborts.Value++; - // m_log.DebugFormat( - // "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after WaitForAgentArrivedAtDestination due to previous client close.", - // sp.Name, finalDestination.RegionName, sp.Scene.Name); + m_log.DebugFormat( + "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after WaitForAgentArrivedAtDestination due to previous client close.", + sp.Name, finalDestination.RegionName, sp.Scene.Name); - // return; - // } + return; + } - // m_log.WarnFormat( - // "[ENTITY TRANSFER MODULE]: Teleport of {0} to {1} from {2} failed due to no callback from destination region. Returning avatar to source region.", - // sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName); - - // Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Destination region did not signal teleport completion."); + m_log.WarnFormat( + "[ENTITY TRANSFER MODULE]: Teleport of {0} to {1} from {2} failed due to no callback from destination region. Returning avatar to source region.", + sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName); - // return; - //} + Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Destination region did not signal teleport completion."); + + return; + } m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); @@ -914,7 +921,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer // // This sleep can be increased if necessary. However, whilst it's active, // an agent cannot teleport back to this region if it has teleported away. - Thread.Sleep(15000); + Thread.Sleep(2000); sp.Scene.IncomingCloseAgent(sp.UUID, false); } @@ -925,6 +932,126 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer } } + private void TransferAgent_V2(ScenePresence sp, AgentCircuitData agentCircuit, GridRegion reg, GridRegion finalDestination, + IPEndPoint endPoint, uint teleportFlags, uint oldRegionX, uint newRegionX, uint oldRegionY, uint newRegionY, string version, out string reason) + { + ulong destinationHandle = finalDestination.RegionHandle; + AgentCircuitData currentAgentCircuit = sp.Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); + + // Let's create an agent there if one doesn't exist yet. + // NOTE: logout will always be false for a non-HG teleport. + bool logout = false; + if (!CreateAgent(sp, reg, finalDestination, agentCircuit, teleportFlags, out reason, out logout)) + { + m_interRegionTeleportFailures.Value++; + + sp.ControllingClient.SendTeleportFailed(String.Format("Teleport refused: {0}", reason)); + + m_log.DebugFormat( + "[ENTITY TRANSFER MODULE]: Teleport of {0} from {1} to {2} was refused because {3}", + sp.Name, sp.Scene.RegionInfo.RegionName, finalDestination.RegionName, reason); + + return; + } + + // Past this point we have to attempt clean up if the teleport fails, so update transfer state. + m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.Transferring); + + IClientIPEndpoint ipepClient; + string capsPath = String.Empty; + if (NeedsNewAgent(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY)) + { + m_log.DebugFormat( + "[ENTITY TRANSFER MODULE]: Determined that region {0} at {1},{2} needs new child agent for agent {3} from {4}", + finalDestination.RegionName, newRegionX, newRegionY, sp.Name, Scene.Name); + + //sp.ControllingClient.SendTeleportProgress(teleportFlags, "Creating agent..."); + #region IP Translation for NAT + // Uses ipepClient above + if (sp.ClientView.TryGet(out ipepClient)) + { + endPoint.Address = NetworkUtil.GetIPFor(ipepClient.EndPoint, endPoint.Address); + } + #endregion + capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath); + } + else + { + agentCircuit.CapsPath = sp.Scene.CapsModule.GetChildSeed(sp.UUID, reg.RegionHandle); + capsPath = finalDestination.ServerURI + CapsUtil.GetCapsSeedPath(agentCircuit.CapsPath); + } + + // We need to set this here to avoid an unlikely race condition when teleporting to a neighbour simulator, + // where that neighbour simulator could otherwise request a child agent create on the source which then + // closes our existing agent which is still signalled as root. + //sp.IsChildAgent = true; + + // New protocol: send TP Finish directly, without prior ES or EAC. That's what happens in the Linden grid + if (m_eqModule != null) + m_eqModule.TeleportFinishEvent(destinationHandle, 13, endPoint, 0, teleportFlags, capsPath, sp.UUID); + else + sp.ControllingClient.SendRegionTeleport(destinationHandle, 13, endPoint, 4, + teleportFlags, capsPath); + + m_log.DebugFormat( + "[ENTITY TRANSFER MODULE]: Sending new CAPS seed url {0} from {1} to {2}", + capsPath, sp.Scene.RegionInfo.RegionName, sp.Name); + + // Let's send a full update of the agent. + AgentData agent = new AgentData(); + sp.CopyTo(agent); + agent.Position = agentCircuit.startpos; + agent.SenderWantsToWaitForRoot = true; + //SetCallbackURL(agent, sp.Scene.RegionInfo); + + // Send the Update. If this returns true, we know the client has contacted the destination + // via CompleteMovementIntoRegion, so we can let go. + // If it returns false, something went wrong, and we need to abort. + m_log.DebugFormat("[ZZZ]: Sending Update"); + if (!UpdateAgent(reg, finalDestination, agent, sp)) + { + if (m_entityTransferStateMachine.GetAgentTransferState(sp.UUID) == AgentTransferState.Aborting) + { + m_interRegionTeleportAborts.Value++; + + m_log.DebugFormat( + "[ENTITY TRANSFER MODULE]: Aborted teleport of {0} to {1} from {2} after UpdateAgent due to previous client close.", + sp.Name, finalDestination.RegionName, sp.Scene.Name); + + return; + } + + m_log.WarnFormat( + "[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1} from {2}. Keeping avatar in source region.", + sp.Name, finalDestination.RegionName, sp.Scene.RegionInfo.RegionName); + + Fail(sp, finalDestination, logout, currentAgentCircuit.SessionID.ToString(), "Connection between viewer and destination region could not be established."); + return; + } + + m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); + + // May need to logout or other cleanup + AgentHasMovedAway(sp, logout); + + // Well, this is it. The agent is over there. + KillEntity(sp.Scene, sp.LocalId); + + // Now let's make it officially a child agent + sp.MakeChildAgent(); + + // OK, it got this agent. Let's close some child agents + sp.CloseChildAgents(newRegionX, newRegionY); + + // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone + + if (NeedsClosing(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) + sp.Scene.IncomingCloseAgent(sp.UUID, false); + else + // now we have a child agent in this region. + sp.Reset(); + } + /// /// Clean up an inter-region teleport that did not complete, either because of simulator failure or cancellation. /// @@ -938,11 +1065,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer { m_entityTransferStateMachine.UpdateInTransit(sp.UUID, AgentTransferState.CleaningUp); - sp.IsChildAgent = false; - ReInstantiateScripts(sp); - - EnableChildAgents(sp); + if (sp.IsChildAgent) // We had set it to child before attempted TP (V1) + { + sp.IsChildAgent = false; + ReInstantiateScripts(sp); + EnableChildAgents(sp); + } // Finally, kill the agent we just created at the destination. // XXX: Possibly this should be done asynchronously. Scene.SimulationService.CloseAgent(finalDestination, sp.UUID, auth_token); diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs index 6d5039b6fe..7dd10f78a5 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs @@ -48,7 +48,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation /// /// Version of this service /// - private const string m_Version = "SIMULATION/0.1"; + private const string m_Version = "SIMULATION/0.2"; /// /// Map region ID to scene. diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 724a2d40b2..75318ffe1e 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -4206,6 +4206,20 @@ namespace OpenSim.Region.Framework.Scenes m_log.WarnFormat("[SCENE]: Attempt to update agent {0} with invalid session id {1} (possibly from simulator in older version; tell them to update).", childAgentUpdate.UUID, cAgentData.SessionID); childAgentUpdate.ChildAgentDataUpdate(cAgentData); + + int ntimes = 20; + if (cAgentData.SenderWantsToWaitForRoot) + { + while (childAgentUpdate.IsChildAgent && ntimes-- > 0) + Thread.Sleep(500); + + m_log.DebugFormat( + "[SCENE PRESENCE]: Found presence {0} {1} {2} in {3} after {4} waits", + childAgentUpdate.Name, childAgentUpdate.UUID, childAgentUpdate.IsChildAgent ? "child" : "root", RegionInfo.RegionName, 20 - ntimes); + + if (childAgentUpdate.IsChildAgent) + return false; + } return true; } return false; @@ -4263,10 +4277,6 @@ namespace OpenSim.Region.Framework.Scenes m_log.WarnFormat( "[SCENE PRESENCE]: Did not find presence with id {0} in {1} before timeout", agentID, RegionInfo.RegionName); -// else -// m_log.DebugFormat( -// "[SCENE PRESENCE]: Found presence {0} {1} {2} in {3} after {4} waits", -// sp.Name, sp.UUID, sp.IsChildAgent ? "child" : "root", RegionInfo.RegionName, 10 - ntimes); return sp; } diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 33db88bcea..bcb8ef11d3 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -29,7 +29,9 @@ using System; using System.Xml; using System.Collections.Generic; using System.Reflection; +using System.Threading; using System.Timers; +using Timer = System.Timers.Timer; using OpenMetaverse; using log4net; using Nini.Config; @@ -1292,6 +1294,26 @@ namespace OpenSim.Region.Framework.Scenes PhysicsActor.Size = new Vector3(0.45f, 0.6f, height); } + private bool WaitForUpdateAgent(IClientAPI client) + { + // Before UpdateAgent, m_originRegionID is UUID.Zero; after, it's non-Zero + int count = 20; + while (m_originRegionID.Equals(UUID.Zero) && count-- > 0) + { + m_log.DebugFormat("[SCENE PRESENCE]: Agent {0} waiting for update in {1}", client.Name, Scene.RegionInfo.RegionName); + Thread.Sleep(200); + } + + if (m_originRegionID.Equals(UUID.Zero)) + { + // Movement into region will fail + m_log.WarnFormat("[SCENE PRESENCE]: Update agent {0} never arrived", client.Name); + return false; + } + + return true; + } + /// /// Complete Avatar's movement into the region. /// @@ -1309,6 +1331,12 @@ namespace OpenSim.Region.Framework.Scenes "[SCENE PRESENCE]: Completing movement of {0} into region {1} in position {2}", client.Name, Scene.RegionInfo.RegionName, AbsolutePosition); + if (m_teleportFlags != TeleportFlags.ViaLogin) + // Let's wait until UpdateAgent (called by departing region) is done + if (!WaitForUpdateAgent(client)) + // The sending region never sent the UpdateAgent data, we have to refuse + return; + Vector3 look = Velocity; if ((look.X == 0) && (look.Y == 0) && (look.Z == 0)) @@ -1329,10 +1357,11 @@ namespace OpenSim.Region.Framework.Scenes bool flying = ((m_AgentControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) != 0); MakeRootAgent(AbsolutePosition, flying); + + // Tell the client that we're totally ready ControllingClient.MoveAgentIntoRegion(m_scene.RegionInfo, AbsolutePosition, look); + // Remember in HandleUseCircuitCode, we delayed this to here - // This will also send the initial data to clients when TP to a neighboring region. - // Not ideal, but until we know we're TP-ing from a neighboring region, there's not much we can do if (m_teleportFlags > 0) SendInitialDataToMe();