diff options
Diffstat (limited to 'OpenSim/Region/Framework/Scenes/Scene.cs')
-rw-r--r-- | OpenSim/Region/Framework/Scenes/Scene.cs | 618 |
1 files changed, 394 insertions, 224 deletions
diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 8034bc6..45d512b 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; } |
@@ -103,8 +108,31 @@ namespace OpenSim.Region.Framework.Scenes | |||
103 | /// </summary> | 108 | /// </summary> |
104 | public bool CollidablePrims { get; private set; } | 109 | public bool CollidablePrims { get; private set; } |
105 | 110 | ||
111 | /// <summary> | ||
112 | /// Minimum value of the size of a non-physical prim in each axis | ||
113 | /// </summary> | ||
114 | public float m_minNonphys = 0.001f; | ||
115 | |||
116 | /// <summary> | ||
117 | /// Maximum value of the size of a non-physical prim in each axis | ||
118 | /// </summary> | ||
106 | public float m_maxNonphys = 256; | 119 | public float m_maxNonphys = 256; |
120 | |||
121 | /// <summary> | ||
122 | /// Minimum value of the size of a physical prim in each axis | ||
123 | /// </summary> | ||
124 | public float m_minPhys = 0.01f; | ||
125 | |||
126 | /// <summary> | ||
127 | /// Maximum value of the size of a physical prim in each axis | ||
128 | /// </summary> | ||
107 | public float m_maxPhys = 10; | 129 | public float m_maxPhys = 10; |
130 | |||
131 | /// <summary> | ||
132 | /// Max prims an object will hold | ||
133 | /// </summary> | ||
134 | public int m_linksetCapacity = 0; | ||
135 | |||
108 | public bool m_clampPrimSize; | 136 | public bool m_clampPrimSize; |
109 | public bool m_trustBinaries; | 137 | public bool m_trustBinaries; |
110 | public bool m_allowScriptCrossings; | 138 | public bool m_allowScriptCrossings; |
@@ -285,6 +313,31 @@ namespace OpenSim.Region.Framework.Scenes | |||
285 | } | 313 | } |
286 | private volatile bool m_shuttingDown; | 314 | private volatile bool m_shuttingDown; |
287 | 315 | ||
316 | /// <summary> | ||
317 | /// Is the scene active? | ||
318 | /// </summary> | ||
319 | /// <remarks> | ||
320 | /// If false, maintenance and update loops are not being run. Updates can still be triggered manually if | ||
321 | /// the scene is not active. | ||
322 | /// </remarks> | ||
323 | public bool Active | ||
324 | { | ||
325 | get { return m_active; } | ||
326 | set | ||
327 | { | ||
328 | if (value) | ||
329 | { | ||
330 | if (!m_active) | ||
331 | Start(); | ||
332 | } | ||
333 | else | ||
334 | { | ||
335 | m_active = false; | ||
336 | } | ||
337 | } | ||
338 | } | ||
339 | private volatile bool m_active; | ||
340 | |||
288 | // private int m_lastUpdate; | 341 | // private int m_lastUpdate; |
289 | private bool m_firstHeartbeat = true; | 342 | private bool m_firstHeartbeat = true; |
290 | 343 | ||
@@ -746,12 +799,24 @@ namespace OpenSim.Region.Framework.Scenes | |||
746 | PhysicalPrims = startupConfig.GetBoolean("physical_prim", true); | 799 | PhysicalPrims = startupConfig.GetBoolean("physical_prim", true); |
747 | CollidablePrims = startupConfig.GetBoolean("collidable_prim", true); | 800 | CollidablePrims = startupConfig.GetBoolean("collidable_prim", true); |
748 | 801 | ||
749 | m_maxNonphys = startupConfig.GetFloat("NonphysicalPrimMax", m_maxNonphys); | 802 | m_minNonphys = startupConfig.GetFloat("NonPhysicalPrimMin", m_minNonphys); |
803 | if (RegionInfo.NonphysPrimMin > 0) | ||
804 | { | ||
805 | m_minNonphys = RegionInfo.NonphysPrimMin; | ||
806 | } | ||
807 | |||
808 | m_maxNonphys = startupConfig.GetFloat("NonPhysicalPrimMax", m_maxNonphys); | ||
750 | if (RegionInfo.NonphysPrimMax > 0) | 809 | if (RegionInfo.NonphysPrimMax > 0) |
751 | { | 810 | { |
752 | m_maxNonphys = RegionInfo.NonphysPrimMax; | 811 | m_maxNonphys = RegionInfo.NonphysPrimMax; |
753 | } | 812 | } |
754 | 813 | ||
814 | m_minPhys = startupConfig.GetFloat("PhysicalPrimMin", m_minPhys); | ||
815 | if (RegionInfo.PhysPrimMin > 0) | ||
816 | { | ||
817 | m_minPhys = RegionInfo.PhysPrimMin; | ||
818 | } | ||
819 | |||
755 | m_maxPhys = startupConfig.GetFloat("PhysicalPrimMax", m_maxPhys); | 820 | m_maxPhys = startupConfig.GetFloat("PhysicalPrimMax", m_maxPhys); |
756 | 821 | ||
757 | if (RegionInfo.PhysPrimMax > 0) | 822 | if (RegionInfo.PhysPrimMax > 0) |
@@ -759,6 +824,12 @@ namespace OpenSim.Region.Framework.Scenes | |||
759 | m_maxPhys = RegionInfo.PhysPrimMax; | 824 | m_maxPhys = RegionInfo.PhysPrimMax; |
760 | } | 825 | } |
761 | 826 | ||
827 | m_linksetCapacity = startupConfig.GetInt("LinksetPrims", m_linksetCapacity); | ||
828 | if (RegionInfo.LinksetCapacity > 0) | ||
829 | { | ||
830 | m_linksetCapacity = RegionInfo.LinksetCapacity; | ||
831 | } | ||
832 | |||
762 | SpawnPointRouting = startupConfig.GetString("SpawnPointRouting", "closest"); | 833 | SpawnPointRouting = startupConfig.GetString("SpawnPointRouting", "closest"); |
763 | TelehubAllowLandmarks = startupConfig.GetBoolean("TelehubAllowLandmark", false); | 834 | TelehubAllowLandmarks = startupConfig.GetBoolean("TelehubAllowLandmark", false); |
764 | 835 | ||
@@ -784,13 +855,6 @@ namespace OpenSim.Region.Framework.Scenes | |||
784 | m_defaultScriptEngine = startupConfig.GetString("DefaultScriptEngine", "XEngine"); | 855 | m_defaultScriptEngine = startupConfig.GetString("DefaultScriptEngine", "XEngine"); |
785 | m_log.InfoFormat("[SCENE]: Default script engine {0}", m_defaultScriptEngine); | 856 | m_log.InfoFormat("[SCENE]: Default script engine {0}", m_defaultScriptEngine); |
786 | 857 | ||
787 | IConfig packetConfig = m_config.Configs["PacketPool"]; | ||
788 | if (packetConfig != null) | ||
789 | { | ||
790 | PacketPool.Instance.RecyclePackets = packetConfig.GetBoolean("RecyclePackets", true); | ||
791 | PacketPool.Instance.RecycleDataBlocks = packetConfig.GetBoolean("RecycleDataBlocks", true); | ||
792 | } | ||
793 | |||
794 | m_strictAccessControl = startupConfig.GetBoolean("StrictAccessControl", m_strictAccessControl); | 858 | m_strictAccessControl = startupConfig.GetBoolean("StrictAccessControl", m_strictAccessControl); |
795 | m_seeIntoBannedRegion = startupConfig.GetBoolean("SeeIntoBannedRegion", m_seeIntoBannedRegion); | 859 | m_seeIntoBannedRegion = startupConfig.GetBoolean("SeeIntoBannedRegion", m_seeIntoBannedRegion); |
796 | CombineRegions = startupConfig.GetBoolean("CombineContiguousRegions", false); | 860 | CombineRegions = startupConfig.GetBoolean("CombineContiguousRegions", false); |
@@ -854,6 +918,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
854 | } | 918 | } |
855 | 919 | ||
856 | // FIXME: Ultimately this should be in a module. | 920 | // FIXME: Ultimately this should be in a module. |
921 | SendPeriodicAppearanceUpdates = true; | ||
922 | |||
857 | IConfig appearanceConfig = m_config.Configs["Appearance"]; | 923 | IConfig appearanceConfig = m_config.Configs["Appearance"]; |
858 | if (appearanceConfig != null) | 924 | if (appearanceConfig != null) |
859 | { | 925 | { |
@@ -1151,6 +1217,14 @@ namespace OpenSim.Region.Framework.Scenes | |||
1151 | 1217 | ||
1152 | public void SetSceneCoreDebug(Dictionary<string, string> options) | 1218 | public void SetSceneCoreDebug(Dictionary<string, string> options) |
1153 | { | 1219 | { |
1220 | if (options.ContainsKey("active")) | ||
1221 | { | ||
1222 | bool active; | ||
1223 | |||
1224 | if (bool.TryParse(options["active"], out active)) | ||
1225 | Active = active; | ||
1226 | } | ||
1227 | |||
1154 | if (options.ContainsKey("scripting")) | 1228 | if (options.ContainsKey("scripting")) |
1155 | { | 1229 | { |
1156 | bool enableScripts = true; | 1230 | bool enableScripts = true; |
@@ -1290,6 +1364,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
1290 | /// </summary> | 1364 | /// </summary> |
1291 | public void Start() | 1365 | public void Start() |
1292 | { | 1366 | { |
1367 | m_active = true; | ||
1368 | |||
1293 | // m_log.DebugFormat("[SCENE]: Starting Heartbeat timer for {0}", RegionInfo.RegionName); | 1369 | // m_log.DebugFormat("[SCENE]: Starting Heartbeat timer for {0}", RegionInfo.RegionName); |
1294 | 1370 | ||
1295 | //m_heartbeatTimer.Enabled = true; | 1371 | //m_heartbeatTimer.Enabled = true; |
@@ -1346,7 +1422,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
1346 | #region Update Methods | 1422 | #region Update Methods |
1347 | 1423 | ||
1348 | /// <summary> | 1424 | /// <summary> |
1349 | /// Performs per-frame updates regularly | 1425 | /// Activate the various loops necessary to continually update the scene. |
1350 | /// </summary> | 1426 | /// </summary> |
1351 | private void Heartbeat() | 1427 | private void Heartbeat() |
1352 | { | 1428 | { |
@@ -1403,7 +1479,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
1403 | List<Vector3> coarseLocations; | 1479 | List<Vector3> coarseLocations; |
1404 | List<UUID> avatarUUIDs; | 1480 | List<UUID> avatarUUIDs; |
1405 | 1481 | ||
1406 | while (!m_shuttingDown && (endRun == null || MaintenanceRun < endRun)) | 1482 | while (!m_shuttingDown && ((endRun == null && Active) || MaintenanceRun < endRun)) |
1407 | { | 1483 | { |
1408 | runtc = Util.EnvironmentTickCount(); | 1484 | runtc = Util.EnvironmentTickCount(); |
1409 | ++MaintenanceRun; | 1485 | ++MaintenanceRun; |
@@ -1465,7 +1541,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
1465 | int sleepMS; | 1541 | int sleepMS; |
1466 | int framestart; | 1542 | int framestart; |
1467 | 1543 | ||
1468 | while (!m_shuttingDown && (endFrame == null || Frame < endFrame)) | 1544 | while (!m_shuttingDown && ((endFrame == null && Active) || Frame < endFrame)) |
1469 | { | 1545 | { |
1470 | framestart = Util.EnvironmentTickCount(); | 1546 | framestart = Util.EnvironmentTickCount(); |
1471 | ++Frame; | 1547 | ++Frame; |
@@ -2182,10 +2258,14 @@ namespace OpenSim.Region.Framework.Scenes | |||
2182 | public bool AddRestoredSceneObject( | 2258 | public bool AddRestoredSceneObject( |
2183 | SceneObjectGroup sceneObject, bool attachToBackup, bool alreadyPersisted, bool sendClientUpdates) | 2259 | SceneObjectGroup sceneObject, bool attachToBackup, bool alreadyPersisted, bool sendClientUpdates) |
2184 | { | 2260 | { |
2185 | bool result = m_sceneGraph.AddRestoredSceneObject(sceneObject, attachToBackup, alreadyPersisted, sendClientUpdates); | 2261 | if (m_sceneGraph.AddRestoredSceneObject(sceneObject, attachToBackup, alreadyPersisted, sendClientUpdates)) |
2186 | if (result) | 2262 | { |
2187 | sceneObject.IsDeleted = false; | 2263 | sceneObject.IsDeleted = false; |
2188 | return result; | 2264 | EventManager.TriggerObjectAddedToScene(sceneObject); |
2265 | return true; | ||
2266 | } | ||
2267 | |||
2268 | return false; | ||
2189 | } | 2269 | } |
2190 | 2270 | ||
2191 | /// <summary> | 2271 | /// <summary> |
@@ -2826,77 +2906,89 @@ namespace OpenSim.Region.Framework.Scenes | |||
2826 | 2906 | ||
2827 | public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type) | 2907 | public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type) |
2828 | { | 2908 | { |
2909 | ScenePresence sp; | ||
2910 | bool vialogin; | ||
2911 | |||
2829 | // Validation occurs in LLUDPServer | 2912 | // Validation occurs in LLUDPServer |
2913 | // | ||
2914 | // XXX: A race condition exists here where two simultaneous calls to AddNewClient can interfere with | ||
2915 | // each other. In practice, this does not currently occur in the code. | ||
2830 | AgentCircuitData aCircuit = m_authenticateHandler.GetAgentCircuitData(client.CircuitCode); | 2916 | AgentCircuitData aCircuit = m_authenticateHandler.GetAgentCircuitData(client.CircuitCode); |
2831 | 2917 | ||
2832 | bool vialogin | 2918 | // We lock here on AgentCircuitData to prevent a race condition between the thread adding a new connection |
2833 | = (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0 | 2919 | // and a simultaneous one that removes it (as can happen if the client is closed at a particular point |
2834 | || (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaLogin) != 0; | 2920 | // whilst connecting). |
2835 | 2921 | // | |
2836 | CheckHeartbeat(); | 2922 | // It would be easier to lock across all NewUserConnection(), AddNewClient() and |
2837 | 2923 | // RemoveClient() calls for all agents, but this would allow a slow call (e.g. because of slow service | |
2838 | ScenePresence sp = GetScenePresence(client.AgentId); | 2924 | // response in some module listening to AddNewClient()) from holding up unrelated agent calls. |
2839 | 2925 | // | |
2840 | // XXX: Not sure how good it is to add a new client if a scene presence already exists. Possibly this | 2926 | // In practice, the lock (this) in LLUDPServer.AddNewClient() currently lock across all |
2841 | // could occur if a viewer crashes and relogs before the old client is kicked out. But this could cause | 2927 | // AddNewClient() operations (though not other ops). |
2842 | // other problems, and possible the code calling AddNewClient() should ensure that no client is already | 2928 | // In the future this can be relieved once locking per agent (not necessarily on AgentCircuitData) is improved. |
2843 | // connected. | 2929 | lock (aCircuit) |
2844 | if (sp == null) | 2930 | { |
2845 | { | 2931 | vialogin |
2846 | m_log.DebugFormat( | 2932 | = (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0 |
2847 | "[SCENE]: Adding new child scene presence {0} {1} to scene {2} at pos {3}", | 2933 | || (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaLogin) != 0; |
2848 | client.Name, client.AgentId, RegionInfo.RegionName, client.StartPos); | 2934 | |
2849 | 2935 | CheckHeartbeat(); | |
2850 | m_clientManager.Add(client); | 2936 | |
2851 | SubscribeToClientEvents(client); | 2937 | sp = GetScenePresence(client.AgentId); |
2852 | |||
2853 | sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type); | ||
2854 | m_eventManager.TriggerOnNewPresence(sp); | ||
2855 | |||
2856 | sp.TeleportFlags = (TPFlags)aCircuit.teleportFlags; | ||
2857 | 2938 | ||
2858 | // The first agent upon login is a root agent by design. | 2939 | // XXX: Not sure how good it is to add a new client if a scene presence already exists. Possibly this |
2859 | // For this agent we will have to rez the attachments. | 2940 | // could occur if a viewer crashes and relogs before the old client is kicked out. But this could cause |
2860 | // All other AddNewClient calls find aCircuit.child to be true. | 2941 | // other problems, and possible the code calling AddNewClient() should ensure that no client is already |
2861 | if (aCircuit.child == false) | 2942 | // connected. |
2943 | if (sp == null) | ||
2862 | { | 2944 | { |
2863 | // We have to set SP to be a root agent here so that SP.MakeRootAgent() will later not try to | 2945 | m_log.DebugFormat( |
2864 | // start the scripts again (since this is done in RezAttachments()). | 2946 | "[SCENE]: Adding new child scene presence {0} {1} to scene {2} at pos {3}", |
2865 | // XXX: This is convoluted. | 2947 | client.Name, client.AgentId, RegionInfo.RegionName, client.StartPos); |
2866 | sp.IsChildAgent = false; | 2948 | |
2867 | 2949 | m_clientManager.Add(client); | |
2868 | if (AttachmentsModule != null) | 2950 | SubscribeToClientEvents(client); |
2869 | Util.FireAndForget(delegate(object o) { AttachmentsModule.RezAttachments(sp); }); | 2951 | |
2952 | sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type); | ||
2953 | m_eventManager.TriggerOnNewPresence(sp); | ||
2954 | |||
2955 | sp.TeleportFlags = (TPFlags)aCircuit.teleportFlags; | ||
2956 | |||
2957 | // The first agent upon login is a root agent by design. | ||
2958 | // For this agent we will have to rez the attachments. | ||
2959 | // All other AddNewClient calls find aCircuit.child to be true. | ||
2960 | if (aCircuit.child == false) | ||
2961 | { | ||
2962 | // We have to set SP to be a root agent here so that SP.MakeRootAgent() will later not try to | ||
2963 | // start the scripts again (since this is done in RezAttachments()). | ||
2964 | // XXX: This is convoluted. | ||
2965 | sp.IsChildAgent = false; | ||
2966 | |||
2967 | if (AttachmentsModule != null) | ||
2968 | Util.FireAndForget(delegate(object o) { AttachmentsModule.RezAttachments(sp); }); | ||
2969 | } | ||
2870 | } | 2970 | } |
2871 | } | 2971 | else |
2872 | else | 2972 | { |
2873 | { | 2973 | m_log.WarnFormat( |
2874 | m_log.WarnFormat( | 2974 | "[SCENE]: Already found {0} scene presence for {1} in {2} when asked to add new scene presence", |
2875 | "[SCENE]: Already found {0} scene presence for {1} in {2} when asked to add new scene presence", | 2975 | sp.IsChildAgent ? "child" : "root", sp.Name, RegionInfo.RegionName); |
2876 | sp.IsChildAgent ? "child" : "root", sp.Name, RegionInfo.RegionName); | 2976 | } |
2877 | } | 2977 | |
2978 | // We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the | ||
2979 | // client is for a root or child agent. | ||
2980 | client.SceneAgent = sp; | ||
2878 | 2981 | ||
2879 | // We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the | 2982 | // Cache the user's name |
2880 | // client is for a root or child agent. | 2983 | CacheUserName(sp, aCircuit); |
2881 | client.SceneAgent = sp; | 2984 | |
2985 | EventManager.TriggerOnNewClient(client); | ||
2986 | if (vialogin) | ||
2987 | EventManager.TriggerOnClientLogin(client); | ||
2988 | } | ||
2882 | 2989 | ||
2883 | m_LastLogin = Util.EnvironmentTickCount(); | 2990 | m_LastLogin = Util.EnvironmentTickCount(); |
2884 | 2991 | ||
2885 | // Cache the user's name | ||
2886 | CacheUserName(sp, aCircuit); | ||
2887 | |||
2888 | EventManager.TriggerOnNewClient(client); | ||
2889 | if (vialogin) | ||
2890 | { | ||
2891 | EventManager.TriggerOnClientLogin(client); | ||
2892 | // Send initial parcel data | ||
2893 | /* this is done on TriggerOnNewClient by landmanegement respective event handler | ||
2894 | Vector3 pos = sp.AbsolutePosition; | ||
2895 | ILandObject land = LandChannel.GetLandObject(pos.X, pos.Y); | ||
2896 | land.SendLandUpdateToClient(client); | ||
2897 | */ | ||
2898 | } | ||
2899 | |||
2900 | return sp; | 2992 | return sp; |
2901 | } | 2993 | } |
2902 | 2994 | ||
@@ -3436,110 +3528,130 @@ namespace OpenSim.Region.Framework.Scenes | |||
3436 | { | 3528 | { |
3437 | // CheckHeartbeat(); | 3529 | // CheckHeartbeat(); |
3438 | bool isChildAgent = false; | 3530 | bool isChildAgent = false; |
3439 | ScenePresence avatar = GetScenePresence(agentID); | 3531 | AgentCircuitData acd; |
3440 | |||
3441 | if (avatar == null) | ||
3442 | { | ||
3443 | m_log.WarnFormat( | ||
3444 | "[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in the scene.", agentID); | ||
3445 | |||
3446 | return; | ||
3447 | } | ||
3448 | 3532 | ||
3449 | try | 3533 | lock (m_removeClientLock) |
3450 | { | 3534 | { |
3451 | isChildAgent = avatar.IsChildAgent; | 3535 | acd = m_authenticateHandler.GetAgentCircuitData(agentID); |
3452 | |||
3453 | m_log.DebugFormat( | ||
3454 | "[SCENE]: Removing {0} agent {1} {2} from {3}", | ||
3455 | (isChildAgent ? "child" : "root"), avatar.Name, agentID, RegionInfo.RegionName); | ||
3456 | 3536 | ||
3457 | // Don't do this to root agents, it's not nice for the viewer | 3537 | if (acd == null) |
3458 | if (closeChildAgents && isChildAgent) | ||
3459 | { | 3538 | { |
3460 | // Tell a single agent to disconnect from the region. | 3539 | m_log.ErrorFormat("[SCENE]: No agent circuit found for {0}, aborting Scene.RemoveClient", agentID); |
3461 | IEventQueue eq = RequestModuleInterface<IEventQueue>(); | 3540 | return; |
3462 | if (eq != null) | ||
3463 | { | ||
3464 | eq.DisableSimulator(RegionInfo.RegionHandle, avatar.UUID); | ||
3465 | } | ||
3466 | else | ||
3467 | { | ||
3468 | avatar.ControllingClient.SendShutdownConnectionNotice(); | ||
3469 | } | ||
3470 | } | 3541 | } |
3471 | 3542 | else | |
3472 | // Only applies to root agents. | ||
3473 | if (avatar.ParentID != 0) | ||
3474 | { | 3543 | { |
3475 | avatar.StandUp(); | 3544 | // We remove the acd up here to avoid later raec conditions if two RemoveClient() calls occurred |
3545 | // simultaneously. | ||
3546 | m_authenticateHandler.RemoveCircuit(acd.circuitcode); | ||
3476 | } | 3547 | } |
3548 | } | ||
3477 | 3549 | ||
3478 | m_sceneGraph.removeUserCount(!isChildAgent); | 3550 | lock (acd) |
3479 | 3551 | { | |
3480 | // TODO: We shouldn't use closeChildAgents here - it's being used by the NPC module to stop | 3552 | ScenePresence avatar = GetScenePresence(agentID); |
3481 | // unnecessary operations. This should go away once NPCs have no accompanying IClientAPI | 3553 | |
3482 | if (closeChildAgents && CapsModule != null) | 3554 | if (avatar == null) |
3483 | CapsModule.RemoveCaps(agentID); | ||
3484 | |||
3485 | // REFACTORING PROBLEM -- well not really a problem, but just to point out that whatever | ||
3486 | // this method is doing is HORRIBLE!!! | ||
3487 | avatar.Scene.NeedSceneCacheClear(avatar.UUID); | ||
3488 | |||
3489 | if (closeChildAgents && !isChildAgent) | ||
3490 | { | 3555 | { |
3491 | List<ulong> regions = avatar.KnownRegionHandles; | 3556 | m_log.WarnFormat( |
3492 | regions.Remove(RegionInfo.RegionHandle); | 3557 | "[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in the scene.", agentID); |
3493 | m_sceneGridService.SendCloseChildAgentConnections(agentID, regions); | 3558 | |
3559 | return; | ||
3494 | } | 3560 | } |
3495 | 3561 | ||
3496 | m_eventManager.TriggerClientClosed(agentID, this); | 3562 | try |
3497 | m_eventManager.TriggerOnRemovePresence(agentID); | ||
3498 | |||
3499 | if (!isChildAgent) | ||
3500 | { | 3563 | { |
3501 | if (AttachmentsModule != null) | 3564 | isChildAgent = avatar.IsChildAgent; |
3565 | |||
3566 | m_log.DebugFormat( | ||
3567 | "[SCENE]: Removing {0} agent {1} {2} from {3}", | ||
3568 | (isChildAgent ? "child" : "root"), avatar.Name, agentID, RegionInfo.RegionName); | ||
3569 | |||
3570 | // Don't do this to root agents, it's not nice for the viewer | ||
3571 | if (closeChildAgents && isChildAgent) | ||
3502 | { | 3572 | { |
3503 | AttachmentsModule.DeRezAttachments(avatar); | 3573 | // Tell a single agent to disconnect from the region. |
3574 | IEventQueue eq = RequestModuleInterface<IEventQueue>(); | ||
3575 | if (eq != null) | ||
3576 | { | ||
3577 | eq.DisableSimulator(RegionInfo.RegionHandle, avatar.UUID); | ||
3578 | } | ||
3579 | else | ||
3580 | { | ||
3581 | avatar.ControllingClient.SendShutdownConnectionNotice(); | ||
3582 | } | ||
3504 | } | 3583 | } |
3505 | 3584 | ||
3506 | ForEachClient( | 3585 | // Only applies to root agents. |
3507 | delegate(IClientAPI client) | 3586 | if (avatar.ParentID != 0) |
3587 | { | ||
3588 | avatar.StandUp(); | ||
3589 | } | ||
3590 | |||
3591 | m_sceneGraph.removeUserCount(!isChildAgent); | ||
3592 | |||
3593 | // TODO: We shouldn't use closeChildAgents here - it's being used by the NPC module to stop | ||
3594 | // unnecessary operations. This should go away once NPCs have no accompanying IClientAPI | ||
3595 | if (closeChildAgents && CapsModule != null) | ||
3596 | CapsModule.RemoveCaps(agentID); | ||
3597 | |||
3598 | // REFACTORING PROBLEM -- well not really a problem, but just to point out that whatever | ||
3599 | // this method is doing is HORRIBLE!!! | ||
3600 | avatar.Scene.NeedSceneCacheClear(avatar.UUID); | ||
3601 | |||
3602 | if (closeChildAgents && !isChildAgent) | ||
3603 | { | ||
3604 | List<ulong> regions = avatar.KnownRegionHandles; | ||
3605 | regions.Remove(RegionInfo.RegionHandle); | ||
3606 | m_sceneGridService.SendCloseChildAgentConnections(agentID, regions); | ||
3607 | } | ||
3608 | |||
3609 | m_eventManager.TriggerClientClosed(agentID, this); | ||
3610 | m_eventManager.TriggerOnRemovePresence(agentID); | ||
3611 | |||
3612 | if (!isChildAgent) | ||
3613 | { | ||
3614 | if (AttachmentsModule != null) | ||
3508 | { | 3615 | { |
3509 | //We can safely ignore null reference exceptions. It means the avatar is dead and cleaned up anyway | 3616 | AttachmentsModule.DeRezAttachments(avatar); |
3510 | try { client.SendKillObject(avatar.RegionHandle, new List<uint> { avatar.LocalId }); } | 3617 | } |
3511 | catch (NullReferenceException) { } | ||
3512 | }); | ||
3513 | } | ||
3514 | |||
3515 | // It's possible for child agents to have transactions if changes are being made cross-border. | ||
3516 | if (AgentTransactionsModule != null) | ||
3517 | AgentTransactionsModule.RemoveAgentAssetTransactions(agentID); | ||
3518 | 3618 | ||
3519 | m_authenticateHandler.RemoveCircuit(avatar.ControllingClient.CircuitCode); | 3619 | ForEachClient( |
3520 | m_log.Debug("[Scene] The avatar has left the building"); | 3620 | delegate(IClientAPI client) |
3521 | } | 3621 | { |
3522 | catch (Exception e) | 3622 | //We can safely ignore null reference exceptions. It means the avatar is dead and cleaned up anyway |
3523 | { | 3623 | try { client.SendKillObject(avatar.RegionHandle, new List<uint> { avatar.LocalId }); } |
3524 | m_log.Error( | 3624 | catch (NullReferenceException) { } |
3525 | string.Format("[SCENE]: Exception removing {0} from {1}. Cleaning up. Exception ", avatar.Name, Name), e); | 3625 | }); |
3526 | } | 3626 | } |
3527 | finally | ||
3528 | { | ||
3529 | try | ||
3530 | { | ||
3531 | // Always clean these structures up so that any failure above doesn't cause them to remain in the | ||
3532 | // scene with possibly bad effects (e.g. continually timing out on unacked packets and triggering | ||
3533 | // the same cleanup exception continually. | ||
3534 | m_sceneGraph.RemoveScenePresence(agentID); | ||
3535 | m_clientManager.Remove(agentID); | ||
3536 | 3627 | ||
3537 | avatar.Close(); | 3628 | // It's possible for child agents to have transactions if changes are being made cross-border. |
3629 | if (AgentTransactionsModule != null) | ||
3630 | AgentTransactionsModule.RemoveAgentAssetTransactions(agentID); | ||
3631 | m_log.Debug("[Scene] The avatar has left the building"); | ||
3538 | } | 3632 | } |
3539 | catch (Exception e) | 3633 | catch (Exception e) |
3540 | { | 3634 | { |
3541 | m_log.Error( | 3635 | m_log.Error( |
3542 | string.Format("[SCENE]: Exception in final clean up of {0} in {1}. Exception ", avatar.Name, Name), e); | 3636 | string.Format("[SCENE]: Exception removing {0} from {1}. Cleaning up. Exception ", avatar.Name, Name), e); |
3637 | } | ||
3638 | finally | ||
3639 | { | ||
3640 | try | ||
3641 | { | ||
3642 | // Always clean these structures up so that any failure above doesn't cause them to remain in the | ||
3643 | // scene with possibly bad effects (e.g. continually timing out on unacked packets and triggering | ||
3644 | // the same cleanup exception continually. | ||
3645 | m_sceneGraph.RemoveScenePresence(agentID); | ||
3646 | m_clientManager.Remove(agentID); | ||
3647 | |||
3648 | avatar.Close(); | ||
3649 | } | ||
3650 | catch (Exception e) | ||
3651 | { | ||
3652 | m_log.Error( | ||
3653 | string.Format("[SCENE]: Exception in final clean up of {0} in {1}. Exception ", avatar.Name, Name), e); | ||
3654 | } | ||
3543 | } | 3655 | } |
3544 | } | 3656 | } |
3545 | 3657 | ||
@@ -3598,11 +3710,9 @@ namespace OpenSim.Region.Framework.Scenes | |||
3598 | 3710 | ||
3599 | /// <summary> | 3711 | /// <summary> |
3600 | /// Do the work necessary to initiate a new user connection for a particular scene. | 3712 | /// Do the work necessary to initiate a new user connection for a particular scene. |
3601 | /// At the moment, this consists of setting up the caps infrastructure | ||
3602 | /// The return bool should allow for connections to be refused, but as not all calling paths | ||
3603 | /// take proper notice of it let, we allowed banned users in still. | ||
3604 | /// </summary> | 3713 | /// </summary> |
3605 | /// <param name="agent">CircuitData of the agent who is connecting</param> | 3714 | /// <param name="agent">CircuitData of the agent who is connecting</param> |
3715 | /// <param name="teleportFlags"></param> | ||
3606 | /// <param name="reason">Outputs the reason for the false response on this string</param> | 3716 | /// <param name="reason">Outputs the reason for the false response on this string</param> |
3607 | /// <returns>True if the region accepts this agent. False if it does not. False will | 3717 | /// <returns>True if the region accepts this agent. False if it does not. False will |
3608 | /// also return a reason.</returns> | 3718 | /// also return a reason.</returns> |
@@ -3613,10 +3723,20 @@ namespace OpenSim.Region.Framework.Scenes | |||
3613 | 3723 | ||
3614 | /// <summary> | 3724 | /// <summary> |
3615 | /// Do the work necessary to initiate a new user connection for a particular scene. | 3725 | /// Do the work necessary to initiate a new user connection for a particular scene. |
3616 | /// At the moment, this consists of setting up the caps infrastructure | 3726 | /// </summary> |
3727 | /// <remarks> | ||
3728 | /// The return bool should allow for connections to be refused, but as not all calling paths | ||
3729 | /// take proper notice of it yet, we still allowed banned users in. | ||
3730 | /// | ||
3731 | /// At the moment this method consists of setting up the caps infrastructure | ||
3617 | /// The return bool should allow for connections to be refused, but as not all calling paths | 3732 | /// The return bool should allow for connections to be refused, but as not all calling paths |
3618 | /// take proper notice of it let, we allowed banned users in still. | 3733 | /// take proper notice of it let, we allowed banned users in still. |
3619 | /// </summary> | 3734 | /// |
3735 | /// This method is called by the login service (in the case of login) or another simulator (in the case of region | ||
3736 | /// cross or teleport) to initiate the connection. It is not triggered by the viewer itself - the connection | ||
3737 | /// is activated later when the viewer sends the initial UseCircuitCodePacket UDP packet (in the case of | ||
3738 | /// the LLUDP stack). | ||
3739 | /// </remarks> | ||
3620 | /// <param name="agent">CircuitData of the agent who is connecting</param> | 3740 | /// <param name="agent">CircuitData of the agent who is connecting</param> |
3621 | /// <param name="reason">Outputs the reason for the false response on this string</param> | 3741 | /// <param name="reason">Outputs the reason for the false response on this string</param> |
3622 | /// <param name="requirePresenceLookup">True for normal presence. False for NPC | 3742 | /// <param name="requirePresenceLookup">True for normal presence. False for NPC |
@@ -3715,83 +3835,86 @@ namespace OpenSim.Region.Framework.Scenes | |||
3715 | "[SCENE]: Existing root scene presence detected for {0} {1} in {2} when connecting. Removing existing presence.", | 3835 | "[SCENE]: Existing root scene presence detected for {0} {1} in {2} when connecting. Removing existing presence.", |
3716 | sp.Name, sp.UUID, RegionInfo.RegionName); | 3836 | sp.Name, sp.UUID, RegionInfo.RegionName); |
3717 | 3837 | ||
3718 | sp.ControllingClient.Close(); | 3838 | sp.ControllingClient.Close(true, true); |
3719 | sp = null; | 3839 | sp = null; |
3720 | } | 3840 | } |
3721 | 3841 | ||
3722 | 3842 | lock (agent) | |
3723 | //On login test land permisions | ||
3724 | if (vialogin) | ||
3725 | { | 3843 | { |
3726 | IUserAccountCacheModule cache = RequestModuleInterface<IUserAccountCacheModule>(); | 3844 | //On login test land permisions |
3727 | if (cache != null) | 3845 | if (vialogin) |
3728 | cache.Remove(agent.firstname + " " + agent.lastname); | ||
3729 | if (!TestLandRestrictions(agent.AgentID, out reason, ref agent.startpos.X, ref agent.startpos.Y)) | ||
3730 | { | 3846 | { |
3731 | m_log.DebugFormat("[CONNECTION BEGIN]: Denying access to {0} due to no land access", agent.AgentID.ToString()); | 3847 | IUserAccountCacheModule cache = RequestModuleInterface<IUserAccountCacheModule>(); |
3732 | return false; | 3848 | if (cache != null) |
3849 | cache.Remove(agent.firstname + " " + agent.lastname); | ||
3850 | if (!TestLandRestrictions(agent.AgentID, out reason, ref agent.startpos.X, ref agent.startpos.Y)) | ||
3851 | { | ||
3852 | m_log.DebugFormat("[CONNECTION BEGIN]: Denying access to {0} due to no land access", agent.AgentID.ToString()); | ||
3853 | return false; | ||
3854 | } | ||
3733 | } | 3855 | } |
3734 | } | ||
3735 | 3856 | ||
3736 | if (sp == null) // We don't have an [child] agent here already | 3857 | if (sp == null) // We don't have an [child] agent here already |
3737 | { | ||
3738 | if (requirePresenceLookup) | ||
3739 | { | 3858 | { |
3740 | try | 3859 | if (requirePresenceLookup) |
3741 | { | 3860 | { |
3742 | if (!VerifyUserPresence(agent, out reason)) | 3861 | try |
3862 | { | ||
3863 | if (!VerifyUserPresence(agent, out reason)) | ||
3864 | return false; | ||
3865 | } catch (Exception e) | ||
3866 | { | ||
3867 | m_log.ErrorFormat( | ||
3868 | "[SCENE]: Exception verifying presence {0}{1}", e.Message, e.StackTrace); | ||
3743 | return false; | 3869 | return false; |
3744 | } catch (Exception e) | 3870 | } |
3871 | } | ||
3872 | |||
3873 | try | ||
3874 | { | ||
3875 | // Always check estate if this is a login. Always | ||
3876 | // check if banned regions are to be blacked out. | ||
3877 | if (vialogin || (!m_seeIntoBannedRegion)) | ||
3878 | { | ||
3879 | if (!AuthorizeUser(agent, out reason)) | ||
3880 | return false; | ||
3881 | } | ||
3882 | } | ||
3883 | catch (Exception e) | ||
3745 | { | 3884 | { |
3746 | m_log.ErrorFormat( | 3885 | m_log.ErrorFormat( |
3747 | "[SCENE]: Exception verifying presence {0}{1}", e.Message, e.StackTrace); | 3886 | "[SCENE]: Exception authorizing user {0}{1}", e.Message, e.StackTrace); |
3748 | return false; | 3887 | return false; |
3749 | } | 3888 | } |
3750 | } | ||
3751 | 3889 | ||
3752 | try | 3890 | m_log.InfoFormat( |
3753 | { | 3891 | "[SCENE]: Region {0} authenticated and authorized incoming {1} agent {2} {3} {4} (circuit code {5})", |
3754 | // Always check estate if this is a login. Always | 3892 | RegionInfo.RegionName, (agent.child ? "child" : "root"), agent.firstname, agent.lastname, |
3755 | // check if banned regions are to be blacked out. | 3893 | agent.AgentID, agent.circuitcode); |
3756 | if (vialogin || (!m_seeIntoBannedRegion)) | 3894 | |
3895 | if (CapsModule != null) | ||
3757 | { | 3896 | { |
3758 | if (!AuthorizeUser(agent, out reason)) | 3897 | CapsModule.SetAgentCapsSeeds(agent); |
3759 | return false; | 3898 | CapsModule.CreateCaps(agent.AgentID); |
3760 | } | 3899 | } |
3761 | } | 3900 | } |
3762 | catch (Exception e) | 3901 | else |
3763 | { | 3902 | { |
3764 | m_log.ErrorFormat( | 3903 | // Let the SP know how we got here. This has a lot of interesting |
3765 | "[SCENE]: Exception authorizing user {0}{1}", e.Message, e.StackTrace); | 3904 | // uses down the line. |
3766 | return false; | 3905 | sp.TeleportFlags = (TPFlags)teleportFlags; |
3767 | } | ||
3768 | |||
3769 | m_log.InfoFormat( | ||
3770 | "[SCENE]: Region {0} authenticated and authorized incoming {1} agent {2} {3} {4} (circuit code {5})", | ||
3771 | RegionInfo.RegionName, (agent.child ? "child" : "root"), agent.firstname, agent.lastname, | ||
3772 | agent.AgentID, agent.circuitcode); | ||
3773 | 3906 | ||
3774 | if (CapsModule != null) | 3907 | if (sp.IsChildAgent) |
3775 | { | 3908 | { |
3776 | CapsModule.SetAgentCapsSeeds(agent); | 3909 | m_log.DebugFormat( |
3777 | CapsModule.CreateCaps(agent.AgentID); | 3910 | "[SCENE]: Adjusting known seeds for existing agent {0} in {1}", |
3778 | } | 3911 | agent.AgentID, RegionInfo.RegionName); |
3779 | } else | ||
3780 | { | ||
3781 | // Let the SP know how we got here. This has a lot of interesting | ||
3782 | // uses down the line. | ||
3783 | sp.TeleportFlags = (TPFlags)teleportFlags; | ||
3784 | 3912 | ||
3785 | if (sp.IsChildAgent) | 3913 | sp.AdjustKnownSeeds(); |
3786 | { | ||
3787 | m_log.DebugFormat( | ||
3788 | "[SCENE]: Adjusting known seeds for existing agent {0} in {1}", | ||
3789 | agent.AgentID, RegionInfo.RegionName); | ||
3790 | 3914 | ||
3791 | sp.AdjustKnownSeeds(); | 3915 | if (CapsModule != null) |
3792 | 3916 | CapsModule.SetAgentCapsSeeds(agent); | |
3793 | if (CapsModule != null) | 3917 | } |
3794 | CapsModule.SetAgentCapsSeeds(agent); | ||
3795 | } | 3918 | } |
3796 | } | 3919 | } |
3797 | 3920 | ||
@@ -4222,8 +4345,9 @@ namespace OpenSim.Region.Framework.Scenes | |||
4222 | return false; | 4345 | return false; |
4223 | } | 4346 | } |
4224 | 4347 | ||
4225 | // We have to wait until the viewer contacts this region after receiving EAC. | 4348 | // We have to wait until the viewer contacts this region |
4226 | // That calls AddNewClient, which finally creates the ScenePresence | 4349 | // after receiving the EnableSimulator HTTP Event Queue message. This triggers the viewer to send |
4350 | // a UseCircuitCode packet which in turn calls AddNewClient which finally creates the ScenePresence. | ||
4227 | ScenePresence childAgentUpdate = WaitGetScenePresence(cAgentData.AgentID); | 4351 | ScenePresence childAgentUpdate = WaitGetScenePresence(cAgentData.AgentID); |
4228 | 4352 | ||
4229 | if (childAgentUpdate != null) | 4353 | if (childAgentUpdate != null) |
@@ -4318,15 +4442,18 @@ namespace OpenSim.Region.Framework.Scenes | |||
4318 | /// Tell a single agent to disconnect from the region. | 4442 | /// Tell a single agent to disconnect from the region. |
4319 | /// </summary> | 4443 | /// </summary> |
4320 | /// <param name="agentID"></param> | 4444 | /// <param name="agentID"></param> |
4321 | /// <param name="childOnly"></param> | 4445 | /// <param name="force"> |
4322 | public bool IncomingCloseAgent(UUID agentID, bool childOnly) | 4446 | /// Force the agent to close even if it might be in the middle of some other operation. You do not want to |
4447 | /// force unless you are absolutely sure that the agent is dead and a normal close is not working. | ||
4448 | /// </param> | ||
4449 | public bool IncomingCloseAgent(UUID agentID, bool force) | ||
4323 | { | 4450 | { |
4324 | //m_log.DebugFormat("[SCENE]: Processing incoming close agent for {0}", agentID); | 4451 | //m_log.DebugFormat("[SCENE]: Processing incoming close agent for {0}", agentID); |
4325 | 4452 | ||
4326 | ScenePresence presence = m_sceneGraph.GetScenePresence(agentID); | 4453 | ScenePresence presence = m_sceneGraph.GetScenePresence(agentID); |
4327 | if (presence != null) | 4454 | if (presence != null) |
4328 | { | 4455 | { |
4329 | presence.ControllingClient.Close(false); | 4456 | presence.ControllingClient.Close(force, force); |
4330 | return true; | 4457 | return true; |
4331 | } | 4458 | } |
4332 | 4459 | ||
@@ -4532,6 +4659,16 @@ namespace OpenSim.Region.Framework.Scenes | |||
4532 | return LandChannel.GetLandObject(x, y).LandData; | 4659 | return LandChannel.GetLandObject(x, y).LandData; |
4533 | } | 4660 | } |
4534 | 4661 | ||
4662 | /// <summary> | ||
4663 | /// Get LandData by position. | ||
4664 | /// </summary> | ||
4665 | /// <param name="pos"></param> | ||
4666 | /// <returns></returns> | ||
4667 | public LandData GetLandData(Vector3 pos) | ||
4668 | { | ||
4669 | return GetLandData(pos.X, pos.Y); | ||
4670 | } | ||
4671 | |||
4535 | public LandData GetLandData(uint x, uint y) | 4672 | public LandData GetLandData(uint x, uint y) |
4536 | { | 4673 | { |
4537 | m_log.DebugFormat("[SCENE]: returning land for {0},{1}", x, y); | 4674 | m_log.DebugFormat("[SCENE]: returning land for {0},{1}", x, y); |
@@ -4780,6 +4917,18 @@ namespace OpenSim.Region.Framework.Scenes | |||
4780 | } | 4917 | } |
4781 | 4918 | ||
4782 | /// <summary> | 4919 | /// <summary> |
4920 | /// Attempt to get the SOG via its UUID | ||
4921 | /// </summary> | ||
4922 | /// <param name="fullID"></param> | ||
4923 | /// <param name="sog"></param> | ||
4924 | /// <returns></returns> | ||
4925 | public bool TryGetSceneObjectGroup(UUID fullID, out SceneObjectGroup sog) | ||
4926 | { | ||
4927 | sog = GetSceneObjectGroup(fullID); | ||
4928 | return sog != null; | ||
4929 | } | ||
4930 | |||
4931 | /// <summary> | ||
4783 | /// Get a prim by name from the scene (will return the first | 4932 | /// Get a prim by name from the scene (will return the first |
4784 | /// found, if there are more than one prim with the same name) | 4933 | /// found, if there are more than one prim with the same name) |
4785 | /// </summary> | 4934 | /// </summary> |
@@ -4811,6 +4960,18 @@ namespace OpenSim.Region.Framework.Scenes | |||
4811 | } | 4960 | } |
4812 | 4961 | ||
4813 | /// <summary> | 4962 | /// <summary> |
4963 | /// Attempt to get a prim via its UUID | ||
4964 | /// </summary> | ||
4965 | /// <param name="fullID"></param> | ||
4966 | /// <param name="sop"></param> | ||
4967 | /// <returns></returns> | ||
4968 | public bool TryGetSceneObjectPart(UUID fullID, out SceneObjectPart sop) | ||
4969 | { | ||
4970 | sop = GetSceneObjectPart(fullID); | ||
4971 | return sop != null; | ||
4972 | } | ||
4973 | |||
4974 | /// <summary> | ||
4814 | /// Get a scene object group that contains the prim with the given local id | 4975 | /// Get a scene object group that contains the prim with the given local id |
4815 | /// </summary> | 4976 | /// </summary> |
4816 | /// <param name="localID"></param> | 4977 | /// <param name="localID"></param> |
@@ -5865,6 +6026,9 @@ Environment.Exit(1); | |||
5865 | 6026 | ||
5866 | public string GetExtraSetting(string name) | 6027 | public string GetExtraSetting(string name) |
5867 | { | 6028 | { |
6029 | if (m_extraSettings == null) | ||
6030 | return String.Empty; | ||
6031 | |||
5868 | string val; | 6032 | string val; |
5869 | 6033 | ||
5870 | if (!m_extraSettings.TryGetValue(name, out val)) | 6034 | if (!m_extraSettings.TryGetValue(name, out val)) |
@@ -5875,6 +6039,9 @@ Environment.Exit(1); | |||
5875 | 6039 | ||
5876 | public void StoreExtraSetting(string name, string val) | 6040 | public void StoreExtraSetting(string name, string val) |
5877 | { | 6041 | { |
6042 | if (m_extraSettings == null) | ||
6043 | return; | ||
6044 | |||
5878 | string oldVal; | 6045 | string oldVal; |
5879 | 6046 | ||
5880 | if (m_extraSettings.TryGetValue(name, out oldVal)) | 6047 | if (m_extraSettings.TryGetValue(name, out oldVal)) |
@@ -5892,6 +6059,9 @@ Environment.Exit(1); | |||
5892 | 6059 | ||
5893 | public void RemoveExtraSetting(string name) | 6060 | public void RemoveExtraSetting(string name) |
5894 | { | 6061 | { |
6062 | if (m_extraSettings == null) | ||
6063 | return; | ||
6064 | |||
5895 | if (!m_extraSettings.ContainsKey(name)) | 6065 | if (!m_extraSettings.ContainsKey(name)) |
5896 | return; | 6066 | return; |
5897 | 6067 | ||