diff options
author | UbitUmarov | 2017-04-25 17:59:53 +0100 |
---|---|---|
committer | UbitUmarov | 2017-04-25 17:59:53 +0100 |
commit | a680d8b8d700f78beb1a9eea98b52d59118efe2e (patch) | |
tree | dd084e74fb61400c704a14dcfa8689670acb9ec2 /OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs | |
parent | Merge branch 'master' into httptests (diff) | |
parent | move mesh pbs creation code out of mesh upload code into to PrimitiveBaseShap... (diff) | |
download | opensim-SC-a680d8b8d700f78beb1a9eea98b52d59118efe2e.zip opensim-SC-a680d8b8d700f78beb1a9eea98b52d59118efe2e.tar.gz opensim-SC-a680d8b8d700f78beb1a9eea98b52d59118efe2e.tar.bz2 opensim-SC-a680d8b8d700f78beb1a9eea98b52d59118efe2e.tar.xz |
fix merge
Diffstat (limited to 'OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs')
-rw-r--r-- | OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs | 310 |
1 files changed, 302 insertions, 8 deletions
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index e769c6d..e12cedf 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs | |||
@@ -260,7 +260,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
260 | wComm.DeliverMessage(ChatTypeEnum.Shout, ScriptBaseClass.DEBUG_CHANNEL, m_host.Name, m_host.UUID, message); | 260 | wComm.DeliverMessage(ChatTypeEnum.Shout, ScriptBaseClass.DEBUG_CHANNEL, m_host.Name, m_host.UUID, message); |
261 | } | 261 | } |
262 | 262 | ||
263 | // Returns of the function is allowed. Throws a script exception if not allowed. | 263 | // Returns if OSSL is enabled. Throws a script exception if OSSL is not allowed.. |
264 | // for safe funtions always active | ||
265 | public void CheckThreatLevel() | ||
266 | { | ||
267 | if (!m_OSFunctionsEnabled) | ||
268 | OSSLError(String.Format("{0} permission denied. All OS functions are disabled.")); // throws | ||
269 | } | ||
270 | |||
271 | // Returns if the function is allowed. Throws a script exception if not allowed. | ||
264 | public void CheckThreatLevel(ThreatLevel level, string function) | 272 | public void CheckThreatLevel(ThreatLevel level, string function) |
265 | { | 273 | { |
266 | if (!m_OSFunctionsEnabled) | 274 | if (!m_OSFunctionsEnabled) |
@@ -1716,7 +1724,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
1716 | 1724 | ||
1717 | public LSL_Integer osCheckODE() | 1725 | public LSL_Integer osCheckODE() |
1718 | { | 1726 | { |
1727 | CheckThreatLevel(); | ||
1719 | m_host.AddScriptLPS(1); | 1728 | m_host.AddScriptLPS(1); |
1729 | |||
1720 | LSL_Integer ret = 0; // false | 1730 | LSL_Integer ret = 0; // false |
1721 | if (m_ScriptEngine.World.PhysicsScene != null) | 1731 | if (m_ScriptEngine.World.PhysicsScene != null) |
1722 | { | 1732 | { |
@@ -1757,10 +1767,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
1757 | 1767 | ||
1758 | public string osGetPhysicsEngineName() | 1768 | public string osGetPhysicsEngineName() |
1759 | { | 1769 | { |
1760 | // not doing security checks | 1770 | CheckThreatLevel(); |
1761 | // this whould limit the use of this | ||
1762 | |||
1763 | m_host.AddScriptLPS(1); | 1771 | m_host.AddScriptLPS(1); |
1772 | |||
1764 | string ret = "NoEngine"; | 1773 | string ret = "NoEngine"; |
1765 | if (m_ScriptEngine.World.PhysicsScene != null) | 1774 | if (m_ScriptEngine.World.PhysicsScene != null) |
1766 | { | 1775 | { |
@@ -1771,6 +1780,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
1771 | } | 1780 | } |
1772 | return ret; | 1781 | return ret; |
1773 | } | 1782 | } |
1783 | |||
1774 | public string osGetSimulatorVersion() | 1784 | public string osGetSimulatorVersion() |
1775 | { | 1785 | { |
1776 | // High because it can be used to target attacks to known weaknesses | 1786 | // High because it can be used to target attacks to known weaknesses |
@@ -2038,6 +2048,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
2038 | m_host.Inventory.AddInventoryItemExclusive(taskItem, false); | 2048 | m_host.Inventory.AddInventoryItemExclusive(taskItem, false); |
2039 | else | 2049 | else |
2040 | m_host.Inventory.AddInventoryItem(taskItem, false); | 2050 | m_host.Inventory.AddInventoryItem(taskItem, false); |
2051 | m_host.ParentGroup.AggregatePerms(); | ||
2041 | 2052 | ||
2042 | return taskItem; | 2053 | return taskItem; |
2043 | } | 2054 | } |
@@ -3537,7 +3548,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
3537 | 3548 | ||
3538 | LSL_Float health = new LSL_Float(-1); | 3549 | LSL_Float health = new LSL_Float(-1); |
3539 | ScenePresence presence = World.GetScenePresence(new UUID(avatar)); | 3550 | ScenePresence presence = World.GetScenePresence(new UUID(avatar)); |
3540 | if (presence != null) health = presence.Health; | 3551 | if (presence != null) |
3552 | health = presence.Health; | ||
3541 | return health; | 3553 | return health; |
3542 | } | 3554 | } |
3543 | 3555 | ||
@@ -3577,7 +3589,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
3577 | UUID avatarId = new UUID(avatar); | 3589 | UUID avatarId = new UUID(avatar); |
3578 | ScenePresence presence = World.GetScenePresence(avatarId); | 3590 | ScenePresence presence = World.GetScenePresence(avatarId); |
3579 | 3591 | ||
3580 | if (presence != null && World.ScriptDanger(m_host.LocalId, m_host.GetWorldPosition())) | 3592 | if (presence != null) |
3581 | { | 3593 | { |
3582 | float health = presence.Health; | 3594 | float health = presence.Health; |
3583 | health += (float)healing; | 3595 | health += (float)healing; |
@@ -3597,7 +3609,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
3597 | UUID avatarId = new UUID(avatar); | 3609 | UUID avatarId = new UUID(avatar); |
3598 | ScenePresence presence = World.GetScenePresence(avatarId); | 3610 | ScenePresence presence = World.GetScenePresence(avatarId); |
3599 | 3611 | ||
3600 | if (presence != null && World.ScriptDanger(m_host.LocalId, m_host.GetWorldPosition())) | 3612 | if (presence != null) |
3601 | { | 3613 | { |
3602 | if (health > 100.0) | 3614 | if (health > 100.0) |
3603 | health = 100.0; | 3615 | health = 100.0; |
@@ -3616,7 +3628,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
3616 | UUID avatarId = new UUID(avatar); | 3628 | UUID avatarId = new UUID(avatar); |
3617 | ScenePresence presence = World.GetScenePresence(avatarId); | 3629 | ScenePresence presence = World.GetScenePresence(avatarId); |
3618 | 3630 | ||
3619 | if (presence != null && World.ScriptDanger(m_host.LocalId, m_host.GetWorldPosition())) | 3631 | if (presence != null) |
3620 | presence.HealRate = (float)healrate; | 3632 | presence.HealRate = (float)healrate; |
3621 | } | 3633 | } |
3622 | 3634 | ||
@@ -4362,6 +4374,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
4362 | 4374 | ||
4363 | public void osCollisionSound(string impact_sound, double impact_volume) | 4375 | public void osCollisionSound(string impact_sound, double impact_volume) |
4364 | { | 4376 | { |
4377 | CheckThreatLevel(); | ||
4365 | m_host.AddScriptLPS(1); | 4378 | m_host.AddScriptLPS(1); |
4366 | 4379 | ||
4367 | if(impact_sound == "") | 4380 | if(impact_sound == "") |
@@ -4394,6 +4407,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
4394 | // still not very usefull, detector is lost on rez, restarts, etc | 4407 | // still not very usefull, detector is lost on rez, restarts, etc |
4395 | public void osVolumeDetect(int detect) | 4408 | public void osVolumeDetect(int detect) |
4396 | { | 4409 | { |
4410 | CheckThreatLevel(); | ||
4397 | m_host.AddScriptLPS(1); | 4411 | m_host.AddScriptLPS(1); |
4398 | 4412 | ||
4399 | if (m_host.ParentGroup == null || m_host.ParentGroup.IsDeleted || m_host.ParentGroup.IsAttachment) | 4413 | if (m_host.ParentGroup == null || m_host.ParentGroup.IsDeleted || m_host.ParentGroup.IsAttachment) |
@@ -4402,5 +4416,285 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
4402 | m_host.ScriptSetVolumeDetect(detect != 0); | 4416 | m_host.ScriptSetVolumeDetect(detect != 0); |
4403 | } | 4417 | } |
4404 | 4418 | ||
4419 | /// <summary> | ||
4420 | /// Get inertial data | ||
4421 | /// </summary> | ||
4422 | /// <remarks> | ||
4423 | /// </remarks> | ||
4424 | /// <returns> | ||
4425 | /// a LSL list with contents: | ||
4426 | /// LSL_Float mass, the total mass of a linkset | ||
4427 | /// LSL_Vector CenterOfMass, center mass relative to root prim | ||
4428 | /// LSL_Vector Inertia, elements of diagonal of inertia Ixx,Iyy,Izz divided by total mass | ||
4429 | /// LSL_Vector aux, elements of upper triagle of inertia Ixy (= Iyx), Ixz (= Izx), Iyz(= Izy) divided by total mass | ||
4430 | /// </returns> | ||
4431 | public LSL_List osGetInertiaData() | ||
4432 | { | ||
4433 | CheckThreatLevel(); | ||
4434 | m_host.AddScriptLPS(1); | ||
4435 | |||
4436 | LSL_List result = new LSL_List(); | ||
4437 | float TotalMass; | ||
4438 | Vector3 CenterOfMass; | ||
4439 | Vector3 Inertia; | ||
4440 | Vector4 aux; | ||
4441 | |||
4442 | SceneObjectGroup sog = m_host.ParentGroup; | ||
4443 | if(sog== null || sog.IsDeleted) | ||
4444 | return result; | ||
4445 | |||
4446 | sog.GetInertiaData(out TotalMass, out CenterOfMass, out Inertia, out aux ); | ||
4447 | if(TotalMass > 0) | ||
4448 | { | ||
4449 | float t = 1.0f/TotalMass; | ||
4450 | Inertia.X *= t; | ||
4451 | Inertia.Y *= t; | ||
4452 | Inertia.Z *= t; | ||
4453 | |||
4454 | aux.X *= t; | ||
4455 | aux.Y *= t; | ||
4456 | aux.Z *= t; | ||
4457 | } | ||
4458 | |||
4459 | result.Add(new LSL_Float(TotalMass)); | ||
4460 | result.Add(new LSL_Vector(CenterOfMass.X, CenterOfMass.Y, CenterOfMass.Z)); | ||
4461 | result.Add(new LSL_Vector(Inertia.X, Inertia.Y, Inertia.Z)); | ||
4462 | result.Add(new LSL_Vector(aux.X, aux.Y, aux.Z)); | ||
4463 | return result; | ||
4464 | } | ||
4465 | |||
4466 | /// <summary> | ||
4467 | /// set inertial data | ||
4468 | /// replaces the automatic calculation of mass, center of mass and inertia | ||
4469 | /// | ||
4470 | /// </summary> | ||
4471 | /// <param name="Mass">total mass of linkset</param> | ||
4472 | /// <param name="centerOfMass">location of center of mass relative to root prim in local coords</param> | ||
4473 | /// <param name="principalInertiaScaled">moment of inertia relative to principal axis and center of mass,Ixx, Iyy, Izz divided by mass</param> | ||
4474 | /// <param name="lslrot">rotation of the inertia, relative to local axis</param> | ||
4475 | /// <remarks> | ||
4476 | /// the inertia argument is is inertia divided by mass, so corresponds only to the geometric distribution of mass and both can be changed independently. | ||
4477 | /// </remarks> | ||
4478 | |||
4479 | public void osSetInertia(LSL_Float mass, LSL_Vector centerOfMass, LSL_Vector principalInertiaScaled, LSL_Rotation lslrot) | ||
4480 | { | ||
4481 | CheckThreatLevel(); | ||
4482 | m_host.AddScriptLPS(1); | ||
4483 | |||
4484 | SceneObjectGroup sog = m_host.ParentGroup; | ||
4485 | if(sog== null || sog.IsDeleted) | ||
4486 | return; | ||
4487 | |||
4488 | if(mass < 0 || principalInertiaScaled.x < 0 || principalInertiaScaled.y < 0 || principalInertiaScaled.z < 0) | ||
4489 | return; | ||
4490 | |||
4491 | // need more checks | ||
4492 | |||
4493 | Vector3 CenterOfMass = new Vector3((float)centerOfMass.x,(float)centerOfMass.y,(float)centerOfMass.z); | ||
4494 | Vector3 Inertia; | ||
4495 | float m = (float)mass; | ||
4496 | |||
4497 | Inertia.X = m * (float)principalInertiaScaled.x; | ||
4498 | Inertia.Y = m * (float)principalInertiaScaled.y; | ||
4499 | Inertia.Z = m * (float)principalInertiaScaled.z; | ||
4500 | |||
4501 | Vector4 rot = new Vector4((float)lslrot.x, (float)lslrot.y, (float)lslrot.y, (float)lslrot.s); | ||
4502 | rot.Normalize(); | ||
4503 | |||
4504 | sog.SetInertiaData(m, CenterOfMass, Inertia, rot ); | ||
4505 | } | ||
4506 | |||
4507 | /// <summary> | ||
4508 | /// set inertial data as a sphere | ||
4509 | /// replaces the automatic calculation of mass, center of mass and inertia | ||
4510 | /// | ||
4511 | /// </summary> | ||
4512 | /// <param name="Mass">total mass of linkset</param> | ||
4513 | /// <param name="boxsize">size of the Box</param> | ||
4514 | /// <param name="centerOfMass">location of center of mass relative to root prim in local coords</param> | ||
4515 | /// <param name="lslrot">rotation of the box, and so inertia, relative to local axis</param> | ||
4516 | /// <remarks> | ||
4517 | /// </remarks> | ||
4518 | public void osSetInertiaAsBox(LSL_Float mass, LSL_Vector boxSize, LSL_Vector centerOfMass, LSL_Rotation lslrot) | ||
4519 | { | ||
4520 | CheckThreatLevel(); | ||
4521 | m_host.AddScriptLPS(1); | ||
4522 | |||
4523 | SceneObjectGroup sog = m_host.ParentGroup; | ||
4524 | if(sog== null || sog.IsDeleted) | ||
4525 | return; | ||
4526 | |||
4527 | if(mass < 0) | ||
4528 | return; | ||
4529 | |||
4530 | // need more checks | ||
4531 | |||
4532 | Vector3 CenterOfMass = new Vector3((float)centerOfMass.x,(float)centerOfMass.y,(float)centerOfMass.z); | ||
4533 | Vector3 Inertia; | ||
4534 | float lx = (float)boxSize.x; | ||
4535 | float ly = (float)boxSize.y; | ||
4536 | float lz = (float)boxSize.z; | ||
4537 | float m = (float)mass; | ||
4538 | float t = m / 12.0f; | ||
4539 | |||
4540 | Inertia.X = t * (ly*ly + lz*lz); | ||
4541 | Inertia.Y = t * (lx*lx + lz*lz); | ||
4542 | Inertia.Z = t * (lx*lx + ly*ly); | ||
4543 | |||
4544 | Vector4 rot = new Vector4((float)lslrot.x, (float)lslrot.y, (float)lslrot.z, (float)lslrot.s); | ||
4545 | rot.Normalize(); | ||
4546 | |||
4547 | sog.SetInertiaData(m, CenterOfMass, Inertia, rot ); | ||
4548 | } | ||
4549 | |||
4550 | /// <summary> | ||
4551 | /// set inertial data as a sphere | ||
4552 | /// replaces the automatic calculation of mass, center of mass and inertia | ||
4553 | /// | ||
4554 | /// </summary> | ||
4555 | /// <param name="Mass">total mass of linkset</param> | ||
4556 | /// <param name="radius">radius of the sphere</param> | ||
4557 | /// <param name="centerOfMass">location of center of mass relative to root prim in local coords</param> | ||
4558 | /// <remarks> | ||
4559 | /// </remarks> | ||
4560 | public void osSetInertiaAsSphere(LSL_Float mass, LSL_Float radius, LSL_Vector centerOfMass) | ||
4561 | { | ||
4562 | CheckThreatLevel(); | ||
4563 | m_host.AddScriptLPS(1); | ||
4564 | |||
4565 | SceneObjectGroup sog = m_host.ParentGroup; | ||
4566 | if(sog== null || sog.IsDeleted) | ||
4567 | return; | ||
4568 | |||
4569 | if(mass < 0) | ||
4570 | return; | ||
4571 | |||
4572 | // need more checks | ||
4573 | |||
4574 | Vector3 CenterOfMass = new Vector3((float)centerOfMass.x,(float)centerOfMass.y,(float)centerOfMass.z); | ||
4575 | Vector3 Inertia; | ||
4576 | float r = (float)radius; | ||
4577 | float m = (float)mass; | ||
4578 | float t = 0.4f * m * r * r; | ||
4579 | |||
4580 | Inertia.X = t; | ||
4581 | Inertia.Y = t; | ||
4582 | Inertia.Z = t; | ||
4583 | |||
4584 | sog.SetInertiaData(m, CenterOfMass, Inertia, new Vector4(0f, 0f, 0f,1.0f)); | ||
4585 | } | ||
4586 | |||
4587 | /// <summary> | ||
4588 | /// set inertial data as a cylinder | ||
4589 | /// replaces the automatic calculation of mass, center of mass and inertia | ||
4590 | /// | ||
4591 | /// </summary> | ||
4592 | /// <param name="Mass">total mass of linkset</param> | ||
4593 | /// <param name="radius">radius of the cylinder</param> | ||
4594 | /// <param name="lenght">lenght of the cylinder</param> | ||
4595 | /// <param name="centerOfMass">location of center of mass relative to root prim in local coords</param> | ||
4596 | /// <param name="lslrot">rotation of the cylinder, and so inertia, relative to local axis</param> | ||
4597 | /// <remarks> | ||
4598 | /// cylinder axis aligned with Z axis. For other orientations provide the rotation. | ||
4599 | /// </remarks> | ||
4600 | public void osSetInertiaAsCylinder(LSL_Float mass, LSL_Float radius, LSL_Float lenght, LSL_Vector centerOfMass, LSL_Rotation lslrot) | ||
4601 | { | ||
4602 | CheckThreatLevel(); | ||
4603 | m_host.AddScriptLPS(1); | ||
4604 | |||
4605 | SceneObjectGroup sog = m_host.ParentGroup; | ||
4606 | if(sog== null || sog.IsDeleted) | ||
4607 | return; | ||
4608 | |||
4609 | if(mass < 0) | ||
4610 | return; | ||
4611 | |||
4612 | // need more checks | ||
4613 | |||
4614 | Vector3 CenterOfMass = new Vector3((float)centerOfMass.x,(float)centerOfMass.y,(float)centerOfMass.z); | ||
4615 | Vector3 Inertia; | ||
4616 | float m = (float)mass; | ||
4617 | float r = (float)radius; | ||
4618 | r *= r; | ||
4619 | Inertia.Z = 0.5f * m * r; | ||
4620 | float t = (float)lenght; | ||
4621 | t *= t; | ||
4622 | t += 3.0f * r; | ||
4623 | t *= 8.333333e-2f * m; | ||
4624 | |||
4625 | Inertia.X = t; | ||
4626 | Inertia.Y = t; | ||
4627 | |||
4628 | Vector4 rot = new Vector4((float)lslrot.x, (float)lslrot.y, (float)lslrot.z, (float)lslrot.s); | ||
4629 | rot.Normalize(); | ||
4630 | |||
4631 | sog.SetInertiaData(m, CenterOfMass, Inertia, rot); | ||
4632 | } | ||
4633 | |||
4634 | /// <summary> | ||
4635 | /// removes inertial data manual override | ||
4636 | /// default automatic calculation is used again | ||
4637 | /// | ||
4638 | /// </summary> | ||
4639 | public void osClearInertia() | ||
4640 | { | ||
4641 | CheckThreatLevel(); | ||
4642 | m_host.AddScriptLPS(1); | ||
4643 | |||
4644 | SceneObjectGroup sog = m_host.ParentGroup; | ||
4645 | if(sog== null || sog.IsDeleted) | ||
4646 | return; | ||
4647 | |||
4648 | sog.SetInertiaData(-1, Vector3.Zero, Vector3.Zero, Vector4.Zero ); | ||
4649 | } | ||
4650 | |||
4651 | /// <summary> | ||
4652 | /// teleports a object (full linkset) | ||
4653 | /// </summary> | ||
4654 | /// <param name="objectUUID">the id of the linkset to teleport</param> | ||
4655 | /// <param name="targetPos">target position</param> | ||
4656 | /// <param name="rotation"> a rotation to apply</param> | ||
4657 | /// <param name="flags">several flags/param> | ||
4658 | /// <remarks> | ||
4659 | /// only does teleport local to region | ||
4660 | /// if object has scripts, owner must have rights to run scripts on target location | ||
4661 | /// object owner must have rights to enter ojects on target location | ||
4662 | /// target location parcel must have enought free prims capacity for the linkset prims | ||
4663 | /// all avatars siting on the object must have access to target location | ||
4664 | /// has a cool down time. retries before expire reset it | ||
4665 | /// fail conditions are silent ignored | ||
4666 | /// </remarks> | ||
4667 | public LSL_Integer osTeleportObject(LSL_Key objectUUID, LSL_Vector targetPos, LSL_Rotation rotation, LSL_Integer flags) | ||
4668 | { | ||
4669 | CheckThreatLevel(ThreatLevel.Severe, "osTeleportObject"); | ||
4670 | m_host.AddScriptLPS(1); | ||
4671 | |||
4672 | UUID objUUID; | ||
4673 | if (!UUID.TryParse(objectUUID, out objUUID)) | ||
4674 | { | ||
4675 | OSSLShoutError("osTeleportObject() invalid object Key"); | ||
4676 | return -1; | ||
4677 | } | ||
4678 | |||
4679 | SceneObjectGroup sog = World.GetSceneObjectGroup(objUUID); | ||
4680 | if(sog== null || sog.IsDeleted) | ||
4681 | return -1; | ||
4682 | |||
4683 | UUID myid = m_host.ParentGroup.UUID; | ||
4684 | |||
4685 | return sog.TeleportObject(myid, targetPos, rotation, flags); | ||
4686 | // a delay here may break vehicles | ||
4687 | } | ||
4688 | |||
4689 | public LSL_Integer osGetLinkNumber(LSL_String name) | ||
4690 | { | ||
4691 | CheckThreatLevel(); | ||
4692 | m_host.AddScriptLPS(1); | ||
4693 | |||
4694 | SceneObjectGroup sog = m_host.ParentGroup; | ||
4695 | if(sog== null || sog.IsDeleted) | ||
4696 | return -1; | ||
4697 | return sog.GetLinkNumber(name); | ||
4698 | } | ||
4405 | } | 4699 | } |
4406 | } | 4700 | } |