diff --git a/Subsurface/Barotrauma.csproj b/Subsurface/Barotrauma.csproj index aa61a1833..d8487f80e 100644 --- a/Subsurface/Barotrauma.csproj +++ b/Subsurface/Barotrauma.csproj @@ -92,6 +92,7 @@ + diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index 02e0393b0..8bdf760ed 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -14,53 +14,13 @@ using System.Xml.Linq; namespace Barotrauma { - class Character : Entity, IDamageable, IPropertyObject, IClientSerializable, IServerSerializable + partial class Character : Entity, IDamageable, IPropertyObject, IClientSerializable, IServerSerializable { public static List CharacterList = new List(); public static bool DisableControls; - - /*private UInt32 netStateID; - public UInt32 NetStateID - { - get { return netStateID; } - }*/ - - [Flags] - private enum InputNetFlags : ushort - { - None = 0x0, - Left = 0x1, - Right = 0x2, - Up = 0x4, - Down = 0x8, - FacingLeft = 0x10, - Run = 0x20, - Select = 0x40, - Use = 0x80, - Aim = 0x100, - Attack = 0x200, - - MaxVal = 0x3FF - } - private InputNetFlags dequeuedInput = 0; - private InputNetFlags prevDequeuedInput = 0; - - private struct NetInputMem - { - public InputNetFlags states; //keys pressed/other boolean states at this step - public UInt16 intAim; //aim angle, represented as an unsigned short where 0=0º, 65535=just a bit under 360º - public UInt16 interact; //id of the item being interacted with - } - - private List memInput = new List(); - //private List memMousePos = new List(); - - private List memPos = new List(); - private List memLocalPos = new List(); - - private bool networkUpdateSent; - + + //the Character that the player is currently controlling private static Character controlled; @@ -76,11 +36,8 @@ namespace Barotrauma } public List SpawnItems = new List(); - - public bool isSynced = false; - + private bool enabled; - public bool Enabled { get @@ -105,8 +62,6 @@ namespace Barotrauma private CharacterInventory inventory; - public UInt16 LastNetworkUpdateID = 0; - protected float lastRecvPositionUpdateTime; public readonly Dictionary Properties; @@ -1358,127 +1313,15 @@ namespace Barotrauma if (isDead) return; - if (this != Character.Controlled) + if (GameMain.NetworkMember != null) { - if (GameMain.Client != null) - { - //freeze AI characters if more than 1 seconds have passed since last update from the server - if (lastRecvPositionUpdateTime < NetTime.Now - 1.0f) - { - AnimController.Frozen = true; - memPos.Clear(); - //hide after 2 seconds - if (lastRecvPositionUpdateTime < NetTime.Now - 2.0f) - { - Enabled = false; - return; - } - } - } - else if (GameMain.Server != null && !(this is AICharacter)) - { - if (!AllowInput) - { - AnimController.Frozen = false; - } - else if (memInput.Count == 0) - { - AnimController.Frozen = true; - } - else - { - AnimController.Frozen = false; - prevDequeuedInput = dequeuedInput; - - dequeuedInput = memInput[memInput.Count - 1].states; - - double aimAngle = ((double)memInput[memInput.Count - 1].intAim / 65535.0) * 2.0 * Math.PI; - cursorPosition = (ViewTarget == null ? AnimController.Collider.Position : ViewTarget.Position) - + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 60.0f; - - var closestEntity = Entity.FindEntityByID(memInput[memInput.Count - 1].interact); - if (closestEntity is Item) - { - closestItem = closestEntity as Item; - } - else if (closestEntity is Character) - { - closestCharacter = closestEntity as Character; - } - - memInput.RemoveAt(memInput.Count - 1); - - TransformCursorPos(); - - if ((dequeuedInput == InputNetFlags.None || dequeuedInput == InputNetFlags.FacingLeft) && Math.Abs(AnimController.Collider.LinearVelocity.X) < 0.005f && Math.Abs(AnimController.Collider.LinearVelocity.Y) < 0.2f) - { - while (memInput.Count > 5 && memInput[memInput.Count - 1].states == dequeuedInput) - { - //remove inputs where the player is not moving at all - //helps the server catch up, shouldn't affect final position - memInput.RemoveAt(memInput.Count - 1); - } - } - } - } - } - else if (GameMain.Client != null) - { - memLocalPos.Add(new PosInfo(SimPosition, AnimController.TargetDir, LastNetworkUpdateID)); - - InputNetFlags newInput = InputNetFlags.None; - if (IsKeyDown(InputType.Left)) newInput |= InputNetFlags.Left; - if (IsKeyDown(InputType.Right)) newInput |= InputNetFlags.Right; - if (IsKeyDown(InputType.Up)) newInput |= InputNetFlags.Up; - if (IsKeyDown(InputType.Down)) newInput |= InputNetFlags.Down; - if (IsKeyDown(InputType.Run)) newInput |= InputNetFlags.Run; - if (IsKeyHit(InputType.Select)) newInput |= InputNetFlags.Select; //TODO: clean up the way this input is registered - if (IsKeyDown(InputType.Use)) newInput |= InputNetFlags.Use; - if (IsKeyDown(InputType.Aim)) newInput |= InputNetFlags.Aim; - if (IsKeyDown(InputType.Attack)) newInput |= InputNetFlags.Attack; - - if (AnimController.TargetDir == Direction.Left) newInput |= InputNetFlags.FacingLeft; - - Vector2 relativeCursorPos = cursorPosition - (ViewTarget == null ? AnimController.Collider.Position : ViewTarget.Position); - relativeCursorPos.Normalize(); - UInt16 intAngle = (UInt16)(65535.0*Math.Atan2(relativeCursorPos.Y,relativeCursorPos.X)/(2.0*Math.PI)); - - NetInputMem newMem = new NetInputMem(); - newMem.states = newInput; - newMem.intAim = intAngle; - if (closestItem != null) - { - newMem.interact = closestItem.ID; - } - else if (closestCharacter != null) - { - newMem.interact = closestCharacter.ID; - } - - memInput.Insert(0, newMem); - LastNetworkUpdateID++; - if (memInput.Count > 60) - { - memInput.RemoveRange(60, memInput.Count - 60); - } + UpdateNetInput(); } else { - //this == Character.Controlled && GameMain.Client == null AnimController.Frozen = false; } - if (networkUpdateSent) - { - foreach (Key key in keys) - { - key.DequeueHit(); - key.DequeueHeld(); - } - - networkUpdateSent = false; - } - DisableImpactDamageTimer -= deltaTime; if (needsAir) @@ -1728,9 +1571,9 @@ namespace Barotrauma localVec1.Y = -localVec1.Y; localVec2.Y = -localVec2.Y; - GUI.DrawLine(spriteBatch, remoteVec, localVec, Color.Yellow, 0, 10); - if (localPos1 != null) GUI.DrawLine(spriteBatch, remoteVec, localVec1, Color.Lime, 0, 6); - if (localPos2 != null) GUI.DrawLine(spriteBatch, remoteVec, localVec2, Color.Red, 0, 3); + //GUI.DrawLine(spriteBatch, remoteVec, localVec, Color.Yellow, 0, 10); + if (localPos1 != null) GUI.DrawLine(spriteBatch, remoteVec, localVec1, Color.Lime, 0, 2); + if (localPos2 != null) GUI.DrawLine(spriteBatch, remoteVec + Vector2.One, localVec2 + Vector2.One, Color.Red, 0, 2); } Vector2 mouseDrawPos = CursorWorldPosition; @@ -1874,8 +1717,10 @@ namespace Barotrauma return attackResult; } - public void StartStun(float stunTimer, bool allowStunDecrease = false) + public void StartStun(float stunTimer, bool allowStunDecrease = false, bool isNetworkMessage = false) { + if (GameMain.Client != null && !isNetworkMessage) return; + if ((stunTimer <= AnimController.StunTimer && !allowStunDecrease) || !MathUtils.IsValid(stunTimer)) return; if (GameMain.Server != null && @@ -2043,537 +1888,5 @@ namespace Barotrauma if (Lights.LightManager.ViewTarget == this) Lights.LightManager.ViewTarget = null; } - - public virtual void ClientWrite(NetBuffer msg, object[] extraData = null) - { - if (GameMain.Server != null) return; - - if (extraData != null && (NetEntityEvent.Type)extraData[0] == NetEntityEvent.Type.InventoryState) - { - inventory.ClientWrite(msg, extraData); - } - else - { - msg.Write((byte)ClientNetObject.CHARACTER_INPUT); - - if (memInput.Count > 60) - { - memInput.RemoveRange(60,memInput.Count - 60); - } - - msg.Write(LastNetworkUpdateID); - byte inputCount = Math.Min((byte)memInput.Count, (byte)60); - msg.Write(inputCount); - for (int i = 0; i < inputCount; i++) - { - msg.WriteRangedInteger(0, (int)InputNetFlags.MaxVal,(int)memInput[i].states); - if (memInput[i].states.HasFlag(InputNetFlags.Aim)) - { - msg.Write(memInput[i].intAim); - } - if (memInput[i].states.HasFlag(InputNetFlags.Select)) - { - msg.Write(memInput[i].interact); - } - /*if (memInput[i].HasFlag(InputNetFlags.Select) || memInput[i].HasFlag(InputNetFlags.Aim)) - { - msg.Write(memMousePos[i].X); - msg.Write(memMousePos[i].Y); - }*/ - } - } - } - public virtual void ServerRead(ClientNetObject type, NetIncomingMessage msg, Client c) - { - if (GameMain.Server == null) return; - - if (c.Character != this) - { -#if DEBUG - DebugConsole.Log("Received a character update message from a client who's not controlling the character"); -#endif - return; - } - - switch (type) - { - case ClientNetObject.CHARACTER_INPUT: - - UInt16 networkUpdateID = msg.ReadUInt16(); - byte inputCount = msg.ReadByte(); - - for (int i = 0; i < inputCount; i++) - { - InputNetFlags newInput = (InputNetFlags)msg.ReadRangedInteger(0, (int)InputNetFlags.MaxVal); - //Vector2 newMousePos = Position; - UInt16 newAim = 0; - UInt16 newInteract = 0; - - if (newInput.HasFlag(InputNetFlags.Aim)) - { - newAim = msg.ReadUInt16(); - } - if (newInput.HasFlag(InputNetFlags.Select)) - { - newInteract = msg.ReadUInt16(); - } - - if (AllowInput) - { - /*if (newInput.HasFlag(InputNetFlags.Select) || newInput.HasFlag(InputNetFlags.Aim)) - { - newMousePos.X = msg.ReadSingle(); - newMousePos.Y = msg.ReadSingle(); - }*/ - - - - if (NetIdUtils.IdMoreRecent((ushort)(networkUpdateID - i), LastNetworkUpdateID) && (i < 60)) - { - NetInputMem newMem = new NetInputMem(); - newMem.states = newInput; - newMem.intAim = newAim; - newMem.interact = newInteract; - - memInput.Insert(i, newMem); - } - } - } - - if (NetIdUtils.IdMoreRecent(networkUpdateID, LastNetworkUpdateID)) - { - LastNetworkUpdateID = networkUpdateID; - } - if (memInput.Count > 60) - { - //deleting inputs from the queue here means the server is way behind and data needs to be dropped - //we'll make the server drop down to 30 inputs for good measure - memInput.RemoveRange(30, memInput.Count - 30); - } - break; - - case ClientNetObject.ENTITY_STATE: - inventory.ServerRead(type, msg, c); - break; - } - } - - public virtual void ServerWrite(NetBuffer msg, Client c, object[] extraData = null) - { - if (GameMain.Server == null) return; - - if (extraData != null) - { - switch ((NetEntityEvent.Type)extraData[0]) - { - case NetEntityEvent.Type.InventoryState: - msg.Write(true); - inventory.ClientWrite(msg, extraData); - break; - case NetEntityEvent.Type.Status: - msg.Write(false); - WriteStatus(msg); - break; - } - } - else - { - msg.Write(ID); - - NetBuffer tempBuffer = new NetBuffer(); - - if (this == c.Character) - { - tempBuffer.Write(true); - if (LastNetworkUpdateID < memInput.Count + 1) - { - tempBuffer.Write((UInt16)0); - } - else - { - tempBuffer.Write((UInt16)(LastNetworkUpdateID - memInput.Count - 1)); - } - } - else - { - tempBuffer.Write(false); - - bool aiming = false; - bool use = false; - bool attack = false; - - if (IsRemotePlayer) - { - aiming = dequeuedInput.HasFlag(InputNetFlags.Aim); - use = dequeuedInput.HasFlag(InputNetFlags.Use); - - attack = dequeuedInput.HasFlag(InputNetFlags.Attack); - } - else - { - aiming = keys[(int)InputType.Aim].GetHeldQueue; - use = keys[(int)InputType.Use].GetHeldQueue; - - attack = keys[(int)InputType.Attack].GetHeldQueue; - - networkUpdateSent = true; - } - - tempBuffer.Write(aiming); - tempBuffer.Write(use); - - if (AnimController.Limbs.Any(l => l != null && l.attack != null)) - { - tempBuffer.Write(attack); - } - - if (selectedCharacter != null || selectedConstruction != null) - { - tempBuffer.Write(true); - tempBuffer.Write(selectedCharacter != null ? selectedCharacter.ID : selectedConstruction.ID); - } - else - { - tempBuffer.Write(false); - } - - if (aiming) - { - Vector2 relativeCursorPos = cursorPosition - (ViewTarget == null ? AnimController.Collider.Position : ViewTarget.Position); - tempBuffer.Write((UInt16)(65535.0 * Math.Atan2(relativeCursorPos.Y, relativeCursorPos.X) / (2.0 * Math.PI))); - } - - tempBuffer.Write(AnimController.TargetDir == Direction.Right); - } - - tempBuffer.Write(SimPosition.X); - tempBuffer.Write(SimPosition.Y); - - msg.Write((byte)tempBuffer.LengthBytes); - msg.Write(tempBuffer); - } - } - - public virtual void ClientRead(ServerNetObject type, NetIncomingMessage msg, float sendingTime) - { - if (GameMain.Server != null) return; - - switch (type) - { - case ServerNetObject.ENTITY_POSITION: - bool facingRight = AnimController.Dir > 0.0f; - - lastRecvPositionUpdateTime = (float)NetTime.Now; - - AnimController.Frozen = false; - Enabled = true; - - UInt16 networkUpdateID = 0; - if (msg.ReadBoolean()) - { - networkUpdateID = msg.ReadUInt16(); - } - else - { - bool aimInput = msg.ReadBoolean(); - keys[(int)InputType.Aim].Held = aimInput; - keys[(int)InputType.Aim].SetState(false, aimInput); - - bool useInput = msg.ReadBoolean(); - keys[(int)InputType.Use].Held = useInput; - keys[(int)InputType.Use].SetState(false, useInput); - - if (AnimController.Limbs.Any(l => l != null && l.attack != null)) - { - bool attackInput = msg.ReadBoolean(); - keys[(int)InputType.Attack].Held = attackInput; - keys[(int)InputType.Attack].SetState(false, attackInput); - } - - bool entitySelected = msg.ReadBoolean(); - if (entitySelected) - { - ushort entityID = msg.ReadUInt16(); - Entity selectedEntity = Entity.FindEntityByID(entityID); - if (selectedEntity is Character) - { - SelectCharacter((Character)selectedEntity); - } - else if (selectedEntity is Item) - { - var newSelectedConstruction = (Item)selectedEntity; - if (newSelectedConstruction != null && selectedConstruction != newSelectedConstruction) - { - newSelectedConstruction.Pick(this, true, true); - } - } - } - else - { - if (selectedCharacter != null) DeselectCharacter(); - selectedConstruction = null; - } - - if (aimInput) - { - double aimAngle = ((double)msg.ReadUInt16() / 65535.0) * 2.0 * Math.PI; - cursorPosition = (ViewTarget == null ? AnimController.Collider.Position : ViewTarget.Position) - + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 60.0f; - - TransformCursorPos(); - } - facingRight = msg.ReadBoolean(); - } - - Vector2 pos = new Vector2( - msg.ReadFloat(), - msg.ReadFloat()); - - var posInfo = new PosInfo(pos, facingRight ? Direction.Right : Direction.Left, networkUpdateID, sendingTime); - - int index = 0; - if (GameMain.NetworkMember.Character == this && AllowInput) - { - while (index < memPos.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memPos[index].ID)) - index++; - } - else - { - while (index < memPos.Count && posInfo.Timestamp > memPos[index].Timestamp) - index++; - } - - memPos.Insert(index, posInfo); - break; - case ServerNetObject.ENTITY_STATE: - bool isInventoryUpdate = msg.ReadBoolean(); - - if (isInventoryUpdate) - { - inventory.ClientRead(type, msg, sendingTime); - } - else - { - ReadStatus(msg); - } - - break; - } - } - - private void WriteStatus(NetBuffer msg) - { - if (GameMain.Client != null) - { - DebugConsole.ThrowError("Client attempted to write character status to a networked message"); - return; - } - - msg.Write(isDead); - if (isDead) - { - msg.Write((byte)causeOfDeath); - } - else - { - msg.WriteRangedSingle(health, minHealth, maxHealth, 8); - - msg.Write(oxygen < 100.0f); - if (oxygen < 100.0f) - { - msg.WriteRangedSingle(oxygen, -100.0f, 100.0f, 8); - } - - msg.Write(bleeding > 0.0f); - if (bleeding > 0.0f) - { - msg.WriteRangedSingle(bleeding, 0.0f, 5.0f, 8); - } - - msg.Write(Stun > 0.0f); - if (Stun > 0.0f) - { - Stun = MathHelper.Clamp(Stun, 0.0f, 60.0f); - msg.WriteRangedSingle(Stun, 0.0f, 60.0f, 8); - } - } - } - - private void ReadStatus(NetBuffer msg) - { - if (GameMain.Server != null) - { - DebugConsole.ThrowError("Server attempted to read character status from a networked message"); - return; - } - - bool isDead = msg.ReadBoolean(); - if (isDead) - { - causeOfDeath = (CauseOfDeath)msg.ReadByte(); - if (causeOfDeath == CauseOfDeath.Pressure) - { - Implode(true); - } - else - { - Kill(causeOfDeath, true); - } - } - else - { - health = msg.ReadRangedSingle(minHealth, maxHealth, 8); - - bool lowOxygen = msg.ReadBoolean(); - if (lowOxygen) - { - Oxygen = msg.ReadRangedSingle(-100.0f, 100.0f, 8); - } - else - { - Oxygen = 100.0f; - } - - bool isBleeding = msg.ReadBoolean(); - if (isBleeding) - { - bleeding = msg.ReadRangedSingle(0.0f, 5.0f, 8); - } - else - { - bleeding = 0.0f; - } - - bool stunned = msg.ReadBoolean(); - if (stunned) - { - float newStunTimer = msg.ReadRangedSingle(0.0f, 60.0f, 8); - StartStun(newStunTimer, true); - } - else - { - StartStun(0.0f, true); - } - } - } - - public void WriteSpawnData(NetBuffer msg) - { - if (GameMain.Server == null) return; - - msg.Write(Info == null); - msg.Write(ID); - msg.Write(ConfigPath); - - msg.Write(WorldPosition.X); - msg.Write(WorldPosition.Y); - - msg.Write(Enabled); - - //character with no characterinfo (e.g. some monster) - if (Info == null) return; - - Client ownerClient = GameMain.Server.ConnectedClients.Find(c => c.Character == this); - if (ownerClient != null) - { - msg.Write(true); - msg.Write(ownerClient.ID); - } - else if (GameMain.Server.Character == this) - { - msg.Write(true); - msg.Write((byte)0); - } - else - { - msg.Write(false); - } - - msg.Write(Info.Name); - msg.Write(TeamID); - - msg.Write(this is AICharacter); - msg.Write(Info.Gender == Gender.Female); - msg.Write((byte)Info.HeadSpriteId); - msg.Write(Info.Job == null ? "" : Info.Job.Name); - } - - public static Character ReadSpawnData(NetIncomingMessage inc, bool spawn=true) - { - if (GameMain.Server != null) return null; - - bool noInfo = inc.ReadBoolean(); - ushort id = inc.ReadUInt16(); - string configPath = inc.ReadString(); - - Vector2 position = new Vector2(inc.ReadFloat(), inc.ReadFloat()); - - bool enabled = inc.ReadBoolean(); - - DebugConsole.Log("Received spawn data for "+configPath); - - Character character = null; - if (noInfo) - { - if (!spawn) return null; - - character = Character.Create(configPath, position, null, true); - character.ID = id; - } - else - { - bool hasOwner = inc.ReadBoolean(); - int ownerId = hasOwner ? inc.ReadByte() : -1; - - - string newName = inc.ReadString(); - byte teamID = inc.ReadByte(); - - bool hasAi = inc.ReadBoolean(); - bool isFemale = inc.ReadBoolean(); - int headSpriteID = inc.ReadByte(); - string jobName = inc.ReadString(); - - if (!spawn) return null; - - JobPrefab jobPrefab = JobPrefab.List.Find(jp => jp.Name == jobName); - - CharacterInfo ch = new CharacterInfo(configPath, newName, isFemale ? Gender.Female : Gender.Male, jobPrefab); - ch.HeadSpriteId = headSpriteID; - - character = Character.Create(configPath, position, ch, GameMain.Client.ID != ownerId, hasAi); - character.ID = id; - character.TeamID = teamID; - - if (GameMain.Client.ID == ownerId) - { - GameMain.Client.Character = character; - Controlled = character; - - GameMain.LightManager.LosEnabled = true; - - character.memInput.Clear(); - character.memPos.Clear(); - character.memLocalPos.Clear(); - } - else - { - var ownerClient = GameMain.Client.ConnectedClients.Find(c => c.ID == ownerId); - if (ownerClient != null) - { - ownerClient.Character = character; - } - } - - if (configPath == Character.HumanConfigFile) - { - GameMain.GameSession.CrewManager.characters.Add(character); - } - } - - character.Enabled = enabled; - - return character; - } } } diff --git a/Subsurface/Source/Characters/CharacterNetworking.cs b/Subsurface/Source/Characters/CharacterNetworking.cs new file mode 100644 index 000000000..778d0c2e0 --- /dev/null +++ b/Subsurface/Source/Characters/CharacterNetworking.cs @@ -0,0 +1,714 @@ +using Barotrauma.Networking; +using Lidgren.Network; +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Barotrauma +{ + partial class Character + { + [Flags] + private enum InputNetFlags : ushort + { + None = 0x0, + Left = 0x1, + Right = 0x2, + Up = 0x4, + Down = 0x8, + FacingLeft = 0x10, + Run = 0x20, + Select = 0x40, + Use = 0x80, + Aim = 0x100, + Attack = 0x200, + + MaxVal = 0x3FF + } + private InputNetFlags dequeuedInput = 0; + private InputNetFlags prevDequeuedInput = 0; + + public UInt16 LastNetworkUpdateID = 0; + + /// + /// ID of the last inputs the server has processed + /// + public UInt16 LastProcessedID; + + private struct NetInputMem + { + public InputNetFlags states; //keys pressed/other boolean states at this step + public UInt16 intAim; //aim angle, represented as an unsigned short where 0=0º, 65535=just a bit under 360º + public UInt16 interact; //id of the item being interacted with + + public UInt16 networkUpdateID; + } + + private List memInput = new List(); + + private List memPos = new List(); + private List memLocalPos = new List(); + + private bool networkUpdateSent; + + public bool isSynced = false; + + private void UpdateNetInput() + { + if (this != Character.Controlled) + { + if (GameMain.Client != null) + { + //freeze AI characters if more than 1 seconds have passed since last update from the server + if (lastRecvPositionUpdateTime < NetTime.Now - 1.0f) + { + AnimController.Frozen = true; + memPos.Clear(); + //hide after 2 seconds + if (lastRecvPositionUpdateTime < NetTime.Now - 2.0f) + { + Enabled = false; + return; + } + } + } + else if (GameMain.Server != null && !(this is AICharacter)) + { + if (!AllowInput) + { + AnimController.Frozen = false; + } + else if (memInput.Count == 0) + { + AnimController.Frozen = true; + } + else + { + AnimController.Frozen = false; + prevDequeuedInput = dequeuedInput; + + LastProcessedID = memInput[memInput.Count - 1].networkUpdateID; + dequeuedInput = memInput[memInput.Count - 1].states; + + double aimAngle = ((double)memInput[memInput.Count - 1].intAim / 65535.0) * 2.0 * Math.PI; + cursorPosition = (ViewTarget == null ? AnimController.Collider.Position : ViewTarget.Position) + + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 60.0f; + + var closestEntity = Entity.FindEntityByID(memInput[memInput.Count - 1].interact); + if (closestEntity is Item) + { + closestItem = closestEntity as Item; + } + else if (closestEntity is Character) + { + closestCharacter = closestEntity as Character; + } + + memInput.RemoveAt(memInput.Count - 1); + + TransformCursorPos(); + + if ((dequeuedInput == InputNetFlags.None || dequeuedInput == InputNetFlags.FacingLeft) && Math.Abs(AnimController.Collider.LinearVelocity.X) < 0.005f && Math.Abs(AnimController.Collider.LinearVelocity.Y) < 0.2f) + { + while (memInput.Count > 5 && memInput[memInput.Count - 1].states == dequeuedInput) + { + //remove inputs where the player is not moving at all + //helps the server catch up, shouldn't affect final position + LastProcessedID = memInput[memInput.Count - 1].networkUpdateID; + memInput.RemoveAt(memInput.Count - 1); + } + } + } + } + } + else if (GameMain.Client != null) + { + memLocalPos.Add(new PosInfo(SimPosition, AnimController.TargetDir, LastNetworkUpdateID)); + + InputNetFlags newInput = InputNetFlags.None; + if (IsKeyDown(InputType.Left)) newInput |= InputNetFlags.Left; + if (IsKeyDown(InputType.Right)) newInput |= InputNetFlags.Right; + if (IsKeyDown(InputType.Up)) newInput |= InputNetFlags.Up; + if (IsKeyDown(InputType.Down)) newInput |= InputNetFlags.Down; + if (IsKeyDown(InputType.Run)) newInput |= InputNetFlags.Run; + if (IsKeyHit(InputType.Select)) newInput |= InputNetFlags.Select; //TODO: clean up the way this input is registered + if (IsKeyDown(InputType.Use)) newInput |= InputNetFlags.Use; + if (IsKeyDown(InputType.Aim)) newInput |= InputNetFlags.Aim; + if (IsKeyDown(InputType.Attack)) newInput |= InputNetFlags.Attack; + + if (AnimController.TargetDir == Direction.Left) newInput |= InputNetFlags.FacingLeft; + + Vector2 relativeCursorPos = cursorPosition - (ViewTarget == null ? AnimController.Collider.Position : ViewTarget.Position); + relativeCursorPos.Normalize(); + UInt16 intAngle = (UInt16)(65535.0 * Math.Atan2(relativeCursorPos.Y, relativeCursorPos.X) / (2.0 * Math.PI)); + + NetInputMem newMem = new NetInputMem(); + newMem.states = newInput; + newMem.intAim = intAngle; + if (closestItem != null) + { + newMem.interact = closestItem.ID; + } + else if (closestCharacter != null) + { + newMem.interact = closestCharacter.ID; + } + + memInput.Insert(0, newMem); + LastNetworkUpdateID++; + if (memInput.Count > 60) + { + memInput.RemoveRange(60, memInput.Count - 60); + } + } + + if (networkUpdateSent) + { + foreach (Key key in keys) + { + key.DequeueHit(); + key.DequeueHeld(); + } + + networkUpdateSent = false; + } + } + + public virtual void ClientWrite(NetBuffer msg, object[] extraData = null) + { + if (GameMain.Server != null) return; + + if (extraData != null && (NetEntityEvent.Type)extraData[0] == NetEntityEvent.Type.InventoryState) + { + inventory.ClientWrite(msg, extraData); + } + else + { + msg.Write((byte)ClientNetObject.CHARACTER_INPUT); + + if (memInput.Count > 60) + { + memInput.RemoveRange(60, memInput.Count - 60); + } + + msg.Write(LastNetworkUpdateID); + byte inputCount = Math.Min((byte)memInput.Count, (byte)60); + msg.Write(inputCount); + for (int i = 0; i < inputCount; i++) + { + msg.WriteRangedInteger(0, (int)InputNetFlags.MaxVal, (int)memInput[i].states); + if (memInput[i].states.HasFlag(InputNetFlags.Aim)) + { + msg.Write(memInput[i].intAim); + } + if (memInput[i].states.HasFlag(InputNetFlags.Select)) + { + msg.Write(memInput[i].interact); + } + /*if (memInput[i].HasFlag(InputNetFlags.Select) || memInput[i].HasFlag(InputNetFlags.Aim)) + { + msg.Write(memMousePos[i].X); + msg.Write(memMousePos[i].Y); + }*/ + } + } + } + public virtual void ServerRead(ClientNetObject type, NetBuffer msg, Client c) + { + if (GameMain.Server == null) return; + + if (c.Character != this) + { +#if DEBUG + DebugConsole.Log("Received a character update message from a client who's not controlling the character"); +#endif + return; + } + + switch (type) + { + case ClientNetObject.CHARACTER_INPUT: + + UInt16 networkUpdateID = msg.ReadUInt16(); + byte inputCount = msg.ReadByte(); + + for (int i = 0; i < inputCount; i++) + { + InputNetFlags newInput = (InputNetFlags)msg.ReadRangedInteger(0, (int)InputNetFlags.MaxVal); + //Vector2 newMousePos = Position; + UInt16 newAim = 0; + UInt16 newInteract = 0; + + if (newInput.HasFlag(InputNetFlags.Aim)) + { + newAim = msg.ReadUInt16(); + } + if (newInput.HasFlag(InputNetFlags.Select)) + { + newInteract = msg.ReadUInt16(); + } + + if (AllowInput) + { + /*if (newInput.HasFlag(InputNetFlags.Select) || newInput.HasFlag(InputNetFlags.Aim)) + { + newMousePos.X = msg.ReadSingle(); + newMousePos.Y = msg.ReadSingle(); + }*/ + + + + if (NetIdUtils.IdMoreRecent((ushort)(networkUpdateID - i), LastNetworkUpdateID) && (i < 60)) + { + NetInputMem newMem = new NetInputMem(); + newMem.states = newInput; + newMem.intAim = newAim; + newMem.interact = newInteract; + + newMem.networkUpdateID = (ushort)(networkUpdateID - i); + + memInput.Insert(i, newMem); + } + } + } + + if (NetIdUtils.IdMoreRecent(networkUpdateID, LastNetworkUpdateID)) + { + LastNetworkUpdateID = networkUpdateID; + } + if (memInput.Count > 60) + { + //deleting inputs from the queue here means the server is way behind and data needs to be dropped + //we'll make the server drop down to 30 inputs for good measure + memInput.RemoveRange(30, memInput.Count - 30); + } + break; + + case ClientNetObject.ENTITY_STATE: + inventory.ServerRead(type, msg, c); + break; + } + } + + public virtual void ServerWrite(NetBuffer msg, Client c, object[] extraData = null) + { + if (GameMain.Server == null) return; + + if (extraData != null) + { + switch ((NetEntityEvent.Type)extraData[0]) + { + case NetEntityEvent.Type.InventoryState: + msg.Write(true); + inventory.ClientWrite(msg, extraData); + break; + case NetEntityEvent.Type.Status: + msg.Write(false); + WriteStatus(msg); + break; + } + } + else + { + msg.Write(ID); + + NetBuffer tempBuffer = new NetBuffer(); + + if (this == c.Character) + { + tempBuffer.Write(true); + if (LastNetworkUpdateID < memInput.Count + 1) + { + tempBuffer.Write((UInt16)0); + } + else + { + tempBuffer.Write((UInt16)(LastNetworkUpdateID - memInput.Count - 1)); + } + } + else + { + tempBuffer.Write(false); + + bool aiming = false; + bool use = false; + bool attack = false; + + if (IsRemotePlayer) + { + aiming = dequeuedInput.HasFlag(InputNetFlags.Aim); + use = dequeuedInput.HasFlag(InputNetFlags.Use); + + attack = dequeuedInput.HasFlag(InputNetFlags.Attack); + } + else + { + aiming = keys[(int)InputType.Aim].GetHeldQueue; + use = keys[(int)InputType.Use].GetHeldQueue; + + attack = keys[(int)InputType.Attack].GetHeldQueue; + + networkUpdateSent = true; + } + + tempBuffer.Write(aiming); + tempBuffer.Write(use); + + if (AnimController.Limbs.Any(l => l != null && l.attack != null)) + { + tempBuffer.Write(attack); + } + + if (selectedCharacter != null || selectedConstruction != null) + { + tempBuffer.Write(true); + tempBuffer.Write(selectedCharacter != null ? selectedCharacter.ID : selectedConstruction.ID); + } + else + { + tempBuffer.Write(false); + } + + if (aiming) + { + Vector2 relativeCursorPos = cursorPosition - (ViewTarget == null ? AnimController.Collider.Position : ViewTarget.Position); + tempBuffer.Write((UInt16)(65535.0 * Math.Atan2(relativeCursorPos.Y, relativeCursorPos.X) / (2.0 * Math.PI))); + } + + tempBuffer.Write(AnimController.TargetDir == Direction.Right); + } + + tempBuffer.Write(SimPosition.X); + tempBuffer.Write(SimPosition.Y); + + msg.Write((byte)tempBuffer.LengthBytes); + msg.Write(tempBuffer); + } + } + + public virtual void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) + { + if (GameMain.Server != null) return; + + switch (type) + { + case ServerNetObject.ENTITY_POSITION: + bool facingRight = AnimController.Dir > 0.0f; + + lastRecvPositionUpdateTime = (float)NetTime.Now; + + AnimController.Frozen = false; + Enabled = true; + + UInt16 networkUpdateID = 0; + if (msg.ReadBoolean()) + { + networkUpdateID = msg.ReadUInt16(); + } + else + { + bool aimInput = msg.ReadBoolean(); + keys[(int)InputType.Aim].Held = aimInput; + keys[(int)InputType.Aim].SetState(false, aimInput); + + bool useInput = msg.ReadBoolean(); + keys[(int)InputType.Use].Held = useInput; + keys[(int)InputType.Use].SetState(false, useInput); + + if (AnimController.Limbs.Any(l => l != null && l.attack != null)) + { + bool attackInput = msg.ReadBoolean(); + keys[(int)InputType.Attack].Held = attackInput; + keys[(int)InputType.Attack].SetState(false, attackInput); + } + + bool entitySelected = msg.ReadBoolean(); + if (entitySelected) + { + ushort entityID = msg.ReadUInt16(); + Entity selectedEntity = Entity.FindEntityByID(entityID); + if (selectedEntity is Character) + { + SelectCharacter((Character)selectedEntity); + } + else if (selectedEntity is Item) + { + var newSelectedConstruction = (Item)selectedEntity; + if (newSelectedConstruction != null && selectedConstruction != newSelectedConstruction) + { + newSelectedConstruction.Pick(this, true, true); + } + } + } + else + { + if (selectedCharacter != null) DeselectCharacter(); + selectedConstruction = null; + } + + if (aimInput) + { + double aimAngle = ((double)msg.ReadUInt16() / 65535.0) * 2.0 * Math.PI; + cursorPosition = (ViewTarget == null ? AnimController.Collider.Position : ViewTarget.Position) + + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 60.0f; + + TransformCursorPos(); + } + facingRight = msg.ReadBoolean(); + } + + Vector2 pos = new Vector2( + msg.ReadFloat(), + msg.ReadFloat()); + + var posInfo = new PosInfo(pos, facingRight ? Direction.Right : Direction.Left, networkUpdateID, sendingTime); + + int index = 0; + if (GameMain.NetworkMember.Character == this && AllowInput) + { + while (index < memPos.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memPos[index].ID)) + index++; + } + else + { + while (index < memPos.Count && posInfo.Timestamp > memPos[index].Timestamp) + index++; + } + + memPos.Insert(index, posInfo); + break; + case ServerNetObject.ENTITY_STATE: + bool isInventoryUpdate = msg.ReadBoolean(); + + if (isInventoryUpdate) + { + inventory.ClientRead(type, msg, sendingTime); + } + else + { + ReadStatus(msg); + } + + break; + } + } + + private void WriteStatus(NetBuffer msg) + { + if (GameMain.Client != null) + { + DebugConsole.ThrowError("Client attempted to write character status to a networked message"); + return; + } + + msg.Write(isDead); + if (isDead) + { + msg.Write((byte)causeOfDeath); + } + else + { + msg.WriteRangedSingle(health, minHealth, maxHealth, 8); + + msg.Write(oxygen < 100.0f); + if (oxygen < 100.0f) + { + msg.WriteRangedSingle(oxygen, -100.0f, 100.0f, 8); + } + + msg.Write(bleeding > 0.0f); + if (bleeding > 0.0f) + { + msg.WriteRangedSingle(bleeding, 0.0f, 5.0f, 8); + } + + msg.Write(Stun > 0.0f); + if (Stun > 0.0f) + { + Stun = MathHelper.Clamp(Stun, 0.0f, 60.0f); + msg.WriteRangedSingle(Stun, 0.0f, 60.0f, 8); + } + } + } + + private void ReadStatus(NetBuffer msg) + { + if (GameMain.Server != null) + { + DebugConsole.ThrowError("Server attempted to read character status from a networked message"); + return; + } + + bool isDead = msg.ReadBoolean(); + if (isDead) + { + causeOfDeath = (CauseOfDeath)msg.ReadByte(); + if (causeOfDeath == CauseOfDeath.Pressure) + { + Implode(true); + } + else + { + Kill(causeOfDeath, true); + } + } + else + { + health = msg.ReadRangedSingle(minHealth, maxHealth, 8); + + bool lowOxygen = msg.ReadBoolean(); + if (lowOxygen) + { + Oxygen = msg.ReadRangedSingle(-100.0f, 100.0f, 8); + } + else + { + Oxygen = 100.0f; + } + + bool isBleeding = msg.ReadBoolean(); + if (isBleeding) + { + bleeding = msg.ReadRangedSingle(0.0f, 5.0f, 8); + } + else + { + bleeding = 0.0f; + } + + bool stunned = msg.ReadBoolean(); + if (stunned) + { + float newStunTimer = msg.ReadRangedSingle(0.0f, 60.0f, 8); + StartStun(newStunTimer, true, true); + } + else + { + StartStun(0.0f, true, true); + } + } + } + + public void WriteSpawnData(NetBuffer msg) + { + if (GameMain.Server == null) return; + + msg.Write(Info == null); + msg.Write(ID); + msg.Write(ConfigPath); + + msg.Write(WorldPosition.X); + msg.Write(WorldPosition.Y); + + msg.Write(Enabled); + + //character with no characterinfo (e.g. some monster) + if (Info == null) return; + + Client ownerClient = GameMain.Server.ConnectedClients.Find(c => c.Character == this); + if (ownerClient != null) + { + msg.Write(true); + msg.Write(ownerClient.ID); + } + else if (GameMain.Server.Character == this) + { + msg.Write(true); + msg.Write((byte)0); + } + else + { + msg.Write(false); + } + + msg.Write(Info.Name); + msg.Write(TeamID); + + msg.Write(this is AICharacter); + msg.Write(Info.Gender == Gender.Female); + msg.Write((byte)Info.HeadSpriteId); + msg.Write(Info.Job == null ? "" : Info.Job.Name); + } + + public static Character ReadSpawnData(NetBuffer inc, bool spawn = true) + { + if (GameMain.Server != null) return null; + + bool noInfo = inc.ReadBoolean(); + ushort id = inc.ReadUInt16(); + string configPath = inc.ReadString(); + + Vector2 position = new Vector2(inc.ReadFloat(), inc.ReadFloat()); + + bool enabled = inc.ReadBoolean(); + + DebugConsole.Log("Received spawn data for " + configPath); + + Character character = null; + if (noInfo) + { + if (!spawn) return null; + + character = Character.Create(configPath, position, null, true); + character.ID = id; + } + else + { + bool hasOwner = inc.ReadBoolean(); + int ownerId = hasOwner ? inc.ReadByte() : -1; + + + string newName = inc.ReadString(); + byte teamID = inc.ReadByte(); + + bool hasAi = inc.ReadBoolean(); + bool isFemale = inc.ReadBoolean(); + int headSpriteID = inc.ReadByte(); + string jobName = inc.ReadString(); + + if (!spawn) return null; + + JobPrefab jobPrefab = JobPrefab.List.Find(jp => jp.Name == jobName); + + CharacterInfo ch = new CharacterInfo(configPath, newName, isFemale ? Gender.Female : Gender.Male, jobPrefab); + ch.HeadSpriteId = headSpriteID; + + character = Character.Create(configPath, position, ch, GameMain.Client.ID != ownerId, hasAi); + character.ID = id; + character.TeamID = teamID; + + if (GameMain.Client.ID == ownerId) + { + GameMain.Client.Character = character; + Controlled = character; + + GameMain.LightManager.LosEnabled = true; + + character.memInput.Clear(); + character.memPos.Clear(); + character.memLocalPos.Clear(); + } + else + { + var ownerClient = GameMain.Client.ConnectedClients.Find(c => c.ID == ownerId); + if (ownerClient != null) + { + ownerClient.Character = character; + } + } + + if (configPath == Character.HumanConfigFile) + { + GameMain.GameSession.CrewManager.characters.Add(character); + } + } + + character.Enabled = enabled; + + return character; + } + } +} diff --git a/Subsurface/Source/Items/Components/DockingPort.cs b/Subsurface/Source/Items/Components/DockingPort.cs index eefd36ac8..e9fdc9539 100644 --- a/Subsurface/Source/Items/Components/DockingPort.cs +++ b/Subsurface/Source/Items/Components/DockingPort.cs @@ -333,9 +333,9 @@ namespace Barotrauma.Items.Components powerConnection.TryAddLink(wire); - wire.Connect(powerConnection, false); + wire.Connect(powerConnection, false, false); recipient.TryAddLink(wire); - wire.Connect(recipient, false); + wire.Connect(recipient, false, false); } private void CreateDoorBody() @@ -756,7 +756,7 @@ namespace Barotrauma.Items.Components } } - public void ClientRead(ServerNetObject type, Lidgren.Network.NetIncomingMessage msg, float sendingTime) + public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime) { bool isDocked = msg.ReadBoolean(); diff --git a/Subsurface/Source/Items/Components/Door.cs b/Subsurface/Source/Items/Components/Door.cs index 68ea75f9f..171e1c906 100644 --- a/Subsurface/Source/Items/Components/Door.cs +++ b/Subsurface/Source/Items/Components/Door.cs @@ -536,7 +536,7 @@ namespace Barotrauma.Items.Components msg.WriteRangedSingle(stuck, 0.0f, 100.0f, 8); } - public void ClientRead(ServerNetObject type, Lidgren.Network.NetIncomingMessage msg, float sendingTime) + public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime) { SetState(msg.ReadBoolean(), true); Stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8); diff --git a/Subsurface/Source/Items/Components/Holdable/Holdable.cs b/Subsurface/Source/Items/Components/Holdable/Holdable.cs index 1b1ced634..2a0e02dc8 100644 --- a/Subsurface/Source/Items/Components/Holdable/Holdable.cs +++ b/Subsurface/Source/Items/Components/Holdable/Holdable.cs @@ -121,13 +121,16 @@ namespace Barotrauma.Items.Components { if (dropper == null) return; picker = dropper; + } if (picker.Inventory == null) return; - - + item.Submarine = picker.Submarine; - - //item.Unequip(); + if (item.body != null) + { + item.body.ResetDynamics(); + item.SetTransform(picker.SimPosition, 0.0f); + } picker.DeselectItem(item); picker.Inventory.RemoveItem(item); diff --git a/Subsurface/Source/Items/Components/Holdable/Pickable.cs b/Subsurface/Source/Items/Components/Holdable/Pickable.cs index 176672899..589dbd8b3 100644 --- a/Subsurface/Source/Items/Components/Holdable/Pickable.cs +++ b/Subsurface/Source/Items/Components/Holdable/Pickable.cs @@ -191,21 +191,15 @@ namespace Barotrauma.Items.Components DropConnectedWires(picker); item.Submarine = picker.Submarine; - - Limb rightHand = picker.AnimController.GetLimb(LimbType.RightHand); - bodyDropPos = rightHand.SimPosition; - - if (item.body != null) - { - item.body.ResetDynamics(); - } - + bodyDropPos = picker.SimPosition; + picker.Inventory.RemoveItem(item); picker = null; } if (item.body != null && !item.body.Enabled) { + item.body.ResetDynamics(); item.SetTransform(bodyDropPos, 0.0f); item.body.Enabled = true; } diff --git a/Subsurface/Source/Items/Components/Machines/Deconstructor.cs b/Subsurface/Source/Items/Components/Machines/Deconstructor.cs index d9c243d73..df537e2a3 100644 --- a/Subsurface/Source/Items/Components/Machines/Deconstructor.cs +++ b/Subsurface/Source/Items/Components/Machines/Deconstructor.cs @@ -147,7 +147,7 @@ namespace Barotrauma.Items.Components msg.Write(IsActive); } - public void ServerRead(ClientNetObject type, NetIncomingMessage msg, Client c) + public void ServerRead(ClientNetObject type, NetBuffer msg, Client c) { bool active = msg.ReadBoolean(); @@ -164,7 +164,7 @@ namespace Barotrauma.Items.Components msg.Write(IsActive); } - public void ClientRead(ServerNetObject type, NetIncomingMessage msg, float sendingTime) + public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) { SetActive(msg.ReadBoolean()); } diff --git a/Subsurface/Source/Items/Components/Machines/Fabricator.cs b/Subsurface/Source/Items/Components/Machines/Fabricator.cs index a644c4323..09d5b8bd2 100644 --- a/Subsurface/Source/Items/Components/Machines/Fabricator.cs +++ b/Subsurface/Source/Items/Components/Machines/Fabricator.cs @@ -453,7 +453,7 @@ namespace Barotrauma.Items.Components msg.WriteRangedInteger(-1, fabricableItems.Count - 1, itemIndex); } - public void ServerRead(ClientNetObject type, NetIncomingMessage msg, Client c) + public void ServerRead(ClientNetObject type, NetBuffer msg, Client c) { int itemIndex = msg.ReadRangedInteger(-1, fabricableItems.Count - 1); @@ -482,7 +482,7 @@ namespace Barotrauma.Items.Components msg.WriteRangedInteger(-1, fabricableItems.Count - 1, itemIndex); } - public void ClientRead(ServerNetObject type, NetIncomingMessage msg, float sendingTime) + public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) { int itemIndex = msg.ReadRangedInteger(-1, fabricableItems.Count - 1); diff --git a/Subsurface/Source/Items/Components/Machines/Pump.cs b/Subsurface/Source/Items/Components/Machines/Pump.cs index 9f1292aea..d14af7cf7 100644 --- a/Subsurface/Source/Items/Components/Machines/Pump.cs +++ b/Subsurface/Source/Items/Components/Machines/Pump.cs @@ -234,7 +234,7 @@ namespace Barotrauma.Items.Components msg.Write(IsActive); } - public void ServerRead(ClientNetObject type, Lidgren.Network.NetIncomingMessage msg, Barotrauma.Networking.Client c) + public void ServerRead(ClientNetObject type, Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c) { float flowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f; bool isActive = msg.ReadBoolean(); @@ -252,7 +252,7 @@ namespace Barotrauma.Items.Components msg.Write(IsActive); } - public void ClientRead(ServerNetObject type, Lidgren.Network.NetIncomingMessage msg, float sendingTime) + public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime) { FlowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f; IsActive = msg.ReadBoolean(); diff --git a/Subsurface/Source/Items/Components/Machines/Radar.cs b/Subsurface/Source/Items/Components/Machines/Radar.cs index ba1ab4797..8da8d9e75 100644 --- a/Subsurface/Source/Items/Components/Machines/Radar.cs +++ b/Subsurface/Source/Items/Components/Machines/Radar.cs @@ -431,7 +431,7 @@ namespace Barotrauma.Items.Components msg.Write(IsActive); } - public void ServerRead(ClientNetObject type, Lidgren.Network.NetIncomingMessage msg, Barotrauma.Networking.Client c) + public void ServerRead(ClientNetObject type, Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c) { bool isActive = msg.ReadBoolean(); @@ -448,7 +448,7 @@ namespace Barotrauma.Items.Components msg.Write(IsActive); } - public void ClientRead(ServerNetObject type, Lidgren.Network.NetIncomingMessage msg, float sendingTime) + public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime) { IsActive = msg.ReadBoolean(); isActiveTickBox.Selected = IsActive; diff --git a/Subsurface/Source/Items/Components/Machines/Reactor.cs b/Subsurface/Source/Items/Components/Machines/Reactor.cs index e3a33ffe8..ce1b91945 100644 --- a/Subsurface/Source/Items/Components/Machines/Reactor.cs +++ b/Subsurface/Source/Items/Components/Machines/Reactor.cs @@ -561,7 +561,7 @@ namespace Barotrauma.Items.Components msg.WriteRangedSingle(fissionRate, 0.0f, 100.0f, 8); } - public void ServerRead(ClientNetObject type, NetIncomingMessage msg, Client c) + public void ServerRead(ClientNetObject type, NetBuffer msg, Client c) { bool autoTemp = msg.ReadBoolean(); float shutDownTemp = msg.ReadRangedSingle(0.0f, 10000.0f, 8); @@ -591,7 +591,7 @@ namespace Barotrauma.Items.Components msg.WriteRangedSingle(fissionRate, 0.0f, 100.0f, 8); } - public void ClientRead(ServerNetObject type, NetIncomingMessage msg, float sendingTime) + public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) { Temperature = msg.ReadRangedSingle(0.0f, 10000.0f, 16); diff --git a/Subsurface/Source/Items/Components/Machines/Steering.cs b/Subsurface/Source/Items/Components/Machines/Steering.cs index f0a685afa..06dcc38f2 100644 --- a/Subsurface/Source/Items/Components/Machines/Steering.cs +++ b/Subsurface/Source/Items/Components/Machines/Steering.cs @@ -486,7 +486,7 @@ namespace Barotrauma.Items.Components } } - public void ServerRead(ClientNetObject type, Lidgren.Network.NetIncomingMessage msg, Barotrauma.Networking.Client c) + public void ServerRead(ClientNetObject type, Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c) { bool autoPilot = msg.ReadBoolean(); Vector2 newTargetVelocity = targetVelocity; @@ -569,7 +569,7 @@ namespace Barotrauma.Items.Components } } - public void ClientRead(ServerNetObject type, Lidgren.Network.NetIncomingMessage msg, float sendingTime) + public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime) { bool autoPilot = msg.ReadBoolean(); Vector2 newTargetVelocity = targetVelocity; diff --git a/Subsurface/Source/Items/Components/Signal/Connection.cs b/Subsurface/Source/Items/Components/Signal/Connection.cs index 2643243a8..80e6e6c02 100644 --- a/Subsurface/Source/Items/Components/Signal/Connection.cs +++ b/Subsurface/Source/Items/Components/Signal/Connection.cs @@ -348,7 +348,7 @@ namespace Barotrauma.Items.Components draggingConnected.RemoveConnection(item); - if (draggingConnected.Connect(this, !alreadyConnected)) Wires[index] = draggingConnected; + if (draggingConnected.Connect(this, !alreadyConnected, true)) Wires[index] = draggingConnected; } } } @@ -477,7 +477,7 @@ namespace Barotrauma.Items.Components if (Wires[i] != null) { if (Wires[i].Item.body != null) Wires[i].Item.body.Enabled = false; - Wires[i].Connect(this, false, true); + Wires[i].Connect(this, false, false); } } } diff --git a/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs b/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs index 403df9dbe..e4e1b4ed7 100644 --- a/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs +++ b/Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs @@ -173,7 +173,7 @@ namespace Barotrauma.Items.Components } } - public void ServerRead(ClientNetObject type, NetIncomingMessage msg, Client c) + public void ServerRead(ClientNetObject type, NetBuffer msg, Client c) { int[] wireCounts = new int[Connections.Count]; Wire[,] wires = new Wire[Connections.Count, Connection.MaxLinked]; @@ -244,7 +244,7 @@ namespace Barotrauma.Items.Components ClientWrite(msg, extraData); } - public void ClientRead(ServerNetObject type, NetIncomingMessage msg, float sendingTime) + public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) { foreach (Connection connection in Connections) { diff --git a/Subsurface/Source/Items/Components/Signal/Wire.cs b/Subsurface/Source/Items/Components/Signal/Wire.cs index f193758a7..ceadb317a 100644 --- a/Subsurface/Source/Items/Components/Signal/Wire.cs +++ b/Subsurface/Source/Items/Components/Signal/Wire.cs @@ -133,7 +133,7 @@ namespace Barotrauma.Items.Components if (connection == connections[1]) connections[1] = null; } - public bool Connect(Connection newConnection, bool addNode = true, bool loading = false) + public bool Connect(Connection newConnection, bool addNode = true, bool sendNetworkEvent = false) { for (int i = 0; i < 2; i++) { @@ -196,7 +196,7 @@ namespace Barotrauma.Items.Components CleanNodes(); } - if (!loading) + if (sendNetworkEvent) { if (GameMain.Server != null) { @@ -726,7 +726,7 @@ namespace Barotrauma.Items.Components } } - public void ServerRead(ClientNetObject type, NetIncomingMessage msg, Client c) + public void ServerRead(ClientNetObject type, NetBuffer msg, Client c) { nodes.Clear(); @@ -752,7 +752,7 @@ namespace Barotrauma.Items.Components ClientWrite(msg, extraData); } - public void ClientRead(ServerNetObject type, NetIncomingMessage msg, float sendingTime) + public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) { nodes.Clear(); diff --git a/Subsurface/Source/Items/Inventory.cs b/Subsurface/Source/Items/Inventory.cs index bd95f5dcd..ff5e1d07f 100644 --- a/Subsurface/Source/Items/Inventory.cs +++ b/Subsurface/Source/Items/Inventory.cs @@ -546,7 +546,7 @@ namespace Barotrauma syncItemsDelay = 1.0f; } - public void ServerRead(ClientNetObject type, NetIncomingMessage msg, Barotrauma.Networking.Client c) + public void ServerRead(ClientNetObject type, NetBuffer msg, Barotrauma.Networking.Client c) { List prevItems = new List(Items); ushort[] newItemIDs = new ushort[capacity]; @@ -565,7 +565,7 @@ namespace Barotrauma { if (newItemIDs[i] == 0) { - if (Items[i] != null) Items[i].Drop(); + if (Items[i] != null) Items[i].Drop(c.Character); System.Diagnostics.Debug.Assert(Items[i]==null); } else @@ -609,7 +609,7 @@ namespace Barotrauma } } - public void ClientRead(ServerNetObject type, NetIncomingMessage msg, float sendingTime) + public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) { receivedItemIDs = new ushort[capacity]; diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 119dd73a4..367f47e1b 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -1733,7 +1733,7 @@ namespace Barotrauma } } - public void ClientRead(ServerNetObject type, NetIncomingMessage msg, float sendingTime) + public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) { if (type == ServerNetObject.ENTITY_POSITION) { @@ -1822,7 +1822,7 @@ namespace Barotrauma } } - public void ServerRead(ClientNetObject type, NetIncomingMessage msg, Client c) + public void ServerRead(ClientNetObject type, NetBuffer msg, Client c) { NetEntityEvent.Type eventType = (NetEntityEvent.Type)msg.ReadRangedInteger(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1); @@ -1898,7 +1898,7 @@ namespace Barotrauma } } - private void ReadPropertyChange(NetIncomingMessage msg) + private void ReadPropertyChange(NetBuffer msg) { var allProperties = GetProperties(); if (allProperties.Count == 0) return; @@ -1965,7 +1965,7 @@ namespace Barotrauma if (Name == "ID Card") msg.Write(Tags); } - public static Item ReadSpawnData(NetIncomingMessage msg, bool spawn = true) + public static Item ReadSpawnData(NetBuffer msg, bool spawn = true) { if (GameMain.Server != null) return null; @@ -2090,7 +2090,7 @@ namespace Barotrauma lastSentPos = SimPosition; } - public void ClientReadPosition(ServerNetObject type, NetIncomingMessage msg, float sendingTime) + public void ClientReadPosition(ServerNetObject type, NetBuffer msg, float sendingTime) { body.TargetPosition = new Vector2( diff --git a/Subsurface/Source/Map/Hull.cs b/Subsurface/Source/Map/Hull.cs index 0f6705a55..71663f13e 100644 --- a/Subsurface/Source/Map/Hull.cs +++ b/Subsurface/Source/Map/Hull.cs @@ -842,7 +842,7 @@ namespace Barotrauma lastSentOxygen = OxygenPercentage; } - public void ClientRead(ServerNetObject type, NetIncomingMessage message, float sendingTime) + public void ClientRead(ServerNetObject type, NetBuffer message, float sendingTime) { Volume = message.ReadRangedSingle(0.0f, 1.5f, 8) * FullVolume; OxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8); diff --git a/Subsurface/Source/Map/Structure.cs b/Subsurface/Source/Map/Structure.cs index 775b66e72..845705ebb 100644 --- a/Subsurface/Source/Map/Structure.cs +++ b/Subsurface/Source/Map/Structure.cs @@ -872,7 +872,7 @@ namespace Barotrauma } } - public void ClientRead(ServerNetObject type, NetIncomingMessage msg, float sendingTime) + public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) { for (int i = 0; i < sections.Count(); i++) { diff --git a/Subsurface/Source/Map/Submarine.cs b/Subsurface/Source/Map/Submarine.cs index 62864dd7b..f93a9f7b4 100644 --- a/Subsurface/Source/Map/Submarine.cs +++ b/Subsurface/Source/Map/Submarine.cs @@ -1286,7 +1286,7 @@ namespace Barotrauma msg.Write(PhysicsBody.SimPosition.Y); } - public void ClientRead(ServerNetObject type, NetIncomingMessage msg, float sendingTime) + public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) { var newTargetPosition = new Vector2( msg.ReadFloat(), diff --git a/Subsurface/Source/Networking/EntitySpawner.cs b/Subsurface/Source/Networking/EntitySpawner.cs index cdb1ff9f9..5b6ba38fd 100644 --- a/Subsurface/Source/Networking/EntitySpawner.cs +++ b/Subsurface/Source/Networking/EntitySpawner.cs @@ -198,7 +198,7 @@ namespace Barotrauma } } - public void ClientRead(ServerNetObject type, Lidgren.Network.NetIncomingMessage message, float sendingTime) + public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer message, float sendingTime) { if (GameMain.Server != null) return; diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 29f73a34f..f33b065b8 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -354,6 +354,8 @@ namespace Barotrauma.Networking { if (respawnManager != null) respawnManager.Update(deltaTime); + entityEventManager.Update(); + bool isCrewDead = connectedClients.Find(c => c.Character != null && !c.Character.IsDead)==null && (myCharacter == null || myCharacter.IsDead); diff --git a/Subsurface/Source/Networking/INetSerializable.cs b/Subsurface/Source/Networking/INetSerializable.cs index cecc8c72d..8467f11f1 100644 --- a/Subsurface/Source/Networking/INetSerializable.cs +++ b/Subsurface/Source/Networking/INetSerializable.cs @@ -11,7 +11,7 @@ namespace Barotrauma.Networking interface IClientSerializable : INetSerializable { void ClientWrite(NetBuffer msg, object[] extraData = null); - void ServerRead(ClientNetObject type, NetIncomingMessage msg, Client c); + void ServerRead(ClientNetObject type, NetBuffer msg, Client c); } /// @@ -20,6 +20,6 @@ namespace Barotrauma.Networking interface IServerSerializable : INetSerializable { void ServerWrite(NetBuffer msg, Client c, object[] extraData = null); - void ClientRead(ServerNetObject type, NetIncomingMessage msg, float sendingTime); + void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime); } } diff --git a/Subsurface/Source/Networking/NetEntityEvent/ClientEntityEventManager.cs b/Subsurface/Source/Networking/NetEntityEvent/ClientEntityEventManager.cs index e82ece6ff..7a830017b 100644 --- a/Subsurface/Source/Networking/NetEntityEvent/ClientEntityEventManager.cs +++ b/Subsurface/Source/Networking/NetEntityEvent/ClientEntityEventManager.cs @@ -36,6 +36,8 @@ namespace Barotrauma.Networking public void CreateEvent(IClientSerializable entity, object[] extraData = null) { + if (GameMain.Client == null || GameMain.Client.Character == null) return; + if (!(entity is Entity)) { DebugConsole.ThrowError("Can't create an entity event for " + entity + "!"); @@ -44,6 +46,7 @@ namespace Barotrauma.Networking ID++; var newEvent = new ClientEntityEvent(entity, ID); + newEvent.CharacterStateID = GameMain.Client.Character.LastNetworkUpdateID; if (extraData != null) newEvent.SetData(extraData); events.Add(newEvent); @@ -95,9 +98,56 @@ namespace Barotrauma.Networking Write(msg, eventsToSync); } + /// + /// Read the events from the message, ignoring ones we've already received + /// public void Read(NetIncomingMessage msg, float sendingTime) { - base.Read(msg, sendingTime, ref lastReceivedID); + UInt32 firstEventID = msg.ReadUInt32(); + int eventCount = msg.ReadByte(); + + for (int i = 0; i < eventCount; i++) + { + UInt32 thisEventID = firstEventID + (UInt32)i; + UInt16 entityID = msg.ReadUInt16(); + byte msgLength = msg.ReadByte(); + + IServerSerializable entity = Entity.FindEntityByID(entityID) as IServerSerializable; + + //skip the event if we've already received it or if the entity isn't found + if (thisEventID != lastReceivedID + 1 || entity == null) + { + if (thisEventID != lastReceivedID + 1) + { + DebugConsole.NewMessage("received msg " + thisEventID, Microsoft.Xna.Framework.Color.Red); + } + else if (entity == null) + { + DebugConsole.NewMessage("received msg " + thisEventID + ", entity " + entityID + " not found", Microsoft.Xna.Framework.Color.Red); + } + msg.Position += msgLength * 8; + } + else + { + long msgPosition = msg.Position; + + DebugConsole.NewMessage("received msg " + thisEventID + " (" + entity.ToString() + ")", Microsoft.Xna.Framework.Color.Green); + lastReceivedID++; + try + { + ReadEvent(msg, entity, sendingTime); + } + + catch (Exception e) + { +#if DEBUG + DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e); +#endif + msg.Position = msgPosition + msgLength * 8; + } + } + msg.ReadPadBits(); + } } protected override void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null) @@ -108,12 +158,9 @@ namespace Barotrauma.Networking clientEvent.Write(buffer); } - protected override void ReadEvent(NetIncomingMessage buffer, INetSerializable entity, float sendingTime, Client sender = null) + protected void ReadEvent(NetIncomingMessage buffer, IServerSerializable entity, float sendingTime) { - var serverEntity = entity as IServerSerializable; - if (serverEntity == null) return; - - serverEntity.ClientRead(ServerNetObject.ENTITY_STATE, buffer, sendingTime); + entity.ClientRead(ServerNetObject.ENTITY_STATE, buffer, sendingTime); } public void Clear() diff --git a/Subsurface/Source/Networking/NetEntityEvent/NetEntityEvent.cs b/Subsurface/Source/Networking/NetEntityEvent/NetEntityEvent.cs index 59bba32eb..403dcdbd6 100644 --- a/Subsurface/Source/Networking/NetEntityEvent/NetEntityEvent.cs +++ b/Subsurface/Source/Networking/NetEntityEvent/NetEntityEvent.cs @@ -83,6 +83,8 @@ namespace Barotrauma.Networking { private IClientSerializable serializable; + public UInt16 CharacterStateID; + public ClientEntityEvent(IClientSerializable entity, UInt32 id) : base(entity, id) { @@ -91,6 +93,7 @@ namespace Barotrauma.Networking public void Write(NetBuffer msg) { + msg.Write(CharacterStateID); serializable.ClientWrite(msg, Data); } } diff --git a/Subsurface/Source/Networking/NetEntityEvent/NetEntityEventManager.cs b/Subsurface/Source/Networking/NetEntityEvent/NetEntityEventManager.cs index d58d92de3..28d9ffe87 100644 --- a/Subsurface/Source/Networking/NetEntityEvent/NetEntityEventManager.cs +++ b/Subsurface/Source/Networking/NetEntityEvent/NetEntityEventManager.cs @@ -33,7 +33,7 @@ namespace Barotrauma.Networking WriteEvent(tempBuffer, e, recipient); Debug.Assert( - tempBuffer.LengthBytes < 256, + tempBuffer.LengthBytes < 128, "Maximum EntityEvent size exceeded when serializing \""+e.Entity+"\"!"); #if DEBUG @@ -50,67 +50,10 @@ namespace Barotrauma.Networking msg.WritePadBits(); } } - - /// - /// Read the events from the message, ignoring ones we've already received - /// - protected void Read(NetIncomingMessage msg, float sendingTime, ref UInt32 lastReceivedID, Client sender = null) - { - UInt32 firstEventID = msg.ReadUInt32(); - int eventCount = msg.ReadByte(); - - for (int i = 0; i < eventCount; i++) - { - UInt32 thisEventID = firstEventID + (UInt32)i; - UInt16 entityID = msg.ReadUInt16(); - byte msgLength = msg.ReadByte(); - - INetSerializable entity = Entity.FindEntityByID(entityID) as INetSerializable; - - //skip the event if we've already received it or if the entity isn't found - if (thisEventID != lastReceivedID + 1 || entity == null) - { - if (thisEventID != lastReceivedID + 1) - { - DebugConsole.NewMessage("received msg "+thisEventID, Microsoft.Xna.Framework.Color.Red); - } - else if (entity == null) - { - DebugConsole.NewMessage("received msg " + thisEventID+", entity "+entityID+" not found", Microsoft.Xna.Framework.Color.Red); - } - msg.Position += msgLength * 8; - } - else - { - long msgPosition = msg.Position; - - DebugConsole.NewMessage("received msg "+thisEventID+ " ("+entity.ToString()+")", Microsoft.Xna.Framework.Color.Green); - lastReceivedID++; - try - { - ReadEvent(msg, entity, sendingTime, sender); - } - - catch (Exception e) - { -#if DEBUG - DebugConsole.ThrowError("Failed to read event for entity \""+entity.ToString()+"\"!", e); -#endif - msg.Position = msgPosition + msgLength * 8; - } - } - msg.ReadPadBits(); - } - } - + protected virtual void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null) { throw new NotImplementedException(); } - - protected virtual void ReadEvent(NetIncomingMessage buffer, INetSerializable entity, float sendingTime, Client sender = null) - { - throw new NotImplementedException(); - } } } diff --git a/Subsurface/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs b/Subsurface/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs index b8ca51f11..08fc9b231 100644 --- a/Subsurface/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs +++ b/Subsurface/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs @@ -16,6 +16,33 @@ namespace Barotrauma.Networking get { return events; } } + private class BufferedEvent + { + public readonly Client Sender; + + public readonly UInt16 CharacterStateID; + public readonly NetBuffer Data; + + public readonly Character Character; + + public readonly IClientSerializable TargetEntity; + + public bool IsProcessed; + + public BufferedEvent(Client sender, Character senderCharacter, UInt16 characterStateID, IClientSerializable targetEntity, NetBuffer data) + { + this.Sender = sender; + this.Character = senderCharacter; + this.CharacterStateID = characterStateID; + + this.TargetEntity = targetEntity; + + this.Data = data; + } + } + + private List bufferedEvents; + private UInt32 ID; private GameServer server; @@ -25,6 +52,8 @@ namespace Barotrauma.Networking events = new List(); this.server = server; + + bufferedEvents = new List(); } public void CreateEvent(IServerSerializable entity, object[] extraData = null) @@ -50,6 +79,51 @@ namespace Barotrauma.Networking events.Add(newEvent); } + public void Update() + { + foreach (BufferedEvent bufferedEvent in bufferedEvents) + { + if (bufferedEvent.Character == null) + { + bufferedEvent.IsProcessed = true; + continue; + } + + if (NetIdUtils.IdMoreRecent(bufferedEvent.CharacterStateID, bufferedEvent.Character.LastProcessedID)) continue; + + try + { + ReadEvent(bufferedEvent.Data, bufferedEvent.TargetEntity, bufferedEvent.Sender); + } + + catch (Exception e) + { +#if DEBUG + DebugConsole.ThrowError("Failed to read event for entity \"" + bufferedEvent.TargetEntity.ToString() + "\"!", e); +#endif + } + + bufferedEvent.IsProcessed = true; + } + + bufferedEvents.RemoveAll(b => b.IsProcessed); + } + + private void BufferEvent(BufferedEvent bufferedEvent) + { + if (bufferedEvents.Count > 512) + { + //should normally never happen + + //a client could potentially spam events with a much higher character state ID + //than the state of their character and/or stop sending character inputs, + //so we'll drop some events to make sure no-one blows up our buffer + bufferedEvents.RemoveRange(0, 256); + } + + bufferedEvents.Add(bufferedEvent); + } + /// /// Writes all the events that the client hasn't received yet into the outgoing message /// @@ -99,6 +173,49 @@ namespace Barotrauma.Networking Write(msg, eventsToSync, client); } + /// + /// Read the events from the message, ignoring ones we've already received + /// + public void Read(NetIncomingMessage msg, Client sender = null) + { + UInt32 firstEventID = msg.ReadUInt32(); + int eventCount = msg.ReadByte(); + + for (int i = 0; i < eventCount; i++) + { + UInt32 thisEventID = firstEventID + (UInt32)i; + UInt16 entityID = msg.ReadUInt16(); + byte msgLength = msg.ReadByte(); + + IClientSerializable entity = Entity.FindEntityByID(entityID) as IClientSerializable; + + //skip the event if we've already received it or if the entity isn't found + if (thisEventID != sender.lastSentEntityEventID + 1 || entity == null) + { + if (thisEventID != sender.lastSentEntityEventID + 1) + { + DebugConsole.NewMessage("received msg " + thisEventID, Microsoft.Xna.Framework.Color.Red); + } + else if (entity == null) + { + DebugConsole.NewMessage("received msg " + thisEventID + ", entity " + entityID + " not found", Microsoft.Xna.Framework.Color.Red); + } + msg.Position += msgLength * 8; + } + else + { + UInt16 characterStateID = msg.ReadUInt16(); + + NetBuffer buffer = new NetBuffer(); + buffer.Write(msg.ReadBytes(msgLength-2)); + BufferEvent(new BufferedEvent(sender, sender.Character, characterStateID, entity, buffer)); + + sender.lastSentEntityEventID++; + } + msg.ReadPadBits(); + } + } + protected override void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null) { var serverEvent = entityEvent as ServerEntityEvent; @@ -107,24 +224,21 @@ namespace Barotrauma.Networking serverEvent.Write(buffer, recipient); } - protected override void ReadEvent(NetIncomingMessage buffer, INetSerializable entity, float sendingTime, Client sender = null) + protected void ReadEvent(NetBuffer buffer, INetSerializable entity, Client sender = null) { var clientEntity = entity as IClientSerializable; if (clientEntity == null) return; - + clientEntity.ServerRead(ClientNetObject.ENTITY_STATE, buffer, sender); } - - public void Read(NetIncomingMessage msg, Client client) - { - base.Read(msg, 0.0f, ref client.lastSentEntityEventID, client); - } - + public void Clear() { ID = 0; events.Clear(); + bufferedEvents.Clear(); + server.ConnectedClients.ForEach(c => c.entityEventLastSent.Clear()); } } diff --git a/Subsurface/Source/Physics/PhysicsBody.cs b/Subsurface/Source/Physics/PhysicsBody.cs index 67e7c6d1f..da8fb2037 100644 --- a/Subsurface/Source/Physics/PhysicsBody.cs +++ b/Subsurface/Source/Physics/PhysicsBody.cs @@ -452,10 +452,8 @@ namespace Barotrauma { return; } - else - { - drawPosition -= ConvertUnits.ToDisplayUnits(Vector2.Lerp(Vector2.Zero, offsetFromTargetPos, offsetLerp)); - } + + drawPosition -= ConvertUnits.ToDisplayUnits(Vector2.Lerp(Vector2.Zero, offsetFromTargetPos, offsetLerp)); offsetLerp -= 0.1f; if (offsetLerp < 0.0f)