From 0844f1eae430b2ad0d2627e230d08f0d94facb7a Mon Sep 17 00:00:00 2001 From: Joonas Rikkonen Date: Wed, 3 Apr 2019 16:21:22 +0300 Subject: [PATCH] (8e2e6e4da) Physics error prevention: - Use the ApplyForce/ApplyImpulse method overrides that clamp the resulting velocity to a value (in this case NetConfig.MaxPhysicsBodyVelocity, 64 m/s). Without this, explosions and attacks for example could apply arbitrarily large forces to bodies, causing physics errors in Ragdoll.CheckValidity. - Make sure angular velocity still has a sane value after applying an impulse to a specific point of a collider. --- .../Source/Characters/Animation/Ragdoll.cs | 4 +- .../Source/Characters/Character.cs | 7 ++- .../Source/Characters/Limb.cs | 9 ++- .../Items/Components/Holdable/RangedWeapon.cs | 6 +- .../Items/Components/Holdable/Throwable.cs | 6 +- .../Source/Items/Components/Projectile.cs | 5 +- .../Items/Components/Signal/LightComponent.cs | 4 ++ .../BarotraumaShared/Source/Items/Item.cs | 2 +- .../BarotraumaShared/Source/Map/Explosion.cs | 3 +- .../BarotraumaShared/Source/Map/Hull.cs | 9 +-- .../Map/Levels/LevelObjects/LevelTrigger.cs | 11 ++-- .../Source/Physics/PhysicsBody.cs | 60 +++++++++++++++---- 12 files changed, 87 insertions(+), 39 deletions(-) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs index f6aa7ed1c..29cbdadcb 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs @@ -1094,7 +1094,7 @@ namespace Barotrauma if (flowForce.LengthSquared() > 0.001f) { - Collider.ApplyForce(flowForce); + Collider.ApplyForce(flowForce, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); } if (currentHull == null || @@ -1132,7 +1132,7 @@ namespace Barotrauma if (flowForce.LengthSquared() > 0.001f) { - limb.body.ApplyForce(flowForce); + limb.body.ApplyForce(flowForce, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); } surfaceY = limbHull.Surface; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs index 612801496..ebbaceea1 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs @@ -2187,7 +2187,7 @@ namespace Barotrauma if (limbHit == null) return new AttackResult(); - limbHit.body?.ApplyLinearImpulse(attack.TargetImpulseWorld + attack.TargetForceWorld * deltaTime); + limbHit.body?.ApplyLinearImpulse(attack.TargetImpulseWorld + attack.TargetForceWorld * deltaTime, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); #if SERVER if (attacker is Character attackingCharacter && attackingCharacter.AIController == null) { @@ -2273,7 +2273,8 @@ namespace Barotrauma { Vector2 diff = dir; if (diff == Vector2.Zero) diff = Rand.Vector(1.0f); - hitLimb.body.ApplyLinearImpulse(Vector2.Normalize(diff) * attackImpulse, hitLimb.SimPosition + ConvertUnits.ToSimUnits(diff)); + hitLimb.body.ApplyLinearImpulse(Vector2.Normalize(diff) * attackImpulse, hitLimb.SimPosition + ConvertUnits.ToSimUnits(diff), + maxVelocity: NetConfig.MaxPhysicsBodyVelocity); } Vector2 simPos = hitLimb.SimPosition + ConvertUnits.ToSimUnits(dir); AttackResult attackResult = hitLimb.AddDamage(simPos, afflictions, playSound); @@ -2362,7 +2363,7 @@ namespace Barotrauma } if (diff == Vector2.Zero) { continue; } - limb.body.ApplyLinearImpulse(diff * 50.0f); + limb.body.ApplyLinearImpulse(diff * 50.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); } ImplodeFX(); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs b/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs index 32f020903..2814b127c 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Limb.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; +using Barotrauma.Networking; namespace Barotrauma { @@ -625,13 +626,17 @@ namespace Barotrauma Limb limb = character.AnimController.Limbs[limbIndex]; Vector2 forcePos = limb.pullJoint == null ? limb.body.SimPosition : limb.pullJoint.WorldAnchorA; - limb.body.ApplyLinearImpulse(limb.Mass * attack.Force * Vector2.Normalize(attackSimPos - SimPosition), forcePos); + limb.body.ApplyLinearImpulse(limb.Mass * attack.Force * Vector2.Normalize(attackSimPos - SimPosition), forcePos, + maxVelocity: NetConfig.MaxPhysicsBodyVelocity); } } else { Vector2 forcePos = pullJoint == null ? body.SimPosition : pullJoint.WorldAnchorA; - body.ApplyLinearImpulse(Mass * attack.Force * Vector2.Normalize(attackSimPos - SimPosition), forcePos); + body.ApplyLinearImpulse( + Mass * attack.Force * Vector2.Normalize(attackSimPos - SimPosition), + forcePos, + maxVelocity: NetConfig.MaxPhysicsBodyVelocity); } } return wasHit; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RangedWeapon.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RangedWeapon.cs index 25845c874..db3f7c9de 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RangedWeapon.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/RangedWeapon.cs @@ -1,4 +1,5 @@ -using FarseerPhysics; +using Barotrauma.Networking; +using FarseerPhysics; using FarseerPhysics.Collision; using FarseerPhysics.Dynamics; using Microsoft.Xna.Framework; @@ -168,7 +169,8 @@ namespace Barotrauma.Items.Components //recoil item.body.ApplyLinearImpulse( - new Vector2((float)Math.Cos(projectile.Item.body.Rotation), (float)Math.Sin(projectile.Item.body.Rotation)) * item.body.Mass * -50.0f); + new Vector2((float)Math.Cos(projectile.Item.body.Rotation), (float)Math.Sin(projectile.Item.body.Rotation)) * item.body.Mass * -50.0f, + maxVelocity: NetConfig.MaxPhysicsBodyVelocity); item.RemoveContained(projectile.Item); diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Throwable.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Throwable.cs index b1634a6b7..d52f4287e 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Throwable.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Throwable.cs @@ -106,10 +106,10 @@ namespace Barotrauma.Items.Components #endif Character thrower = picker; item.Drop(thrower, createNetworkEvent: GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer); - item.body.ApplyLinearImpulse(throwVector * throwForce * item.body.Mass * 3.0f); + item.body.ApplyLinearImpulse(throwVector * throwForce * item.body.Mass * 3.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); - ac.GetLimb(LimbType.Head).body.ApplyLinearImpulse(throwVector*10.0f); - ac.GetLimb(LimbType.Torso).body.ApplyLinearImpulse(throwVector * 10.0f); + ac.GetLimb(LimbType.Head).body.ApplyLinearImpulse(throwVector * 10.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); + ac.GetLimb(LimbType.Torso).body.ApplyLinearImpulse(throwVector * 10.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); Limb rightHand = ac.GetLimb(LimbType.RightHand); item.body.AngularVelocity = rightHand.body.AngularVelocity; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Projectile.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Projectile.cs index 1afa079da..596e50d1e 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Projectile.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Projectile.cs @@ -1,4 +1,5 @@ -using FarseerPhysics; +using Barotrauma.Networking; +using FarseerPhysics; using FarseerPhysics.Dynamics; using FarseerPhysics.Dynamics.Contacts; using FarseerPhysics.Dynamics.Joints; @@ -176,7 +177,7 @@ namespace Barotrauma.Items.Components item.Drop(null); item.body.Enabled = true; - item.body.ApplyLinearImpulse(impulse); + item.body.ApplyLinearImpulse(impulse, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); item.body.FarseerBody.OnCollision += OnProjectileCollision; item.body.FarseerBody.IsBullet = true; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/LightComponent.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/LightComponent.cs index 7845f609c..eebf1146a 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/LightComponent.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/LightComponent.cs @@ -26,6 +26,10 @@ namespace Barotrauma.Items.Components private float blinkTimer; + private bool itemLoaded; + + private float blinkTimer; + public PhysicsBody ParentBody; [Editable(MinValueFloat = 0.0f, MaxValueFloat = 2048.0f), Serialize(100.0f, true)] diff --git a/Barotrauma/BarotraumaShared/Source/Items/Item.cs b/Barotrauma/BarotraumaShared/Source/Items/Item.cs index a92c7583b..af2ac7d8e 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Item.cs @@ -1134,7 +1134,7 @@ namespace Barotrauma Vector2 drag = body.LinearVelocity * volume; - body.ApplyForce((uplift - drag) * 10.0f); + body.ApplyForce((uplift - drag) * 10.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); //apply simple angular drag body.ApplyTorque(body.AngularVelocity * volume * -0.05f); diff --git a/Barotrauma/BarotraumaShared/Source/Map/Explosion.cs b/Barotrauma/BarotraumaShared/Source/Map/Explosion.cs index 13c6965e9..a73ac8907 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Explosion.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Explosion.cs @@ -236,8 +236,9 @@ namespace Barotrauma { Vector2 limbDiff = Vector2.Normalize(limb.WorldPosition - worldPosition); if (!MathUtils.IsValid(limbDiff)) limbDiff = Rand.Vector(1.0f); + Vector2 impulse = limbDiff * distFactor * force; Vector2 impulsePoint = limb.SimPosition - limbDiff * limbRadius; - limb.body.ApplyLinearImpulse(limbDiff * distFactor * force, impulsePoint); + limb.body.ApplyLinearImpulse(impulse, impulsePoint, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); } } diff --git a/Barotrauma/BarotraumaShared/Source/Map/Hull.cs b/Barotrauma/BarotraumaShared/Source/Map/Hull.cs index 883c85f65..cc1b0cbbd 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Hull.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Hull.cs @@ -561,17 +561,14 @@ namespace Barotrauma { foreach (var gap in ConnectedGaps.Where(gap => gap.Open > 0)) { - //var pos = gap.Position - body.Position; - var distance = MathHelper.Max(Vector2.DistanceSquared(item.Position, gap.Position)/1000, 1f); - - //pos.Normalize(); - item.body.ApplyForce((gap.LerpedFlowForce/distance) * deltaTime); + var distance = MathHelper.Max(Vector2.DistanceSquared(item.Position, gap.Position) / 1000, 1f); + item.body.ApplyForce((gap.LerpedFlowForce / distance) * deltaTime, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); } } public void Extinguish(float deltaTime, float amount, Vector2 position) { - for (int i = FireSources.Count - 1; i >= 0; i-- ) + for (int i = FireSources.Count - 1; i >= 0; i--) { FireSources[i].Extinguish(deltaTime, amount, position); } diff --git a/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelTrigger.cs b/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelTrigger.cs index 1c3ec2339..0bcb4a4e7 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelTrigger.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Levels/LevelObjects/LevelTrigger.cs @@ -543,19 +543,19 @@ namespace Barotrauma if (ForceVelocityLimit < 1000.0f) body.ApplyForce(Force * currentForceFluctuation * distFactor, ForceVelocityLimit); else - body.ApplyForce(Force * currentForceFluctuation * distFactor); + body.ApplyForce(Force * currentForceFluctuation * distFactor, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); break; case TriggerForceMode.Acceleration: if (ForceVelocityLimit < 1000.0f) body.ApplyForce(Force * body.Mass * currentForceFluctuation * distFactor, ForceVelocityLimit); else - body.ApplyForce(Force * body.Mass * currentForceFluctuation * distFactor); + body.ApplyForce(Force * body.Mass * currentForceFluctuation * distFactor, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); break; case TriggerForceMode.Impulse: if (ForceVelocityLimit < 1000.0f) - body.ApplyLinearImpulse(Force * currentForceFluctuation * distFactor, ForceVelocityLimit); + body.ApplyLinearImpulse(Force * currentForceFluctuation * distFactor, maxVelocity: ForceVelocityLimit); else - body.ApplyLinearImpulse(Force * currentForceFluctuation * distFactor); + body.ApplyLinearImpulse(Force * currentForceFluctuation * distFactor, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); break; case TriggerForceMode.LimitVelocity: float maxVel = ForceVelocityLimit * currentForceFluctuation * distFactor; @@ -563,7 +563,8 @@ namespace Barotrauma { body.ApplyForce( Vector2.Normalize(-body.LinearVelocity) * - Force.Length() * body.Mass * currentForceFluctuation * distFactor); + Force.Length() * body.Mass * currentForceFluctuation * distFactor, + maxVelocity: NetConfig.MaxPhysicsBodyVelocity); } break; } diff --git a/Barotrauma/BarotraumaShared/Source/Physics/PhysicsBody.cs b/Barotrauma/BarotraumaShared/Source/Physics/PhysicsBody.cs index d87c6ab3b..e4ee52c4c 100644 --- a/Barotrauma/BarotraumaShared/Source/Physics/PhysicsBody.cs +++ b/Barotrauma/BarotraumaShared/Source/Physics/PhysicsBody.cs @@ -587,11 +587,16 @@ namespace Barotrauma if (!IsValidValue(impulse / body.Mass, "new velocity")) return; if (!IsValidValue(impulse, "impulse", -1e10f, 1e10f)) return; if (!IsValidValue(maxVelocity, "max velocity")) return; - - float currSpeed = body.LinearVelocity.Length(); + Vector2 velocityAddition = impulse / Mass; Vector2 newVelocity = body.LinearVelocity + velocityAddition; - newVelocity = newVelocity.ClampLength(Math.Max(currSpeed, maxVelocity)); + float newSpeedSqr = newVelocity.LengthSquared(); + if (newSpeedSqr > maxVelocity * maxVelocity) + { + newVelocity = newVelocity.ClampLength(maxVelocity); + } + + if (!IsValidValue((newVelocity - body.LinearVelocity), "new velocity", -1000.0f, 1000.0f)) return; body.ApplyLinearImpulse((newVelocity - body.LinearVelocity) * Mass); } @@ -600,10 +605,36 @@ namespace Barotrauma { if (!IsValidValue(impulse, "impulse", -1e10f, 1e10f)) return; if (!IsValidValue(point, "point")) return; - if (!IsValidValue(impulse / body.Mass, "new velocity")) return; + if (!IsValidValue(impulse / body.Mass, "new velocity", -1000.0f, 1000.0f)) return; body.ApplyLinearImpulse(impulse, point); } + /// + /// Apply an impulse to the body without increasing it's velocity above a specific limit. + /// + public void ApplyLinearImpulse(Vector2 impulse, Vector2 point, float maxVelocity) + { + if (!IsValidValue(impulse, "impulse", -1e10f, 1e10f)) return; + if (!IsValidValue(point, "point")) return; + if (!IsValidValue(maxVelocity, "max velocity")) return; + + Vector2 velocityAddition = impulse / Mass; + Vector2 newVelocity = body.LinearVelocity + velocityAddition; + float newSpeedSqr = newVelocity.LengthSquared(); + if (newSpeedSqr > maxVelocity * maxVelocity) + { + newVelocity = newVelocity.ClampLength(maxVelocity); + } + + if (!IsValidValue((newVelocity - body.LinearVelocity), "new velocity", -1000.0f, 1000.0f)) return; + + body.ApplyLinearImpulse((newVelocity - body.LinearVelocity) * Mass, point); + body.AngularVelocity = MathHelper.Clamp( + body.AngularVelocity, + -NetConfig.MaxPhysicsBodyAngularVelocity, + NetConfig.MaxPhysicsBodyAngularVelocity); + } + public void ApplyForce(Vector2 force) { if (!IsValidValue(force, "force", -1e10f, 1e10f)) return; @@ -618,11 +649,14 @@ namespace Barotrauma if (!IsValidValue(force, "force", -1e10f, 1e10f)) return; if (!IsValidValue(maxVelocity, "max velocity")) return; - float currSpeed = body.LinearVelocity.Length(); Vector2 velocityAddition = force / Mass * (float)Timing.Step; Vector2 newVelocity = body.LinearVelocity + velocityAddition; - newVelocity = newVelocity.ClampLength(Math.Max(currSpeed, maxVelocity)); - + + float newSpeedSqr = newVelocity.LengthSquared(); + if (newSpeedSqr > maxVelocity * maxVelocity) + { + newVelocity = newVelocity.ClampLength(maxVelocity); + } body.ApplyForce((newVelocity - body.LinearVelocity) * Mass / (float)Timing.Step); } @@ -725,17 +759,19 @@ namespace Barotrauma Vector2 dragForce = Vector2.Zero; - if (LinearVelocity.LengthSquared() > 0.00001f) + float speedSqr = LinearVelocity.LengthSquared(); + if (speedSqr > 0.00001f) { //drag - Vector2 velDir = Vector2.Normalize(LinearVelocity); + float speed = (float)Math.Sqrt(speedSqr); + Vector2 velDir = LinearVelocity / speed; - float vel = LinearVelocity.Length() * 2.0f; + float vel = speed * 2.0f; float drag = vel * vel * Math.Max(height + radius * 2, height); - dragForce = Math.Min(drag, Mass * 500.0f) * -velDir; + dragForce = Math.Min(drag, Mass * 500.0f) * -velDir; } - ApplyForce(dragForce + buoyancy); + ApplyForce(dragForce + buoyancy, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); ApplyTorque(body.AngularVelocity * body.Mass * -0.08f); }