diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index e2eb89e566..30b44b15b2 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -3179,6 +3179,11 @@ namespace OpenSim.Region.Framework.Scenes return m_sceneGridService.CrossToNeighbouringRegion(regionHandle, agentID, position, isFlying); } + public void CrossAgentToNewRegion(ScenePresence agent, bool isFlying) + { + m_sceneGridService.CrossAgentToNewRegion(this, agent, isFlying); + } + public void SendOutChildAgentUpdates(AgentPosition cadu, ScenePresence presence) { m_sceneGridService.SendChildAgentDataUpdate(cadu, presence); diff --git a/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs b/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs index bef57a0096..98b0f97f24 100644 --- a/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs +++ b/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs @@ -35,6 +35,7 @@ using OpenMetaverse.StructuredData; using log4net; using OpenSim.Framework; using OpenSim.Framework.Communications; +using OpenSim.Framework.Communications.Cache; using OpenSim.Framework.Communications.Capabilities; using OpenSim.Region.Framework.Interfaces; using OSD = OpenMetaverse.StructuredData.OSD; @@ -1018,6 +1019,175 @@ namespace OpenSim.Region.Framework.Scenes return m_commsProvider.InterRegion.ExpectAvatarCrossing(regionhandle, agentID, position, isFlying); } + public void CrossAgentToNewRegion(Scene scene, ScenePresence agent, bool isFlying) + { + Vector3 pos = agent.AbsolutePosition; + Vector3 newpos = new Vector3(pos.X, pos.Y, pos.Z); + uint neighbourx = m_regionInfo.RegionLocX; + uint neighboury = m_regionInfo.RegionLocY; + + // distance to edge that will trigger crossing + const float boundaryDistance = 1.7f; + + // distance into new region to place avatar + const float enterDistance = 0.1f; + + if (pos.X < boundaryDistance) + { + neighbourx--; + newpos.X = Constants.RegionSize - enterDistance; + } + else if (pos.X > Constants.RegionSize - boundaryDistance) + { + neighbourx++; + newpos.X = enterDistance; + } + + if (pos.Y < boundaryDistance) + { + neighboury--; + newpos.Y = Constants.RegionSize - enterDistance; + } + else if (pos.Y > Constants.RegionSize - boundaryDistance) + { + neighboury++; + newpos.Y = enterDistance; + } + + Vector3 vel = agent.Velocity; + + CrossAgentToNewRegionDelegate d = CrossAgentToNewRegionAsync; + d.BeginInvoke(agent, newpos, neighbourx, neighboury, isFlying, CrossAgentToNewRegionCompleted, d); + + + } + + public delegate ScenePresence CrossAgentToNewRegionDelegate(ScenePresence agent, Vector3 pos, uint neighbourx, uint neighboury, bool isFlying); + + /// + /// This Closes child agents on neighboring regions + /// Calls an asynchronous method to do so.. so it doesn't lag the sim. + /// + protected ScenePresence CrossAgentToNewRegionAsync(ScenePresence agent, Vector3 pos, uint neighbourx, uint neighboury, bool isFlying) + { + m_log.DebugFormat("[SCENE COMM]: Crossing agent {0} {1} to {2}-{3}", agent.Firstname, agent.Lastname, neighbourx, neighboury); + + ulong neighbourHandle = Utils.UIntsToLong((uint)(neighbourx * Constants.RegionSize), (uint)(neighboury * Constants.RegionSize)); + SimpleRegionInfo neighbourRegion = RequestNeighbouringRegionInfo(neighbourHandle); + if (neighbourRegion != null && agent.ValidateAttachments()) + { + pos = pos + (agent.Velocity); + + CachedUserInfo userInfo = m_commsProvider.UserProfileCacheService.GetUserDetails(agent.UUID); + if (userInfo != null) + { + userInfo.DropInventory(); + } + else + { + m_log.WarnFormat("[SCENE COMM]: No cached user info found for {0} {1} on leaving region {2}", + agent.Name, agent.UUID, agent.Scene.RegionInfo.RegionName); + } + + bool crossingSuccessful = + CrossToNeighbouringRegion(neighbourHandle, agent.ControllingClient.AgentId, pos, + isFlying); + if (crossingSuccessful) + { + // Next, let's close the child agent connections that are too far away. + agent.CloseChildAgents(neighbourx, neighboury); + + //AgentCircuitData circuitdata = m_controllingClient.RequestClientInfo(); + agent.ControllingClient.RequestClientInfo(); + + //Console.WriteLine("BEFORE CROSS"); + //Scene.DumpChildrenSeeds(UUID); + //DumpKnownRegions(); + string agentcaps; + if (!agent.KnownRegions.TryGetValue(neighbourRegion.RegionHandle, out agentcaps)) + { + m_log.ErrorFormat("[SCENE COMM]: No CAPS information for region handle {0}, exiting CrossToNewRegion.", + neighbourRegion.RegionHandle); + return agent; + } + // TODO Should construct this behind a method + string capsPath = + "http://" + neighbourRegion.ExternalHostName + ":" + neighbourRegion.HttpPort + + "/CAPS/" + agentcaps /*circuitdata.CapsPath*/ + "0000/"; + + m_log.DebugFormat("[CAPS]: Sending new CAPS seed url {0} to client {1}", capsPath, agent.UUID); + + IEventQueue eq = agent.Scene.RequestModuleInterface(); + if (eq != null) + { + eq.CrossRegion(neighbourHandle, pos, agent.Velocity, neighbourRegion.ExternalEndPoint, + capsPath, agent.UUID, agent.ControllingClient.SessionId); + } + else + { + agent.ControllingClient.CrossRegion(neighbourHandle, pos, agent.Velocity, neighbourRegion.ExternalEndPoint, + capsPath); + } + + agent.MakeChildAgent(); + // now we have a child agent in this region. Request all interesting data about other (root) agents + agent.SendInitialFullUpdateToAllClients(); + + agent.CrossAttachmentsIntoNewRegion(neighbourHandle, true); + + // m_scene.SendKillObject(m_localId); + + agent.Scene.NotifyMyCoarseLocationChange(); + // the user may change their profile information in other region, + // so the userinfo in UserProfileCache is not reliable any more, delete it + if (agent.Scene.NeedSceneCacheClear(agent.UUID)) + { + agent.Scene.CommsManager.UserProfileCacheService.RemoveUser(agent.UUID); + m_log.DebugFormat( + "[SCENE COMM]: User {0} is going to another region, profile cache removed", agent.UUID); + } + } + else + { + //// Restore the user structures that we needed to delete before asking the receiving region + //// to complete the crossing + //userInfo.FetchInventory(); + //agent.Scene.CapsModule.AddCapsHandler(agent.UUID); + } + } + + //Console.WriteLine("AFTER CROSS"); + //Scene.DumpChildrenSeeds(UUID); + //DumpKnownRegions(); + return agent; + } + + private void CrossAgentToNewRegionCompleted(IAsyncResult iar) + { + CrossAgentToNewRegionDelegate icon = (CrossAgentToNewRegionDelegate)iar.AsyncState; + ScenePresence agent = icon.EndInvoke(iar); + + // If the cross was successful, this agent is a child agent + if (agent.IsChildAgent) + { + // Put the child agent back at the center + agent.AbsolutePosition = new Vector3(128, 128, 70); + } + else // Not successful + { + CachedUserInfo userInfo = m_commsProvider.UserProfileCacheService.GetUserDetails(agent.UUID); + if (userInfo != null) + { + userInfo.FetchInventory(); + } + agent.RestoreInCurrentScene(); + } + agent.IsInTransit = false; + + //m_log.DebugFormat("[SCENE COMM]: Crossing agent {0} {1} completed.", agent.Firstname, agent.Lastname); + } + + public bool PrimCrossToNeighboringRegion(ulong regionhandle, UUID primID, string objData, int XMLMethod) { return m_commsProvider.InterRegion.InformRegionOfPrimCrossing(regionhandle, primID, objData, XMLMethod); diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 2dd305a916..f841707da5 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -547,6 +547,13 @@ namespace OpenSim.Region.Framework.Scenes get { return m_animations; } } + private bool m_inTransit; + public bool IsInTransit + { + get { return m_inTransit; } + set { m_inTransit = value; } + } + #endregion #region Constructor(s) @@ -850,7 +857,7 @@ namespace OpenSim.Region.Framework.Scenes CachedUserInfo userInfo = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(m_uuid); if (userInfo != null) - userInfo.FetchInventory(); + userInfo.FetchInventory(); else m_log.ErrorFormat("[SCENE]: Could not find user info for {0} when making it a root agent", m_uuid); @@ -2377,15 +2384,31 @@ namespace OpenSim.Region.Framework.Scenes pos2.Y = pos2.Y + (vel.Y*timeStep); pos2.Z = pos2.Z + (vel.Z*timeStep); - if ((pos2.X < 0) || (pos2.X > Constants.RegionSize)) + if (!IsInTransit) { - CrossToNewRegion(); + if ((pos2.X < 0) || (pos2.X > Constants.RegionSize)) + { + CrossToNewRegion(); + } + + if ((pos2.Y < 0) || (pos2.Y > Constants.RegionSize)) + { + CrossToNewRegion(); + } + } + else + { + RemoveFromPhysicalScene(); + // This constant has been inferred from experimentation + // I'm not sure what this value should be, so I tried a few values. + timeStep = 0.04f; + pos2 = AbsolutePosition; + pos2.X = pos2.X + (vel.X * timeStep); + pos2.Y = pos2.Y + (vel.Y * timeStep); + pos2.Z = pos2.Z + (vel.Z * timeStep); + m_pos = pos2; } - if ((pos2.Y < 0) || (pos2.Y > Constants.RegionSize)) - { - CrossToNewRegion(); - } } /// @@ -2396,130 +2419,13 @@ namespace OpenSim.Region.Framework.Scenes /// protected void CrossToNewRegion() { - Vector3 pos = AbsolutePosition; - Vector3 newpos = new Vector3(pos.X, pos.Y, pos.Z); - uint neighbourx = m_regionInfo.RegionLocX; - uint neighboury = m_regionInfo.RegionLocY; + m_inTransit = true; + m_scene.CrossAgentToNewRegion(this, m_physicsActor.Flying); + } - // distance to edge that will trigger crossing - const float boundaryDistance = 1.7f; - - // distance into new region to place avatar - const float enterDistance = 0.1f; - - if (pos.X < boundaryDistance) - { - neighbourx--; - newpos.X = Constants.RegionSize - enterDistance; - } - else if (pos.X > Constants.RegionSize - boundaryDistance) - { - neighbourx++; - newpos.X = enterDistance; - } - - if (pos.Y < boundaryDistance) - { - neighboury--; - newpos.Y = Constants.RegionSize - enterDistance; - } - else if (pos.Y > Constants.RegionSize - boundaryDistance) - { - neighboury++; - newpos.Y = enterDistance; - } - - Vector3 vel = m_velocity; - ulong neighbourHandle = Utils.UIntsToLong((uint)(neighbourx * Constants.RegionSize), (uint)(neighboury * Constants.RegionSize)); - SimpleRegionInfo neighbourRegion = m_scene.RequestNeighbouringRegionInfo(neighbourHandle); - if (neighbourRegion != null && ValidateAttachments()) - { - // When the neighbour is informed of the border crossing, it will set up CAPS handlers for the avatar - // This means we need to remove the current caps handler here and possibly compensate later, - // in case both scenes are being hosted on the same region server. Messy - //m_scene.RemoveCapsHandler(UUID); - newpos = newpos + (vel); - - CachedUserInfo userInfo = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(UUID); - if (userInfo != null) - { - userInfo.DropInventory(); - } - else - { - m_log.WarnFormat("[SCENE PRESENCE]: No cached user info found for {0} {1} on leaving region", Name, UUID); - } - - bool crossingSuccessful = - m_scene.InformNeighbourOfCrossing(neighbourHandle, m_controllingClient.AgentId, newpos, - m_physicsActor.Flying); - if (crossingSuccessful) - { - // Next, let's close the child agent connections that are too far away. - CloseChildAgents(neighbourx, neighboury); - - //AgentCircuitData circuitdata = m_controllingClient.RequestClientInfo(); - m_controllingClient.RequestClientInfo(); - - //Console.WriteLine("BEFORE CROSS"); - //Scene.DumpChildrenSeeds(UUID); - //DumpKnownRegions(); - string agentcaps; - if (!m_knownChildRegions.TryGetValue(neighbourRegion.RegionHandle, out agentcaps)) - { - m_log.ErrorFormat("[SCENE PRESENCE]: No CAPS information for region handle {0}, exiting CrossToNewRegion.", - neighbourRegion.RegionHandle); - return; - } - // TODO Should construct this behind a method - string capsPath = - "http://" + neighbourRegion.ExternalHostName + ":" + neighbourRegion.HttpPort - + "/CAPS/" + agentcaps /*circuitdata.CapsPath*/ + "0000/"; - - m_log.DebugFormat("[CAPS]: Sending new CAPS seed url {0} to client {1}", capsPath, m_uuid); - - IEventQueue eq = m_scene.RequestModuleInterface(); - if (eq != null) - { - eq.CrossRegion(neighbourHandle, newpos, vel, neighbourRegion.ExternalEndPoint, - capsPath, UUID, ControllingClient.SessionId); - } - else - { - m_controllingClient.CrossRegion(neighbourHandle, newpos, vel, neighbourRegion.ExternalEndPoint, - capsPath); - } - - MakeChildAgent(); - // now we have a child agent in this region. Request all interesting data about other (root) agents - SendInitialFullUpdateToAllClients(); - - CrossAttachmentsIntoNewRegion(neighbourHandle, true); - - // m_scene.SendKillObject(m_localId); - - m_scene.NotifyMyCoarseLocationChange(); - // the user may change their profile information in other region, - // so the userinfo in UserProfileCache is not reliable any more, delete it - if (m_scene.NeedSceneCacheClear(UUID)) - { - m_scene.CommsManager.UserProfileCacheService.RemoveUser(UUID); - m_log.DebugFormat( - "[SCENE PRESENCE]: User {0} is going to another region, profile cache removed", UUID); - } - } - else - { - // Restore the user structures that we needed to delete before asking the receiving region - // to complete the crossing - userInfo.FetchInventory(); - m_scene.CapsModule.AddCapsHandler(UUID); - } - } - - //Console.WriteLine("AFTER CROSS"); - //Scene.DumpChildrenSeeds(UUID); - //DumpKnownRegions(); + public void RestoreInCurrentScene() + { + AddToPhysicalScene(); } /// diff --git a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTests.cs b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTests.cs index db88878f09..2f2bc19b33 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTests.cs @@ -66,11 +66,11 @@ namespace OpenSim.Region.Framework.Scenes.Tests scene2 = SceneSetupHelpers.SetupScene("Neighbour x+1", UUID.Random(), 1001, 1000, cm); scene3 = SceneSetupHelpers.SetupScene("Neighbour x-1", UUID.Random(), 999, 1000, cm); - IRegionModule interregionComms = new RESTInterregionComms(); - interregionComms.Initialise(scene, new IniConfigSource()); - interregionComms.Initialise(scene2, new IniConfigSource()); - interregionComms.Initialise(scene3, new IniConfigSource()); - interregionComms.PostInitialise(); + IRegionModule interregionComms = new RESTInterregionComms(); + interregionComms.Initialise(scene, new IniConfigSource()); + interregionComms.Initialise(scene2, new IniConfigSource()); + interregionComms.Initialise(scene3, new IniConfigSource()); + interregionComms.PostInitialise(); SceneSetupHelpers.SetupSceneModules(scene, new IniConfigSource(), interregionComms); SceneSetupHelpers.SetupSceneModules(scene2, new IniConfigSource(), interregionComms); SceneSetupHelpers.SetupSceneModules(scene3, new IniConfigSource(), interregionComms); @@ -203,6 +203,8 @@ namespace OpenSim.Region.Framework.Scenes.Tests scene.RegisterRegionWithGrid(); scene2.RegisterRegionWithGrid(); presence.Update(); + // Crossings are asynchronous + while (presence.IsInTransit) { } ; Assert.That(presence.IsChildAgent, Is.True, "Did not complete region cross as expected."); Assert.That(presence2.IsChildAgent, Is.False, "Did not receive root status after receiving agent."); @@ -210,6 +212,8 @@ namespace OpenSim.Region.Framework.Scenes.Tests // Cross Back presence2.AbsolutePosition = new Vector3(-1, 3, 100); presence2.Update(); + // Crossings are asynchronous + while (presence.IsInTransit) { }; Assert.That(presence2.IsChildAgent, Is.True, "Did not return from region as expected."); Assert.That(presence.IsChildAgent, Is.False, "Presence was not made root in old region again.");