diff options
author | Melanie | 2010-11-23 14:05:09 +0000 |
---|---|---|
committer | Melanie | 2010-11-23 14:05:09 +0000 |
commit | 6557d5f3596e6b187131f3dec325104d79b5eef7 (patch) | |
tree | e20aea5c5e7127cf2548126c04db77ac5a43dd22 /OpenSim/Region/Framework | |
parent | Merge branch 'master' into careminster-presence-refactor (diff) | |
parent | Merge branch 'master' of melanie@opensimulator.org:/var/git/opensim (diff) | |
download | opensim-SC-6557d5f3596e6b187131f3dec325104d79b5eef7.zip opensim-SC-6557d5f3596e6b187131f3dec325104d79b5eef7.tar.gz opensim-SC-6557d5f3596e6b187131f3dec325104d79b5eef7.tar.bz2 opensim-SC-6557d5f3596e6b187131f3dec325104d79b5eef7.tar.xz |
Merge branch 'master' into careminster-presence-refactor
Diffstat (limited to 'OpenSim/Region/Framework')
6 files changed, 194 insertions, 25 deletions
diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 36ce245..e6201a8 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs | |||
@@ -1738,7 +1738,12 @@ namespace OpenSim.Region.Framework.Scenes | |||
1738 | // Autoreturn has a null client. Nothing else does. So | 1738 | // Autoreturn has a null client. Nothing else does. So |
1739 | // allow only returns | 1739 | // allow only returns |
1740 | if (action != DeRezAction.Return) | 1740 | if (action != DeRezAction.Return) |
1741 | { | ||
1742 | m_log.WarnFormat( | ||
1743 | "[AGENT INVENTORY]: Ignoring attempt to {0} {1} {2} without a client", | ||
1744 | action, grp.Name, grp.UUID); | ||
1741 | return; | 1745 | return; |
1746 | } | ||
1742 | 1747 | ||
1743 | permissionToTakeCopy = false; | 1748 | permissionToTakeCopy = false; |
1744 | } | 1749 | } |
@@ -1755,7 +1760,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
1755 | } | 1760 | } |
1756 | if (!Permissions.CanTakeObject(grp.UUID, remoteClient.AgentId)) | 1761 | if (!Permissions.CanTakeObject(grp.UUID, remoteClient.AgentId)) |
1757 | permissionToTake = false; | 1762 | permissionToTake = false; |
1758 | 1763 | ||
1759 | if (!Permissions.CanDeleteObject(grp.UUID, remoteClient.AgentId)) | 1764 | if (!Permissions.CanDeleteObject(grp.UUID, remoteClient.AgentId)) |
1760 | permissionToDelete = false; | 1765 | permissionToDelete = false; |
1761 | } | 1766 | } |
diff --git a/OpenSim/Region/Framework/Scenes/Scene.Permissions.cs b/OpenSim/Region/Framework/Scenes/Scene.Permissions.cs index 06890a0..d67638a 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Permissions.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Permissions.cs | |||
@@ -27,13 +27,15 @@ | |||
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections.Generic; | 29 | using System.Collections.Generic; |
30 | using System.Reflection; | ||
30 | using System.Text; | 31 | using System.Text; |
32 | using log4net; | ||
31 | using OpenMetaverse; | 33 | using OpenMetaverse; |
32 | using OpenSim.Framework; | 34 | using OpenSim.Framework; |
33 | using OpenSim.Region.Framework.Interfaces; | 35 | using OpenSim.Region.Framework.Interfaces; |
34 | 36 | ||
35 | namespace OpenSim.Region.Framework.Scenes | 37 | namespace OpenSim.Region.Framework.Scenes |
36 | { | 38 | { |
37 | #region Delegates | 39 | #region Delegates |
38 | public delegate uint GenerateClientFlagsHandler(UUID userID, UUID objectID); | 40 | public delegate uint GenerateClientFlagsHandler(UUID userID, UUID objectID); |
39 | public delegate void SetBypassPermissionsHandler(bool value); | 41 | public delegate void SetBypassPermissionsHandler(bool value); |
@@ -88,6 +90,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
88 | 90 | ||
89 | public class ScenePermissions | 91 | public class ScenePermissions |
90 | { | 92 | { |
93 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
94 | |||
91 | private Scene m_scene; | 95 | private Scene m_scene; |
92 | 96 | ||
93 | public ScenePermissions(Scene scene) | 97 | public ScenePermissions(Scene scene) |
@@ -242,6 +246,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
242 | #region DELETE OBJECT | 246 | #region DELETE OBJECT |
243 | public bool CanDeleteObject(UUID objectID, UUID deleter) | 247 | public bool CanDeleteObject(UUID objectID, UUID deleter) |
244 | { | 248 | { |
249 | bool result = true; | ||
250 | |||
245 | DeleteObjectHandler handler = OnDeleteObject; | 251 | DeleteObjectHandler handler = OnDeleteObject; |
246 | if (handler != null) | 252 | if (handler != null) |
247 | { | 253 | { |
@@ -249,10 +255,18 @@ namespace OpenSim.Region.Framework.Scenes | |||
249 | foreach (DeleteObjectHandler h in list) | 255 | foreach (DeleteObjectHandler h in list) |
250 | { | 256 | { |
251 | if (h(objectID, deleter, m_scene) == false) | 257 | if (h(objectID, deleter, m_scene) == false) |
252 | return false; | 258 | { |
259 | result = false; | ||
260 | break; | ||
261 | } | ||
253 | } | 262 | } |
254 | } | 263 | } |
255 | return true; | 264 | |
265 | // m_log.DebugFormat( | ||
266 | // "[SCENE PERMISSIONS]: CanDeleteObject() fired for object {0}, deleter {1}, result {2}", | ||
267 | // objectID, deleter, result); | ||
268 | |||
269 | return result; | ||
256 | } | 270 | } |
257 | 271 | ||
258 | #endregion | 272 | #endregion |
@@ -260,6 +274,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
260 | #region TAKE OBJECT | 274 | #region TAKE OBJECT |
261 | public bool CanTakeObject(UUID objectID, UUID AvatarTakingUUID) | 275 | public bool CanTakeObject(UUID objectID, UUID AvatarTakingUUID) |
262 | { | 276 | { |
277 | bool result = true; | ||
278 | |||
263 | TakeObjectHandler handler = OnTakeObject; | 279 | TakeObjectHandler handler = OnTakeObject; |
264 | if (handler != null) | 280 | if (handler != null) |
265 | { | 281 | { |
@@ -267,10 +283,18 @@ namespace OpenSim.Region.Framework.Scenes | |||
267 | foreach (TakeObjectHandler h in list) | 283 | foreach (TakeObjectHandler h in list) |
268 | { | 284 | { |
269 | if (h(objectID, AvatarTakingUUID, m_scene) == false) | 285 | if (h(objectID, AvatarTakingUUID, m_scene) == false) |
270 | return false; | 286 | { |
287 | result = false; | ||
288 | break; | ||
289 | } | ||
271 | } | 290 | } |
272 | } | 291 | } |
273 | return true; | 292 | |
293 | // m_log.DebugFormat( | ||
294 | // "[SCENE PERMISSIONS]: CanTakeObject() fired for object {0}, taker {1}, result {2}", | ||
295 | // objectID, AvatarTakingUUID, result); | ||
296 | |||
297 | return result; | ||
274 | } | 298 | } |
275 | 299 | ||
276 | #endregion | 300 | #endregion |
@@ -278,6 +302,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
278 | #region TAKE COPY OBJECT | 302 | #region TAKE COPY OBJECT |
279 | public bool CanTakeCopyObject(UUID objectID, UUID userID) | 303 | public bool CanTakeCopyObject(UUID objectID, UUID userID) |
280 | { | 304 | { |
305 | bool result = true; | ||
306 | |||
281 | TakeCopyObjectHandler handler = OnTakeCopyObject; | 307 | TakeCopyObjectHandler handler = OnTakeCopyObject; |
282 | if (handler != null) | 308 | if (handler != null) |
283 | { | 309 | { |
@@ -285,10 +311,18 @@ namespace OpenSim.Region.Framework.Scenes | |||
285 | foreach (TakeCopyObjectHandler h in list) | 311 | foreach (TakeCopyObjectHandler h in list) |
286 | { | 312 | { |
287 | if (h(objectID, userID, m_scene) == false) | 313 | if (h(objectID, userID, m_scene) == false) |
288 | return false; | 314 | { |
315 | result = false; | ||
316 | break; | ||
317 | } | ||
289 | } | 318 | } |
290 | } | 319 | } |
291 | return true; | 320 | |
321 | // m_log.DebugFormat( | ||
322 | // "[SCENE PERMISSIONS]: CanTakeCopyObject() fired for object {0}, user {1}, result {2}", | ||
323 | // objectID, userID, result); | ||
324 | |||
325 | return result; | ||
292 | } | 326 | } |
293 | 327 | ||
294 | #endregion | 328 | #endregion |
@@ -383,6 +417,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
383 | #region RETURN OBJECT | 417 | #region RETURN OBJECT |
384 | public bool CanReturnObjects(ILandObject land, UUID user, List<SceneObjectGroup> objects) | 418 | public bool CanReturnObjects(ILandObject land, UUID user, List<SceneObjectGroup> objects) |
385 | { | 419 | { |
420 | bool result = true; | ||
421 | |||
386 | ReturnObjectsHandler handler = OnReturnObjects; | 422 | ReturnObjectsHandler handler = OnReturnObjects; |
387 | if (handler != null) | 423 | if (handler != null) |
388 | { | 424 | { |
@@ -390,10 +426,18 @@ namespace OpenSim.Region.Framework.Scenes | |||
390 | foreach (ReturnObjectsHandler h in list) | 426 | foreach (ReturnObjectsHandler h in list) |
391 | { | 427 | { |
392 | if (h(land, user, objects, m_scene) == false) | 428 | if (h(land, user, objects, m_scene) == false) |
393 | return false; | 429 | { |
430 | result = false; | ||
431 | break; | ||
432 | } | ||
394 | } | 433 | } |
395 | } | 434 | } |
396 | return true; | 435 | |
436 | // m_log.DebugFormat( | ||
437 | // "[SCENE PERMISSIONS]: CanReturnObjects() fired for user {0} for {1} objects on {2}, result {3}", | ||
438 | // user, objects.Count, land.LandData.Name, result); | ||
439 | |||
440 | return result; | ||
397 | } | 441 | } |
398 | 442 | ||
399 | #endregion | 443 | #endregion |
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index ee08072..f3964c2 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs | |||
@@ -2950,6 +2950,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
2950 | part.Resize(scale); | 2950 | part.Resize(scale); |
2951 | 2951 | ||
2952 | HasGroupChanged = true; | 2952 | HasGroupChanged = true; |
2953 | part.TriggerScriptChangedEvent(Changed.SCALE); | ||
2953 | ScheduleGroupForFullUpdate(); | 2954 | ScheduleGroupForFullUpdate(); |
2954 | 2955 | ||
2955 | //if (part.UUID == m_rootPart.UUID) | 2956 | //if (part.UUID == m_rootPart.UUID) |
@@ -3110,6 +3111,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
3110 | 3111 | ||
3111 | part.IgnoreUndoUpdate = false; | 3112 | part.IgnoreUndoUpdate = false; |
3112 | HasGroupChanged = true; | 3113 | HasGroupChanged = true; |
3114 | m_rootPart.TriggerScriptChangedEvent(Changed.SCALE); | ||
3113 | ScheduleGroupForTerseUpdate(); | 3115 | ScheduleGroupForTerseUpdate(); |
3114 | } | 3116 | } |
3115 | } | 3117 | } |
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index f69a30c..bdd42fc 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs | |||
@@ -1026,21 +1026,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
1026 | public PrimitiveBaseShape Shape | 1026 | public PrimitiveBaseShape Shape |
1027 | { | 1027 | { |
1028 | get { return m_shape; } | 1028 | get { return m_shape; } |
1029 | set | 1029 | set { m_shape = value; } |
1030 | { | ||
1031 | bool shape_changed = false; | ||
1032 | // TODO: this should really be restricted to the right | ||
1033 | // set of attributes on shape change. For instance, | ||
1034 | // changing the lighting on a shape shouldn't cause | ||
1035 | // this. | ||
1036 | if (m_shape != null) | ||
1037 | shape_changed = true; | ||
1038 | |||
1039 | m_shape = value; | ||
1040 | |||
1041 | if (shape_changed) | ||
1042 | TriggerScriptChangedEvent(Changed.SHAPE); | ||
1043 | } | ||
1044 | } | 1030 | } |
1045 | 1031 | ||
1046 | public Vector3 Scale | 1032 | public Vector3 Scale |
@@ -4592,6 +4578,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
4592 | ParentGroup.RootPart.Rezzed = DateTime.UtcNow; | 4578 | ParentGroup.RootPart.Rezzed = DateTime.UtcNow; |
4593 | 4579 | ||
4594 | ParentGroup.HasGroupChanged = true; | 4580 | ParentGroup.HasGroupChanged = true; |
4581 | TriggerScriptChangedEvent(Changed.SHAPE); | ||
4595 | ScheduleFullUpdate(); | 4582 | ScheduleFullUpdate(); |
4596 | } | 4583 | } |
4597 | 4584 | ||
diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectUserTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectUserTests.cs new file mode 100644 index 0000000..b3b99f4 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectUserTests.cs | |||
@@ -0,0 +1,129 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Reflection; | ||
31 | using Nini.Config; | ||
32 | using NUnit.Framework; | ||
33 | using NUnit.Framework.SyntaxHelpers; | ||
34 | using OpenMetaverse; | ||
35 | using OpenSim.Framework; | ||
36 | using OpenSim.Framework.Communications; | ||
37 | using OpenSim.Region.CoreModules.World.Permissions; | ||
38 | using OpenSim.Region.Framework.Scenes; | ||
39 | using OpenSim.Tests.Common; | ||
40 | using OpenSim.Tests.Common.Mock; | ||
41 | using OpenSim.Tests.Common.Setup; | ||
42 | |||
43 | namespace OpenSim.Region.Framework.Scenes.Tests | ||
44 | { | ||
45 | /// <summary> | ||
46 | /// Tests manipulation of scene objects by users. | ||
47 | /// </summary> | ||
48 | /// | ||
49 | /// This is at a level above the SceneObjectBasicTests, which act on the scene directly. | ||
50 | /// FIXME: These tests are very incomplete - they only test for a few conditions. | ||
51 | [TestFixture] | ||
52 | public class SceneObjectUserTests | ||
53 | { | ||
54 | /// <summary> | ||
55 | /// Test deleting an object from a scene. | ||
56 | /// </summary> | ||
57 | [Test] | ||
58 | public void TestDeRezSceneObject() | ||
59 | { | ||
60 | TestHelper.InMethod(); | ||
61 | // log4net.Config.XmlConfigurator.Configure(); | ||
62 | |||
63 | UUID userId = UUID.Parse("10000000-0000-0000-0000-000000000001"); | ||
64 | |||
65 | TestScene scene = SceneSetupHelpers.SetupScene(); | ||
66 | IConfigSource configSource = new IniConfigSource(); | ||
67 | IConfig config = configSource.AddConfig("Startup"); | ||
68 | config.Set("serverside_object_permissions", true); | ||
69 | SceneSetupHelpers.SetupSceneModules(scene, configSource, new object[] { new PermissionsModule() }); | ||
70 | TestClient client = SceneSetupHelpers.AddRootAgent(scene, userId); | ||
71 | |||
72 | // Turn off the timer on the async sog deleter - we'll crank it by hand for this test. | ||
73 | AsyncSceneObjectGroupDeleter sogd = scene.SceneObjectGroupDeleter; | ||
74 | sogd.Enabled = false; | ||
75 | |||
76 | SceneObjectPart part | ||
77 | = new SceneObjectPart(userId, PrimitiveBaseShape.Default, Vector3.Zero, Quaternion.Identity, Vector3.Zero); | ||
78 | part.Name = "obj1"; | ||
79 | scene.AddNewSceneObject(new SceneObjectGroup(part), false); | ||
80 | List<uint> localIds = new List<uint>(); | ||
81 | localIds.Add(part.LocalId); | ||
82 | |||
83 | scene.DeRezObjects(client, localIds, UUID.Zero, DeRezAction.Delete, UUID.Zero); | ||
84 | sogd.InventoryDeQueueAndDelete(); | ||
85 | |||
86 | SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId); | ||
87 | Assert.That(retrievedPart, Is.Null); | ||
88 | } | ||
89 | |||
90 | /// <summary> | ||
91 | /// Test deleting an object from a scene where the deleter is not the owner | ||
92 | /// </summary> | ||
93 | /// | ||
94 | /// This test assumes that the deleter is not a god. | ||
95 | [Test] | ||
96 | public void TestDeRezSceneObjectNotOwner() | ||
97 | { | ||
98 | TestHelper.InMethod(); | ||
99 | // log4net.Config.XmlConfigurator.Configure(); | ||
100 | |||
101 | UUID userId = UUID.Parse("10000000-0000-0000-0000-000000000001"); | ||
102 | UUID objectOwnerId = UUID.Parse("20000000-0000-0000-0000-000000000001"); | ||
103 | |||
104 | TestScene scene = SceneSetupHelpers.SetupScene(); | ||
105 | IConfigSource configSource = new IniConfigSource(); | ||
106 | IConfig config = configSource.AddConfig("Startup"); | ||
107 | config.Set("serverside_object_permissions", true); | ||
108 | SceneSetupHelpers.SetupSceneModules(scene, configSource, new object[] { new PermissionsModule() }); | ||
109 | TestClient client = SceneSetupHelpers.AddRootAgent(scene, userId); | ||
110 | |||
111 | // Turn off the timer on the async sog deleter - we'll crank it by hand for this test. | ||
112 | AsyncSceneObjectGroupDeleter sogd = scene.SceneObjectGroupDeleter; | ||
113 | sogd.Enabled = false; | ||
114 | |||
115 | SceneObjectPart part | ||
116 | = new SceneObjectPart(objectOwnerId, PrimitiveBaseShape.Default, Vector3.Zero, Quaternion.Identity, Vector3.Zero); | ||
117 | part.Name = "obj1"; | ||
118 | scene.AddNewSceneObject(new SceneObjectGroup(part), false); | ||
119 | List<uint> localIds = new List<uint>(); | ||
120 | localIds.Add(part.LocalId); | ||
121 | |||
122 | scene.DeRezObjects(client, localIds, UUID.Zero, DeRezAction.Delete, UUID.Zero); | ||
123 | sogd.InventoryDeQueueAndDelete(); | ||
124 | |||
125 | SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId); | ||
126 | Assert.That(retrievedPart.UUID, Is.EqualTo(part.UUID)); | ||
127 | } | ||
128 | } | ||
129 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTests.cs b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTests.cs index ab5968c..ef52363 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTests.cs | |||
@@ -65,6 +65,8 @@ namespace OpenSim.Region.Framework.Scenes.Tests | |||
65 | [TestFixtureSetUp] | 65 | [TestFixtureSetUp] |
66 | public void Init() | 66 | public void Init() |
67 | { | 67 | { |
68 | TestHelper.InMethod(); | ||
69 | |||
68 | scene = SceneSetupHelpers.SetupScene("Neighbour x", UUID.Random(), 1000, 1000); | 70 | scene = SceneSetupHelpers.SetupScene("Neighbour x", UUID.Random(), 1000, 1000); |
69 | scene2 = SceneSetupHelpers.SetupScene("Neighbour x+1", UUID.Random(), 1001, 1000); | 71 | scene2 = SceneSetupHelpers.SetupScene("Neighbour x+1", UUID.Random(), 1001, 1000); |
70 | scene3 = SceneSetupHelpers.SetupScene("Neighbour x-1", UUID.Random(), 999, 1000); | 72 | scene3 = SceneSetupHelpers.SetupScene("Neighbour x-1", UUID.Random(), 999, 1000); |