From df09fdf65d72d481df9251d6e3842ab06c66efca Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 22 Jul 2013 11:54:35 -0700 Subject: [PATCH] New Teleport protocol (V2), still compatible with V1 and older. (version of the destination is being checked) In this new protocol, and as committed before, the viewer is not sent EnableSimulator/EstablishChildCommunication for the destination. Instead, it is sent TeleportFinish directly. TeleportFinish, in turn, makes the viewer send a UserCircuitCode packet followed by CompleteMovementIntoRegion packet. These 2 packets tend to occur one after the other almost immediately to the point that when CMIR arrives the client is not even connected yet and that packet is ignored (there might have been some race conditions here before); then the viewer sends CMIR again within 5-8 secs. But the delay between them may be higher in busier regions, which may lead to race conditions. This commit improves the process so there are are no race conditions at the destination. CompleteMovement (triggered by the viewer) waits until Update has been sent from the origin. Update, in turn, waits until there is a *root* scene presence -- so making sure CompleteMovement has run MakeRoot. In other words, there are two threadlets at the destination, one from the viewer and one from the origin region, waiting for each other to do the right thing. That makes it safe to close the agent at the origin upon return of the Update call without having to wait for callback, because we are absolutely sure that the viewer knows it is in th new region. Note also that in the V1 protocol, the destination was getting UseCircuitCode from the viewer twice -- once on EstablishAgentCommunication and then again on TeleportFinish. The second UCC was being ignored, but it shows how we were not following the expected steps... --- OpenSim/Framework/ChildAgentDataUpdate.cs | 11 +- .../ClientStack/Linden/UDP/LLUDPServer.cs | 68 +++++ .../EntityTransfer/EntityTransferModule.cs | 253 +++++++++++++----- .../Simulation/LocalSimulationConnector.cs | 2 +- OpenSim/Region/Framework/Scenes/Scene.cs | 18 +- .../Region/Framework/Scenes/ScenePresence.cs | 33 ++- 6 files changed, 311 insertions(+), 74 deletions(-) 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();