ServerEntityEventManager doesn't process received events until the character inputs of the client for the corresponding frame have been processed (fixes character movement lagging behind EntityEvents at the servers side)

+ split character networking logic into a separate file, characters drop items at the position of their collider instead of hands
This commit is contained in:
Regalis
2017-02-09 18:56:28 +02:00
parent 1f16bef01c
commit 52bf73722f
29 changed files with 959 additions and 827 deletions
+1
View File
@@ -92,6 +92,7 @@
<Compile Include="Source\Characters\CharacterHUD.cs" />
<Compile Include="Source\Characters\CharacterInfo.cs" />
<Compile Include="Source\Characters\AI\ISteerable.cs" />
<Compile Include="Source\Characters\CharacterNetworking.cs" />
<Compile Include="Source\Characters\CharacterSound.cs" />
<Compile Include="Source\Characters\DelayedEffect.cs" />
<Compile Include="Source\Characters\HUDProgressBar.cs" />
+12 -699
View File
@@ -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<Character> CharacterList = new List<Character>();
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<NetInputMem> memInput = new List<NetInputMem>();
//private List<Vector2> memMousePos = new List<Vector2>();
private List<PosInfo> memPos = new List<PosInfo>();
private List<PosInfo> memLocalPos = new List<PosInfo>();
private bool networkUpdateSent;
//the Character that the player is currently controlling
private static Character controlled;
@@ -76,11 +36,8 @@ namespace Barotrauma
}
public List<Item> SpawnItems = new List<Item>();
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<string, ObjectProperty> 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;
}
}
}
@@ -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;
/// <summary>
/// ID of the last inputs the server has processed
/// </summary>
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<NetInputMem> memInput = new List<NetInputMem>();
private List<PosInfo> memPos = new List<PosInfo>();
private List<PosInfo> memLocalPos = new List<PosInfo>();
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;
}
}
}
@@ -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();
+1 -1
View File
@@ -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);
@@ -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);
@@ -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;
}
@@ -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());
}
@@ -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);
@@ -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();
@@ -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;
@@ -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);
@@ -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;
@@ -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);
}
}
}
@@ -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)
{
@@ -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();
+3 -3
View File
@@ -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<Item> prevItems = new List<Item>(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];
+5 -5
View File
@@ -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<InGameEditable>();
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(
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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++)
{
+1 -1
View File
@@ -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(),
@@ -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;
@@ -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);
@@ -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);
}
/// <summary>
@@ -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);
}
}
@@ -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);
}
/// <summary>
/// Read the events from the message, ignoring ones we've already received
/// </summary>
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()
@@ -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);
}
}
@@ -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();
}
}
/// <summary>
/// Read the events from the message, ignoring ones we've already received
/// </summary>
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();
}
}
}
@@ -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<BufferedEvent> bufferedEvents;
private UInt32 ID;
private GameServer server;
@@ -25,6 +52,8 @@ namespace Barotrauma.Networking
events = new List<ServerEntityEvent>();
this.server = server;
bufferedEvents = new List<BufferedEvent>();
}
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);
}
/// <summary>
/// Writes all the events that the client hasn't received yet into the outgoing message
/// </summary>
@@ -99,6 +173,49 @@ namespace Barotrauma.Networking
Write(msg, eventsToSync, client);
}
/// <summary>
/// Read the events from the message, ignoring ones we've already received
/// </summary>
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());
}
}
+2 -4
View File
@@ -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)