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)