aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Framework/Scenes/Scene.cs
diff options
context:
space:
mode:
authorJustin Clark-Casey (justincc)2012-10-10 00:26:43 +0100
committerJustin Clark-Casey (justincc)2012-10-10 00:26:43 +0100
commite76b01a201a9b2d45b56cd5dc2a207b08b4529e5 (patch)
tree81bff879c3bc3eec19ff0bde5d5029014cc8b9f4 /OpenSim/Region/Framework/Scenes/Scene.cs
parentminor: elaborate method doc on Scene.NewUserConnection() (diff)
downloadopensim-SC_OLD-e76b01a201a9b2d45b56cd5dc2a207b08b4529e5.zip
opensim-SC_OLD-e76b01a201a9b2d45b56cd5dc2a207b08b4529e5.tar.gz
opensim-SC_OLD-e76b01a201a9b2d45b56cd5dc2a207b08b4529e5.tar.bz2
opensim-SC_OLD-e76b01a201a9b2d45b56cd5dc2a207b08b4529e5.tar.xz
Lock on AgentCircuitData during Scene.AddClient() and RemoveClient() to prevent an inactive connection being left behind if the user closes the viewer whilst the connection is being established.
This should remove the need to run the console command "kick user --force" when these connections are left around.
Diffstat (limited to 'OpenSim/Region/Framework/Scenes/Scene.cs')
-rw-r--r--OpenSim/Region/Framework/Scenes/Scene.cs457
1 files changed, 256 insertions, 201 deletions
diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs
index ed88571..fb2decc 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.cs
@@ -80,6 +80,11 @@ namespace OpenSim.Region.Framework.Scenes
80 public SynchronizeSceneHandler SynchronizeScene; 80 public SynchronizeSceneHandler SynchronizeScene;
81 81
82 /// <summary> 82 /// <summary>
83 /// Used to prevent simultaneous calls to RemoveClient() for the same agent from interfering with each other.
84 /// </summary>
85 private object m_removeClientLock = new object();
86
87 /// <summary>
83 /// Statistical information for this scene. 88 /// Statistical information for this scene.
84 /// </summary> 89 /// </summary>
85 public SimStatsReporter StatsReporter { get; private set; } 90 public SimStatsReporter StatsReporter { get; private set; }
@@ -2709,69 +2714,89 @@ namespace OpenSim.Region.Framework.Scenes
2709 2714
2710 public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type) 2715 public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type)
2711 { 2716 {
2717 ScenePresence sp;
2718 bool vialogin;
2719
2712 // Validation occurs in LLUDPServer 2720 // Validation occurs in LLUDPServer
2721 //
2722 // XXX: A race condition exists here where two simultaneous calls to AddNewClient can interfere with
2723 // each other. In practice, this does not currently occur in the code.
2713 AgentCircuitData aCircuit = m_authenticateHandler.GetAgentCircuitData(client.CircuitCode); 2724 AgentCircuitData aCircuit = m_authenticateHandler.GetAgentCircuitData(client.CircuitCode);
2714 2725
2715 bool vialogin 2726 // We lock here on AgentCircuitData to prevent a race condition between the thread adding a new connection
2716 = (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0 2727 // and a simultaneous one that removes it (as can happen if the client is closed at a particular point
2717 || (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaLogin) != 0; 2728 // whilst connecting).
2718 2729 //
2719// CheckHeartbeat(); 2730 // It would be easier to lock across all NewUserConnection(), AddNewClient() and
2720 2731 // RemoveClient() calls for all agents, but this would allow a slow call (e.g. because of slow service
2721 ScenePresence sp = GetScenePresence(client.AgentId); 2732 // response in some module listening to AddNewClient()) from holding up unrelated agent calls.
2722 2733 //
2723 // XXX: Not sure how good it is to add a new client if a scene presence already exists. Possibly this 2734 // In practice, the lock (this) in LLUDPServer.AddNewClient() currently lock across all
2724 // could occur if a viewer crashes and relogs before the old client is kicked out. But this could cause 2735 // AddNewClient() operations (though not other ops).
2725 // other problems, and possible the code calling AddNewClient() should ensure that no client is already 2736 // In the future this can be relieved once locking per agent (not necessarily on AgentCircuitData) is improved.
2726 // connected. 2737 lock (aCircuit)
2727 if (sp == null) 2738 {
2728 { 2739 vialogin
2729 m_log.DebugFormat( 2740 = (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0
2730 "[SCENE]: Adding new child scene presence {0} {1} to scene {2} at pos {3}", 2741 || (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaLogin) != 0;
2731 client.Name, client.AgentId, RegionInfo.RegionName, client.StartPos); 2742
2732 2743 // CheckHeartbeat();
2733 m_clientManager.Add(client); 2744
2734 SubscribeToClientEvents(client); 2745 sp = GetScenePresence(client.AgentId);
2735
2736 sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type);
2737 m_eventManager.TriggerOnNewPresence(sp);
2738
2739 sp.TeleportFlags = (TPFlags)aCircuit.teleportFlags;
2740 2746
2741 // The first agent upon login is a root agent by design. 2747 // XXX: Not sure how good it is to add a new client if a scene presence already exists. Possibly this
2742 // For this agent we will have to rez the attachments. 2748 // could occur if a viewer crashes and relogs before the old client is kicked out. But this could cause
2743 // All other AddNewClient calls find aCircuit.child to be true. 2749 // other problems, and possible the code calling AddNewClient() should ensure that no client is already
2744 if (aCircuit.child == false) 2750 // connected.
2751 if (sp == null)
2745 { 2752 {
2746 // We have to set SP to be a root agent here so that SP.MakeRootAgent() will later not try to 2753 m_log.DebugFormat(
2747 // start the scripts again (since this is done in RezAttachments()). 2754 "[SCENE]: Adding new child scene presence {0} {1} to scene {2} at pos {3}",
2748 // XXX: This is convoluted. 2755 client.Name, client.AgentId, RegionInfo.RegionName, client.StartPos);
2749 sp.IsChildAgent = false; 2756
2750 2757 m_clientManager.Add(client);
2751 if (AttachmentsModule != null) 2758 SubscribeToClientEvents(client);
2752 Util.FireAndForget(delegate(object o) { AttachmentsModule.RezAttachments(sp); }); 2759
2760 sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type);
2761 m_eventManager.TriggerOnNewPresence(sp);
2762
2763 sp.TeleportFlags = (TPFlags)aCircuit.teleportFlags;
2764
2765 // The first agent upon login is a root agent by design.
2766 // For this agent we will have to rez the attachments.
2767 // All other AddNewClient calls find aCircuit.child to be true.
2768 if (aCircuit.child == false)
2769 {
2770 // We have to set SP to be a root agent here so that SP.MakeRootAgent() will later not try to
2771 // start the scripts again (since this is done in RezAttachments()).
2772 // XXX: This is convoluted.
2773 sp.IsChildAgent = false;
2774
2775 if (AttachmentsModule != null)
2776 Util.FireAndForget(delegate(object o) { AttachmentsModule.RezAttachments(sp); });
2777 }
2753 } 2778 }
2754 } 2779 else
2755 else 2780 {
2756 { 2781 m_log.WarnFormat(
2757 m_log.WarnFormat( 2782 "[SCENE]: Already found {0} scene presence for {1} in {2} when asked to add new scene presence",
2758 "[SCENE]: Already found {0} scene presence for {1} in {2} when asked to add new scene presence", 2783 sp.IsChildAgent ? "child" : "root", sp.Name, RegionInfo.RegionName);
2759 sp.IsChildAgent ? "child" : "root", sp.Name, RegionInfo.RegionName); 2784 }
2760 } 2785
2786 // We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the
2787 // client is for a root or child agent.
2788 client.SceneAgent = sp;
2761 2789
2762 // We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the 2790 // Cache the user's name
2763 // client is for a root or child agent. 2791 CacheUserName(sp, aCircuit);
2764 client.SceneAgent = sp; 2792
2793 EventManager.TriggerOnNewClient(client);
2794 if (vialogin)
2795 EventManager.TriggerOnClientLogin(client);
2796 }
2765 2797
2766 m_LastLogin = Util.EnvironmentTickCount(); 2798 m_LastLogin = Util.EnvironmentTickCount();
2767 2799
2768 // Cache the user's name
2769 CacheUserName(sp, aCircuit);
2770
2771 EventManager.TriggerOnNewClient(client);
2772 if (vialogin)
2773 EventManager.TriggerOnClientLogin(client);
2774
2775 return sp; 2800 return sp;
2776 } 2801 }
2777 2802
@@ -3300,109 +3325,129 @@ namespace OpenSim.Region.Framework.Scenes
3300 { 3325 {
3301// CheckHeartbeat(); 3326// CheckHeartbeat();
3302 bool isChildAgent = false; 3327 bool isChildAgent = false;
3303 ScenePresence avatar = GetScenePresence(agentID); 3328 AgentCircuitData acd;
3304
3305 if (avatar == null)
3306 {
3307 m_log.WarnFormat(
3308 "[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in the scene.", agentID);
3309
3310 return;
3311 }
3312 3329
3313 try 3330 lock (m_removeClientLock)
3314 { 3331 {
3315 isChildAgent = avatar.IsChildAgent; 3332 acd = m_authenticateHandler.GetAgentCircuitData(agentID);
3316
3317 m_log.DebugFormat(
3318 "[SCENE]: Removing {0} agent {1} {2} from {3}",
3319 (isChildAgent ? "child" : "root"), avatar.Name, agentID, RegionInfo.RegionName);
3320 3333
3321 // Don't do this to root agents, it's not nice for the viewer 3334 if (acd == null)
3322 if (closeChildAgents && isChildAgent)
3323 { 3335 {
3324 // Tell a single agent to disconnect from the region. 3336 m_log.ErrorFormat("[SCENE]: No agent circuit found for {0}, aborting Scene.RemoveClient", agentID);
3325 IEventQueue eq = RequestModuleInterface<IEventQueue>(); 3337 return;
3326 if (eq != null)
3327 {
3328 eq.DisableSimulator(RegionInfo.RegionHandle, avatar.UUID);
3329 }
3330 else
3331 {
3332 avatar.ControllingClient.SendShutdownConnectionNotice();
3333 }
3334 } 3338 }
3335 3339 else
3336 // Only applies to root agents.
3337 if (avatar.ParentID != 0)
3338 { 3340 {
3339 avatar.StandUp(); 3341 // We remove the acd up here to avoid later raec conditions if two RemoveClient() calls occurred
3342 // simultaneously.
3343 m_authenticateHandler.RemoveCircuit(acd.circuitcode);
3340 } 3344 }
3345 }
3341 3346
3342 m_sceneGraph.removeUserCount(!isChildAgent); 3347 lock (acd)
3343 3348 {
3344 // TODO: We shouldn't use closeChildAgents here - it's being used by the NPC module to stop 3349 ScenePresence avatar = GetScenePresence(agentID);
3345 // unnecessary operations. This should go away once NPCs have no accompanying IClientAPI 3350
3346 if (closeChildAgents && CapsModule != null) 3351 if (avatar == null)
3347 CapsModule.RemoveCaps(agentID);
3348
3349 // REFACTORING PROBLEM -- well not really a problem, but just to point out that whatever
3350 // this method is doing is HORRIBLE!!!
3351 avatar.Scene.NeedSceneCacheClear(avatar.UUID);
3352
3353 if (closeChildAgents && !isChildAgent)
3354 { 3352 {
3355 List<ulong> regions = avatar.KnownRegionHandles; 3353 m_log.WarnFormat(
3356 regions.Remove(RegionInfo.RegionHandle); 3354 "[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in the scene.", agentID);
3357 m_sceneGridService.SendCloseChildAgentConnections(agentID, regions); 3355
3356 return;
3358 } 3357 }
3359 3358
3360 m_eventManager.TriggerClientClosed(agentID, this); 3359 try
3361 m_eventManager.TriggerOnRemovePresence(agentID);
3362
3363 if (!isChildAgent)
3364 { 3360 {
3365 if (AttachmentsModule != null) 3361 isChildAgent = avatar.IsChildAgent;
3362
3363 m_log.DebugFormat(
3364 "[SCENE]: Removing {0} agent {1} {2} from {3}",
3365 (isChildAgent ? "child" : "root"), avatar.Name, agentID, RegionInfo.RegionName);
3366
3367 // Don't do this to root agents, it's not nice for the viewer
3368 if (closeChildAgents && isChildAgent)
3366 { 3369 {
3367 AttachmentsModule.DeRezAttachments(avatar); 3370 // Tell a single agent to disconnect from the region.
3371 IEventQueue eq = RequestModuleInterface<IEventQueue>();
3372 if (eq != null)
3373 {
3374 eq.DisableSimulator(RegionInfo.RegionHandle, avatar.UUID);
3375 }
3376 else
3377 {
3378 avatar.ControllingClient.SendShutdownConnectionNotice();
3379 }
3368 } 3380 }
3369 3381
3370 ForEachClient( 3382 // Only applies to root agents.
3371 delegate(IClientAPI client) 3383 if (avatar.ParentID != 0)
3384 {
3385 avatar.StandUp();
3386 }
3387
3388 m_sceneGraph.removeUserCount(!isChildAgent);
3389
3390 // TODO: We shouldn't use closeChildAgents here - it's being used by the NPC module to stop
3391 // unnecessary operations. This should go away once NPCs have no accompanying IClientAPI
3392 if (closeChildAgents && CapsModule != null)
3393 CapsModule.RemoveCaps(agentID);
3394
3395 // REFACTORING PROBLEM -- well not really a problem, but just to point out that whatever
3396 // this method is doing is HORRIBLE!!!
3397 avatar.Scene.NeedSceneCacheClear(avatar.UUID);
3398
3399 if (closeChildAgents && !isChildAgent)
3400 {
3401 List<ulong> regions = avatar.KnownRegionHandles;
3402 regions.Remove(RegionInfo.RegionHandle);
3403 m_sceneGridService.SendCloseChildAgentConnections(agentID, regions);
3404 }
3405
3406 m_eventManager.TriggerClientClosed(agentID, this);
3407 m_eventManager.TriggerOnRemovePresence(agentID);
3408
3409 if (!isChildAgent)
3410 {
3411 if (AttachmentsModule != null)
3372 { 3412 {
3373 //We can safely ignore null reference exceptions. It means the avatar is dead and cleaned up anyway 3413 AttachmentsModule.DeRezAttachments(avatar);
3374 try { client.SendKillObject(avatar.RegionHandle, new List<uint> { avatar.LocalId }); } 3414 }
3375 catch (NullReferenceException) { }
3376 });
3377 }
3378
3379 // It's possible for child agents to have transactions if changes are being made cross-border.
3380 if (AgentTransactionsModule != null)
3381 AgentTransactionsModule.RemoveAgentAssetTransactions(agentID);
3382 3415
3383 m_authenticateHandler.RemoveCircuit(avatar.ControllingClient.CircuitCode); 3416 ForEachClient(
3384 } 3417 delegate(IClientAPI client)
3385 catch (Exception e) 3418 {
3386 { 3419 //We can safely ignore null reference exceptions. It means the avatar is dead and cleaned up anyway
3387 m_log.Error( 3420 try { client.SendKillObject(avatar.RegionHandle, new List<uint> { avatar.LocalId }); }
3388 string.Format("[SCENE]: Exception removing {0} from {1}. Cleaning up. Exception ", avatar.Name, Name), e); 3421 catch (NullReferenceException) { }
3389 } 3422 });
3390 finally 3423 }
3391 {
3392 try
3393 {
3394 // Always clean these structures up so that any failure above doesn't cause them to remain in the
3395 // scene with possibly bad effects (e.g. continually timing out on unacked packets and triggering
3396 // the same cleanup exception continually.
3397 m_sceneGraph.RemoveScenePresence(agentID);
3398 m_clientManager.Remove(agentID);
3399 3424
3400 avatar.Close(); 3425 // It's possible for child agents to have transactions if changes are being made cross-border.
3426 if (AgentTransactionsModule != null)
3427 AgentTransactionsModule.RemoveAgentAssetTransactions(agentID);
3401 } 3428 }
3402 catch (Exception e) 3429 catch (Exception e)
3403 { 3430 {
3404 m_log.Error( 3431 m_log.Error(
3405 string.Format("[SCENE]: Exception in final clean up of {0} in {1}. Exception ", avatar.Name, Name), e); 3432 string.Format("[SCENE]: Exception removing {0} from {1}. Cleaning up. Exception ", avatar.Name, Name), e);
3433 }
3434 finally
3435 {
3436 try
3437 {
3438 // Always clean these structures up so that any failure above doesn't cause them to remain in the
3439 // scene with possibly bad effects (e.g. continually timing out on unacked packets and triggering
3440 // the same cleanup exception continually.
3441 m_sceneGraph.RemoveScenePresence(agentID);
3442 m_clientManager.Remove(agentID);
3443
3444 avatar.Close();
3445 }
3446 catch (Exception e)
3447 {
3448 m_log.Error(
3449 string.Format("[SCENE]: Exception in final clean up of {0} in {1}. Exception ", avatar.Name, Name), e);
3450 }
3406 } 3451 }
3407 } 3452 }
3408 3453
@@ -3572,87 +3617,97 @@ namespace OpenSim.Region.Framework.Scenes
3572 agent.firstname, agent.lastname, agent.Viewer); 3617 agent.firstname, agent.lastname, agent.Viewer);
3573 reason = "Access denied, your viewer is banned by the region owner"; 3618 reason = "Access denied, your viewer is banned by the region owner";
3574 return false; 3619 return false;
3575 }
3576
3577 ScenePresence sp = GetScenePresence(agent.AgentID);
3578
3579 if (sp != null && !sp.IsChildAgent)
3580 {
3581 // We have a zombie from a crashed session.
3582 // Or the same user is trying to be root twice here, won't work.
3583 // Kill it.
3584 m_log.WarnFormat(
3585 "[SCENE]: Existing root scene presence detected for {0} {1} in {2} when connecting. Removing existing presence.",
3586 sp.Name, sp.UUID, RegionInfo.RegionName);
3587
3588 sp.ControllingClient.Close(true);
3589 sp = null;
3590 } 3620 }
3591 3621
3592 ILandObject land = LandChannel.GetLandObject(agent.startpos.X, agent.startpos.Y); 3622 ILandObject land;
3593 3623
3594 //On login test land permisions 3624 lock (agent)
3595 if (vialogin)
3596 { 3625 {
3597 if (land != null && !TestLandRestrictions(agent, land, out reason)) 3626 ScenePresence sp = GetScenePresence(agent.AgentID);
3627
3628 if (sp != null && !sp.IsChildAgent)
3598 { 3629 {
3599 return false; 3630 // We have a zombie from a crashed session.
3631 // Or the same user is trying to be root twice here, won't work.
3632 // Kill it.
3633 m_log.WarnFormat(
3634 "[SCENE]: Existing root scene presence detected for {0} {1} in {2} when connecting. Removing existing presence.",
3635 sp.Name, sp.UUID, RegionInfo.RegionName);
3636
3637 sp.ControllingClient.Close(true);
3638 sp = null;
3600 } 3639 }
3601 } 3640
3602 3641 land = LandChannel.GetLandObject(agent.startpos.X, agent.startpos.Y);
3603 if (sp == null) // We don't have an [child] agent here already 3642
3604 { 3643 //On login test land permisions
3605 if (requirePresenceLookup) 3644 if (vialogin)
3606 { 3645 {
3607 try 3646 if (land != null && !TestLandRestrictions(agent, land, out reason))
3608 {
3609 if (!VerifyUserPresence(agent, out reason))
3610 return false;
3611 } catch (Exception e)
3612 { 3647 {
3613 m_log.ErrorFormat(
3614 "[SCENE]: Exception verifying presence {0}{1}", e.Message, e.StackTrace);
3615 return false; 3648 return false;
3616 } 3649 }
3617 } 3650 }
3618 3651
3619 try 3652 if (sp == null) // We don't have an [child] agent here already
3620 {
3621 if (!AuthorizeUser(agent, out reason))
3622 return false;
3623 } catch (Exception e)
3624 {
3625 m_log.ErrorFormat(
3626 "[SCENE]: Exception authorizing user {0}{1}", e.Message, e.StackTrace);
3627 return false;
3628 }
3629
3630 m_log.InfoFormat(
3631 "[SCENE]: Region {0} authenticated and authorized incoming {1} agent {2} {3} {4} (circuit code {5})",
3632 RegionInfo.RegionName, (agent.child ? "child" : "root"), agent.firstname, agent.lastname,
3633 agent.AgentID, agent.circuitcode);
3634
3635 if (CapsModule != null)
3636 { 3653 {
3637 CapsModule.SetAgentCapsSeeds(agent); 3654 if (requirePresenceLookup)
3638 CapsModule.CreateCaps(agent.AgentID); 3655 {
3639 } 3656 try
3640 } else 3657 {
3641 { 3658 if (!VerifyUserPresence(agent, out reason))
3642 // Let the SP know how we got here. This has a lot of interesting 3659 return false;
3643 // uses down the line. 3660 }
3644 sp.TeleportFlags = (TPFlags)teleportFlags; 3661 catch (Exception e)
3662 {
3663 m_log.ErrorFormat(
3664 "[SCENE]: Exception verifying presence {0}{1}", e.Message, e.StackTrace);
3645 3665
3646 if (sp.IsChildAgent) 3666 return false;
3647 { 3667 }
3648 m_log.DebugFormat( 3668 }
3649 "[SCENE]: Adjusting known seeds for existing agent {0} in {1}", 3669
3650 agent.AgentID, RegionInfo.RegionName); 3670 try
3671 {
3672 if (!AuthorizeUser(agent, out reason))
3673 return false;
3674 }
3675 catch (Exception e)
3676 {
3677 m_log.ErrorFormat(
3678 "[SCENE]: Exception authorizing user {0}{1}", e.Message, e.StackTrace);
3651 3679
3652 sp.AdjustKnownSeeds(); 3680 return false;
3653 3681 }
3682
3683 m_log.InfoFormat(
3684 "[SCENE]: Region {0} authenticated and authorized incoming {1} agent {2} {3} {4} (circuit code {5})",
3685 RegionInfo.RegionName, (agent.child ? "child" : "root"), agent.firstname, agent.lastname,
3686 agent.AgentID, agent.circuitcode);
3687
3654 if (CapsModule != null) 3688 if (CapsModule != null)
3689 {
3655 CapsModule.SetAgentCapsSeeds(agent); 3690 CapsModule.SetAgentCapsSeeds(agent);
3691 CapsModule.CreateCaps(agent.AgentID);
3692 }
3693 }
3694 else
3695 {
3696 // Let the SP know how we got here. This has a lot of interesting
3697 // uses down the line.
3698 sp.TeleportFlags = (TPFlags)teleportFlags;
3699
3700 if (sp.IsChildAgent)
3701 {
3702 m_log.DebugFormat(
3703 "[SCENE]: Adjusting known seeds for existing agent {0} in {1}",
3704 agent.AgentID, RegionInfo.RegionName);
3705
3706 sp.AdjustKnownSeeds();
3707
3708 if (CapsModule != null)
3709 CapsModule.SetAgentCapsSeeds(agent);
3710 }
3656 } 3711 }
3657 } 3712 }
3658 3713