diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 7dd4ba181..e5447f09b 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -38,6 +38,14 @@ namespace Barotrauma //components that determine the functionality of the item public List components; + public PhysicsBody body; + + private float condition; + + private bool inWater; + + private Inventory parentInventory; + public readonly Dictionary properties; public Dictionary ObjectProperties { @@ -57,14 +65,6 @@ namespace Barotrauma } } - public PhysicsBody body; - - private float condition; - - private bool inWater; - - private Inventory parentInventory; - //the inventory in which the item is contained in public Inventory ParentInventory { @@ -617,38 +617,65 @@ namespace Barotrauma if (Math.Abs(body.LinearVelocity.X) > 0.01f || Math.Abs(body.LinearVelocity.Y) > 0.01f) { FindHull(); + + Vector2 moveAmount = body.SimPosition - body.LastSentPosition; + if (moveAmount != Vector2.Zero && moveAmount.Length() > NetConfig.ItemPosUpdateDistance) + { + new NetworkEvent(NetworkEventType.PhysicsBodyPosition, ID, false); + } Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition); - rect.X = (int)(displayPos.X - rect.Width / 2.0f); rect.Y = (int)(displayPos.Y + rect.Height / 2.0f); } - body.SetToTargetPosition(); + body.MoveToTargetPosition(); inWater = IsInWater(); - if (!inWater) return; + if (!inWater || Container != null || body == null) return; - //calculate (a rough approximation of) buoyancy - float volume = body.Mass / body.Density; - Vector2 buoyancy = new Vector2(0, volume * 10.0f); - - //apply buoyancy and drag - - //if ((buoyancy - body.LinearVelocity * volume) == Vector2.Zero) DebugConsole.ThrowError("v.zero "); if (body.LinearVelocity != Vector2.Zero && body.LinearVelocity.Length() > 1000.0f) { body.ResetDynamics(); } - body.ApplyForce(buoyancy - body.LinearVelocity * volume); + + ApplyWaterForces(); //TODO: make sure items stay in sync between clients before letting flowing water move items - //if(CurrentHull != null) - // CurrentHull.HandleItems(deltaTime, this); + if(CurrentHull != null) CurrentHull.ApplyFlowForces(deltaTime, this); + + } + + /// + /// Applies buoyancy, drag and angular drag caused by water + /// + private void ApplyWaterForces() + { + if (!InWater || body == null || Container != null) return; + + float forceFactor = 1.0f; + if (CurrentHull != null) + { + float floor = CurrentHull.Rect.Y - CurrentHull.Rect.Height; + float waterLevel = (floor + CurrentHull.Volume / CurrentHull.Rect.Width); + + //forceFactor is 1.0f if the item is completely submerged, + //and goes to 0.0f as the item goes through the surface + forceFactor = Math.Min((waterLevel - Position.Y) / rect.Height, 1.0f); + if (forceFactor <= 0.0f) return; + } + + float volume = body.Mass / body.Density; + + var uplift = -GameMain.World.Gravity * forceFactor * volume; + + Vector2 drag = body.LinearVelocity * volume; + + body.ApplyForce((uplift - drag) * 10.0f); //apply simple angular drag - body.ApplyTorque(body.AngularVelocity * volume * -0.05f); + body.ApplyTorque(body.AngularVelocity * volume * -0.05f); } public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) @@ -1374,11 +1401,11 @@ namespace Barotrauma switch (type) { case NetworkEventType.DropItem: - if (body != null) - { - message.Write(body.SimPosition.X); - message.Write(body.SimPosition.Y); - } + if (body != null) body.FillNetworkData(message); + break; + case NetworkEventType.PhysicsBodyPosition: + System.Diagnostics.Debug.Assert(body != null, "Tried to send a PhysicsBodyPosition message for an item that has no physics body"); + body.FillNetworkData(message); break; case NetworkEventType.ItemFixed: byte requirementIndex = (byte)data; @@ -1456,10 +1483,15 @@ namespace Barotrauma switch (type) { case NetworkEventType.DropItem: - Vector2 newSimPos = Vector2.Zero; - newSimPos = new Vector2(message.ReadFloat(), message.ReadFloat()); - SetTransform(newSimPos, body.Rotation); Drop(null, false); + if (body != null) + { + body.ReadNetworkData(message, sendingTime); + body.MoveToTargetPosition(false); + } + break; + case NetworkEventType.PhysicsBodyPosition: + if (body != null) body.ReadNetworkData(message, sendingTime); break; case NetworkEventType.ItemFixed: diff --git a/Subsurface/Source/Map/Hull.cs b/Subsurface/Source/Map/Hull.cs index e79f407f1..dd6192653 100644 --- a/Subsurface/Source/Map/Hull.cs +++ b/Subsurface/Source/Map/Hull.cs @@ -453,29 +453,15 @@ namespace Barotrauma } } - public void HandleItems(float deltaTime, Item body) + public void ApplyFlowForces(float deltaTime, Item item) { - if (!body.InWater || body.body == null || body.Container != null) - return; - float floor = rect.Y - rect.Height; - float waterLevel = (floor + Volume/rect.Width); - - - var actionPoint = new Vector2(body.Rect.X, body.Rect.Y); - var forceFactor = 1f - ((actionPoint.Y - waterLevel)/Math.Max(body.body.Density - 10, 1)); - - if (!(forceFactor > 0f)) return; - - var uplift = -GameMain.World.Gravity*(forceFactor - body.body.LinearVelocity.Y*5); - body.body.FarseerBody.ApplyForce(uplift*deltaTime); - foreach (var gap in ConnectedGaps.Where(gap => gap.Open > 0)) { - var pos = gap.Position - body.Position; - var distance = MathHelper.Max(Vector2.DistanceSquared(body.Position, gap.Position)/1000,1f); + //var pos = gap.Position - body.Position; + var distance = MathHelper.Max(Vector2.DistanceSquared(item.Position, gap.Position)/1000, 1f); - pos.Normalize(); - body.body.ApplyForce((pos * (gap.LerpedFlowForce/distance)) * deltaTime); + //pos.Normalize(); + item.body.ApplyForce((gap.LerpedFlowForce/distance) * deltaTime); } } diff --git a/Subsurface/Source/Networking/NetConfig.cs b/Subsurface/Source/Networking/NetConfig.cs index 871360a5d..ba50cc10b 100644 --- a/Subsurface/Source/Networking/NetConfig.cs +++ b/Subsurface/Source/Networking/NetConfig.cs @@ -27,6 +27,10 @@ namespace Barotrauma.Networking //if the ragdoll is closer than this, don't try to correct its position public const float AllowedRagdollDistance = 0.1f; + //how much the physics body of an item has to move until the server + //send a position update to clients (in sim units) + public const float ItemPosUpdateDistance = 2.0f; + public const float LargeCharacterUpdateInterval = 5.0f; public const float DeleteDisconnectedTime = 10.0f; diff --git a/Subsurface/Source/Networking/NetworkEvent.cs b/Subsurface/Source/Networking/NetworkEvent.cs index 03511e2ab..c980b87c6 100644 --- a/Subsurface/Source/Networking/NetworkEvent.cs +++ b/Subsurface/Source/Networking/NetworkEvent.cs @@ -28,7 +28,9 @@ namespace Barotrauma.Networking ItemFixed = 9, UpdateProperty = 10, - WallDamage = 11 + WallDamage = 11, + + PhysicsBodyPosition = 12 } class NetworkEvent diff --git a/Subsurface/Source/Physics/PhysicsBody.cs b/Subsurface/Source/Physics/PhysicsBody.cs index f73ee51fe..e255f2e98 100644 --- a/Subsurface/Source/Physics/PhysicsBody.cs +++ b/Subsurface/Source/Physics/PhysicsBody.cs @@ -35,6 +35,13 @@ namespace Barotrauma private float drawRotation; + private float lastNetworkUpdateTime; + public Vector2 LastSentPosition + { + get; + private set; + } + public readonly Shape bodyShape; public readonly float height, width, radius; @@ -191,6 +198,8 @@ namespace Barotrauma dir = 1.0f; + LastSentPosition = body.Position; + list.Add(this); } @@ -241,8 +250,8 @@ namespace Barotrauma SetTransform(position, 0.0f); - //prevPosition = ConvertUnits.ToDisplayUnits(position); - + LastSentPosition = position; + list.Add(this); } @@ -288,23 +297,42 @@ namespace Barotrauma prevRotation = rotation; } - public void SetToTargetPosition() + public void MoveToTargetPosition(bool lerp = true) { - if (targetPosition != Vector2.Zero) + if (targetPosition == Vector2.Zero) { - body.SetTransform(targetPosition, targetRotation); - body.LinearVelocity = targetVelocity; - body.AngularVelocity = targetAngularVelocity; - targetPosition = Vector2.Zero; + diffToTargetPos = Vector2.Zero; + return; } + + if (lerp) diffToTargetPos = targetPosition - body.Position; + + body.SetTransform(targetPosition, targetRotation); + body.LinearVelocity = targetVelocity; + body.AngularVelocity = targetAngularVelocity; + targetPosition = Vector2.Zero; } + + Vector2 diffToTargetPos; public void UpdateDrawPosition() { - drawPosition = Physics.Interpolate(prevPosition, body.Position); + drawPosition = Physics.Interpolate(prevPosition, body.Position) - diffToTargetPos; drawPosition = ConvertUnits.ToDisplayUnits(drawPosition); drawRotation = Physics.Interpolate(prevRotation, body.Rotation); + + if (diffToTargetPos == Vector2.Zero) return; + + float diff = diffToTargetPos.Length(); + if (diff < 0.05f) + { + diffToTargetPos = Vector2.Zero; + } + else + { + diffToTargetPos -= (diffToTargetPos / diff) * 0.05f; + } } public void Draw(SpriteBatch spriteBatch, Sprite sprite, Color color, float? depth = null, float scale = 1.0f) @@ -337,7 +365,59 @@ namespace Barotrauma body.ApplyTorque(torque); } - + + + public void FillNetworkData(NetBuffer message) + { + message.Write(body.Position.X); + message.Write(body.Position.Y); + message.Write(body.LinearVelocity.X); + message.Write(body.LinearVelocity.Y); + + message.Write(body.Rotation); + message.Write(body.AngularVelocity); + + LastSentPosition = body.Position; + } + + public void ReadNetworkData(NetIncomingMessage message, float sendingTime) + { + if (sendingTime < lastNetworkUpdateTime) return; + + Vector2 newTargetPos = Vector2.Zero; + Vector2 newTargetVel = Vector2.Zero; + + float newTargetRotation = 0.0f, newTargetAngularVel = 0.0f; + try + { + newTargetPos = new Vector2(message.ReadFloat(), message.ReadFloat()); + newTargetVel = new Vector2(message.ReadFloat(), message.ReadFloat()); + + newTargetRotation = message.ReadFloat(); + newTargetAngularVel = message.ReadFloat(); + } + + catch (Exception e) + { +#if DEBUG + DebugConsole.ThrowError("invalid network message", e); +#endif + return; + } + + if (!MathUtils.IsValid(newTargetPos) || !MathUtils.IsValid(newTargetVel) || + !MathUtils.IsValid(newTargetRotation) || !MathUtils.IsValid(newTargetAngularVel)) + return; + + targetPosition = newTargetPos; + targetVelocity = newTargetVel; + + targetRotation = newTargetRotation; + targetAngularVelocity = newTargetAngularVel; + + lastNetworkUpdateTime = sendingTime; + } + public void Remove() {