diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs index 141af8a937..626932f53d 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs @@ -91,7 +91,7 @@ namespace OpenSim.Region.ClientStack.Linden.Tests public void RemoveForClient() { TestHelpers.InMethod(); -// log4net.Config.XmlConfigurator.Configure(); +// TestHelpers.EnableLogging(); UUID spId = TestHelpers.ParseTail(0x1); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 8b2440a63f..e775a81275 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -512,7 +512,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP // We still perform a force close inside the sync lock since this is intended to attempt close where // there is some unidentified connection problem, not where we have issues due to deadlock if (!IsActive && !force) + { + m_log.DebugFormat( + "[CLIENT]: Not attempting to close inactive client {0} in {1} since force flag is not set", + Name, m_scene.Name); + return; + } IsActive = false; CloseWithoutChecks(); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 5c38399c08..de2f9d4e17 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -1799,9 +1799,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (!client.SceneAgent.IsChildAgent) client.Kick("Simulator logged you out due to connection timeout."); - - client.CloseWithoutChecks(); } + + m_scene.IncomingCloseAgent(client.AgentId, true); } private void IncomingPacketHandler() @@ -2142,7 +2142,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (!client.IsLoggingOut) { client.IsLoggingOut = true; - client.Close(); + m_scene.IncomingCloseAgent(client.AgentId, false); } } } diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs index b47ff54efe..97002244fc 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs @@ -200,7 +200,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests public void TestLogoutClientDueToAck() { TestHelpers.InMethod(); -// TestHelpers.EnableLogging(); + TestHelpers.EnableLogging(); IniConfigSource ics = new IniConfigSource(); IConfig config = ics.AddConfig("ClientStack.LindenUDP"); diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index 01ef710387..2f7425310a 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -1064,8 +1064,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer // Now let's make it officially a child agent sp.MakeChildAgent(); - // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone + // May still need to signal neighbours whether child agents may need closing irrespective of whether this + // one needed closing. Neighbour regions also contain logic to prevent a close if a subsequent move or + // teleport re-established the child connection. + 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.DoNotCloseAfterTeleport = false; @@ -1081,14 +1085,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer if (!sp.DoNotCloseAfterTeleport) { // OK, it got this agent. Let's close everything - m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Closing in agent {0} in region {1}", sp.Name, Scene.Name); - sp.CloseChildAgents(newRegionX, newRegionY); + m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Closing agent {0} in {1}", sp.Name, Scene.Name); sp.Scene.IncomingCloseAgent(sp.UUID, false); - } else { - m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Not closing agent {0}, user is back in {0}", sp.Name, Scene.Name); + m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Not closing agent {0}, user is back in {1}", sp.Name, Scene.Name); sp.DoNotCloseAfterTeleport = false; } } @@ -1863,10 +1865,10 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer List newRegions = NewNeighbours(neighbourHandles, previousRegionNeighbourHandles); List oldRegions = OldNeighbours(neighbourHandles, previousRegionNeighbourHandles); - //Dump("Current Neighbors", neighbourHandles); - //Dump("Previous Neighbours", previousRegionNeighbourHandles); - //Dump("New Neighbours", newRegions); - //Dump("Old Neighbours", oldRegions); + Dump("Current Neighbors", neighbourHandles); + Dump("Previous Neighbours", previousRegionNeighbourHandles); + Dump("New Neighbours", newRegions); + Dump("Old Neighbours", oldRegions); /// Update the scene presence's known regions here on this region sp.DropOldNeighbours(oldRegions); @@ -1874,8 +1876,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer /// Collect as many seeds as possible Dictionary seeds; if (sp.Scene.CapsModule != null) - seeds - = new Dictionary(sp.Scene.CapsModule.GetChildrenSeeds(sp.UUID)); + seeds = new Dictionary(sp.Scene.CapsModule.GetChildrenSeeds(sp.UUID)); else seeds = new Dictionary(); @@ -1945,6 +1946,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer newAgent = true; else newAgent = false; +// continue; if (neighbour.RegionHandle != sp.Scene.RegionInfo.RegionHandle) { @@ -2178,18 +2180,18 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer return handles; } -// private void Dump(string msg, List handles) -// { -// m_log.InfoFormat("-------------- HANDLE DUMP ({0}) ---------", msg); -// foreach (ulong handle in handles) -// { -// uint x, y; -// Utils.LongToUInts(handle, out x, out y); -// x = x / Constants.RegionSize; -// y = y / Constants.RegionSize; -// m_log.InfoFormat("({0}, {1})", x, y); -// } -// } + private void Dump(string msg, List handles) + { + m_log.InfoFormat("-------------- HANDLE DUMP ({0}) ---------", msg); + foreach (ulong handle in handles) + { + uint x, y; + Utils.LongToUInts(handle, out x, out y); + x = x / Constants.RegionSize; + y = y / Constants.RegionSize; + m_log.InfoFormat("({0}, {1})", x, y); + } + } #endregion diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs index 121b2aa8e5..31547a69cb 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs @@ -562,7 +562,7 @@ namespace OpenSim.Region.CoreModules.World.Estate if (!Scene.TeleportClientHome(user, s.ControllingClient)) { s.ControllingClient.Kick("Your access to the region was revoked and TP home failed - you have been logged out."); - s.ControllingClient.Close(); + Scene.IncomingCloseAgent(s.UUID, false); } } } @@ -797,7 +797,7 @@ namespace OpenSim.Region.CoreModules.World.Estate if (!Scene.TeleportClientHome(prey, s.ControllingClient)) { s.ControllingClient.Kick("You were teleported home by the region owner, but the TP failed - you have been logged out."); - s.ControllingClient.Close(); + Scene.IncomingCloseAgent(s.UUID, false); } } } @@ -820,7 +820,7 @@ namespace OpenSim.Region.CoreModules.World.Estate if (!Scene.TeleportClientHome(p.UUID, p.ControllingClient)) { p.ControllingClient.Kick("You were teleported home by the region owner, but the TP failed - you have been logged out."); - p.ControllingClient.Close(); + Scene.IncomingCloseAgent(p.UUID, false); } } } diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 56cd57e9df..5f10869072 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -151,7 +151,7 @@ namespace OpenSim.Region.Framework.Scenes public SynchronizeSceneHandler SynchronizeScene; /// - /// Used to prevent simultaneous calls to RemoveClient() for the same agent from interfering with each other. + /// Used to prevent simultaneous calls to code that adds and removes agents. /// private object m_removeClientLock = new object(); @@ -1312,7 +1312,7 @@ namespace OpenSim.Region.Framework.Scenes Thread.Sleep(500); // Stop all client threads. - ForEachScenePresence(delegate(ScenePresence avatar) { avatar.ControllingClient.Close(); }); + ForEachScenePresence(delegate(ScenePresence avatar) { IncomingCloseAgent(avatar.UUID, false); }); m_log.Debug("[SCENE]: Persisting changed objects"); EventManager.TriggerSceneShuttingDown(this); @@ -2972,7 +2972,7 @@ namespace OpenSim.Region.Framework.Scenes { PresenceService.LogoutAgent(sp.ControllingClient.SessionId); - sp.ControllingClient.Close(); + IncomingCloseAgent(sp.UUID, false); } else { @@ -3384,47 +3384,48 @@ namespace OpenSim.Region.Framework.Scenes public override void RemoveClient(UUID agentID, bool closeChildAgents) { -// CheckHeartbeat(); - bool isChildAgent = false; - AgentCircuitData acd; + AgentCircuitData acd = m_authenticateHandler.GetAgentCircuitData(agentID); - lock (m_removeClientLock) + // Shouldn't be necessary since RemoveClient() is currently only called by IClientAPI.Close() which + // in turn is only called by Scene.IncomingCloseAgent() which checks whether the presence exists or not + // However, will keep for now just in case. + if (acd == null) { - acd = m_authenticateHandler.GetAgentCircuitData(agentID); + m_log.ErrorFormat( + "[SCENE]: No agent circuit found for {0} in {1}, aborting Scene.RemoveClient", agentID, Name); - if (acd == null) - { - m_log.ErrorFormat("[SCENE]: No agent circuit found for {0}, aborting Scene.RemoveClient", agentID); - return; - } - else - { - // We remove the acd up here to avoid later race conditions if two RemoveClient() calls occurred - // simultaneously. - // We also need to remove by agent ID since NPCs will have no circuit code. - m_authenticateHandler.RemoveCircuit(agentID); - } + return; + } + else + { + m_authenticateHandler.RemoveCircuit(agentID); } + // TODO: Can we now remove this lock? lock (acd) - { + { + bool isChildAgent = false; + ScenePresence avatar = GetScenePresence(agentID); - + + // Shouldn't be necessary since RemoveClient() is currently only called by IClientAPI.Close() which + // in turn is only called by Scene.IncomingCloseAgent() which checks whether the presence exists or not + // However, will keep for now just in case. if (avatar == null) { - m_log.WarnFormat( + m_log.ErrorFormat( "[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in the scene.", agentID); return; } - + try { isChildAgent = avatar.IsChildAgent; m_log.DebugFormat( "[SCENE]: Removing {0} agent {1} {2} from {3}", - (isChildAgent ? "child" : "root"), avatar.Name, agentID, RegionInfo.RegionName); + isChildAgent ? "child" : "root", avatar.Name, agentID, Name); // Don't do this to root agents, it's not nice for the viewer if (closeChildAgents && isChildAgent) @@ -3587,13 +3588,13 @@ namespace OpenSim.Region.Framework.Scenes /// is activated later when the viewer sends the initial UseCircuitCodePacket UDP packet (in the case of /// the LLUDP stack). /// - /// CircuitData of the agent who is connecting + /// CircuitData of the agent who is connecting /// Outputs the reason for the false response on this string /// True for normal presence. False for NPC /// or other applications where a full grid/Hypergrid presence may not be required. /// True if the region accepts this agent. False if it does not. False will /// also return a reason. - public bool NewUserConnection(AgentCircuitData agent, uint teleportFlags, out string reason, bool requirePresenceLookup) + public bool NewUserConnection(AgentCircuitData acd, uint teleportFlags, out string reason, bool requirePresenceLookup) { bool vialogin = ((teleportFlags & (uint)TPFlags.ViaLogin) != 0 || (teleportFlags & (uint)TPFlags.ViaHGLogin) != 0); @@ -3613,15 +3614,15 @@ namespace OpenSim.Region.Framework.Scenes m_log.DebugFormat( "[SCENE]: Region {0} told of incoming {1} agent {2} {3} {4} (circuit code {5}, IP {6}, viewer {7}, teleportflags ({8}), position {9})", RegionInfo.RegionName, - (agent.child ? "child" : "root"), - agent.firstname, - agent.lastname, - agent.AgentID, - agent.circuitcode, - agent.IPAddress, - agent.Viewer, + (acd.child ? "child" : "root"), + acd.firstname, + acd.lastname, + acd.AgentID, + acd.circuitcode, + acd.IPAddress, + acd.Viewer, ((TPFlags)teleportFlags).ToString(), - agent.startpos + acd.startpos ); if (!LoginsEnabled) @@ -3639,7 +3640,7 @@ namespace OpenSim.Region.Framework.Scenes { foreach (string viewer in m_AllowedViewers) { - if (viewer == agent.Viewer.Substring(0, viewer.Length).Trim().ToLower()) + if (viewer == acd.Viewer.Substring(0, viewer.Length).Trim().ToLower()) { ViewerDenied = false; break; @@ -3656,7 +3657,7 @@ namespace OpenSim.Region.Framework.Scenes { foreach (string viewer in m_BannedViewers) { - if (viewer == agent.Viewer.Substring(0, viewer.Length).Trim().ToLower()) + if (viewer == acd.Viewer.Substring(0, viewer.Length).Trim().ToLower()) { ViewerDenied = true; break; @@ -3668,61 +3669,104 @@ namespace OpenSim.Region.Framework.Scenes { m_log.DebugFormat( "[SCENE]: Access denied for {0} {1} using {2}", - agent.firstname, agent.lastname, agent.Viewer); + acd.firstname, acd.lastname, acd.Viewer); reason = "Access denied, your viewer is banned by the region owner"; return false; } ILandObject land; + ScenePresence sp; - lock (agent) + lock (m_removeClientLock) { - ScenePresence sp = GetScenePresence(agent.AgentID); - - if (sp != null) + sp = GetScenePresence(acd.AgentID); + + // We need to ensure that we are not already removing the scene presence before we ask it not to be + // closed. + if (sp != null && sp.IsChildAgent && sp.LifecycleState == ScenePresenceState.Running) { - if (!sp.IsChildAgent) - { - // We have a root agent. Is it in transit? - if (!EntityTransferModule.IsInTransit(sp.UUID)) - { - // We have a zombie from a crashed session. - // Or the same user is trying to be root twice here, won't work. - // Kill it. - m_log.WarnFormat( - "[SCENE]: Existing root scene presence detected for {0} {1} in {2} when connecting. Removing existing presence.", - sp.Name, sp.UUID, RegionInfo.RegionName); + m_log.DebugFormat( + "[SCENE]: Reusing existing child scene presence for {0} in {1}", sp.Name, Name); - if (sp.ControllingClient != null) - sp.ControllingClient.Close(true); - - sp = null; - } - //else - // m_log.WarnFormat("[SCENE]: Existing root scene presence for {0} {1} in {2}, but agent is in trasit", sp.Name, sp.UUID, RegionInfo.RegionName); - } - else + // In the case where, for example, an A B C D region layout, an avatar may + // teleport from A -> D, but then -> C before A has asked B to close its old child agent. When C + // renews the lease on the child agent at B, we must make sure that the close from A does not succeed. + if (!acd.ChildrenCapSeeds.ContainsKey(RegionInfo.RegionHandle)) { - // We have a child agent here + m_log.DebugFormat( + "[SCENE]: Setting DoNotCloseAfterTeleport for child scene presence {0} in {1} because source will attempt close.", + sp.Name, Name); + sp.DoNotCloseAfterTeleport = true; - //m_log.WarnFormat("[SCENE]: Existing child scene presence for {0} {1} in {2}", sp.Name, sp.UUID, RegionInfo.RegionName); } } + } + + // Need to poll here in case we are currently deleting an sp. Letting threads run over each other will + // allow unpredictable things to happen. + if (sp != null) + { + const int polls = 10; + const int pollInterval = 1000; + int pollsLeft = polls; + + while (sp.LifecycleState == ScenePresenceState.Removing && pollsLeft-- > 0) + Thread.Sleep(pollInterval); + + if (sp.LifecycleState == ScenePresenceState.Removing) + { + m_log.WarnFormat( + "[SCENE]: Agent {0} in {1} was still being removed after {2}s. Aborting NewUserConnection.", + sp.Name, Name, polls * pollInterval / 1000); + + return false; + } + else if (polls != pollsLeft) + { + m_log.DebugFormat( + "[SCENE]: NewUserConnection for agent {0} in {1} had to wait {2}s for in-progress removal to complete on an old presence.", + sp.Name, Name, polls * pollInterval / 1000); + } + } + + // TODO: can we remove this lock? + lock (acd) + { + if (sp != null && !sp.IsChildAgent) + { + // We have a root agent. Is it in transit? + if (!EntityTransferModule.IsInTransit(sp.UUID)) + { + // We have a zombie from a crashed session. + // Or the same user is trying to be root twice here, won't work. + // Kill it. + m_log.WarnFormat( + "[SCENE]: Existing root scene presence detected for {0} {1} in {2} when connecting. Removing existing presence.", + sp.Name, sp.UUID, RegionInfo.RegionName); + + if (sp.ControllingClient != null) + IncomingCloseAgent(sp.UUID, true); + + sp = null; + } + //else + // m_log.WarnFormat("[SCENE]: Existing root scene presence for {0} {1} in {2}, but agent is in trasit", sp.Name, sp.UUID, RegionInfo.RegionName); + } // Optimistic: add or update the circuit data with the new agent circuit data and teleport flags. // We need the circuit data here for some of the subsequent checks. (groups, for example) // If the checks fail, we remove the circuit. - agent.teleportFlags = teleportFlags; - m_authenticateHandler.AddNewCircuit(agent.circuitcode, agent); + acd.teleportFlags = teleportFlags; + m_authenticateHandler.AddNewCircuit(acd.circuitcode, acd); - land = LandChannel.GetLandObject(agent.startpos.X, agent.startpos.Y); + land = LandChannel.GetLandObject(acd.startpos.X, acd.startpos.Y); // On login test land permisions if (vialogin) { - if (land != null && !TestLandRestrictions(agent.AgentID, out reason, ref agent.startpos.X, ref agent.startpos.Y)) + if (land != null && !TestLandRestrictions(acd.AgentID, out reason, ref acd.startpos.X, ref acd.startpos.Y)) { - m_authenticateHandler.RemoveCircuit(agent.circuitcode); + m_authenticateHandler.RemoveCircuit(acd.circuitcode); return false; } } @@ -3733,9 +3777,9 @@ namespace OpenSim.Region.Framework.Scenes { try { - if (!VerifyUserPresence(agent, out reason)) + if (!VerifyUserPresence(acd, out reason)) { - m_authenticateHandler.RemoveCircuit(agent.circuitcode); + m_authenticateHandler.RemoveCircuit(acd.circuitcode); return false; } } @@ -3744,16 +3788,16 @@ namespace OpenSim.Region.Framework.Scenes m_log.ErrorFormat( "[SCENE]: Exception verifying presence {0}{1}", e.Message, e.StackTrace); - m_authenticateHandler.RemoveCircuit(agent.circuitcode); + m_authenticateHandler.RemoveCircuit(acd.circuitcode); return false; } } try { - if (!AuthorizeUser(agent, SeeIntoRegion, out reason)) + if (!AuthorizeUser(acd, SeeIntoRegion, out reason)) { - m_authenticateHandler.RemoveCircuit(agent.circuitcode); + m_authenticateHandler.RemoveCircuit(acd.circuitcode); return false; } } @@ -3762,19 +3806,19 @@ namespace OpenSim.Region.Framework.Scenes m_log.ErrorFormat( "[SCENE]: Exception authorizing user {0}{1}", e.Message, e.StackTrace); - m_authenticateHandler.RemoveCircuit(agent.circuitcode); + m_authenticateHandler.RemoveCircuit(acd.circuitcode); return false; } m_log.InfoFormat( "[SCENE]: Region {0} authenticated and authorized incoming {1} agent {2} {3} {4} (circuit code {5})", - RegionInfo.RegionName, (agent.child ? "child" : "root"), agent.firstname, agent.lastname, - agent.AgentID, agent.circuitcode); + Name, (acd.child ? "child" : "root"), acd.firstname, acd.lastname, + acd.AgentID, acd.circuitcode); if (CapsModule != null) { - CapsModule.SetAgentCapsSeeds(agent); - CapsModule.CreateCaps(agent.AgentID); + CapsModule.SetAgentCapsSeeds(acd); + CapsModule.CreateCaps(acd.AgentID); } } else @@ -3787,14 +3831,14 @@ namespace OpenSim.Region.Framework.Scenes { m_log.DebugFormat( "[SCENE]: Adjusting known seeds for existing agent {0} in {1}", - agent.AgentID, RegionInfo.RegionName); + acd.AgentID, RegionInfo.RegionName); sp.AdjustKnownSeeds(); if (CapsModule != null) { - CapsModule.SetAgentCapsSeeds(agent); - CapsModule.CreateCaps(agent.AgentID); + CapsModule.SetAgentCapsSeeds(acd); + CapsModule.CreateCaps(acd.AgentID); } } } @@ -3802,23 +3846,23 @@ namespace OpenSim.Region.Framework.Scenes // Try caching an incoming user name much earlier on to see if this helps with an issue // where HG users are occasionally seen by others as "Unknown User" because their UUIDName // request for the HG avatar appears to trigger before the user name is cached. - CacheUserName(null, agent); + CacheUserName(null, acd); } if (vialogin) { // CleanDroppedAttachments(); - if (TestBorderCross(agent.startpos, Cardinals.E)) + if (TestBorderCross(acd.startpos, Cardinals.E)) { - Border crossedBorder = GetCrossedBorder(agent.startpos, Cardinals.E); - agent.startpos.X = crossedBorder.BorderLine.Z - 1; + Border crossedBorder = GetCrossedBorder(acd.startpos, Cardinals.E); + acd.startpos.X = crossedBorder.BorderLine.Z - 1; } - if (TestBorderCross(agent.startpos, Cardinals.N)) + if (TestBorderCross(acd.startpos, Cardinals.N)) { - Border crossedBorder = GetCrossedBorder(agent.startpos, Cardinals.N); - agent.startpos.Y = crossedBorder.BorderLine.Z - 1; + Border crossedBorder = GetCrossedBorder(acd.startpos, Cardinals.N); + acd.startpos.Y = crossedBorder.BorderLine.Z - 1; } //Mitigate http://opensimulator.org/mantis/view.php?id=3522 @@ -3828,39 +3872,39 @@ namespace OpenSim.Region.Framework.Scenes { lock (EastBorders) { - if (agent.startpos.X > EastBorders[0].BorderLine.Z) + if (acd.startpos.X > EastBorders[0].BorderLine.Z) { m_log.Warn("FIX AGENT POSITION"); - agent.startpos.X = EastBorders[0].BorderLine.Z * 0.5f; - if (agent.startpos.Z > 720) - agent.startpos.Z = 720; + acd.startpos.X = EastBorders[0].BorderLine.Z * 0.5f; + if (acd.startpos.Z > 720) + acd.startpos.Z = 720; } } lock (NorthBorders) { - if (agent.startpos.Y > NorthBorders[0].BorderLine.Z) + if (acd.startpos.Y > NorthBorders[0].BorderLine.Z) { m_log.Warn("FIX Agent POSITION"); - agent.startpos.Y = NorthBorders[0].BorderLine.Z * 0.5f; - if (agent.startpos.Z > 720) - agent.startpos.Z = 720; + acd.startpos.Y = NorthBorders[0].BorderLine.Z * 0.5f; + if (acd.startpos.Z > 720) + acd.startpos.Z = 720; } } } else { - if (agent.startpos.X > EastBorders[0].BorderLine.Z) + if (acd.startpos.X > EastBorders[0].BorderLine.Z) { m_log.Warn("FIX AGENT POSITION"); - agent.startpos.X = EastBorders[0].BorderLine.Z * 0.5f; - if (agent.startpos.Z > 720) - agent.startpos.Z = 720; + acd.startpos.X = EastBorders[0].BorderLine.Z * 0.5f; + if (acd.startpos.Z > 720) + acd.startpos.Z = 720; } - if (agent.startpos.Y > NorthBorders[0].BorderLine.Z) + if (acd.startpos.Y > NorthBorders[0].BorderLine.Z) { m_log.Warn("FIX Agent POSITION"); - agent.startpos.Y = NorthBorders[0].BorderLine.Z * 0.5f; - if (agent.startpos.Z > 720) - agent.startpos.Z = 720; + acd.startpos.Y = NorthBorders[0].BorderLine.Z * 0.5f; + if (acd.startpos.Z > 720) + acd.startpos.Z = 720; } } @@ -3876,12 +3920,12 @@ namespace OpenSim.Region.Framework.Scenes { // We have multiple SpawnPoints, Route the agent to a random or sequential one if (SpawnPointRouting == "random") - agent.startpos = spawnpoints[Util.RandomClass.Next(spawnpoints.Count) - 1].GetLocation( + acd.startpos = spawnpoints[Util.RandomClass.Next(spawnpoints.Count) - 1].GetLocation( telehub.AbsolutePosition, telehub.GroupRotation ); else - agent.startpos = spawnpoints[SpawnPoint()].GetLocation( + acd.startpos = spawnpoints[SpawnPoint()].GetLocation( telehub.AbsolutePosition, telehub.GroupRotation ); @@ -3889,7 +3933,7 @@ namespace OpenSim.Region.Framework.Scenes else { // We have a single SpawnPoint and will route the agent to it - agent.startpos = spawnpoints[0].GetLocation(telehub.AbsolutePosition, telehub.GroupRotation); + acd.startpos = spawnpoints[0].GetLocation(telehub.AbsolutePosition, telehub.GroupRotation); } return true; @@ -3900,7 +3944,7 @@ namespace OpenSim.Region.Framework.Scenes { if (land.LandData.LandingType == (byte)1 && land.LandData.UserLocation != Vector3.Zero) { - agent.startpos = land.LandData.UserLocation; + acd.startpos = land.LandData.UserLocation; } } } @@ -4359,11 +4403,51 @@ namespace OpenSim.Region.Framework.Scenes /// public bool IncomingCloseAgent(UUID agentID, bool force) { - //m_log.DebugFormat("[SCENE]: Processing incoming close agent for {0}", agentID); - ScenePresence presence = m_sceneGraph.GetScenePresence(agentID); - if (presence != null) + ScenePresence sp; + + lock (m_removeClientLock) { - presence.ControllingClient.Close(force); + sp = GetScenePresence(agentID); + + if (sp == null) + { + m_log.DebugFormat( + "[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in {1}", + agentID, Name); + + return false; + } + + if (sp.LifecycleState != ScenePresenceState.Running) + { + m_log.DebugFormat( + "[SCENE]: Called RemoveClient() for {0} in {1} but presence is already in state {2}", + sp.Name, Name, sp.LifecycleState); + + return false; + } + + // We need to avoid a race condition where in, for example, an A B C D region layout, an avatar may + // teleport from A -> D, but then -> C before A has asked B to close its old child agent. We do not + // want to obey this close since C may have renewed the child agent lease on B. + if (sp.DoNotCloseAfterTeleport) + { + m_log.DebugFormat( + "[SCENE]: Not closing {0} agent {1} in {2} since another simulator has re-established the child connection", + sp.IsChildAgent ? "child" : "root", sp.Name, Name); + + // Need to reset the flag so that a subsequent close after another teleport can succeed. + sp.DoNotCloseAfterTeleport = false; + + return false; + } + + sp.LifecycleState = ScenePresenceState.Removing; + } + + if (sp != null) + { + sp.ControllingClient.Close(force); return true; } diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 7fd13027aa..bdcdf03911 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -74,6 +74,8 @@ namespace OpenSim.Region.Framework.Scenes public class ScenePresence : EntityBase, IScenePresence { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + // ~ScenePresence() // { // m_log.DebugFormat("[SCENE PRESENCE]: Destructor called on {0}", Name); @@ -85,10 +87,27 @@ namespace OpenSim.Region.Framework.Scenes m_scene.EventManager.TriggerScenePresenceUpdated(this); } - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public PresenceType PresenceType { get; private set; } + private ScenePresenceStateMachine m_stateMachine; + + /// + /// The current state of this presence. Governs only the existence lifecycle. See ScenePresenceStateMachine + /// for more details. + /// + public ScenePresenceState LifecycleState + { + get + { + return m_stateMachine.GetState(); + } + + set + { + m_stateMachine.SetState(value); + } + } + // private static readonly byte[] DEFAULT_TEXTURE = AvatarAppearance.GetDefaultTexture().GetBytes(); private static readonly Array DIR_CONTROL_FLAGS = Enum.GetValues(typeof(Dir_ControlFlags)); private static readonly Vector3 HEAD_ADJUSTMENT = new Vector3(0f, 0f, 0.3f); @@ -766,7 +785,7 @@ namespace OpenSim.Region.Framework.Scenes public ScenePresence( IClientAPI client, Scene world, AvatarAppearance appearance, PresenceType type) - { + { AttachmentsSyncLock = new Object(); AllowMovement = true; IsChildAgent = true; @@ -811,6 +830,8 @@ namespace OpenSim.Region.Framework.Scenes SetDirectionVectors(); Appearance = appearance; + + m_stateMachine = new ScenePresenceStateMachine(this); } public void RegisterToEvents() @@ -879,7 +900,7 @@ namespace OpenSim.Region.Framework.Scenes /// public void MakeRootAgent(Vector3 pos, bool isFlying) { - m_log.DebugFormat( + m_log.InfoFormat( "[SCENE]: Upgrading child to root agent for {0} in {1}", Name, m_scene.RegionInfo.RegionName); @@ -887,6 +908,11 @@ namespace OpenSim.Region.Framework.Scenes IsChildAgent = false; + // Must reset this here so that a teleport to a region next to an existing region does not keep the flag + // set and prevent the close of the connection on a subsequent re-teleport. + // Should not be needed if we are not trying to tell this region to close +// DoNotCloseAfterTeleport = false; + IGroupsModule gm = m_scene.RequestModuleInterface(); if (gm != null) Grouptitle = gm.GetGroupTitle(m_uuid); @@ -3738,6 +3764,8 @@ namespace OpenSim.Region.Framework.Scenes // m_reprioritizationTimer.Dispose(); RemoveFromPhysicalScene(); + + LifecycleState = ScenePresenceState.Removed; } public void AddAttachment(SceneObjectGroup gobj)