diff options
11 files changed, 243 insertions, 63 deletions
diff --git a/OpenSim/Framework/ThrottleOutPacketType.cs b/OpenSim/Framework/ThrottleOutPacketType.cs index d56231a..ca4b126 100644 --- a/OpenSim/Framework/ThrottleOutPacketType.cs +++ b/OpenSim/Framework/ThrottleOutPacketType.cs | |||
@@ -47,9 +47,6 @@ namespace OpenSim.Framework | |||
47 | Texture = 5, | 47 | Texture = 5, |
48 | /// <summary>Non-texture assets</summary> | 48 | /// <summary>Non-texture assets</summary> |
49 | Asset = 6, | 49 | Asset = 6, |
50 | /// <summary>Avatar and primitive data</summary> | ||
51 | /// <remarks>This is a sub-category of Task</remarks> | ||
52 | State = 7, | ||
53 | } | 50 | } |
54 | 51 | ||
55 | [Flags] | 52 | [Flags] |
@@ -61,6 +58,5 @@ namespace OpenSim.Framework | |||
61 | Task = 1 << 3, | 58 | Task = 1 << 3, |
62 | Texture = 1 << 4, | 59 | Texture = 1 << 4, |
63 | Asset = 1 << 5, | 60 | Asset = 1 << 5, |
64 | State = 1 << 6, | ||
65 | } | 61 | } |
66 | } | 62 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 6428b86..12e9d4d 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs | |||
@@ -1607,7 +1607,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1607 | 1607 | ||
1608 | if (localIDs.Count == 1 && m_scene.GetScenePresence(localIDs[0]) != null) | 1608 | if (localIDs.Count == 1 && m_scene.GetScenePresence(localIDs[0]) != null) |
1609 | { | 1609 | { |
1610 | OutPacket(kill, ThrottleOutPacketType.State); | 1610 | OutPacket(kill, ThrottleOutPacketType.Task); |
1611 | } | 1611 | } |
1612 | else | 1612 | else |
1613 | { | 1613 | { |
@@ -2788,7 +2788,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
2788 | Transfer.TransferInfo.Size = req.AssetInf.Data.Length; | 2788 | Transfer.TransferInfo.Size = req.AssetInf.Data.Length; |
2789 | Transfer.TransferInfo.TransferID = req.TransferRequestID; | 2789 | Transfer.TransferInfo.TransferID = req.TransferRequestID; |
2790 | Transfer.Header.Zerocoded = true; | 2790 | Transfer.Header.Zerocoded = true; |
2791 | OutPacket(Transfer, isWearable ? ThrottleOutPacketType.State : ThrottleOutPacketType.Asset); | 2791 | OutPacket(Transfer, isWearable ? ThrottleOutPacketType.Task : ThrottleOutPacketType.Asset); |
2792 | 2792 | ||
2793 | if (req.NumPackets == 1) | 2793 | if (req.NumPackets == 1) |
2794 | { | 2794 | { |
@@ -2799,7 +2799,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
2799 | TransferPacket.TransferData.Data = req.AssetInf.Data; | 2799 | TransferPacket.TransferData.Data = req.AssetInf.Data; |
2800 | TransferPacket.TransferData.Status = 1; | 2800 | TransferPacket.TransferData.Status = 1; |
2801 | TransferPacket.Header.Zerocoded = true; | 2801 | TransferPacket.Header.Zerocoded = true; |
2802 | OutPacket(TransferPacket, isWearable ? ThrottleOutPacketType.State : ThrottleOutPacketType.Asset); | 2802 | OutPacket(TransferPacket, isWearable ? ThrottleOutPacketType.Task : ThrottleOutPacketType.Asset); |
2803 | } | 2803 | } |
2804 | else | 2804 | else |
2805 | { | 2805 | { |
@@ -2832,7 +2832,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
2832 | TransferPacket.TransferData.Status = 1; | 2832 | TransferPacket.TransferData.Status = 1; |
2833 | } | 2833 | } |
2834 | TransferPacket.Header.Zerocoded = true; | 2834 | TransferPacket.Header.Zerocoded = true; |
2835 | OutPacket(TransferPacket, isWearable ? ThrottleOutPacketType.State : ThrottleOutPacketType.Asset); | 2835 | OutPacket(TransferPacket, isWearable ? ThrottleOutPacketType.Task : ThrottleOutPacketType.Asset); |
2836 | 2836 | ||
2837 | processedLength += chunkSize; | 2837 | processedLength += chunkSize; |
2838 | packetNumber++; | 2838 | packetNumber++; |
@@ -3605,7 +3605,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3605 | } | 3605 | } |
3606 | } | 3606 | } |
3607 | 3607 | ||
3608 | OutPacket(aw, ThrottleOutPacketType.State); | 3608 | OutPacket(aw, ThrottleOutPacketType.Task); |
3609 | } | 3609 | } |
3610 | 3610 | ||
3611 | public void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry) | 3611 | public void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry) |
@@ -3630,7 +3630,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
3630 | avp.Sender.IsTrial = false; | 3630 | avp.Sender.IsTrial = false; |
3631 | avp.Sender.ID = agentID; | 3631 | avp.Sender.ID = agentID; |
3632 | m_log.DebugFormat("[CLIENT]: Sending appearance for {0} to {1}", agentID.ToString(), AgentId.ToString()); | 3632 | m_log.DebugFormat("[CLIENT]: Sending appearance for {0} to {1}", agentID.ToString(), AgentId.ToString()); |
3633 | OutPacket(avp, ThrottleOutPacketType.State); | 3633 | OutPacket(avp, ThrottleOutPacketType.Task); |
3634 | } | 3634 | } |
3635 | 3635 | ||
3636 | public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) | 3636 | public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs index f675377..f1a1812 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs | |||
@@ -279,7 +279,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
279 | public string GetStats() | 279 | public string GetStats() |
280 | { | 280 | { |
281 | return string.Format( | 281 | return string.Format( |
282 | "{0,7} {1,7} {2,7} {3,9} {4,7} {5,7} {6,7} {7,7} {8,7} {9,8} {10,7} {11,7} {12,7}", | 282 | "{0,7} {1,7} {2,7} {3,9} {4,7} {5,7} {6,7} {7,7} {8,7} {9,8} {10,7} {11,7}", |
283 | Util.EnvironmentTickCountSubtract(TickLastPacketReceived), | 283 | Util.EnvironmentTickCountSubtract(TickLastPacketReceived), |
284 | PacketsReceived, | 284 | PacketsReceived, |
285 | PacketsSent, | 285 | PacketsSent, |
@@ -291,8 +291,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
291 | m_packetOutboxes[(int)ThrottleOutPacketType.Cloud].Count, | 291 | m_packetOutboxes[(int)ThrottleOutPacketType.Cloud].Count, |
292 | m_packetOutboxes[(int)ThrottleOutPacketType.Task].Count, | 292 | m_packetOutboxes[(int)ThrottleOutPacketType.Task].Count, |
293 | m_packetOutboxes[(int)ThrottleOutPacketType.Texture].Count, | 293 | m_packetOutboxes[(int)ThrottleOutPacketType.Texture].Count, |
294 | m_packetOutboxes[(int)ThrottleOutPacketType.Asset].Count, | 294 | m_packetOutboxes[(int)ThrottleOutPacketType.Asset].Count); |
295 | m_packetOutboxes[(int)ThrottleOutPacketType.State].Count); | ||
296 | } | 295 | } |
297 | 296 | ||
298 | public void SendPacketStats() | 297 | public void SendPacketStats() |
@@ -338,8 +337,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
338 | int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; | 337 | int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; |
339 | int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; | 338 | int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; |
340 | int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); | 339 | int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); |
341 | // State is a subcategory of task that we allocate a percentage to | ||
342 | int state = 0; | ||
343 | 340 | ||
344 | // Make sure none of the throttles are set below our packet MTU, | 341 | // Make sure none of the throttles are set below our packet MTU, |
345 | // otherwise a throttle could become permanently clogged | 342 | // otherwise a throttle could become permanently clogged |
@@ -376,9 +373,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
376 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; | 373 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; |
377 | bucket.RequestedDripRate = task; | 374 | bucket.RequestedDripRate = task; |
378 | 375 | ||
379 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; | ||
380 | bucket.RequestedDripRate = state; | ||
381 | |||
382 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; | 376 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; |
383 | bucket.RequestedDripRate = texture; | 377 | bucket.RequestedDripRate = texture; |
384 | 378 | ||
@@ -709,9 +703,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
709 | Texture = 5, | 703 | Texture = 5, |
710 | /// <summary>Non-texture assets</summary> | 704 | /// <summary>Non-texture assets</summary> |
711 | Asset = 6, | 705 | Asset = 6, |
712 | /// <summary>Avatar and primitive data</summary> | ||
713 | /// <remarks>This is a sub-category of Task</remarks> | ||
714 | State = 7, | ||
715 | */ | 706 | */ |
716 | 707 | ||
717 | switch (category) | 708 | switch (category) |
@@ -728,8 +719,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
728 | return ThrottleOutPacketTypeFlags.Texture; | 719 | return ThrottleOutPacketTypeFlags.Texture; |
729 | case ThrottleOutPacketType.Asset: | 720 | case ThrottleOutPacketType.Asset: |
730 | return ThrottleOutPacketTypeFlags.Asset; | 721 | return ThrottleOutPacketTypeFlags.Asset; |
731 | case ThrottleOutPacketType.State: | ||
732 | return ThrottleOutPacketTypeFlags.State; | ||
733 | default: | 722 | default: |
734 | return 0; | 723 | return 0; |
735 | } | 724 | } |
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs index f3a0b01..9b78b3b 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs | |||
@@ -212,11 +212,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
212 | protected override GridRegion GetFinalDestination(GridRegion region) | 212 | protected override GridRegion GetFinalDestination(GridRegion region) |
213 | { | 213 | { |
214 | int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, region.RegionID); | 214 | int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, region.RegionID); |
215 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: region {0} flags: {1}", region.RegionID, flags); | 215 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: region {0} flags: {1}", region.RegionName, flags); |
216 | 216 | ||
217 | if ((flags & (int)OpenSim.Framework.RegionFlags.Hyperlink) != 0) | 217 | if ((flags & (int)OpenSim.Framework.RegionFlags.Hyperlink) != 0) |
218 | { | 218 | { |
219 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Destination region {0} is hyperlink", region.RegionID); | 219 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Destination region is hyperlink"); |
220 | GridRegion real_destination = m_GatekeeperConnector.GetHyperlinkRegion(region, region.RegionID); | 220 | GridRegion real_destination = m_GatekeeperConnector.GetHyperlinkRegion(region, region.RegionID); |
221 | if (real_destination != null) | 221 | if (real_destination != null) |
222 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: GetFinalDestination serveruri -> {0}", real_destination.ServerURI); | 222 | m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: GetFinalDestination serveruri -> {0}", real_destination.ServerURI); |
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index a5fec87..87a06c1 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs | |||
@@ -215,7 +215,7 @@ public sealed class BSCharacter : BSPhysObject | |||
215 | // Add special movement force to allow avatars to walk up stepped surfaces. | 215 | // Add special movement force to allow avatars to walk up stepped surfaces. |
216 | moveForce += WalkUpStairs(); | 216 | moveForce += WalkUpStairs(); |
217 | 217 | ||
218 | DetailLog("{0},BSCharacter.MoveMotor,move,stepVel={1},vel={2},mass={3},moveForce={4}", LocalID, stepVelocity, _velocity, Mass, moveForce); | 218 | // DetailLog("{0},BSCharacter.MoveMotor,move,stepVel={1},vel={2},mass={3},moveForce={4}", LocalID, stepVelocity, _velocity, Mass, moveForce); |
219 | PhysicsScene.PE.ApplyCentralImpulse(PhysBody, moveForce); | 219 | PhysicsScene.PE.ApplyCentralImpulse(PhysBody, moveForce); |
220 | }); | 220 | }); |
221 | } | 221 | } |
@@ -855,7 +855,10 @@ public sealed class BSCharacter : BSPhysObject | |||
855 | _rotationalVelocity = entprop.RotationalVelocity; | 855 | _rotationalVelocity = entprop.RotationalVelocity; |
856 | 856 | ||
857 | // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. | 857 | // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. |
858 | PositionSanityCheck(true); | 858 | if (PositionSanityCheck(true)) |
859 | { | ||
860 | entprop.Position = _position; | ||
861 | } | ||
859 | 862 | ||
860 | // remember the current and last set values | 863 | // remember the current and last set values |
861 | LastEntityProperties = CurrentEntityProperties; | 864 | LastEntityProperties = CurrentEntityProperties; |
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index e434412..6601479 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs | |||
@@ -1160,8 +1160,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
1160 | if (!Prim.IsColliding && VehicleVelocity.Z > 0.1) | 1160 | if (!Prim.IsColliding && VehicleVelocity.Z > 0.1) |
1161 | { | 1161 | { |
1162 | // Get rid of any of the velocity vector that is pushing us up. | 1162 | // Get rid of any of the velocity vector that is pushing us up. |
1163 | VehicleVelocity += new Vector3(0, 0, -VehicleVelocity.Z); | 1163 | float upVelocity = VehicleVelocity.Z; |
1164 | VehicleVelocity += new Vector3(0, 0, -upVelocity); | ||
1164 | 1165 | ||
1166 | /* | ||
1165 | // If we're pointed up into the air, we should nose down | 1167 | // If we're pointed up into the air, we should nose down |
1166 | Vector3 pointingDirection = Vector3.UnitX * VehicleOrientation; | 1168 | Vector3 pointingDirection = Vector3.UnitX * VehicleOrientation; |
1167 | // The rotation around the Y axis is pitch up or down | 1169 | // The rotation around the Y axis is pitch up or down |
@@ -1175,11 +1177,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
1175 | VDetailLog("{0}, MoveLinear,limitMotorUp,newVel={1},pntDir={2},corrFrc={3},aCorr={4}", | 1177 | VDetailLog("{0}, MoveLinear,limitMotorUp,newVel={1},pntDir={2},corrFrc={3},aCorr={4}", |
1176 | Prim.LocalID, VehicleVelocity, pointingDirection, angularCorrectionForce, angularCorrectionVector); | 1178 | Prim.LocalID, VehicleVelocity, pointingDirection, angularCorrectionForce, angularCorrectionVector); |
1177 | } | 1179 | } |
1178 | else | 1180 | */ |
1179 | { | 1181 | VDetailLog("{0}, MoveLinear,limitMotorUp,collide={1},upVel={2},newVel={3}", |
1180 | VDetailLog("{0}, MoveLinear,limitMotorUp,newVel={1},pntDir={2}", | 1182 | Prim.LocalID, Prim.IsColliding, upVelocity, VehicleVelocity); |
1181 | Prim.LocalID, VehicleVelocity, pointingDirection); | ||
1182 | } | ||
1183 | } | 1183 | } |
1184 | } | 1184 | } |
1185 | } | 1185 | } |
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs index 2dc89b5..8c9a774 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs | |||
@@ -108,8 +108,8 @@ public sealed class BSLinksetCompound : BSLinkset | |||
108 | // Schedule a refresh to happen after all the other taint processing. | 108 | // Schedule a refresh to happen after all the other taint processing. |
109 | private void ScheduleRebuild(BSPhysObject requestor) | 109 | private void ScheduleRebuild(BSPhysObject requestor) |
110 | { | 110 | { |
111 | DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1},hasChildren={2}", | 111 | DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1},hasChildren={2},actuallyScheduling={3}", |
112 | requestor.LocalID, Rebuilding, HasAnyChildren); | 112 | requestor.LocalID, Rebuilding, HasAnyChildren, (!Rebuilding && HasAnyChildren)); |
113 | // When rebuilding, it is possible to set properties that would normally require a rebuild. | 113 | // When rebuilding, it is possible to set properties that would normally require a rebuild. |
114 | // If already rebuilding, don't request another rebuild. | 114 | // If already rebuilding, don't request another rebuild. |
115 | // If a linkset with just a root prim (simple non-linked prim) don't bother rebuilding. | 115 | // If a linkset with just a root prim (simple non-linked prim) don't bother rebuilding. |
@@ -311,7 +311,7 @@ public sealed class BSLinksetCompound : BSLinkset | |||
311 | else | 311 | else |
312 | { | 312 | { |
313 | // Rebuild the compound shape with the child removed | 313 | // Rebuild the compound shape with the child removed |
314 | ScheduleRebuild(child); | 314 | ScheduleRebuild(LinksetRoot); |
315 | } | 315 | } |
316 | } | 316 | } |
317 | return; | 317 | return; |
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs index 862dbf6..3e80aa4 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs | |||
@@ -94,16 +94,16 @@ public static class BSParam | |||
94 | public static float PID_D { get; private set; } // derivative | 94 | public static float PID_D { get; private set; } // derivative |
95 | public static float PID_P { get; private set; } // proportional | 95 | public static float PID_P { get; private set; } // proportional |
96 | 96 | ||
97 | // Various constants that come from that other virtual world that shall not be named | 97 | // Various constants that come from that other virtual world that shall not be named. |
98 | public const float MinGravityZ = -1f; | 98 | public const float MinGravityZ = -1f; |
99 | public const float MaxGravityZ = 28f; | 99 | public const float MaxGravityZ = 28f; |
100 | public const float MinFriction = 0f; | 100 | public const float MinFriction = 0f; |
101 | public const float MaxFriction = 255f; | 101 | public const float MaxFriction = 255f; |
102 | public const float MinDensity = 0f; | 102 | public const float MinDensity = 0.01f; |
103 | public const float MaxDensity = 22587f; | 103 | public const float MaxDensity = 22587f; |
104 | public const float MinRestitution = 0f; | 104 | public const float MinRestitution = 0f; |
105 | public const float MaxRestitution = 1f; | 105 | public const float MaxRestitution = 1f; |
106 | public const float MaxAddForceMagnitude = 20000f; | 106 | public const float MaxAddForceMagnitude = 20f; |
107 | 107 | ||
108 | // =========================================================================== | 108 | // =========================================================================== |
109 | public delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val); | 109 | public delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val); |
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index 838c845..473ef10 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs | |||
@@ -442,7 +442,8 @@ public sealed class BSShapeCollection : IDisposable | |||
442 | return ret; | 442 | return ret; |
443 | } | 443 | } |
444 | 444 | ||
445 | // Create a mesh/hull shape or a native shape if 'nativeShapePossible' is 'true'. | 445 | // Create a mesh, hull or native shape. |
446 | // Return 'true' if the prim's shape was changed. | ||
446 | public bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback) | 447 | public bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback) |
447 | { | 448 | { |
448 | bool ret = false; | 449 | bool ret = false; |
@@ -472,7 +473,7 @@ public sealed class BSShapeCollection : IDisposable | |||
472 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}", | 473 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}", |
473 | prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.type); | 474 | prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.type); |
474 | 475 | ||
475 | // It doesn't look like Bullet scales spheres so make sure the scales are all equal | 476 | // It doesn't look like Bullet scales native spheres so make sure the scales are all equal |
476 | if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) | 477 | if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) |
477 | && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z) | 478 | && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z) |
478 | { | 479 | { |
@@ -484,9 +485,9 @@ public sealed class BSShapeCollection : IDisposable | |||
484 | { | 485 | { |
485 | ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE, | 486 | ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE, |
486 | FixedShapeKey.KEY_SPHERE, shapeCallback); | 487 | FixedShapeKey.KEY_SPHERE, shapeCallback); |
487 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}", | ||
488 | prim.LocalID, forceRebuild, prim.PhysShape); | ||
489 | } | 488 | } |
489 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},rebuilt={2},shape={3}", | ||
490 | prim.LocalID, forceRebuild, ret, prim.PhysShape); | ||
490 | } | 491 | } |
491 | if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) | 492 | if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) |
492 | { | 493 | { |
@@ -498,9 +499,9 @@ public sealed class BSShapeCollection : IDisposable | |||
498 | { | 499 | { |
499 | ret = GetReferenceToNativeShape( prim, BSPhysicsShapeType.SHAPE_BOX, | 500 | ret = GetReferenceToNativeShape( prim, BSPhysicsShapeType.SHAPE_BOX, |
500 | FixedShapeKey.KEY_BOX, shapeCallback); | 501 | FixedShapeKey.KEY_BOX, shapeCallback); |
501 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}", | ||
502 | prim.LocalID, forceRebuild, prim.PhysShape); | ||
503 | } | 502 | } |
503 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},rebuilt={2},shape={3}", | ||
504 | prim.LocalID, forceRebuild, ret, prim.PhysShape); | ||
504 | } | 505 | } |
505 | } | 506 | } |
506 | 507 | ||
@@ -513,6 +514,7 @@ public sealed class BSShapeCollection : IDisposable | |||
513 | return ret; | 514 | return ret; |
514 | } | 515 | } |
515 | 516 | ||
517 | // return 'true' if the prim's shape was changed. | ||
516 | public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback) | 518 | public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback) |
517 | { | 519 | { |
518 | 520 | ||
@@ -872,8 +874,7 @@ public sealed class BSShapeCollection : IDisposable | |||
872 | { | 874 | { |
873 | prim.LastAssetBuildFailed = true; | 875 | prim.LastAssetBuildFailed = true; |
874 | BSPhysObject xprim = prim; | 876 | BSPhysObject xprim = prim; |
875 | DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lID={1},lastFailed={2}", | 877 | DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lastFailed={1}", prim.LocalID, prim.LastAssetBuildFailed); |
876 | LogHeader, prim.LocalID, prim.LastAssetBuildFailed); | ||
877 | Util.FireAndForget(delegate | 878 | Util.FireAndForget(delegate |
878 | { | 879 | { |
879 | RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod; | 880 | RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod; |
@@ -882,19 +883,34 @@ public sealed class BSShapeCollection : IDisposable | |||
882 | BSPhysObject yprim = xprim; // probably not necessary, but, just in case. | 883 | BSPhysObject yprim = xprim; // probably not necessary, but, just in case. |
883 | assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) | 884 | assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) |
884 | { | 885 | { |
885 | if (!yprim.BaseShape.SculptEntry) | 886 | bool assetFound = false; // DEBUG DEBUG |
886 | return; | 887 | string mismatchIDs = String.Empty; // DEBUG DEBUG |
887 | if (yprim.BaseShape.SculptTexture.ToString() != asset.ID) | 888 | if (yprim.BaseShape.SculptEntry) |
888 | return; | 889 | { |
889 | 890 | if (yprim.BaseShape.SculptTexture.ToString() == asset.ID) | |
890 | yprim.BaseShape.SculptData = asset.Data; | 891 | { |
891 | // This will cause the prim to see that the filler shape is not the right | 892 | yprim.BaseShape.SculptData = asset.Data; |
892 | // one and try again to build the object. | 893 | // This will cause the prim to see that the filler shape is not the right |
893 | // No race condition with the normal shape setting since the rebuild is at taint time. | 894 | // one and try again to build the object. |
894 | yprim.ForceBodyShapeRebuild(false); | 895 | // No race condition with the normal shape setting since the rebuild is at taint time. |
896 | yprim.ForceBodyShapeRebuild(false /* inTaintTime */); | ||
897 | assetFound = true; | ||
898 | } | ||
899 | else | ||
900 | { | ||
901 | mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID; | ||
902 | } | ||
903 | } | ||
904 | DetailLog("{0},BSShapeCollection,fetchAssetCallback,found={1},isSculpt={2},ids={3}", | ||
905 | yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs ); | ||
895 | 906 | ||
896 | }); | 907 | }); |
897 | } | 908 | } |
909 | else | ||
910 | { | ||
911 | PhysicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}", | ||
912 | LogHeader, PhysicsScene.Name); | ||
913 | } | ||
898 | }); | 914 | }); |
899 | } | 915 | } |
900 | else | 916 | else |
@@ -906,9 +922,9 @@ public sealed class BSShapeCollection : IDisposable | |||
906 | } | 922 | } |
907 | } | 923 | } |
908 | 924 | ||
909 | // While we figure out the real problem, stick in a simple box for the object. | 925 | // While we wait for the mesh defining asset to be loaded, stick in a simple box for the object. |
910 | BulletShape fillinShape = | 926 | BulletShape fillinShape = BuildPhysicalNativeShape(prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); |
911 | BuildPhysicalNativeShape(prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); | 927 | DetailLog("{0},BSShapeCollection.VerifyMeshCreated,boxTempShape", prim.LocalID); |
912 | 928 | ||
913 | return fillinShape; | 929 | return fillinShape; |
914 | } | 930 | } |
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt index 59cbab9..d4545f7 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt | |||
@@ -1,7 +1,14 @@ | |||
1 | CURRENT PRIORITIES | 1 | CURRENT PRIORITIES |
2 | ================================================= | 2 | ================================================= |
3 | Mantis 6040 script http://opensimulator.org/mantis/view.php?id=6040 | ||
4 | Msg Kayaker on OSGrid when working | ||
5 | Teravus llMoveToTarget script debug | ||
6 | Mixing of hover, buoyancy/gravity, moveToTarget, into one force | ||
7 | Boats floating at proper level | ||
3 | Nebadon vehicles turning funny in arena | 8 | Nebadon vehicles turning funny in arena |
4 | limitMotorUp calibration (more down?) | 9 | limitMotorUp calibration (more down?) |
10 | llRotLookAt | ||
11 | llLookAt | ||
5 | Vehicle angular vertical attraction | 12 | Vehicle angular vertical attraction |
6 | Vehicle angular deflection | 13 | Vehicle angular deflection |
7 | Preferred orientation angular correction fix | 14 | Preferred orientation angular correction fix |
@@ -9,8 +16,6 @@ vehicle angular banking | |||
9 | Avatars walking up stairs (HALF DONE) | 16 | Avatars walking up stairs (HALF DONE) |
10 | Radius of the capsule affects ability to climb edges. | 17 | Radius of the capsule affects ability to climb edges. |
11 | Vehicle movement on terrain smoothness | 18 | Vehicle movement on terrain smoothness |
12 | Surfboard go wonky when turning | ||
13 | Angular motor direction is global coordinates rather than local coordinates? | ||
14 | Boats float low in the water (DONE) | 19 | Boats float low in the water (DONE) |
15 | Avatar movement | 20 | Avatar movement |
16 | flying into a wall doesn't stop avatar who keeps appearing to move through the obstacle (DONE) | 21 | flying into a wall doesn't stop avatar who keeps appearing to move through the obstacle (DONE) |
@@ -27,6 +32,10 @@ Add material densities to the material types | |||
27 | 32 | ||
28 | CRASHES | 33 | CRASHES |
29 | ================================================= | 34 | ================================================= |
35 | Crazyness during 20130115 office hours was PositionAdjustUnderground for both char and prim | ||
36 | m1:logs/20130115.0934/physics-BulletSim-20130115083613.log | ||
37 | Creation of Neb's terrain made the terrain "disappear". Everything started to fall | ||
38 | and then get restored to be above terrain. | ||
30 | 20121129.1411: editting/moving phys object across region boundries causes crash | 39 | 20121129.1411: editting/moving phys object across region boundries causes crash |
31 | getPos-> btRigidBody::upcast -> getBodyType -> BOOM | 40 | getPos-> btRigidBody::upcast -> getBodyType -> BOOM |
32 | 20121128.1600: mesh object not rezzing (no physics mesh). | 41 | 20121128.1600: mesh object not rezzing (no physics mesh). |
@@ -111,6 +120,8 @@ Physical and phantom will drop through the terrain | |||
111 | 120 | ||
112 | LINKSETS | 121 | LINKSETS |
113 | ====================================================== | 122 | ====================================================== |
123 | Editing a child of a linkset causes the child to go phantom | ||
124 | Move a child prim once when it is physical and can never move it again without it going phantom | ||
114 | Offset the center of the linkset to be the geometric center of all the prims | 125 | Offset the center of the linkset to be the geometric center of all the prims |
115 | Not quite the same as the center-of-gravity | 126 | Not quite the same as the center-of-gravity |
116 | Linksets should allow collisions to individual children | 127 | Linksets should allow collisions to individual children |
@@ -133,6 +144,10 @@ Eliminate collisions between objects in a linkset. (LinksetConstraint) | |||
133 | 144 | ||
134 | MORE | 145 | MORE |
135 | ====================================================== | 146 | ====================================================== |
147 | Create tests for different interface components | ||
148 | Have test objects/scripts measure themselves and turn color if correct/bad | ||
149 | Test functions in SL and calibrate correctness there | ||
150 | Create auto rezzer and tracker to run through the tests | ||
136 | Use the HACD convex hull routine in Bullet rather than the C# version. | 151 | Use the HACD convex hull routine in Bullet rather than the C# version. |
137 | Do we need to do convex hulls all the time? Can complex meshes be left meshes? | 152 | Do we need to do convex hulls all the time? Can complex meshes be left meshes? |
138 | There is some problem with meshes and collisions | 153 | There is some problem with meshes and collisions |
@@ -167,6 +182,7 @@ Enforce physical parameter min/max: | |||
167 | Restitution [0, 1] | 182 | Restitution [0, 1] |
168 | http://wiki.secondlife.com/wiki/Physics_Material_Settings_test | 183 | http://wiki.secondlife.com/wiki/Physics_Material_Settings_test |
169 | Avatar attachments have no mass? http://forums-archive.secondlife.com/54/f0/31796/1.html | 184 | Avatar attachments have no mass? http://forums-archive.secondlife.com/54/f0/31796/1.html |
185 | Keep avatar scaling correct. http://pennycow.blogspot.fr/2011/07/matter-of-scale.html | ||
170 | 186 | ||
171 | INTERNAL IMPROVEMENT/CLEANUP | 187 | INTERNAL IMPROVEMENT/CLEANUP |
172 | ================================================= | 188 | ================================================= |
@@ -287,4 +303,7 @@ Disable activity of passive linkset children. (DONE) | |||
287 | Since the linkset is a compound object, the old prims are left lying | 303 | Since the linkset is a compound object, the old prims are left lying |
288 | around and need to be phantomized so they don't collide, ... | 304 | around and need to be phantomized so they don't collide, ... |
289 | Remove HeightmapInfo from terrain specification (DONE) | 305 | Remove HeightmapInfo from terrain specification (DONE) |
290 | Since C++ code does not need terrain height, this structure et al are not needed. \ No newline at end of file | 306 | Since C++ code does not need terrain height, this structure et al are not needed. |
307 | Surfboard go wonky when turning (DONE) | ||
308 | Angular motor direction is global coordinates rather than local coordinates? | ||
309 | (Resolution: made angular motor direction correct coordinate system) \ No newline at end of file | ||
diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/Tests/CoopTerminationTests.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/Tests/CoopTerminationTests.cs new file mode 100644 index 0000000..8c3e9e0 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/Shared/Instance/Tests/CoopTerminationTests.cs | |||
@@ -0,0 +1,157 @@ | |||
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.Threading; | ||
31 | using Nini.Config; | ||
32 | using NUnit.Framework; | ||
33 | using OpenMetaverse; | ||
34 | using OpenSim.Framework; | ||
35 | using OpenSim.Region.CoreModules.Scripting.WorldComm; | ||
36 | using OpenSim.Region.Framework.Scenes; | ||
37 | using OpenSim.Region.Framework.Interfaces; | ||
38 | using OpenSim.Region.ScriptEngine.XEngine; | ||
39 | using OpenSim.Tests.Common; | ||
40 | using OpenSim.Tests.Common.Mock; | ||
41 | |||
42 | namespace OpenSim.Region.ScriptEngine.Shared.Instance.Tests | ||
43 | { | ||
44 | /// <summary> | ||
45 | /// Test that co-operative script thread termination is working correctly. | ||
46 | /// </summary> | ||
47 | [TestFixture] | ||
48 | public class CoopTerminationTests : OpenSimTestCase | ||
49 | { | ||
50 | private TestScene m_scene; | ||
51 | private OpenSim.Region.ScriptEngine.XEngine.XEngine m_xEngine; | ||
52 | |||
53 | private AutoResetEvent m_chatEvent = new AutoResetEvent(false); | ||
54 | private AutoResetEvent m_stoppedEvent = new AutoResetEvent(false); | ||
55 | |||
56 | private OSChatMessage m_osChatMessageReceived; | ||
57 | |||
58 | [TestFixtureSetUp] | ||
59 | public void Init() | ||
60 | { | ||
61 | //AppDomain.CurrentDomain.SetData("APPBASE", Environment.CurrentDirectory + "/bin"); | ||
62 | // Console.WriteLine(AppDomain.CurrentDomain.BaseDirectory); | ||
63 | m_xEngine = new OpenSim.Region.ScriptEngine.XEngine.XEngine(); | ||
64 | |||
65 | IniConfigSource configSource = new IniConfigSource(); | ||
66 | |||
67 | IConfig startupConfig = configSource.AddConfig("Startup"); | ||
68 | startupConfig.Set("DefaultScriptEngine", "XEngine"); | ||
69 | |||
70 | IConfig xEngineConfig = configSource.AddConfig("XEngine"); | ||
71 | xEngineConfig.Set("Enabled", "true"); | ||
72 | xEngineConfig.Set("StartDelay", "0"); | ||
73 | |||
74 | // These tests will not run with AppDomainLoading = true, at least on mono. For unknown reasons, the call | ||
75 | // to AssemblyResolver.OnAssemblyResolve fails. | ||
76 | xEngineConfig.Set("AppDomainLoading", "false"); | ||
77 | |||
78 | xEngineConfig.Set("ScriptStopStrategy", "co-op"); | ||
79 | |||
80 | m_scene = new SceneHelpers().SetupScene("My Test", UUID.Random(), 1000, 1000, configSource); | ||
81 | SceneHelpers.SetupSceneModules(m_scene, configSource, m_xEngine); | ||
82 | m_scene.StartScripts(); | ||
83 | } | ||
84 | |||
85 | /// <summary> | ||
86 | /// Test co-operative termination on derez of an object containing a script with a long-running event. | ||
87 | /// </summary> | ||
88 | /// <remarks> | ||
89 | /// TODO: Actually compiling the script is incidental to this test. Really want a way to compile test scripts | ||
90 | /// within the build itself. | ||
91 | /// </remarks> | ||
92 | [Test] | ||
93 | public void TestStopOnLongSleep() | ||
94 | { | ||
95 | TestHelpers.InMethod(); | ||
96 | // TestHelpers.EnableLogging(); | ||
97 | |||
98 | UUID userId = TestHelpers.ParseTail(0x1); | ||
99 | // UUID objectId = TestHelpers.ParseTail(0x100); | ||
100 | // UUID itemId = TestHelpers.ParseTail(0x3); | ||
101 | string itemName = "TestStopOnObjectDerezLongSleep() Item"; | ||
102 | |||
103 | SceneObjectGroup so = SceneHelpers.CreateSceneObject(1, userId, "TestStopOnObjectDerezLongSleep", 0x100); | ||
104 | m_scene.AddNewSceneObject(so, true); | ||
105 | |||
106 | InventoryItemBase itemTemplate = new InventoryItemBase(); | ||
107 | // itemTemplate.ID = itemId; | ||
108 | itemTemplate.Name = itemName; | ||
109 | itemTemplate.Folder = so.UUID; | ||
110 | itemTemplate.InvType = (int)InventoryType.LSL; | ||
111 | |||
112 | m_scene.EventManager.OnChatFromWorld += OnChatFromWorld; | ||
113 | |||
114 | SceneObjectPart partWhereRezzed = m_scene.RezNewScript(userId, itemTemplate, | ||
115 | @"default | ||
116 | { | ||
117 | state_entry() | ||
118 | { | ||
119 | llSay(0, ""Thin Lizzy""); | ||
120 | llSleep(60); | ||
121 | } | ||
122 | }"); | ||
123 | |||
124 | TaskInventoryItem rezzedItem = partWhereRezzed.Inventory.GetInventoryItem(itemName); | ||
125 | |||
126 | // Wait for the script to start the event before we try stopping it. | ||
127 | m_chatEvent.WaitOne(60000); | ||
128 | |||
129 | Console.WriteLine("Script started with message [{0}]", m_osChatMessageReceived.Message); | ||
130 | |||
131 | // FIXME: This is a very poor way of trying to avoid a low-probability race condition where the script | ||
132 | // executes llSay() but has not started the sleep before we try to stop it. | ||
133 | Thread.Sleep(1000); | ||
134 | |||
135 | // We need a way of carrying on if StopScript() fail, since it won't return if the script isn't actually | ||
136 | // stopped. This kind of multi-threading is far from ideal in a regression test. | ||
137 | new Thread(() => { m_xEngine.StopScript(rezzedItem.ItemID); m_stoppedEvent.Set(); }).Start(); | ||
138 | |||
139 | if (!m_stoppedEvent.WaitOne(30000)) | ||
140 | Assert.Fail("Script did not co-operatively stop."); | ||
141 | |||
142 | bool running; | ||
143 | TaskInventoryItem scriptItem = partWhereRezzed.Inventory.GetInventoryItem(itemName); | ||
144 | Assert.That( | ||
145 | SceneObjectPartInventory.TryGetScriptInstanceRunning(m_scene, scriptItem, out running), Is.True); | ||
146 | Assert.That(running, Is.False); | ||
147 | } | ||
148 | |||
149 | private void OnChatFromWorld(object sender, OSChatMessage oscm) | ||
150 | { | ||
151 | // Console.WriteLine("Got chat [{0}]", oscm.Message); | ||
152 | |||
153 | m_osChatMessageReceived = oscm; | ||
154 | m_chatEvent.Set(); | ||
155 | } | ||
156 | } | ||
157 | } \ No newline at end of file | ||