diff options
Diffstat (limited to 'OpenSim/Region/Framework')
-rw-r--r-- | OpenSim/Region/Framework/Interfaces/IWorldComm.cs | 26 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs | 2 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs | 22 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Scenes/Scene.cs | 57 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Scenes/SceneGraph.cs | 24 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Scenes/ScenePresence.cs | 14 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Scenes/Tests/SceneTests.cs | 17 |
7 files changed, 132 insertions, 30 deletions
diff --git a/OpenSim/Region/Framework/Interfaces/IWorldComm.cs b/OpenSim/Region/Framework/Interfaces/IWorldComm.cs index e8e375e..20e0199 100644 --- a/OpenSim/Region/Framework/Interfaces/IWorldComm.cs +++ b/OpenSim/Region/Framework/Interfaces/IWorldComm.cs | |||
@@ -45,6 +45,13 @@ namespace OpenSim.Region.Framework.Interfaces | |||
45 | void Deactivate(); | 45 | void Deactivate(); |
46 | void Activate(); | 46 | void Activate(); |
47 | UUID GetID(); | 47 | UUID GetID(); |
48 | |||
49 | /// <summary> | ||
50 | /// Bitfield indicating which strings should be processed as regex. | ||
51 | /// 1 corresponds to IWorldCommListenerInfo::GetName() | ||
52 | /// 2 corresponds to IWorldCommListenerInfo::GetMessage() | ||
53 | /// </summary> | ||
54 | int RegexBitfield { get; } | ||
48 | } | 55 | } |
49 | 56 | ||
50 | public interface IWorldComm | 57 | public interface IWorldComm |
@@ -60,7 +67,7 @@ namespace OpenSim.Region.Framework.Interfaces | |||
60 | /// the script during 'peek' time. Parameter hostID is needed to | 67 | /// the script during 'peek' time. Parameter hostID is needed to |
61 | /// determine the position of the script. | 68 | /// determine the position of the script. |
62 | /// </summary> | 69 | /// </summary> |
63 | /// <param name="localID">localID of the script engine</param> | 70 | /// <param name="LocalID">localID of the script engine</param> |
64 | /// <param name="itemID">UUID of the script engine</param> | 71 | /// <param name="itemID">UUID of the script engine</param> |
65 | /// <param name="hostID">UUID of the SceneObjectPart</param> | 72 | /// <param name="hostID">UUID of the SceneObjectPart</param> |
66 | /// <param name="channel">channel to listen on</param> | 73 | /// <param name="channel">channel to listen on</param> |
@@ -70,6 +77,23 @@ namespace OpenSim.Region.Framework.Interfaces | |||
70 | /// <returns>number of the scripts handle</returns> | 77 | /// <returns>number of the scripts handle</returns> |
71 | int Listen(uint LocalID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg); | 78 | int Listen(uint LocalID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg); |
72 | 79 | ||
80 | /// <summary> | ||
81 | /// Create a listen event callback with the specified filters. | ||
82 | /// The parameters localID,itemID are needed to uniquely identify | ||
83 | /// the script during 'peek' time. Parameter hostID is needed to | ||
84 | /// determine the position of the script. | ||
85 | /// </summary> | ||
86 | /// <param name="LocalID">localID of the script engine</param> | ||
87 | /// <param name="itemID">UUID of the script engine</param> | ||
88 | /// <param name="hostID">UUID of the SceneObjectPart</param> | ||
89 | /// <param name="channel">channel to listen on</param> | ||
90 | /// <param name="name">name to filter on</param> | ||
91 | /// <param name="id">key to filter on (user given, could be totally faked)</param> | ||
92 | /// <param name="msg">msg to filter on</param> | ||
93 | /// <param name="regexBitfield">Bitfield indicating which strings should be processed as regex.</param> | ||
94 | /// <returns>number of the scripts handle</returns> | ||
95 | int Listen(uint LocalID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg, int regexBitfield); | ||
96 | |||
73 | /// <summary> | 97 | /// <summary> |
74 | /// This method scans over the objects which registered an interest in listen callbacks. | 98 | /// This method scans over the objects which registered an interest in listen callbacks. |
75 | /// For everyone it finds, it checks if it fits the given filter. If it does, then | 99 | /// For everyone it finds, it checks if it fits the given filter. If it does, then |
diff --git a/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs b/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs index ad421ee..9ed4f47 100644 --- a/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs +++ b/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs | |||
@@ -87,7 +87,7 @@ namespace OpenSim.Region.Framework.Scenes.Animation | |||
87 | { | 87 | { |
88 | if (m_defaultAnimation.AnimID == animID) | 88 | if (m_defaultAnimation.AnimID == animID) |
89 | { | 89 | { |
90 | m_defaultAnimation = new OpenSim.Framework.Animation(UUID.Zero, 1, UUID.Zero); | 90 | m_defaultAnimation = new OpenSim.Framework.Animation(animID, 1, UUID.Zero); |
91 | } | 91 | } |
92 | else if (HasAnimation(animID)) | 92 | else if (HasAnimation(animID)) |
93 | { | 93 | { |
diff --git a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs index 16c0d25..ce6415a 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs | |||
@@ -38,8 +38,20 @@ namespace OpenSim.Region.Framework.Scenes | |||
38 | { | 38 | { |
39 | public partial class Scene | 39 | public partial class Scene |
40 | { | 40 | { |
41 | /// <summary> | ||
42 | /// Send chat to listeners. | ||
43 | /// </summary> | ||
44 | /// <param name='message'></param> | ||
45 | /// <param name='type'>/param> | ||
46 | /// <param name='channel'></param> | ||
47 | /// <param name='fromPos'></param> | ||
48 | /// <param name='fromName'></param> | ||
49 | /// <param name='fromID'></param> | ||
50 | /// <param name='targetID'></param> | ||
51 | /// <param name='fromAgent'></param> | ||
52 | /// <param name='broadcast'></param> | ||
41 | public void SimChat(byte[] message, ChatTypeEnum type, int channel, Vector3 fromPos, string fromName, | 53 | public void SimChat(byte[] message, ChatTypeEnum type, int channel, Vector3 fromPos, string fromName, |
42 | UUID fromID, bool fromAgent, bool broadcast, UUID destination) | 54 | UUID fromID, UUID targetID, bool fromAgent, bool broadcast) |
43 | { | 55 | { |
44 | OSChatMessage args = new OSChatMessage(); | 56 | OSChatMessage args = new OSChatMessage(); |
45 | 57 | ||
@@ -49,7 +61,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
49 | args.Position = fromPos; | 61 | args.Position = fromPos; |
50 | args.SenderUUID = fromID; | 62 | args.SenderUUID = fromID; |
51 | args.Scene = this; | 63 | args.Scene = this; |
52 | args.Destination = destination; | 64 | args.Destination = targetID; |
53 | 65 | ||
54 | if (fromAgent) | 66 | if (fromAgent) |
55 | { | 67 | { |
@@ -66,6 +78,10 @@ namespace OpenSim.Region.Framework.Scenes | |||
66 | args.From = fromName; | 78 | args.From = fromName; |
67 | //args. | 79 | //args. |
68 | 80 | ||
81 | // m_log.DebugFormat( | ||
82 | // "[SCENE]: Sending message {0} on channel {1}, type {2} from {3}, broadcast {4}", | ||
83 | // args.Message.Replace("\n", "\\n"), args.Channel, args.Type, fromName, broadcast); | ||
84 | |||
69 | if (broadcast) | 85 | if (broadcast) |
70 | EventManager.TriggerOnChatBroadcast(this, args); | 86 | EventManager.TriggerOnChatBroadcast(this, args); |
71 | else | 87 | else |
@@ -75,7 +91,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
75 | protected void SimChat(byte[] message, ChatTypeEnum type, int channel, Vector3 fromPos, string fromName, | 91 | protected void SimChat(byte[] message, ChatTypeEnum type, int channel, Vector3 fromPos, string fromName, |
76 | UUID fromID, bool fromAgent, bool broadcast) | 92 | UUID fromID, bool fromAgent, bool broadcast) |
77 | { | 93 | { |
78 | SimChat(message, type, channel, fromPos, fromName, fromID, fromAgent, broadcast, UUID.Zero); | 94 | SimChat(message, type, channel, fromPos, fromName, fromID, UUID.Zero, fromAgent, broadcast); |
79 | } | 95 | } |
80 | 96 | ||
81 | /// <summary> | 97 | /// <summary> |
diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 45d512b..218d4e4 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs | |||
@@ -1300,6 +1300,12 @@ namespace OpenSim.Region.Framework.Scenes | |||
1300 | // This is the method that shuts down the scene. | 1300 | // This is the method that shuts down the scene. |
1301 | public override void Close() | 1301 | public override void Close() |
1302 | { | 1302 | { |
1303 | if (m_shuttingDown) | ||
1304 | { | ||
1305 | m_log.WarnFormat("[SCENE]: Ignoring close request because already closing {0}", Name); | ||
1306 | return; | ||
1307 | } | ||
1308 | |||
1303 | m_log.InfoFormat("[SCENE]: Closing down the single simulator: {0}", RegionInfo.RegionName); | 1309 | m_log.InfoFormat("[SCENE]: Closing down the single simulator: {0}", RegionInfo.RegionName); |
1304 | 1310 | ||
1305 | StatsReporter.Close(); | 1311 | StatsReporter.Close(); |
@@ -1343,6 +1349,14 @@ namespace OpenSim.Region.Framework.Scenes | |||
1343 | 1349 | ||
1344 | m_sceneGraph.Close(); | 1350 | m_sceneGraph.Close(); |
1345 | 1351 | ||
1352 | if (!GridService.DeregisterRegion(RegionInfo.RegionID)) | ||
1353 | m_log.WarnFormat("[SCENE]: Deregister from grid failed for region {0}", Name); | ||
1354 | |||
1355 | base.Close(); | ||
1356 | |||
1357 | // XEngine currently listens to the EventManager.OnShutdown event to trigger script stop and persistence. | ||
1358 | // Therefore. we must dispose of the PhysicsScene after this to prevent a window where script code can | ||
1359 | // attempt to reference a null or disposed physics scene. | ||
1346 | if (PhysicsScene != null) | 1360 | if (PhysicsScene != null) |
1347 | { | 1361 | { |
1348 | PhysicsScene phys = PhysicsScene; | 1362 | PhysicsScene phys = PhysicsScene; |
@@ -1351,12 +1365,6 @@ namespace OpenSim.Region.Framework.Scenes | |||
1351 | phys.Dispose(); | 1365 | phys.Dispose(); |
1352 | phys = null; | 1366 | phys = null; |
1353 | } | 1367 | } |
1354 | |||
1355 | if (!GridService.DeregisterRegion(RegionInfo.RegionID)) | ||
1356 | m_log.WarnFormat("[SCENE]: Deregister from grid failed for region {0}", Name); | ||
1357 | |||
1358 | // call the base class Close method. | ||
1359 | base.Close(); | ||
1360 | } | 1368 | } |
1361 | 1369 | ||
1362 | /// <summary> | 1370 | /// <summary> |
@@ -3595,9 +3603,10 @@ namespace OpenSim.Region.Framework.Scenes | |||
3595 | if (closeChildAgents && CapsModule != null) | 3603 | if (closeChildAgents && CapsModule != null) |
3596 | CapsModule.RemoveCaps(agentID); | 3604 | CapsModule.RemoveCaps(agentID); |
3597 | 3605 | ||
3598 | // REFACTORING PROBLEM -- well not really a problem, but just to point out that whatever | 3606 | // // REFACTORING PROBLEM -- well not really a problem, but just to point out that whatever |
3599 | // this method is doing is HORRIBLE!!! | 3607 | // // this method is doing is HORRIBLE!!! |
3600 | avatar.Scene.NeedSceneCacheClear(avatar.UUID); | 3608 | // Commented pending deletion since this method no longer appears to do anything at all |
3609 | // avatar.Scene.NeedSceneCacheClear(avatar.UUID); | ||
3601 | 3610 | ||
3602 | if (closeChildAgents && !isChildAgent) | 3611 | if (closeChildAgents && !isChildAgent) |
3603 | { | 3612 | { |
@@ -4899,13 +4908,24 @@ namespace OpenSim.Region.Framework.Scenes | |||
4899 | /// Get a group via its UUID | 4908 | /// Get a group via its UUID |
4900 | /// </summary> | 4909 | /// </summary> |
4901 | /// <param name="fullID"></param> | 4910 | /// <param name="fullID"></param> |
4902 | /// <returns>null if no group with that name exists</returns> | 4911 | /// <returns>null if no group with that id exists</returns> |
4903 | public SceneObjectGroup GetSceneObjectGroup(UUID fullID) | 4912 | public SceneObjectGroup GetSceneObjectGroup(UUID fullID) |
4904 | { | 4913 | { |
4905 | return m_sceneGraph.GetSceneObjectGroup(fullID); | 4914 | return m_sceneGraph.GetSceneObjectGroup(fullID); |
4906 | } | 4915 | } |
4907 | 4916 | ||
4908 | /// <summary> | 4917 | /// <summary> |
4918 | /// Get a group via its local ID | ||
4919 | /// </summary> | ||
4920 | /// <remarks>This will only return a group if the local ID matches a root part</remarks> | ||
4921 | /// <param name="localID"></param> | ||
4922 | /// <returns>null if no group with that id exists</returns> | ||
4923 | public SceneObjectGroup GetSceneObjectGroup(uint localID) | ||
4924 | { | ||
4925 | return m_sceneGraph.GetSceneObjectGroup(localID); | ||
4926 | } | ||
4927 | |||
4928 | /// <summary> | ||
4909 | /// Get a group by name from the scene (will return the first | 4929 | /// Get a group by name from the scene (will return the first |
4910 | /// found, if there are more than one prim with the same name) | 4930 | /// found, if there are more than one prim with the same name) |
4911 | /// </summary> | 4931 | /// </summary> |
@@ -5065,14 +5085,15 @@ namespace OpenSim.Region.Framework.Scenes | |||
5065 | client.SendRegionHandle(regionID, handle); | 5085 | client.SendRegionHandle(regionID, handle); |
5066 | } | 5086 | } |
5067 | 5087 | ||
5068 | public bool NeedSceneCacheClear(UUID agentID) | 5088 | // Commented pending deletion since this method no longer appears to do anything at all |
5069 | { | 5089 | // public bool NeedSceneCacheClear(UUID agentID) |
5070 | IInventoryTransferModule inv = RequestModuleInterface<IInventoryTransferModule>(); | 5090 | // { |
5071 | if (inv == null) | 5091 | // IInventoryTransferModule inv = RequestModuleInterface<IInventoryTransferModule>(); |
5072 | return true; | 5092 | // if (inv == null) |
5073 | 5093 | // return true; | |
5074 | return inv.NeedSceneCacheClear(agentID, this); | 5094 | // |
5075 | } | 5095 | // return inv.NeedSceneCacheClear(agentID, this); |
5096 | // } | ||
5076 | 5097 | ||
5077 | public void CleanTempObjects() | 5098 | public void CleanTempObjects() |
5078 | { | 5099 | { |
diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index 1beb584..e599e90 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs | |||
@@ -1063,6 +1063,30 @@ namespace OpenSim.Region.Framework.Scenes | |||
1063 | } | 1063 | } |
1064 | 1064 | ||
1065 | /// <summary> | 1065 | /// <summary> |
1066 | /// Get a group in the scene | ||
1067 | /// </summary> | ||
1068 | /// <remarks> | ||
1069 | /// This will only return a group if the local ID matches the root part, not other parts. | ||
1070 | /// </remarks> | ||
1071 | /// <param name="localID">Local id of the root part of the group</param> | ||
1072 | /// <returns>null if no such group was found</returns> | ||
1073 | protected internal SceneObjectGroup GetSceneObjectGroup(uint localID) | ||
1074 | { | ||
1075 | lock (SceneObjectGroupsByLocalPartID) | ||
1076 | { | ||
1077 | if (SceneObjectGroupsByLocalPartID.ContainsKey(localID)) | ||
1078 | { | ||
1079 | SceneObjectGroup so = SceneObjectGroupsByLocalPartID[localID]; | ||
1080 | |||
1081 | if (so.LocalId == localID) | ||
1082 | return so; | ||
1083 | } | ||
1084 | } | ||
1085 | |||
1086 | return null; | ||
1087 | } | ||
1088 | |||
1089 | /// <summary> | ||
1066 | /// Get a group by name from the scene (will return the first | 1090 | /// Get a group by name from the scene (will return the first |
1067 | /// found, if there are more than one prim with the same name) | 1091 | /// found, if there are more than one prim with the same name) |
1068 | /// </summary> | 1092 | /// </summary> |
diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index adb3d38..5be826d 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs | |||
@@ -69,7 +69,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
69 | public ScriptControlled eventControls; | 69 | public ScriptControlled eventControls; |
70 | } | 70 | } |
71 | 71 | ||
72 | public delegate void SendCourseLocationsMethod(UUID scene, ScenePresence presence, List<Vector3> coarseLocations, List<UUID> avatarUUIDs); | 72 | public delegate void SendCoarseLocationsMethod(UUID scene, ScenePresence presence, List<Vector3> coarseLocations, List<UUID> avatarUUIDs); |
73 | 73 | ||
74 | public class ScenePresence : EntityBase, IScenePresence | 74 | public class ScenePresence : EntityBase, IScenePresence |
75 | { | 75 | { |
@@ -188,7 +188,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
188 | /// </summary> | 188 | /// </summary> |
189 | public bool SitGround { get; private set; } | 189 | public bool SitGround { get; private set; } |
190 | 190 | ||
191 | private SendCourseLocationsMethod m_sendCourseLocationsMethod; | 191 | private SendCoarseLocationsMethod m_sendCoarseLocationsMethod; |
192 | 192 | ||
193 | //private Vector3 m_requestedSitOffset = new Vector3(); | 193 | //private Vector3 m_requestedSitOffset = new Vector3(); |
194 | 194 | ||
@@ -711,7 +711,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
711 | AttachmentsSyncLock = new Object(); | 711 | AttachmentsSyncLock = new Object(); |
712 | AllowMovement = true; | 712 | AllowMovement = true; |
713 | IsChildAgent = true; | 713 | IsChildAgent = true; |
714 | m_sendCourseLocationsMethod = SendCoarseLocationsDefault; | 714 | m_sendCoarseLocationsMethod = SendCoarseLocationsDefault; |
715 | Animator = new ScenePresenceAnimator(this); | 715 | Animator = new ScenePresenceAnimator(this); |
716 | PresenceType = type; | 716 | PresenceType = type; |
717 | DrawDistance = world.DefaultDrawDistance; | 717 | DrawDistance = world.DefaultDrawDistance; |
@@ -2629,17 +2629,17 @@ namespace OpenSim.Region.Framework.Scenes | |||
2629 | 2629 | ||
2630 | public void SendCoarseLocations(List<Vector3> coarseLocations, List<UUID> avatarUUIDs) | 2630 | public void SendCoarseLocations(List<Vector3> coarseLocations, List<UUID> avatarUUIDs) |
2631 | { | 2631 | { |
2632 | SendCourseLocationsMethod d = m_sendCourseLocationsMethod; | 2632 | SendCoarseLocationsMethod d = m_sendCoarseLocationsMethod; |
2633 | if (d != null) | 2633 | if (d != null) |
2634 | { | 2634 | { |
2635 | d.Invoke(m_scene.RegionInfo.originRegionID, this, coarseLocations, avatarUUIDs); | 2635 | d.Invoke(m_scene.RegionInfo.originRegionID, this, coarseLocations, avatarUUIDs); |
2636 | } | 2636 | } |
2637 | } | 2637 | } |
2638 | 2638 | ||
2639 | public void SetSendCourseLocationMethod(SendCourseLocationsMethod d) | 2639 | public void SetSendCoarseLocationMethod(SendCoarseLocationsMethod d) |
2640 | { | 2640 | { |
2641 | if (d != null) | 2641 | if (d != null) |
2642 | m_sendCourseLocationsMethod = d; | 2642 | m_sendCoarseLocationsMethod = d; |
2643 | } | 2643 | } |
2644 | 2644 | ||
2645 | public void SendCoarseLocationsDefault(UUID sceneId, ScenePresence p, List<Vector3> coarseLocations, List<UUID> avatarUUIDs) | 2645 | public void SendCoarseLocationsDefault(UUID sceneId, ScenePresence p, List<Vector3> coarseLocations, List<UUID> avatarUUIDs) |
@@ -2843,7 +2843,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
2843 | #region Significant Movement Method | 2843 | #region Significant Movement Method |
2844 | 2844 | ||
2845 | /// <summary> | 2845 | /// <summary> |
2846 | /// This checks for a significant movement and sends a courselocationchange update | 2846 | /// This checks for a significant movement and sends a coarselocationchange update |
2847 | /// </summary> | 2847 | /// </summary> |
2848 | protected void CheckForSignificantMovement() | 2848 | protected void CheckForSignificantMovement() |
2849 | { | 2849 | { |
diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneTests.cs index d722a09..ac3da1e 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/SceneTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/SceneTests.cs | |||
@@ -65,5 +65,22 @@ namespace OpenSim.Region.Framework.Scenes.Tests | |||
65 | 65 | ||
66 | Assert.That(scene.Frame, Is.EqualTo(1)); | 66 | Assert.That(scene.Frame, Is.EqualTo(1)); |
67 | } | 67 | } |
68 | |||
69 | [Test] | ||
70 | public void TestShutdownScene() | ||
71 | { | ||
72 | TestHelpers.InMethod(); | ||
73 | |||
74 | Scene scene = new SceneHelpers().SetupScene(); | ||
75 | scene.Close(); | ||
76 | |||
77 | Assert.That(scene.ShuttingDown, Is.True); | ||
78 | Assert.That(scene.Active, Is.False); | ||
79 | |||
80 | // Trying to update a shutdown scene should result in no update | ||
81 | scene.Update(1); | ||
82 | |||
83 | Assert.That(scene.Frame, Is.EqualTo(0)); | ||
84 | } | ||
68 | } | 85 | } |
69 | } \ No newline at end of file | 86 | } \ No newline at end of file |