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:
@@ -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" />
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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++)
|
||||
{
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user