From b68eeda8a86b8d0f095af9f0cddba26e1459185e Mon Sep 17 00:00:00 2001 From: Regalis Date: Mon, 17 Oct 2016 22:28:55 +0300 Subject: [PATCH] WIP position syncing --- .../Animation/HumanoidAnimController.cs | 92 ++++++++++-------- .../Source/Characters/Animation/Ragdoll.cs | 72 ++++++++++++-- Subsurface/Source/Characters/Character.cs | 95 ++++++++++++++++--- Subsurface/Source/Networking/GameClient.cs | 13 +-- Subsurface/Source/Networking/GameServer.cs | 14 +-- 5 files changed, 207 insertions(+), 79 deletions(-) diff --git a/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs b/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs index d6e56fa69..22e0becb8 100644 --- a/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs +++ b/Subsurface/Source/Characters/Animation/HumanoidAnimController.cs @@ -75,19 +75,6 @@ namespace Barotrauma return; } - //re-enable collider - if (!collider.FarseerBody.Enabled) - { - var lowestLimb = FindLowestLimb(); - - collider.SetTransform(new Vector2( - collider.SimPosition.X, - Math.Max(lowestLimb.SimPosition.Y + (collider.radius + collider.height / 2), collider.SimPosition.Y)), - 0.0f); - - collider.FarseerBody.Enabled = true; - } - //stun (= disable the animations) if the ragdoll receives a large enough impact if (strongestImpact > 0.0f) { @@ -95,26 +82,47 @@ namespace Barotrauma strongestImpact = 0.0f; return; } + + + if (!character.IsNetworkPlayer || true) + { + //re-enable collider + if (!collider.FarseerBody.Enabled) + { + var lowestLimb = FindLowestLimb(); + + collider.SetTransform(new Vector2( + collider.SimPosition.X, + Math.Max(lowestLimb.SimPosition.Y + (collider.radius + collider.height / 2), collider.SimPosition.Y)), + 0.0f); + + collider.FarseerBody.Enabled = true; + } + - if (swimming) - { - collider.FarseerBody.FixedRotation = false; - } - else if (!collider.FarseerBody.FixedRotation) - { - if (Math.Abs(MathUtils.GetShortestAngle(collider.Rotation, 0.0f)) > 0.001f) + if (swimming) { - //rotate collider back upright - collider.AngularVelocity = MathUtils.GetShortestAngle(collider.Rotation, 0.0f) * 60.0f; - collider.FarseerBody.FixedRotation = false; + collider.FarseerBody.FixedRotation = false; } - else + else if (!collider.FarseerBody.FixedRotation) { - collider.FarseerBody.FixedRotation = true; + if (Math.Abs(MathUtils.GetShortestAngle(collider.Rotation, 0.0f)) > 0.001f) + { + //rotate collider back upright + collider.AngularVelocity = MathUtils.GetShortestAngle(collider.Rotation, 0.0f) * 60.0f; + collider.FarseerBody.FixedRotation = false; + } + else + { + collider.FarseerBody.FixedRotation = true; + } } } - + else + { + collider.FarseerBody.Enabled = false; + } if (character.LockHands) { @@ -207,6 +215,7 @@ namespace Barotrauma } aiming = false; + if (character.IsNetworkPlayer) collider.LinearVelocity = Vector2.Zero; } @@ -231,7 +240,7 @@ namespace Barotrauma Limb rightLeg = GetLimb(LimbType.RightLeg); float getUpSpeed = 0.3f; - float walkCycleSpeed = collider.LinearVelocity.X * walkAnimSpeed; + float walkCycleSpeed = movement.X * walkAnimSpeed; if (stairs != null) { TargetMovement = new Vector2(MathHelper.Clamp(TargetMovement.X, -1.5f, 1.5f), TargetMovement.Y); @@ -240,12 +249,7 @@ namespace Barotrauma TargetMovement.X < 0.0f && stairs.StairDirection == Direction.Left) { TargetMovement *= 1.7f; - walkCycleSpeed *= 2.0f; - } - else - { - TargetMovement /= 1.0f; - walkCycleSpeed *= 2.0f; + //walkCycleSpeed *= 1.0f; } } @@ -278,7 +282,10 @@ namespace Barotrauma float footMid = colliderPos.X;// (leftFoot.SimPosition.X + rightFoot.SimPosition.X) / 2.0f; - movement = MathUtils.SmoothStep(movement, TargetMovement * walkSpeed, movementLerp); + movement = overrideTargetMovement == Vector2.Zero ? + MathUtils.SmoothStep(movement, TargetMovement * walkSpeed, movementLerp) : + overrideTargetMovement; + movement.Y = 0.0f; for (int i = 0; i < 2; i++) @@ -305,7 +312,7 @@ namespace Barotrauma //if (LowestLimb == null) return; - if (onGround) + if (onGround && !character.IsNetworkPlayer) { collider.LinearVelocity = new Vector2( movement.X, @@ -511,11 +518,6 @@ namespace Barotrauma Limb head = GetLimb(LimbType.Head); Limb torso = GetLimb(LimbType.Torso); - //collider.FarseerBody.Enabled = false; - //collider.SetTransform(torso.SimPosition, 0.0f); - - // float colliderTop = collider.SimPosition.Y + (float)Math.Cos(collider.Rotation) * (collider.radius + collider.height / 2); - if (currentHull != null && (currentHull.Rect.Y - currentHull.Surface > 50.0f)) { surfaceLimiter = (ConvertUnits.ToDisplayUnits(collider.SimPosition.Y + 0.4f) - surfaceY); @@ -606,7 +608,10 @@ namespace Barotrauma movement.Y = movement.Y - (surfaceLimiter - 1.0f) * 0.01f; } - collider.LinearVelocity = Vector2.Lerp(collider.LinearVelocity, movement * swimSpeed, movementLerp); + if (!character.IsNetworkPlayer) + { + collider.LinearVelocity = Vector2.Lerp(collider.LinearVelocity, movement * swimSpeed, movementLerp); + } walkPos += movement.Length() * 0.15f; footPos = collider.SimPosition - new Vector2((float)Math.Sin(-collider.Rotation), (float)Math.Cos(-collider.Rotation)) * 0.4f; @@ -725,7 +730,10 @@ namespace Barotrauma MoveLimb(torso, new Vector2(ladderSimPos.X - 0.27f * Dir, collider.SimPosition.Y+0.5f), 10.5f); MoveLimb(waist, new Vector2(ladderSimPos.X - 0.35f * Dir, collider.SimPosition.Y+0.4f), 10.5f); - collider.MoveToPos(new Vector2(ladderSimPos.X - 0.2f * Dir, collider.SimPosition.Y), 10.5f); + if (!character.IsNetworkPlayer) + { + collider.MoveToPos(new Vector2(ladderSimPos.X - 0.2f * Dir, collider.SimPosition.Y), 10.5f); + } bool slide = targetMovement.Y < -1.1f; diff --git a/Subsurface/Source/Characters/Animation/Ragdoll.cs b/Subsurface/Source/Characters/Animation/Ragdoll.cs index 8c0d962e3..19edfc14e 100644 --- a/Subsurface/Source/Characters/Animation/Ragdoll.cs +++ b/Subsurface/Source/Characters/Animation/Ragdoll.cs @@ -9,6 +9,7 @@ using FarseerPhysics.Dynamics.Joints; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Barotrauma.Networking; +using Lidgren.Network; namespace Barotrauma { @@ -61,7 +62,7 @@ namespace Barotrauma //a movement vector that overrides targetmovement if trying to steer //a Character to the position sent by server in multiplayer mode - protected Vector2 correctionMovement; + protected Vector2 overrideTargetMovement; protected float floorY; protected float surfaceY; @@ -140,7 +141,7 @@ namespace Barotrauma { get { - return (correctionMovement == Vector2.Zero) ? targetMovement : correctionMovement; + return (overrideTargetMovement == Vector2.Zero) ? targetMovement : overrideTargetMovement; } set { @@ -465,9 +466,7 @@ namespace Barotrauma SoundPlayer.PlayDamageSound(DamageSoundType.LimbBlunt, strongestImpact, collider); strongestImpact = Math.Max(strongestImpact, impact - 8.0f); } - } - - + } if (Character.Controlled == character) GameMain.GameScreen.Cam.Shake = strongestImpact; } @@ -528,6 +527,25 @@ namespace Barotrauma } } + if (character.MemPos.Count > 1) + { + Vector2 prevPos = ConvertUnits.ToDisplayUnits(character.MemPos[0].Position); + if (currentHull != null) prevPos += currentHull.Submarine.DrawPosition; + prevPos.Y = -prevPos.Y; + + for (int i = 1; i < character.MemPos.Count; i++ ) + { + Vector2 currPos = ConvertUnits.ToDisplayUnits(character.MemPos[i].Position); + if (currentHull != null) currPos += currentHull.Submarine.DrawPosition; + currPos.Y = -currPos.Y; + + GUI.DrawRectangle(spriteBatch, new Rectangle((int)currPos.X - 3, (int)currPos.Y - 3, 6, 6), Color.Cyan*0.6f, true, 0.01f); + GUI.DrawLine(spriteBatch, prevPos, currPos, Color.Cyan*0.6f, 0, 3); + + prevPos = currPos; + } + } + if (ignorePlatforms) { GUI.DrawLine(spriteBatch, @@ -718,7 +736,7 @@ namespace Barotrauma if (Frozen) return; - UpdateNetPlayerPosition(); + UpdateNetPlayerPosition(deltaTime); Vector2 flowForce = Vector2.Zero; @@ -1027,8 +1045,46 @@ namespace Barotrauma // } //} - private void UpdateNetPlayerPosition() + float t = 0.0f; + + private void UpdateNetPlayerPosition(float deltaTime) { + if (character.MemPos.Count < 2) return; + + PosInfo prev = character.MemPos[0]; + PosInfo next = character.MemPos[1]; + + Vector2 currPos = collider.SimPosition; + + //interpolate the position of the collider from the first position in the buffer towards the second + if (prev.Timestamp < next.Timestamp) + { + //if there are more than 2 positions in the buffer, + //increase the interpolation speed to catch up with the server + float speedMultiplier = 1.0f + (float)Math.Pow((character.MemPos.Count - 2) / 2.0f, 2.0f); + + t += (deltaTime * speedMultiplier) / (next.Timestamp - prev.Timestamp); + currPos = Vector2.Lerp(prev.Position, next.Position, t); + + //override the targetMovement to make the character play the walking/running animation + overrideTargetMovement = (next.Position - prev.Position) / (next.Timestamp - prev.Timestamp); + } + else + { + currPos = next.Position; + t = 1.0f; + + return; + } + + collider.SetTransform(currPos, collider.Rotation); + + if (t >= 1.0f) + { + t = 0.0f; + character.MemPos.RemoveAt(0); + } + //if (refLimb.body.TargetPosition == Vector2.Zero) //{ // correctionMovement = Vector2.Zero; @@ -1051,7 +1107,7 @@ namespace Barotrauma //} - //float dist = Vector2.Distance(refLimb.body.SimPosition, refLimb.body.TargetPosition); + //float dist = Vector2.Distance(collider.SimPosition, character.MemPos[0].Position); ////if the limb is further away than resetdistance, all limbs are immediately snapped to their targetpositions //bool resetAll = dist > NetConfig.ResetRagdollDistance; diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index 06ddf5627..d70c0399d 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -14,7 +14,20 @@ using System.Xml.Linq; namespace Barotrauma { - + struct PosInfo + { + public readonly Vector2 Position; + public Direction Direction; + public readonly float Timestamp; + + public PosInfo(Vector2 pos, Direction dir, float time) + { + Position = pos; + Direction = dir; + Timestamp = time; + } + } + class Character : Entity, IDamageable, IPropertyObject, IClientSerializable, IServerSerializable { public static List CharacterList = new List(); @@ -29,12 +42,10 @@ namespace Barotrauma byte dequeuedInput = 0; byte prevDequeuedInput = 0; List memInput = new List(); - List memMouseX = new List(); - List memMouseY = new List(); - - List memPosX = new List(); - List memPosY = new List(); + List memMousePos = new List(); + List memPos = new List(); + //the Character that the player is currently controlling private static Character controlled; @@ -75,8 +86,6 @@ namespace Barotrauma public readonly bool IsNetworkPlayer; - private bool networkUpdateSent; - private CharacterInventory inventory; private UInt32 LastNetworkUpdateID = 0; @@ -211,6 +220,11 @@ namespace Barotrauma get { return Submarine == null ? cursorPosition : cursorPosition + Submarine.Position; } } + public List MemPos + { + get { return memPos; } + } + public Character ClosestCharacter { get { return closestCharacter; } @@ -301,6 +315,7 @@ namespace Barotrauma set { if (!MathUtils.IsValid(value)) return; + health = MathHelper.Clamp(value, minHealth, maxHealth); } } @@ -761,8 +776,13 @@ namespace Barotrauma if (length > 0.0f) targetMovement = targetMovement / length; } - if (Math.Sign(targetMovement.X) != -Math.Sign(AnimController.Dir) && IsKeyDown(InputType.Run)) + if (AnimController is HumanoidAnimController && + !((HumanoidAnimController)AnimController).Crouching && + Math.Sign(targetMovement.X) != -Math.Sign(AnimController.Dir) && + IsKeyDown(InputType.Run)) + { targetMovement *= 3.0f; + } targetMovement *= SpeedMultiplier; SpeedMultiplier = 1.0f; @@ -812,6 +832,13 @@ namespace Barotrauma AnimController.TargetDir = Direction.Right; } } + else if (GameMain.Client != null && Character.controlled != this) + { + if (memPos.Count > 0) + { + AnimController.TargetDir = memPos[0].Direction; + } + } if (attackCoolDown >0.0f) { @@ -1770,10 +1797,10 @@ namespace Barotrauma UInt32 networkUpdateID = msg.ReadUInt32(); byte inputCount = msg.ReadByte(); - for (int i=0;i 0.0f); + + msg.Write(SimPosition.X); + msg.Write(SimPosition.Y); + } + + public static void ClientReadStatic(NetIncomingMessage msg) + { + UInt16 id = msg.ReadUInt16(); + var character = Entity.FindEntityByID(id) as Character; + + if (character == null) + { + //skip through the rest of the message + //todo: a better way to skip through the message? + msg.Position += 32 + 1 + 32 + 32; + } + else + { + character.ClientRead(msg); + } } public virtual void ClientRead(NetIncomingMessage msg) - { - //TODO: read positions health, etc + { + float sendingTime = msg.ReadSingle(); + bool facingRight = msg.ReadBoolean(); + Vector2 pos = new Vector2(msg.ReadFloat(), msg.ReadFloat()); + + + var posInfo = new PosInfo( + pos, + facingRight ? Direction.Right : Direction.Left, + sendingTime - msg.SenderConnection.RemoteTimeOffset); + + int index = 0; + while (index < memPos.Count && posInfo.Timestamp > memPos[index].Timestamp) + { + index++; + } + + memPos.Insert(index, posInfo); } public void WriteSpawnData(NetOutgoingMessage msg) diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index ee936f5e3..78bf2f6ad 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -705,13 +705,14 @@ namespace Barotrauma.Networking lastSentChatMsgID = inc.ReadUInt32(); break; case ServerNetObject.CHARACTER_POSITION: - bool dead = inc.ReadBoolean(); + //bool dead = inc.ReadBoolean(); + Character.ClientReadStatic(inc); inc.ReadPadBits(); - if (Character.Controlled != null) - { - if (dead && !Character.Controlled.IsDead) - Character.Controlled.Kill(CauseOfDeath.Damage); - } + //if (Character.Controlled != null) + //{ + // if (dead && !Character.Controlled.IsDead) + // Character.Controlled.Kill(CauseOfDeath.Damage); + //} break; case ServerNetObject.CHAT_MESSAGE: ChatMessage.ClientRead(inc); diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 19e17c968..b0eb2f905 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -644,16 +644,12 @@ namespace Barotrauma.Networking } } + foreach (Character character in Character.CharacterList) + { + if (character is AICharacter || character == c.Character) continue; - outmsg.Write((byte)ServerNetObject.CHARACTER_POSITION); - if (c.Character != null && !c.Character.IsDead) - { - outmsg.Write(false); //not dead - outmsg.WritePadBits(); - } - else - { - outmsg.Write(true); //dead + outmsg.Write((byte)ServerNetObject.CHARACTER_POSITION); + character.ServerWrite(outmsg, c); outmsg.WritePadBits(); }