diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs index 4cd03da2c6..c8c594d99d 100644 --- a/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs @@ -829,7 +829,13 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests sceneB, config, new CapabilitiesModule(), etmB, attModB, new BasicInventoryAccessModule()); UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(sceneA, 0x1); - ScenePresence beforeTeleportSp = SceneHelpers.AddScenePresence(sceneA, ua1.PrincipalID, sh.SceneManager); + + AgentCircuitData acd = SceneHelpers.GenerateAgentData(ua1.PrincipalID); + TestClient tc = new TestClient(acd, sceneA, sh.SceneManager); + List destinationTestClients = new List(); + EntityTransferHelpers.SetUpInformClientOfNeighbour(tc, destinationTestClients); + + ScenePresence beforeTeleportSp = SceneHelpers.AddScenePresence(sceneA, tc, acd, sh.SceneManager); beforeTeleportSp.AbsolutePosition = new Vector3(30, 31, 32); InventoryItemBase attItem = CreateAttachmentItem(sceneA, ua1.PrincipalID, "att", 0x10, 0x20); @@ -848,7 +854,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests teleportLookAt, (uint)TeleportFlags.ViaLocation); - ((TestClient)beforeTeleportSp.ControllingClient).CompleteTeleportClientSide(); + destinationTestClients[0].CompleteMovement(); // Check attachments have made it into sceneB ScenePresence afterTeleportSceneBSp = sceneB.GetScenePresence(ua1.PrincipalID); diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index 9b1b69aaa4..58a6654c6e 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -517,12 +517,6 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer "[ENTITY TRANSFER MODULE]: Failed validation of all attachments for teleport of {0} from {1} to {2}. Continuing.", sp.Name, sp.Scene.RegionInfo.RegionName, finalDestination.RegionName); -// if (!sp.ValidateAttachments()) -// { -// sp.ControllingClient.SendTeleportFailed("Inconsistent attachment state"); -// return; -// } - string reason; string version; if (!Scene.SimulationService.QueryAccess( @@ -583,6 +577,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer } // 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)) { @@ -625,11 +620,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer 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); - // ES makes the client send a UseCircuitCode message to the destination, - // which triggers a bunch of things there. - // So let's wait + // 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 @@ -640,6 +637,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer } 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); } } @@ -657,14 +657,17 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer //sp.ControllingClient.SendTeleportProgress(teleportFlags, "Updating agent..."); + // 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 + // there's a further 10 second wait whilst we attempt to tell the destination to delete the agent in Fail(). if (!UpdateAgent(reg, finalDestination, agent, sp)) { - // Region doesn't take it m_log.WarnFormat( - "[ENTITY TRANSFER MODULE]: UpdateAgent failed on teleport of {0} to {1} from {2}. Returning avatar to source region.", + "[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); + Fail(sp, finalDestination, logout, "Connection between viewer and destination region could not be established."); return; } @@ -705,7 +708,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer "[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); + Fail(sp, finalDestination, logout, "Destination region did not signal teleport completion."); return; } @@ -788,12 +791,15 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer /// /// /// - protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout) + /// Human readable reason for teleport failure. Will be sent to client. + protected virtual void Fail(ScenePresence sp, GridRegion finalDestination, bool logout, string reason) { CleanupAbortedInterRegionTeleport(sp, finalDestination); sp.ControllingClient.SendTeleportFailed( - string.Format("Problems connecting to destination {0}", finalDestination.RegionName)); + string.Format( + "Problems connecting to destination {0}, reason: {1}", finalDestination.RegionName, reason)); + sp.Scene.EventManager.TriggerTeleportFail(sp.ControllingClient, logout); } diff --git a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceCrossingTests.cs b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceCrossingTests.cs index 81a2fcc74d..b67b8b96e1 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceCrossingTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceCrossingTests.cs @@ -66,7 +66,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests public void TestCrossOnSameSimulator() { TestHelpers.InMethod(); -// TestHelpers.EnableLogging(); + TestHelpers.EnableLogging(); UUID userId = TestHelpers.ParseTail(0x1); @@ -94,7 +94,12 @@ namespace OpenSim.Region.Framework.Scenes.Tests // SceneHelpers.SetupSceneModules(sceneA, config, new CapabilitiesModule(), etmA, eqmA); SceneHelpers.SetupSceneModules(sceneB, config, new CapabilitiesModule(), etmB); - ScenePresence originalSp = SceneHelpers.AddScenePresence(sceneA, userId, sh.SceneManager); + AgentCircuitData acd = SceneHelpers.GenerateAgentData(userId); + TestClient tc = new TestClient(acd, sceneA, sh.SceneManager); + List destinationTestClients = new List(); + EntityTransferHelpers.SetUpInformClientOfNeighbour(tc, destinationTestClients); + + ScenePresence originalSp = SceneHelpers.AddScenePresence(sceneA, tc, acd, sh.SceneManager); originalSp.AbsolutePosition = new Vector3(128, 32, 10); // originalSp.Flying = true; diff --git a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTeleportTests.cs b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTeleportTests.cs index 8dd1f3d5c3..d6bc3130a3 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTeleportTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTeleportTests.cs @@ -26,7 +26,10 @@ */ using System; -using System.Reflection; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; using Nini.Config; using NUnit.Framework; using OpenMetaverse; @@ -40,8 +43,6 @@ using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation; using OpenSim.Region.CoreModules.World.Permissions; using OpenSim.Tests.Common; using OpenSim.Tests.Common.Mock; -using System.IO; -using System.Text; namespace OpenSim.Region.Framework.Scenes.Tests { @@ -68,7 +69,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests } [Test] - public void TestSameRegionTeleport() + public void TestSameRegion() { TestHelpers.InMethod(); // log4net.Config.XmlConfigurator.Configure(); @@ -106,10 +107,10 @@ namespace OpenSim.Region.Framework.Scenes.Tests } [Test] - public void TestSameSimulatorSeparatedRegionsTeleport() + public void TestSameSimulatorSeparatedRegions() { TestHelpers.InMethod(); -// log4net.Config.XmlConfigurator.Configure(); +// TestHelpers.EnableLogging(); UUID userId = TestHelpers.ParseTail(0x1); @@ -141,9 +142,8 @@ namespace OpenSim.Region.Framework.Scenes.Tests ScenePresence sp = SceneHelpers.AddScenePresence(sceneA, userId, sh.SceneManager); sp.AbsolutePosition = new Vector3(30, 31, 32); - // XXX: A very nasty hack to tell the client about the destination scene without having to crank the whole - // UDP stack (?) -// ((TestClient)sp.ControllingClient).TeleportTargetScene = sceneB; + List destinationTestClients = new List(); + EntityTransferHelpers.SetUpInformClientOfNeighbour((TestClient)sp.ControllingClient, destinationTestClients); sceneA.RequestTeleportLocation( sp.ControllingClient, @@ -152,7 +152,10 @@ namespace OpenSim.Region.Framework.Scenes.Tests teleportLookAt, (uint)TeleportFlags.ViaLocation); - ((TestClient)sp.ControllingClient).CompleteTeleportClientSide(); + // SetupInformClientOfNeighbour() will have handled the callback into the target scene to setup the child + // agent. This call will now complete the movement of the user into the destination and upgrade the agent + // from child to root. + destinationTestClients[0].CompleteMovement(); Assert.That(sceneA.GetScenePresence(userId), Is.Null); @@ -177,7 +180,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests /// Test teleport procedures when the target simulator returns false when queried about access. /// [Test] - public void TestSameSimulatorSeparatedRegionsQueryAccessFails() + public void TestSameSimulatorSeparatedRegions_DeniedOnQueryAccess() { TestHelpers.InMethod(); // TestHelpers.EnableLogging(); @@ -261,7 +264,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests /// Test teleport procedures when the target simulator create agent step is refused. /// [Test] - public void TestSameSimulatorSeparatedRegionsCreateAgentFails() + public void TestSameSimulatorSeparatedRegions_DeniedOnCreateAgent() { TestHelpers.InMethod(); // TestHelpers.EnableLogging(); @@ -333,12 +336,100 @@ namespace OpenSim.Region.Framework.Scenes.Tests // TestHelpers.DisableLogging(); } + /// + /// Test teleport when the destination region does not process (or does not receive) the connection attempt + /// from the viewer. + /// + /// + /// This could be quite a common case where the source region can connect to a remove destination region + /// (for CreateAgent) but the viewer cannot reach the destination region due to network issues. + /// [Test] - public void TestSameSimulatorNeighbouringRegionsTeleport() + public void TestSameSimulatorSeparatedRegions_DestinationDidNotProcessViewerConnection() { TestHelpers.InMethod(); // TestHelpers.EnableLogging(); + UUID userId = TestHelpers.ParseTail(0x1); + Vector3 preTeleportPosition = new Vector3(30, 31, 32); + + EntityTransferModule etmA = new EntityTransferModule(); + EntityTransferModule etmB = new EntityTransferModule(); + + LocalSimulationConnectorModule lscm = new LocalSimulationConnectorModule(); + + IConfigSource config = new IniConfigSource(); + config.AddConfig("Modules"); + config.Configs["Modules"].Set("EntityTransferModule", etmA.Name); + config.Configs["Modules"].Set("SimulationServices", lscm.Name); + + config.AddConfig("EntityTransfer"); + + // In order to run a single threaded regression test we do not want the entity transfer module waiting + // for a callback from the destination scene before removing its avatar data. + config.Configs["EntityTransfer"].Set("wait_for_callback", false); + +// config.AddConfig("Startup"); +// config.Configs["Startup"].Set("serverside_object_permissions", true); + + SceneHelpers sh = new SceneHelpers(); + TestScene sceneA = sh.SetupScene("sceneA", TestHelpers.ParseTail(0x100), 1000, 1000); + TestScene sceneB = sh.SetupScene("sceneB", TestHelpers.ParseTail(0x200), 1002, 1000); + + SceneHelpers.SetupSceneModules(sceneA, config, etmA ); + + // We need to set up the permisions module on scene B so that our later use of agent limit to deny + // QueryAccess won't succeed anyway because administrators are always allowed in and the default + // IsAdministrator if no permissions module is present is true. + SceneHelpers.SetupSceneModules(sceneB, config, new object[] { new PermissionsModule(), etmB }); + + // Shared scene modules + SceneHelpers.SetupSceneModules(new Scene[] { sceneA, sceneB }, config, lscm); + + Vector3 teleportPosition = new Vector3(10, 11, 12); + Vector3 teleportLookAt = new Vector3(20, 21, 22); + + ScenePresence sp = SceneHelpers.AddScenePresence(sceneA, userId, sh.SceneManager); + sp.AbsolutePosition = preTeleportPosition; + + sceneA.RequestTeleportLocation( + sp.ControllingClient, + sceneB.RegionInfo.RegionHandle, + teleportPosition, + teleportLookAt, + (uint)TeleportFlags.ViaLocation); + + // FIXME: Not setting up InformClientOfNeighbour on the TestClient means that it does not initiate + // communication with the destination region. But this is a very non-obvious way of doing it - really we + // should be forced to expicitly set this up. + + Assert.That(sceneB.GetScenePresence(userId), Is.Null); + + ScenePresence sceneASp = sceneA.GetScenePresence(userId); + Assert.That(sceneASp, Is.Not.Null); + Assert.That(sceneASp.Scene.RegionInfo.RegionName, Is.EqualTo(sceneA.RegionInfo.RegionName)); + Assert.That(sceneASp.AbsolutePosition, Is.EqualTo(preTeleportPosition)); + + Assert.That(sceneA.GetRootAgentCount(), Is.EqualTo(1)); + Assert.That(sceneA.GetChildAgentCount(), Is.EqualTo(0)); + Assert.That(sceneB.GetRootAgentCount(), Is.EqualTo(0)); + Assert.That(sceneB.GetChildAgentCount(), Is.EqualTo(0)); + + // TODO: Add assertions to check correct circuit details in both scenes. + + // Lookat is sent to the client only - sp.Lookat does not yield the same thing (calculation from camera + // position instead). +// Assert.That(sp.Lookat, Is.EqualTo(teleportLookAt)); + +// TestHelpers.DisableLogging(); + } + + [Test] + public void TestSameSimulatorNeighbouringRegions() + { + TestHelpers.InMethod(); + TestHelpers.EnableLogging(); + UUID userId = TestHelpers.ParseTail(0x1); EntityTransferModule etmA = new EntityTransferModule(); @@ -366,10 +457,14 @@ namespace OpenSim.Region.Framework.Scenes.Tests Vector3 teleportPosition = new Vector3(10, 11, 12); Vector3 teleportLookAt = new Vector3(20, 21, 22); - ScenePresence originalSp = SceneHelpers.AddScenePresence(sceneA, userId, sh.SceneManager); - originalSp.AbsolutePosition = new Vector3(30, 31, 32); + AgentCircuitData acd = SceneHelpers.GenerateAgentData(userId); + TestClient tc = new TestClient(acd, sceneA, sh.SceneManager); + List destinationTestClients = new List(); + EntityTransferHelpers.SetUpInformClientOfNeighbour(tc, destinationTestClients); + + ScenePresence beforeSceneASp = SceneHelpers.AddScenePresence(sceneA, tc, acd, sh.SceneManager); + beforeSceneASp.AbsolutePosition = new Vector3(30, 31, 32); - ScenePresence beforeSceneASp = sceneA.GetScenePresence(userId); Assert.That(beforeSceneASp, Is.Not.Null); Assert.That(beforeSceneASp.IsChildAgent, Is.False); @@ -377,10 +472,8 @@ namespace OpenSim.Region.Framework.Scenes.Tests Assert.That(beforeSceneBSp, Is.Not.Null); Assert.That(beforeSceneBSp.IsChildAgent, Is.True); - // XXX: A very nasty hack to tell the client about the destination scene without having to crank the whole - // UDP stack (?) -// ((TestClient)beforeSceneASp.ControllingClient).TeleportTargetScene = sceneB; - + // In this case, we will not receieve a second InformClientOfNeighbour since the viewer already knows + // about the neighbour region it is teleporting to. sceneA.RequestTeleportLocation( beforeSceneASp.ControllingClient, sceneB.RegionInfo.RegionHandle, @@ -388,7 +481,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests teleportLookAt, (uint)TeleportFlags.ViaLocation); - ((TestClient)beforeSceneASp.ControllingClient).CompleteTeleportClientSide(); + destinationTestClients[0].CompleteMovement(); ScenePresence afterSceneASp = sceneA.GetScenePresence(userId); Assert.That(afterSceneASp, Is.Not.Null); diff --git a/OpenSim/Tests/Common/Helpers/SceneHelpers.cs b/OpenSim/Tests/Common/Helpers/SceneHelpers.cs index dc20f13555..bdd90939e7 100644 --- a/OpenSim/Tests/Common/Helpers/SceneHelpers.cs +++ b/OpenSim/Tests/Common/Helpers/SceneHelpers.cs @@ -531,6 +531,31 @@ namespace OpenSim.Tests.Common /// /// public static ScenePresence AddScenePresence(Scene scene, AgentCircuitData agentData, SceneManager sceneManager) + { + return AddScenePresence(scene, new TestClient(agentData, scene, sceneManager), agentData, sceneManager); + } + + /// + /// Add a root agent. + /// + /// + /// This function + /// + /// 1) Tells the scene that an agent is coming. Normally, the login service (local if standalone, from the + /// userserver if grid) would give initial login data back to the client and separately tell the scene that the + /// agent was coming. + /// + /// 2) Connects the agent with the scene + /// + /// This function performs actions equivalent with notifying the scene that an agent is + /// coming and then actually connecting the agent to the scene. The one step missed out is the very first + /// + /// + /// + /// + /// + public static ScenePresence AddScenePresence( + Scene scene, IClientAPI client, AgentCircuitData agentData, SceneManager sceneManager) { // We emulate the proper login sequence here by doing things in four stages @@ -541,7 +566,7 @@ namespace OpenSim.Tests.Common lpsc.m_PresenceService.LoginAgent(agentData.AgentID.ToString(), agentData.SessionID, agentData.SecureSessionID); // Stages 1 & 2 - ScenePresence sp = IntroduceClientToScene(scene, sceneManager, agentData, TeleportFlags.ViaLogin); + ScenePresence sp = IntroduceClientToScene(scene, client, agentData, TeleportFlags.ViaLogin); // Stage 3: Complete the entrance into the region. This converts the child agent into a root agent. sp.CompleteMovement(sp.ControllingClient, true); @@ -558,11 +583,11 @@ namespace OpenSim.Tests.Common /// neighbours and where no teleporting takes place. /// /// - /// /// /// private static ScenePresence IntroduceClientToScene( - Scene scene, SceneManager sceneManager, AgentCircuitData agentData, TeleportFlags tf) + Scene scene, IClientAPI client, AgentCircuitData agentData, TeleportFlags tf) { string reason; @@ -571,10 +596,9 @@ namespace OpenSim.Tests.Common Console.WriteLine("NewUserConnection failed: " + reason); // Stage 2: add the new client as a child agent to the scene - TestClient client = new TestClient(agentData, scene, sceneManager); scene.AddNewClient(client, PresenceType.User); - return scene.GetScenePresence(agentData.AgentID); + return scene.GetScenePresence(client.AgentId); } public static ScenePresence AddChildScenePresence(Scene scene, UUID agentId) @@ -583,7 +607,8 @@ namespace OpenSim.Tests.Common acd.child = true; // XXX: ViaLogin may not be correct for child agents - return IntroduceClientToScene(scene, null, acd, TeleportFlags.ViaLogin); + TestClient client = new TestClient(acd, scene, null); + return IntroduceClientToScene(scene, client, acd, TeleportFlags.ViaLogin); } /// diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index a448cc52b1..2d4fef16de 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -46,8 +46,6 @@ namespace OpenSim.Tests.Common.Mock EventWaitHandle wh = new EventWaitHandle (false, EventResetMode.AutoReset, "Crossing"); - private TestClient TeleportSceneClient; - private Scene m_scene; private SceneManager m_sceneManager; @@ -60,7 +58,9 @@ namespace OpenSim.Tests.Common.Mock public List SentImagePacketPackets { get; private set; } public List SentImageNotInDatabasePackets { get; private set; } + // Test client specific events - for use by tests to implement some IClientAPI behaviour. public event Action OnReceivedMoveAgentIntoRegion; + public event Action OnTestClientInformClientOfNeighbour; // disable warning: public events, part of the public API #pragma warning disable 67 @@ -595,23 +595,8 @@ namespace OpenSim.Tests.Common.Mock public virtual void InformClientOfNeighbour(ulong neighbourHandle, IPEndPoint neighbourExternalEndPoint) { - m_log.DebugFormat("[TEST CLIENT]: Processing inform client of neighbour"); - - // In response to this message, we are going to make a teleport to the scene we've previous been told - // about by test code (this needs to be improved). - AgentCircuitData newAgent = RequestClientInfo(); - - // Stage 2: add the new client as a child agent to the scene - uint x, y; - Utils.LongToUInts(neighbourHandle, out x, out y); - x /= Constants.RegionSize; - y /= Constants.RegionSize; - - Scene neighbourScene; - m_sceneManager.TryGetScene(x, y, out neighbourScene); - - TeleportSceneClient = new TestClient(newAgent, neighbourScene, m_sceneManager); - neighbourScene.AddNewClient(TeleportSceneClient, PresenceType.User); + if (OnTestClientInformClientOfNeighbour != null) + OnTestClientInformClientOfNeighbour(neighbourHandle, neighbourExternalEndPoint); } public virtual void SendRegionTeleport(ulong regionHandle, byte simAccess, IPEndPoint regionExternalEndPoint, @@ -626,12 +611,6 @@ namespace OpenSim.Tests.Common.Mock // CompleteTeleportClientSide(); } - public void CompleteTeleportClientSide() - { - TeleportSceneClient.CompleteMovement(); - //TeleportTargetScene.AgentCrossing(newAgent.AgentID, new Vector3(90, 90, 90), false); - } - public virtual void SendTeleportFailed(string reason) { m_log.DebugFormat("[TEST CLIENT]: Teleport failed with reason {0}", reason);