diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index b70e9df0ac..4dc724d082 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -552,12 +552,29 @@ namespace OpenSim.Region.Framework.Scenes av.IsInTransit = true; - CrossAgentToNewRegionDelegate d = entityTransfer.CrossAgentToNewRegionAsync; - d.BeginInvoke(av, val, destination, av.Flying, version, CrossAgentToNewRegionCompleted, d); + // A temporary measure to allow regression tests to work. + // Quite possibly, all BeginInvoke() calls should be replaced by Util.FireAndForget + // or similar since BeginInvoke() always uses the system threadpool to launch + // threads rather than any replace threadpool that we might be using. + if (Util.FireAndForgetMethod == FireAndForgetMethod.RegressionTest) + { + entityTransfer.CrossAgentToNewRegionAsync(av, val, destination, av.Flying, version); + CrossAgentToNewRegionCompleted(av); + } + else + { + CrossAgentToNewRegionDelegate d = entityTransfer.CrossAgentToNewRegionAsync; + d.BeginInvoke( + av, val, destination, av.Flying, version, + ar => CrossAgentToNewRegionCompleted(d.EndInvoke(ar)), null); + } } else + { m_log.DebugFormat("[SCENE OBJECT]: Crossing avatar alreasy in transit {0} to {1}", av.Name, val); + } } + avsToCross.Clear(); return; } @@ -630,11 +647,8 @@ namespace OpenSim.Region.Framework.Scenes set { RootPart.Velocity = value; } } - private void CrossAgentToNewRegionCompleted(IAsyncResult iar) + private void CrossAgentToNewRegionCompleted(ScenePresence agent) { - CrossAgentToNewRegionDelegate icon = (CrossAgentToNewRegionDelegate)iar.AsyncState; - ScenePresence agent = icon.EndInvoke(iar); - //// If the cross was successful, this agent is a child agent if (agent.IsChildAgent) { @@ -1698,10 +1712,15 @@ namespace OpenSim.Region.Framework.Scenes /// public SceneObjectGroup Copy(bool userExposed) { + // FIXME: This is dangerous since it's easy to forget to reset some references when necessary and end up + // with bugs that only occur in some circumstances (e.g. crossing between regions on the same simulator + // but not between regions on different simulators). Really, all copying should be done explicitly. SceneObjectGroup dupe = (SceneObjectGroup)MemberwiseClone(); + dupe.Backup = false; dupe.m_parts = new MapAndArray(); dupe.m_sittingAvatars = new List(); + dupe.m_linkedAvatars = new List(); dupe.CopyRootPart(m_rootPart, OwnerID, GroupID, userExposed); dupe.m_rootPart.LinkNum = m_rootPart.LinkNum; diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index db4e285014..c06175e7dc 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -1753,7 +1753,11 @@ namespace OpenSim.Region.Framework.Scenes /// public SceneObjectPart Copy(uint localID, UUID AgentID, UUID GroupID, int linkNum, bool userExposed) { + // FIXME: This is dangerous since it's easy to forget to reset some references when necessary and end up + // with bugs that only occur in some circumstances (e.g. crossing between regions on the same simulator + // but not between regions on different simulators). Really, all copying should be done explicitly. SceneObjectPart dupe = (SceneObjectPart)MemberwiseClone(); + dupe.m_shape = m_shape.Copy(); dupe.m_regionHandle = m_regionHandle; if (userExposed) @@ -1799,6 +1803,8 @@ namespace OpenSim.Region.Framework.Scenes Array.Copy(Shape.ExtraParams, extraP, extraP.Length); dupe.Shape.ExtraParams = extraP; + dupe.m_sittingAvatars = new HashSet(); + // safeguard actual copy is done in sog.copy dupe.KeyframeMotion = null; dupe.PayPrice = (int[])PayPrice.Clone(); diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 080cdb4e37..6386a4507d 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -1023,6 +1023,7 @@ namespace OpenSim.Region.Framework.Scenes else { part.ParentGroup.AddAvatar(UUID); + part.AddSittingAvatar(UUID); if (part.SitTargetPosition != Vector3.Zero) part.SitTargetAvatar = UUID; // ParentPosition = part.GetWorldPosition(); @@ -2848,7 +2849,6 @@ namespace OpenSim.Region.Framework.Scenes part.ParentGroup.TriggerScriptChangedEvent(Changed.LINK); } - public void HandleAgentSit(IClientAPI remoteClient, UUID agentID) { if (IsChildAgent) diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectCrossingTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectCrossingTests.cs index 4d0774133d..d65b0b624b 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectCrossingTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectCrossingTests.cs @@ -26,10 +26,12 @@ */ using System; +using System.Collections.Generic; using Nini.Config; using NUnit.Framework; using OpenMetaverse; using OpenSim.Framework; +using OpenSim.Region.CoreModules.Framework; using OpenSim.Region.CoreModules.Framework.EntityTransfer; using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation; using OpenSim.Region.CoreModules.World.Land; @@ -101,7 +103,110 @@ namespace OpenSim.Region.Framework.Scenes.Tests /// Test cross with no prim limit module. /// /// - /// XXX: This test may be better off in a specific PrimLimitsModuleTest class in optional module tests in the + /// Possibly this should belong in ScenePresenceCrossingTests, though here it is the object that is being moved + /// where the avatar is just a passenger. + /// + [Test] + public void TestCrossOnSameSimulatorWithSittingAvatar() + { + TestHelpers.InMethod(); +// TestHelpers.EnableLogging(); + + UUID userId = TestHelpers.ParseTail(0x1); + int sceneObjectIdTail = 0x2; + Vector3 so1StartPos = new Vector3(128, 10, 20); + + EntityTransferModule etmA = new EntityTransferModule(); + EntityTransferModule etmB = new EntityTransferModule(); + LocalSimulationConnectorModule lscm = new LocalSimulationConnectorModule(); + + IConfigSource config = new IniConfigSource(); + IConfig modulesConfig = config.AddConfig("Modules"); + modulesConfig.Set("EntityTransferModule", etmA.Name); + modulesConfig.Set("SimulationServices", lscm.Name); + IConfig entityTransferConfig = 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. + entityTransferConfig.Set("wait_for_callback", false); + + SceneHelpers sh = new SceneHelpers(); + TestScene sceneA = sh.SetupScene("sceneA", TestHelpers.ParseTail(0x100), 1000, 1000); + TestScene sceneB = sh.SetupScene("sceneB", TestHelpers.ParseTail(0x200), 1000, 999); + + SceneHelpers.SetupSceneModules(new Scene[] { sceneA, sceneB }, config, lscm); + SceneHelpers.SetupSceneModules(sceneA, config, new CapabilitiesModule(), etmA); + SceneHelpers.SetupSceneModules(sceneB, config, new CapabilitiesModule(), etmB); + + SceneObjectGroup so1 = SceneHelpers.AddSceneObject(sceneA, 1, userId, "", sceneObjectIdTail); + UUID so1Id = so1.UUID; + so1.AbsolutePosition = so1StartPos; + + AgentCircuitData acd = SceneHelpers.GenerateAgentData(userId); + TestClient tc = new TestClient(acd, sceneA); + List destinationTestClients = new List(); + EntityTransferHelpers.SetupInformClientOfNeighbourTriggersNeighbourClientCreate(tc, destinationTestClients); + + ScenePresence sp1SceneA = SceneHelpers.AddScenePresence(sceneA, tc, acd); + sp1SceneA.AbsolutePosition = so1StartPos; + sp1SceneA.HandleAgentRequestSit(sp1SceneA.ControllingClient, sp1SceneA.UUID, so1.UUID, Vector3.Zero); + + // Cross + sceneA.SceneGraph.UpdatePrimGroupPosition( + so1.LocalId, new Vector3(so1StartPos.X, so1StartPos.Y - 20, so1StartPos.Z), userId); + + SceneObjectGroup so1PostCross; + + { + ScenePresence sp1SceneAPostCross = sceneA.GetScenePresence(userId); + Assert.IsTrue(sp1SceneAPostCross.IsChildAgent, "sp1SceneAPostCross.IsChildAgent unexpectedly false"); + + ScenePresence sp1SceneBPostCross = sceneB.GetScenePresence(userId); + TestClient sceneBTc = ((TestClient)sp1SceneBPostCross.ControllingClient); + sceneBTc.CompleteMovement(); + + Assert.IsFalse(sp1SceneBPostCross.IsChildAgent, "sp1SceneAPostCross.IsChildAgent unexpectedly true"); + Assert.IsTrue(sp1SceneBPostCross.IsSatOnObject); + + Assert.IsNull(sceneA.GetSceneObjectGroup(so1Id), "uck"); + so1PostCross = sceneB.GetSceneObjectGroup(so1Id); + Assert.NotNull(so1PostCross); + Assert.AreEqual(1, so1PostCross.GetSittingAvatarsCount()); + Assert.AreEqual(1, so1PostCross.GetLinkedAvatars().Count); + } + + Vector3 so1PostCrossPos = so1PostCross.AbsolutePosition; + +// Console.WriteLine("CRISSCROSS"); + + // Recross + sceneB.SceneGraph.UpdatePrimGroupPosition( + so1PostCross.LocalId, new Vector3(so1PostCrossPos.X, so1PostCrossPos.Y + 20, so1PostCrossPos.Z), userId); + + { + ScenePresence sp1SceneBPostReCross = sceneB.GetScenePresence(userId); + Assert.IsTrue(sp1SceneBPostReCross.IsChildAgent, "sp1SceneBPostReCross.IsChildAgent unexpectedly false"); + + ScenePresence sp1SceneAPostReCross = sceneA.GetScenePresence(userId); + TestClient sceneATc = ((TestClient)sp1SceneAPostReCross.ControllingClient); + sceneATc.CompleteMovement(); + + Assert.IsFalse(sp1SceneAPostReCross.IsChildAgent, "sp1SceneAPostCross.IsChildAgent unexpectedly true"); + Assert.IsTrue(sp1SceneAPostReCross.IsSatOnObject); + + Assert.IsNull(sceneB.GetSceneObjectGroup(so1Id), "uck2"); + SceneObjectGroup so1PostReCross = sceneA.GetSceneObjectGroup(so1Id); + Assert.NotNull(so1PostReCross); + Assert.AreEqual(1, so1PostReCross.GetSittingAvatarsCount()); + Assert.AreEqual(1, so1PostReCross.GetLinkedAvatars().Count); + } + } + + /// + /// Test cross with no prim limit module. + /// + /// + /// XXX: This test may FCbe better off in a specific PrimLimitsModuleTest class in optional module tests in the /// future (though it is configured as active by default, so not really optional). /// [Test]