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\CharacterHUD.cs" />
|
||||||
<Compile Include="Source\Characters\CharacterInfo.cs" />
|
<Compile Include="Source\Characters\CharacterInfo.cs" />
|
||||||
<Compile Include="Source\Characters\AI\ISteerable.cs" />
|
<Compile Include="Source\Characters\AI\ISteerable.cs" />
|
||||||
|
<Compile Include="Source\Characters\CharacterNetworking.cs" />
|
||||||
<Compile Include="Source\Characters\CharacterSound.cs" />
|
<Compile Include="Source\Characters\CharacterSound.cs" />
|
||||||
<Compile Include="Source\Characters\DelayedEffect.cs" />
|
<Compile Include="Source\Characters\DelayedEffect.cs" />
|
||||||
<Compile Include="Source\Characters\HUDProgressBar.cs" />
|
<Compile Include="Source\Characters\HUDProgressBar.cs" />
|
||||||
|
|||||||
@@ -14,52 +14,12 @@ using System.Xml.Linq;
|
|||||||
|
|
||||||
namespace Barotrauma
|
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 List<Character> CharacterList = new List<Character>();
|
||||||
|
|
||||||
public static bool DisableControls;
|
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
|
//the Character that the player is currently controlling
|
||||||
private static Character controlled;
|
private static Character controlled;
|
||||||
@@ -77,10 +37,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public List<Item> SpawnItems = new List<Item>();
|
public List<Item> SpawnItems = new List<Item>();
|
||||||
|
|
||||||
public bool isSynced = false;
|
|
||||||
|
|
||||||
private bool enabled;
|
private bool enabled;
|
||||||
|
|
||||||
public bool Enabled
|
public bool Enabled
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -105,8 +62,6 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private CharacterInventory inventory;
|
private CharacterInventory inventory;
|
||||||
|
|
||||||
public UInt16 LastNetworkUpdateID = 0;
|
|
||||||
|
|
||||||
protected float lastRecvPositionUpdateTime;
|
protected float lastRecvPositionUpdateTime;
|
||||||
|
|
||||||
public readonly Dictionary<string, ObjectProperty> Properties;
|
public readonly Dictionary<string, ObjectProperty> Properties;
|
||||||
@@ -1358,127 +1313,15 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (isDead) return;
|
if (isDead) return;
|
||||||
|
|
||||||
if (this != Character.Controlled)
|
if (GameMain.NetworkMember != null)
|
||||||
{
|
{
|
||||||
if (GameMain.Client != null)
|
UpdateNetInput();
|
||||||
{
|
|
||||||
//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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//this == Character.Controlled && GameMain.Client == null
|
|
||||||
AnimController.Frozen = false;
|
AnimController.Frozen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (networkUpdateSent)
|
|
||||||
{
|
|
||||||
foreach (Key key in keys)
|
|
||||||
{
|
|
||||||
key.DequeueHit();
|
|
||||||
key.DequeueHeld();
|
|
||||||
}
|
|
||||||
|
|
||||||
networkUpdateSent = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DisableImpactDamageTimer -= deltaTime;
|
DisableImpactDamageTimer -= deltaTime;
|
||||||
|
|
||||||
if (needsAir)
|
if (needsAir)
|
||||||
@@ -1728,9 +1571,9 @@ namespace Barotrauma
|
|||||||
localVec1.Y = -localVec1.Y;
|
localVec1.Y = -localVec1.Y;
|
||||||
localVec2.Y = -localVec2.Y;
|
localVec2.Y = -localVec2.Y;
|
||||||
|
|
||||||
GUI.DrawLine(spriteBatch, remoteVec, localVec, Color.Yellow, 0, 10);
|
//GUI.DrawLine(spriteBatch, remoteVec, localVec, Color.Yellow, 0, 10);
|
||||||
if (localPos1 != null) GUI.DrawLine(spriteBatch, remoteVec, localVec1, Color.Lime, 0, 6);
|
if (localPos1 != null) GUI.DrawLine(spriteBatch, remoteVec, localVec1, Color.Lime, 0, 2);
|
||||||
if (localPos2 != null) GUI.DrawLine(spriteBatch, remoteVec, localVec2, Color.Red, 0, 3);
|
if (localPos2 != null) GUI.DrawLine(spriteBatch, remoteVec + Vector2.One, localVec2 + Vector2.One, Color.Red, 0, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 mouseDrawPos = CursorWorldPosition;
|
Vector2 mouseDrawPos = CursorWorldPosition;
|
||||||
@@ -1874,8 +1717,10 @@ namespace Barotrauma
|
|||||||
return attackResult;
|
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 ((stunTimer <= AnimController.StunTimer && !allowStunDecrease) || !MathUtils.IsValid(stunTimer)) return;
|
||||||
|
|
||||||
if (GameMain.Server != null &&
|
if (GameMain.Server != null &&
|
||||||
@@ -2043,537 +1888,5 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (Lights.LightManager.ViewTarget == this) Lights.LightManager.ViewTarget = null;
|
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);
|
powerConnection.TryAddLink(wire);
|
||||||
wire.Connect(powerConnection, false);
|
wire.Connect(powerConnection, false, false);
|
||||||
recipient.TryAddLink(wire);
|
recipient.TryAddLink(wire);
|
||||||
wire.Connect(recipient, false);
|
wire.Connect(recipient, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateDoorBody()
|
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();
|
bool isDocked = msg.ReadBoolean();
|
||||||
|
|
||||||
|
|||||||
@@ -536,7 +536,7 @@ namespace Barotrauma.Items.Components
|
|||||||
msg.WriteRangedSingle(stuck, 0.0f, 100.0f, 8);
|
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);
|
SetState(msg.ReadBoolean(), true);
|
||||||
Stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
Stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
||||||
|
|||||||
@@ -121,13 +121,16 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
if (dropper == null) return;
|
if (dropper == null) return;
|
||||||
picker = dropper;
|
picker = dropper;
|
||||||
|
|
||||||
}
|
}
|
||||||
if (picker.Inventory == null) return;
|
if (picker.Inventory == null) return;
|
||||||
|
|
||||||
|
|
||||||
item.Submarine = picker.Submarine;
|
item.Submarine = picker.Submarine;
|
||||||
|
if (item.body != null)
|
||||||
//item.Unequip();
|
{
|
||||||
|
item.body.ResetDynamics();
|
||||||
|
item.SetTransform(picker.SimPosition, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
picker.DeselectItem(item);
|
picker.DeselectItem(item);
|
||||||
picker.Inventory.RemoveItem(item);
|
picker.Inventory.RemoveItem(item);
|
||||||
|
|||||||
@@ -191,14 +191,7 @@ namespace Barotrauma.Items.Components
|
|||||||
DropConnectedWires(picker);
|
DropConnectedWires(picker);
|
||||||
|
|
||||||
item.Submarine = picker.Submarine;
|
item.Submarine = picker.Submarine;
|
||||||
|
bodyDropPos = picker.SimPosition;
|
||||||
Limb rightHand = picker.AnimController.GetLimb(LimbType.RightHand);
|
|
||||||
bodyDropPos = rightHand.SimPosition;
|
|
||||||
|
|
||||||
if (item.body != null)
|
|
||||||
{
|
|
||||||
item.body.ResetDynamics();
|
|
||||||
}
|
|
||||||
|
|
||||||
picker.Inventory.RemoveItem(item);
|
picker.Inventory.RemoveItem(item);
|
||||||
picker = null;
|
picker = null;
|
||||||
@@ -206,6 +199,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
if (item.body != null && !item.body.Enabled)
|
if (item.body != null && !item.body.Enabled)
|
||||||
{
|
{
|
||||||
|
item.body.ResetDynamics();
|
||||||
item.SetTransform(bodyDropPos, 0.0f);
|
item.SetTransform(bodyDropPos, 0.0f);
|
||||||
item.body.Enabled = true;
|
item.body.Enabled = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ namespace Barotrauma.Items.Components
|
|||||||
msg.Write(IsActive);
|
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();
|
bool active = msg.ReadBoolean();
|
||||||
|
|
||||||
@@ -164,7 +164,7 @@ namespace Barotrauma.Items.Components
|
|||||||
msg.Write(IsActive);
|
msg.Write(IsActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClientRead(ServerNetObject type, NetIncomingMessage msg, float sendingTime)
|
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
|
||||||
{
|
{
|
||||||
SetActive(msg.ReadBoolean());
|
SetActive(msg.ReadBoolean());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -453,7 +453,7 @@ namespace Barotrauma.Items.Components
|
|||||||
msg.WriteRangedInteger(-1, fabricableItems.Count - 1, itemIndex);
|
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);
|
int itemIndex = msg.ReadRangedInteger(-1, fabricableItems.Count - 1);
|
||||||
|
|
||||||
@@ -482,7 +482,7 @@ namespace Barotrauma.Items.Components
|
|||||||
msg.WriteRangedInteger(-1, fabricableItems.Count - 1, itemIndex);
|
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);
|
int itemIndex = msg.ReadRangedInteger(-1, fabricableItems.Count - 1);
|
||||||
|
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ namespace Barotrauma.Items.Components
|
|||||||
msg.Write(IsActive);
|
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;
|
float flowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f;
|
||||||
bool isActive = msg.ReadBoolean();
|
bool isActive = msg.ReadBoolean();
|
||||||
@@ -252,7 +252,7 @@ namespace Barotrauma.Items.Components
|
|||||||
msg.Write(IsActive);
|
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;
|
FlowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f;
|
||||||
IsActive = msg.ReadBoolean();
|
IsActive = msg.ReadBoolean();
|
||||||
|
|||||||
@@ -431,7 +431,7 @@ namespace Barotrauma.Items.Components
|
|||||||
msg.Write(IsActive);
|
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();
|
bool isActive = msg.ReadBoolean();
|
||||||
|
|
||||||
@@ -448,7 +448,7 @@ namespace Barotrauma.Items.Components
|
|||||||
msg.Write(IsActive);
|
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();
|
IsActive = msg.ReadBoolean();
|
||||||
isActiveTickBox.Selected = IsActive;
|
isActiveTickBox.Selected = IsActive;
|
||||||
|
|||||||
@@ -561,7 +561,7 @@ namespace Barotrauma.Items.Components
|
|||||||
msg.WriteRangedSingle(fissionRate, 0.0f, 100.0f, 8);
|
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();
|
bool autoTemp = msg.ReadBoolean();
|
||||||
float shutDownTemp = msg.ReadRangedSingle(0.0f, 10000.0f, 8);
|
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);
|
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);
|
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();
|
bool autoPilot = msg.ReadBoolean();
|
||||||
Vector2 newTargetVelocity = targetVelocity;
|
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();
|
bool autoPilot = msg.ReadBoolean();
|
||||||
Vector2 newTargetVelocity = targetVelocity;
|
Vector2 newTargetVelocity = targetVelocity;
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
draggingConnected.RemoveConnection(item);
|
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] != null)
|
||||||
{
|
{
|
||||||
if (Wires[i].Item.body != null) Wires[i].Item.body.Enabled = false;
|
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];
|
int[] wireCounts = new int[Connections.Count];
|
||||||
Wire[,] wires = new Wire[Connections.Count, Connection.MaxLinked];
|
Wire[,] wires = new Wire[Connections.Count, Connection.MaxLinked];
|
||||||
@@ -244,7 +244,7 @@ namespace Barotrauma.Items.Components
|
|||||||
ClientWrite(msg, extraData);
|
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)
|
foreach (Connection connection in Connections)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ namespace Barotrauma.Items.Components
|
|||||||
if (connection == connections[1]) connections[1] = null;
|
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++)
|
for (int i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
@@ -196,7 +196,7 @@ namespace Barotrauma.Items.Components
|
|||||||
CleanNodes();
|
CleanNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!loading)
|
if (sendNetworkEvent)
|
||||||
{
|
{
|
||||||
if (GameMain.Server != null)
|
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();
|
nodes.Clear();
|
||||||
|
|
||||||
@@ -752,7 +752,7 @@ namespace Barotrauma.Items.Components
|
|||||||
ClientWrite(msg, extraData);
|
ClientWrite(msg, extraData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClientRead(ServerNetObject type, NetIncomingMessage msg, float sendingTime)
|
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
|
||||||
{
|
{
|
||||||
nodes.Clear();
|
nodes.Clear();
|
||||||
|
|
||||||
|
|||||||
@@ -546,7 +546,7 @@ namespace Barotrauma
|
|||||||
syncItemsDelay = 1.0f;
|
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);
|
List<Item> prevItems = new List<Item>(Items);
|
||||||
ushort[] newItemIDs = new ushort[capacity];
|
ushort[] newItemIDs = new ushort[capacity];
|
||||||
@@ -565,7 +565,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (newItemIDs[i] == 0)
|
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);
|
System.Diagnostics.Debug.Assert(Items[i]==null);
|
||||||
}
|
}
|
||||||
else
|
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];
|
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)
|
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 eventType =
|
||||||
(NetEntityEvent.Type)msg.ReadRangedInteger(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1);
|
(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>();
|
var allProperties = GetProperties<InGameEditable>();
|
||||||
if (allProperties.Count == 0) return;
|
if (allProperties.Count == 0) return;
|
||||||
@@ -1965,7 +1965,7 @@ namespace Barotrauma
|
|||||||
if (Name == "ID Card") msg.Write(Tags);
|
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;
|
if (GameMain.Server != null) return null;
|
||||||
|
|
||||||
@@ -2090,7 +2090,7 @@ namespace Barotrauma
|
|||||||
lastSentPos = SimPosition;
|
lastSentPos = SimPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClientReadPosition(ServerNetObject type, NetIncomingMessage msg, float sendingTime)
|
public void ClientReadPosition(ServerNetObject type, NetBuffer msg, float sendingTime)
|
||||||
{
|
{
|
||||||
|
|
||||||
body.TargetPosition = new Vector2(
|
body.TargetPosition = new Vector2(
|
||||||
|
|||||||
@@ -842,7 +842,7 @@ namespace Barotrauma
|
|||||||
lastSentOxygen = OxygenPercentage;
|
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;
|
Volume = message.ReadRangedSingle(0.0f, 1.5f, 8) * FullVolume;
|
||||||
OxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8);
|
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++)
|
for (int i = 0; i < sections.Count(); i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1286,7 +1286,7 @@ namespace Barotrauma
|
|||||||
msg.Write(PhysicsBody.SimPosition.Y);
|
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(
|
var newTargetPosition = new Vector2(
|
||||||
msg.ReadFloat(),
|
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;
|
if (GameMain.Server != null) return;
|
||||||
|
|
||||||
|
|||||||
@@ -354,6 +354,8 @@ namespace Barotrauma.Networking
|
|||||||
{
|
{
|
||||||
if (respawnManager != null) respawnManager.Update(deltaTime);
|
if (respawnManager != null) respawnManager.Update(deltaTime);
|
||||||
|
|
||||||
|
entityEventManager.Update();
|
||||||
|
|
||||||
bool isCrewDead =
|
bool isCrewDead =
|
||||||
connectedClients.Find(c => c.Character != null && !c.Character.IsDead)==null &&
|
connectedClients.Find(c => c.Character != null && !c.Character.IsDead)==null &&
|
||||||
(myCharacter == null || myCharacter.IsDead);
|
(myCharacter == null || myCharacter.IsDead);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace Barotrauma.Networking
|
|||||||
interface IClientSerializable : INetSerializable
|
interface IClientSerializable : INetSerializable
|
||||||
{
|
{
|
||||||
void ClientWrite(NetBuffer msg, object[] extraData = null);
|
void ClientWrite(NetBuffer msg, object[] extraData = null);
|
||||||
void ServerRead(ClientNetObject type, NetIncomingMessage msg, Client c);
|
void ServerRead(ClientNetObject type, NetBuffer msg, Client c);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -20,6 +20,6 @@ namespace Barotrauma.Networking
|
|||||||
interface IServerSerializable : INetSerializable
|
interface IServerSerializable : INetSerializable
|
||||||
{
|
{
|
||||||
void ServerWrite(NetBuffer msg, Client c, object[] extraData = null);
|
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)
|
public void CreateEvent(IClientSerializable entity, object[] extraData = null)
|
||||||
{
|
{
|
||||||
|
if (GameMain.Client == null || GameMain.Client.Character == null) return;
|
||||||
|
|
||||||
if (!(entity is Entity))
|
if (!(entity is Entity))
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError("Can't create an entity event for " + entity + "!");
|
DebugConsole.ThrowError("Can't create an entity event for " + entity + "!");
|
||||||
@@ -44,6 +46,7 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
ID++;
|
ID++;
|
||||||
var newEvent = new ClientEntityEvent(entity, ID);
|
var newEvent = new ClientEntityEvent(entity, ID);
|
||||||
|
newEvent.CharacterStateID = GameMain.Client.Character.LastNetworkUpdateID;
|
||||||
if (extraData != null) newEvent.SetData(extraData);
|
if (extraData != null) newEvent.SetData(extraData);
|
||||||
|
|
||||||
events.Add(newEvent);
|
events.Add(newEvent);
|
||||||
@@ -95,9 +98,56 @@ namespace Barotrauma.Networking
|
|||||||
Write(msg, eventsToSync);
|
Write(msg, eventsToSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read the events from the message, ignoring ones we've already received
|
||||||
|
/// </summary>
|
||||||
public void Read(NetIncomingMessage msg, float sendingTime)
|
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)
|
protected override void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null)
|
||||||
@@ -108,12 +158,9 @@ namespace Barotrauma.Networking
|
|||||||
clientEvent.Write(buffer);
|
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;
|
entity.ClientRead(ServerNetObject.ENTITY_STATE, buffer, sendingTime);
|
||||||
if (serverEntity == null) return;
|
|
||||||
|
|
||||||
serverEntity.ClientRead(ServerNetObject.ENTITY_STATE, buffer, sendingTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ namespace Barotrauma.Networking
|
|||||||
{
|
{
|
||||||
private IClientSerializable serializable;
|
private IClientSerializable serializable;
|
||||||
|
|
||||||
|
public UInt16 CharacterStateID;
|
||||||
|
|
||||||
public ClientEntityEvent(IClientSerializable entity, UInt32 id)
|
public ClientEntityEvent(IClientSerializable entity, UInt32 id)
|
||||||
: base(entity, id)
|
: base(entity, id)
|
||||||
{
|
{
|
||||||
@@ -91,6 +93,7 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
public void Write(NetBuffer msg)
|
public void Write(NetBuffer msg)
|
||||||
{
|
{
|
||||||
|
msg.Write(CharacterStateID);
|
||||||
serializable.ClientWrite(msg, Data);
|
serializable.ClientWrite(msg, Data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ namespace Barotrauma.Networking
|
|||||||
WriteEvent(tempBuffer, e, recipient);
|
WriteEvent(tempBuffer, e, recipient);
|
||||||
|
|
||||||
Debug.Assert(
|
Debug.Assert(
|
||||||
tempBuffer.LengthBytes < 256,
|
tempBuffer.LengthBytes < 128,
|
||||||
"Maximum EntityEvent size exceeded when serializing \""+e.Entity+"\"!");
|
"Maximum EntityEvent size exceeded when serializing \""+e.Entity+"\"!");
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
@@ -51,66 +51,9 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <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)
|
protected virtual void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
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; }
|
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 UInt32 ID;
|
||||||
|
|
||||||
private GameServer server;
|
private GameServer server;
|
||||||
@@ -25,6 +52,8 @@ namespace Barotrauma.Networking
|
|||||||
events = new List<ServerEntityEvent>();
|
events = new List<ServerEntityEvent>();
|
||||||
|
|
||||||
this.server = server;
|
this.server = server;
|
||||||
|
|
||||||
|
bufferedEvents = new List<BufferedEvent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CreateEvent(IServerSerializable entity, object[] extraData = null)
|
public void CreateEvent(IServerSerializable entity, object[] extraData = null)
|
||||||
@@ -50,6 +79,51 @@ namespace Barotrauma.Networking
|
|||||||
events.Add(newEvent);
|
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>
|
/// <summary>
|
||||||
/// Writes all the events that the client hasn't received yet into the outgoing message
|
/// Writes all the events that the client hasn't received yet into the outgoing message
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -99,6 +173,49 @@ namespace Barotrauma.Networking
|
|||||||
Write(msg, eventsToSync, client);
|
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)
|
protected override void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null)
|
||||||
{
|
{
|
||||||
var serverEvent = entityEvent as ServerEntityEvent;
|
var serverEvent = entityEvent as ServerEntityEvent;
|
||||||
@@ -107,7 +224,7 @@ namespace Barotrauma.Networking
|
|||||||
serverEvent.Write(buffer, recipient);
|
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;
|
var clientEntity = entity as IClientSerializable;
|
||||||
if (clientEntity == null) return;
|
if (clientEntity == null) return;
|
||||||
@@ -115,16 +232,13 @@ namespace Barotrauma.Networking
|
|||||||
clientEntity.ServerRead(ClientNetObject.ENTITY_STATE, buffer, sender);
|
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()
|
public void Clear()
|
||||||
{
|
{
|
||||||
ID = 0;
|
ID = 0;
|
||||||
events.Clear();
|
events.Clear();
|
||||||
|
|
||||||
|
bufferedEvents.Clear();
|
||||||
|
|
||||||
server.ConnectedClients.ForEach(c => c.entityEventLastSent.Clear());
|
server.ConnectedClients.ForEach(c => c.entityEventLastSent.Clear());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -452,10 +452,8 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
drawPosition -= ConvertUnits.ToDisplayUnits(Vector2.Lerp(Vector2.Zero, offsetFromTargetPos, offsetLerp));
|
||||||
drawPosition -= ConvertUnits.ToDisplayUnits(Vector2.Lerp(Vector2.Zero, offsetFromTargetPos, offsetLerp));
|
|
||||||
}
|
|
||||||
|
|
||||||
offsetLerp -= 0.1f;
|
offsetLerp -= 0.1f;
|
||||||
if (offsetLerp < 0.0f)
|
if (offsetLerp < 0.0f)
|
||||||
|
|||||||
Reference in New Issue
Block a user