diff --git a/Subsurface/Barotrauma.csproj b/Subsurface/Barotrauma.csproj index cc27f9265..1d4fa3d64 100644 --- a/Subsurface/Barotrauma.csproj +++ b/Subsurface/Barotrauma.csproj @@ -157,7 +157,10 @@ + + + diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index 7c4f171c3..55e535e02 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -1911,7 +1911,7 @@ namespace Barotrauma if (AnimController != null) AnimController.Remove(); } - public virtual void ClientWrite(NetOutgoingMessage msg) + public virtual void ClientWrite(NetBuffer msg) { if (GameMain.Server != null) return; @@ -1972,7 +1972,7 @@ namespace Barotrauma } } - public virtual void ServerWrite(NetOutgoingMessage msg, Client c) + public virtual void ServerWrite(NetBuffer msg, Client c) { if (GameMain.Server == null) return; @@ -2037,7 +2037,7 @@ namespace Barotrauma memPos.Insert(index, posInfo); } - public void WriteSpawnData(NetOutgoingMessage msg) + public void WriteSpawnData(NetBuffer msg) { if (GameMain.Server == null) return; diff --git a/Subsurface/Source/Items/Components/DockingPort.cs b/Subsurface/Source/Items/Components/DockingPort.cs index d663b7d81..f1acb40ed 100644 --- a/Subsurface/Source/Items/Components/DockingPort.cs +++ b/Subsurface/Source/Items/Components/DockingPort.cs @@ -648,7 +648,7 @@ namespace Barotrauma.Items.Components } } - public override void ServerWrite(Lidgren.Network.NetOutgoingMessage msg, Barotrauma.Networking.Client c) + public override void ServerWrite(Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c) { msg.Write(docked); diff --git a/Subsurface/Source/Items/Components/Door.cs b/Subsurface/Source/Items/Components/Door.cs index f2f66c538..a3fe6d03a 100644 --- a/Subsurface/Source/Items/Components/Door.cs +++ b/Subsurface/Source/Items/Components/Door.cs @@ -493,7 +493,7 @@ namespace Barotrauma.Items.Components } } - public override void ServerWrite(Lidgren.Network.NetOutgoingMessage msg, Barotrauma.Networking.Client c) + public override void ServerWrite(Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c) { msg.Write(isOpen); msg.WriteRangedSingle(stuck, 0.0f, 100.0f, 8); diff --git a/Subsurface/Source/Items/Components/ItemComponent.cs b/Subsurface/Source/Items/Components/ItemComponent.cs index d052621a3..dd6c9c652 100644 --- a/Subsurface/Source/Items/Components/ItemComponent.cs +++ b/Subsurface/Source/Items/Components/ItemComponent.cs @@ -662,10 +662,10 @@ namespace Barotrauma.Items.Components } } - public virtual void ClientWrite(NetOutgoingMessage msg) { } + public virtual void ClientWrite(NetBuffer msg) { } public virtual void ServerRead(NetIncomingMessage msg, Client c) { } - public virtual void ServerWrite(NetOutgoingMessage msg, Client c) { } + public virtual void ServerWrite(NetBuffer msg, Client c) { } public virtual void ClientRead(NetIncomingMessage msg, float sendingTime) { } public virtual XElement Save(XElement parentElement) diff --git a/Subsurface/Source/Items/Components/Machines/Pump.cs b/Subsurface/Source/Items/Components/Machines/Pump.cs index a9707373f..2ea3985f9 100644 --- a/Subsurface/Source/Items/Components/Machines/Pump.cs +++ b/Subsurface/Source/Items/Components/Machines/Pump.cs @@ -196,7 +196,7 @@ namespace Barotrauma.Items.Components if (!IsActive) currPowerConsumption = 0.0f; } - public override void ClientWrite(Lidgren.Network.NetOutgoingMessage msg) + public override void ClientWrite(Lidgren.Network.NetBuffer msg) { //flowpercentage can only be adjusted at 10% intervals -> no need for more accuracy than this msg.WriteRangedInteger(-10, 10, (int)(flowPercentage / 10.0f)); @@ -209,7 +209,7 @@ namespace Barotrauma.Items.Components IsActive = msg.ReadBoolean(); } - public override void ServerWrite(Lidgren.Network.NetOutgoingMessage msg, Barotrauma.Networking.Client c) + public override void ServerWrite(Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c) { //flowpercentage can only be adjusted at 10% intervals -> no need for more accuracy than this msg.WriteRangedInteger(-10, 10, (int)(flowPercentage / 10.0f)); diff --git a/Subsurface/Source/Items/Components/Machines/Radar.cs b/Subsurface/Source/Items/Components/Machines/Radar.cs index 82702c46a..97e5a136e 100644 --- a/Subsurface/Source/Items/Components/Machines/Radar.cs +++ b/Subsurface/Source/Items/Components/Machines/Radar.cs @@ -411,7 +411,7 @@ namespace Barotrauma.Items.Components if (screenOverlay != null) screenOverlay.Remove(); } - public override void ClientWrite(Lidgren.Network.NetOutgoingMessage msg) + public override void ClientWrite(Lidgren.Network.NetBuffer msg) { msg.Write(IsActive); } @@ -422,7 +422,7 @@ namespace Barotrauma.Items.Components isActiveTickBox.Selected = IsActive; } - public override void ServerWrite(Lidgren.Network.NetOutgoingMessage msg, Barotrauma.Networking.Client c) + public override void ServerWrite(Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c) { msg.Write(IsActive); } diff --git a/Subsurface/Source/Items/Components/Machines/Reactor.cs b/Subsurface/Source/Items/Components/Machines/Reactor.cs index 5664ec018..6915abf0b 100644 --- a/Subsurface/Source/Items/Components/Machines/Reactor.cs +++ b/Subsurface/Source/Items/Components/Machines/Reactor.cs @@ -538,7 +538,7 @@ namespace Barotrauma.Items.Components } } - public override void ClientWrite(NetOutgoingMessage msg) + public override void ClientWrite(NetBuffer msg) { msg.Write(autoTemp); msg.WriteRangedSingle(shutDownTemp, 0.0f, 10000.0f, 8); @@ -556,7 +556,7 @@ namespace Barotrauma.Items.Components FissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8); } - public override void ServerWrite(NetOutgoingMessage msg, Client c) + public override void ServerWrite(NetBuffer msg, Client c) { msg.WriteRangedSingle(temperature, 0.0f, 10000.0f, 16); diff --git a/Subsurface/Source/Items/Components/Machines/Steering.cs b/Subsurface/Source/Items/Components/Machines/Steering.cs index d4b564723..417d14718 100644 --- a/Subsurface/Source/Items/Components/Machines/Steering.cs +++ b/Subsurface/Source/Items/Components/Machines/Steering.cs @@ -443,7 +443,7 @@ namespace Barotrauma.Items.Components } } - public override void ClientWrite(Lidgren.Network.NetOutgoingMessage msg) + public override void ClientWrite(Lidgren.Network.NetBuffer msg) { msg.Write(autoPilot); @@ -503,7 +503,7 @@ namespace Barotrauma.Items.Components } } - public override void ServerWrite(Lidgren.Network.NetOutgoingMessage msg, Barotrauma.Networking.Client c) + public override void ServerWrite(Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c) { msg.Write(autoPilot); diff --git a/Subsurface/Source/Items/Components/Power/PowerContainer.cs b/Subsurface/Source/Items/Components/Power/PowerContainer.cs index d6f9633f1..1d10c4ca1 100644 --- a/Subsurface/Source/Items/Components/Power/PowerContainer.cs +++ b/Subsurface/Source/Items/Components/Power/PowerContainer.cs @@ -249,7 +249,7 @@ namespace Barotrauma.Items.Components GuiFrame.Update(1.0f / 60.0f); } - public override void ClientWrite(Lidgren.Network.NetOutgoingMessage msg) + public override void ClientWrite(Lidgren.Network.NetBuffer msg) { float chargeSpeed = MathHelper.Clamp(rechargeSpeed / MaxRechargeSpeed, 0.0f, 1.0f); msg.WriteRangedSingle(chargeSpeed, 0.0f, 1.0f, 8); @@ -260,7 +260,7 @@ namespace Barotrauma.Items.Components RechargeSpeed = msg.ReadRangedSingle(0.0f, 1.0f, 8) * maxRechargeSpeed; } - public override void ServerWrite(Lidgren.Network.NetOutgoingMessage msg, Barotrauma.Networking.Client c) + public override void ServerWrite(Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c) { float chargeSpeed = MathHelper.Clamp(rechargeSpeed / MaxRechargeSpeed, 0.0f, 1.0f); msg.WriteRangedSingle(chargeSpeed, 0.0f, 1.0f, 8); diff --git a/Subsurface/Source/Items/Components/Signal/Wire.cs b/Subsurface/Source/Items/Components/Signal/Wire.cs index 0240050dc..c511abcde 100644 --- a/Subsurface/Source/Items/Components/Signal/Wire.cs +++ b/Subsurface/Source/Items/Components/Signal/Wire.cs @@ -532,7 +532,7 @@ namespace Barotrauma.Items.Components base.RemoveComponentSpecific(); } - public override void ClientWrite(Lidgren.Network.NetOutgoingMessage msg) + public override void ClientWrite(Lidgren.Network.NetBuffer msg) { msg.Write((byte)Math.Min(Nodes.Count, 255)); for (int i = 0; i < Math.Min(Nodes.Count, 255); i++) diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index d1f52bcdf..b1d66f3bf 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -1579,11 +1579,11 @@ namespace Barotrauma return element; } - - public void ServerWrite(NetOutgoingMessage msg, Client c) { } + + public void ServerWrite(NetBuffer msg, Client c) { } public void ClientRead(NetIncomingMessage msg, float sendingTime) { } - public void WriteSpawnData(NetOutgoingMessage msg) + public void WriteSpawnData(NetBuffer msg) { if (GameMain.Server == null) return; diff --git a/Subsurface/Source/Map/Structure.cs b/Subsurface/Source/Map/Structure.cs index 067902cf9..a33b131e8 100644 --- a/Subsurface/Source/Map/Structure.cs +++ b/Subsurface/Source/Map/Structure.cs @@ -845,7 +845,7 @@ namespace Barotrauma return newBody; } - public void ServerWrite(NetOutgoingMessage msg, Client c) + public void ServerWrite(NetBuffer msg, Client c) { for (int i = 0; i < sections.Length; i++) { diff --git a/Subsurface/Source/Map/Submarine.cs b/Subsurface/Source/Map/Submarine.cs index 8f08fbb4f..a5e1e56d9 100644 --- a/Subsurface/Source/Map/Submarine.cs +++ b/Subsurface/Source/Map/Submarine.cs @@ -1163,7 +1163,7 @@ namespace Barotrauma DockedTo.Clear(); } - public void ServerWrite(NetOutgoingMessage msg, Client c) + public void ServerWrite(NetBuffer msg, Client c) { msg.Write(ID); //length in bytes diff --git a/Subsurface/Source/Networking/Client.cs b/Subsurface/Source/Networking/Client.cs index 754158eb9..6f7e75df3 100644 --- a/Subsurface/Source/Networking/Client.cs +++ b/Subsurface/Source/Networking/Client.cs @@ -35,6 +35,7 @@ namespace Barotrauma.Networking public bool hasLobbyData = false; public UInt32 lastSentChatMsgID = 0; //last msg this client said public UInt32 lastRecvChatMsgID = 0; //last msg this client knows about + public UInt32 lastRecvEntityEventID = 0; public UInt32 lastRecvEntitySpawnID = 0; diff --git a/Subsurface/Source/Networking/EntitySpawner.cs b/Subsurface/Source/Networking/EntitySpawner.cs index 7b7755241..f7224bd76 100644 --- a/Subsurface/Source/Networking/EntitySpawner.cs +++ b/Subsurface/Source/Networking/EntitySpawner.cs @@ -152,7 +152,7 @@ namespace Barotrauma NetStateID = (UInt32)spawnHistory.Count; } - public void ServerWrite(Lidgren.Network.NetOutgoingMessage message, Client client) + public void ServerWrite(Lidgren.Network.NetBuffer message, Client client) { if (GameMain.Server == null) return; diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 635af34e6..764f66131 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -39,6 +39,8 @@ namespace Barotrauma.Networking private UInt32 lastQueueChatMsgID = 0; //last message added to the queue private List chatMsgQueue = new List(); + public UInt32 LastSentEntityEventID; + public byte ID { get { return myID; } diff --git a/Subsurface/Source/Networking/INetSerializable.cs b/Subsurface/Source/Networking/INetSerializable.cs index 0d51ca02b..75db0fc8a 100644 --- a/Subsurface/Source/Networking/INetSerializable.cs +++ b/Subsurface/Source/Networking/INetSerializable.cs @@ -3,25 +3,27 @@ using Lidgren.Network; namespace Barotrauma.Networking { + interface INetSerializable { } + /// /// Interface for entities that the clients can send information of to the server /// - interface IClientSerializable + interface IClientSerializable : INetSerializable { UInt32 NetStateID { get; } - void ClientWrite(NetOutgoingMessage msg); + void ClientWrite(NetBuffer msg); void ServerRead(NetIncomingMessage msg, Client c); } /// /// Interface for entities that the server can send information of to the clients /// - interface IServerSerializable + interface IServerSerializable : INetSerializable { UInt32 NetStateID { get; } - void ServerWrite(NetOutgoingMessage msg, Client c); + void ServerWrite(NetBuffer msg, Client c); void ClientRead(NetIncomingMessage msg, float sendingTime); } } diff --git a/Subsurface/Source/Networking/NetEntityEvent/ClientEntityEventManager.cs b/Subsurface/Source/Networking/NetEntityEvent/ClientEntityEventManager.cs new file mode 100644 index 000000000..c7dcfddf1 --- /dev/null +++ b/Subsurface/Source/Networking/NetEntityEvent/ClientEntityEventManager.cs @@ -0,0 +1,56 @@ +using Lidgren.Network; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Barotrauma.Networking +{ + class ClientEntityEventManager : NetEntityEventManager + { + private List events; + + private UInt32 ID; + + private GameClient thisClient; + + public ClientEntityEventManager(GameClient client) { } + + public void CreateEvent(IClientSerializable entity) + { + if (!(entity is Entity)) + { + DebugConsole.ThrowError("Can't create an entity event for " + entity + "!"); + return; + } + + ID++; + events.Add(new ClientEntityEvent(entity, ID)); + } + + public void Write(NetOutgoingMessage msg) + { + var eventsToSync = events.SkipWhile(e => e.ID >= thisClient.LastSentEntityEventID).ToList(); + if (eventsToSync.Count == 0) return; + + Write(msg, eventsToSync.Cast().ToList()); + } + + protected override void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null) + { + var clientEvent = entityEvent as ClientEntityEvent; + if (clientEvent == null) return; + + clientEvent.Write(buffer); + } + + protected override void ReadEvent(NetIncomingMessage buffer, INetSerializable entity, float sendingTime, Client sender = null) + { + var clientEntity = entity as IClientSerializable; + if (clientEntity == null) return; + + clientEntity.ServerRead(buffer, sender); + } + } +} diff --git a/Subsurface/Source/Networking/NetEntityEvent/NetEntityEvent.cs b/Subsurface/Source/Networking/NetEntityEvent/NetEntityEvent.cs new file mode 100644 index 000000000..3d0626a8b --- /dev/null +++ b/Subsurface/Source/Networking/NetEntityEvent/NetEntityEvent.cs @@ -0,0 +1,54 @@ +using Lidgren.Network; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Barotrauma.Networking +{ + abstract class NetEntityEvent + { + public readonly Entity Entity; + public readonly UInt32 ID; + + protected NetEntityEvent(INetSerializable entity, UInt32 id) + { + this.ID = id; + this.Entity = entity as Entity; + } + } + + class ServerEntityEvent : NetEntityEvent + { + private IServerSerializable serializable; + + public ServerEntityEvent(IServerSerializable entity, UInt32 id) + : base(entity, id) + { + serializable = entity; + } + + public void Write(NetBuffer msg, Client recipient) + { + serializable.ServerWrite(msg, recipient); + } + } + + class ClientEntityEvent : NetEntityEvent + { + private IClientSerializable serializable; + + public ClientEntityEvent(IClientSerializable entity, UInt32 id) + : base(entity, id) + { + serializable = entity; + } + + public void Write(NetBuffer msg) + { + serializable.ClientWrite(msg); + } + } + +} diff --git a/Subsurface/Source/Networking/NetEntityEvent/NetEntityEventManager.cs b/Subsurface/Source/Networking/NetEntityEvent/NetEntityEventManager.cs new file mode 100644 index 000000000..761cf8349 --- /dev/null +++ b/Subsurface/Source/Networking/NetEntityEvent/NetEntityEventManager.cs @@ -0,0 +1,89 @@ +using Lidgren.Network; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Barotrauma.Networking +{ + abstract class NetEntityEventManager + { + const int MaxEventsPerWrite = 255; + + private UInt32 lastReceivedEntityEventID; + + /// + /// Write the events to the outgoing message. The recipient parameter is only needed for ServerEntityEventManager + /// + protected void Write(NetOutgoingMessage msg, List eventsToSync, Client recipient = null) + { + //too many events for one packet + if (eventsToSync.Count > MaxEventsPerWrite) + { + eventsToSync.RemoveRange(MaxEventsPerWrite, eventsToSync.Count - MaxEventsPerWrite); + } + + msg.Write((byte)ServerNetObject.ENTITY_STATE); + + msg.Write(eventsToSync[0].ID); + msg.Write((byte)eventsToSync.Count); + + foreach (NetEntityEvent e in eventsToSync) + { + //write into a temporary buffer so we can write the length before the actual data + NetBuffer tempBuffer = new NetBuffer(); + WriteEvent(tempBuffer, e, recipient); + tempBuffer.WritePadBits(); + + Debug.Assert( + tempBuffer.LengthBytes < 256, + "Maximum EntityEvent size exceeded when serializing \""+e.Entity+"\"!"); + + msg.Write((UInt16)e.Entity.ID); + msg.Write((byte)tempBuffer.LengthBytes); + msg.Write(tempBuffer); + } + } + + /// + /// Read the events from the message, ignoring ones we've already received + /// + public void Read(NetIncomingMessage msg, float sendingTime) + { + UInt32 firstEventID = msg.ReadUInt32(); + byte 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 <= lastReceivedEntityEventID || entity == null) + { + msg.Position += msgLength * 8; + } + else + { + ReadEvent(msg, entity, sendingTime); + lastReceivedEntityEventID = thisEventID; + } + } + } + + protected virtual void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null) + { + throw new NotImplementedException(); + } + + protected virtual void ReadEvent(NetIncomingMessage buffer, INetSerializable entity, float sendingTime, Client sender = null) + { + throw new NotImplementedException(); + } + } +} diff --git a/Subsurface/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs b/Subsurface/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs new file mode 100644 index 000000000..cb3fda394 --- /dev/null +++ b/Subsurface/Source/Networking/NetEntityEvent/ServerEntityEventManager.cs @@ -0,0 +1,57 @@ +using Lidgren.Network; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Barotrauma.Networking +{ + class ServerEntityEventManager : NetEntityEventManager + { + private List events; + + private UInt32 ID; + + public ServerEntityEventManager(GameServer server) { } + + public void CreateEvent(IServerSerializable entity) + { + if (!(entity is Entity)) + { + DebugConsole.ThrowError("Can't create an entity event for " + entity + "!"); + return; + } + + ID++; + events.Add(new ServerEntityEvent(entity, ID)); + } + + /// + /// Writes all the events that the client hasn't received yet into the outgoing message + /// + public void Write(Client client, NetOutgoingMessage msg) + { + var eventsToSync = events.SkipWhile(e => e.ID >= client.lastRecvEntityEventID).ToList(); + if (eventsToSync.Count == 0) return; + + Write(msg, eventsToSync.Cast().ToList(), client); + } + + protected override void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null) + { + var serverEvent = entityEvent as ServerEntityEvent; + if (serverEvent == null) return; + + serverEvent.Write(buffer, recipient); + } + + protected override void ReadEvent(NetIncomingMessage buffer, INetSerializable entity, float sendingTime, Client sender = null) + { + var clientEntity = entity as IClientSerializable; + if (clientEntity == null) return; + + clientEntity.ServerRead(buffer, sender); + } + } +} diff --git a/Subsurface/Source/Networking/NetworkMember.cs b/Subsurface/Source/Networking/NetworkMember.cs index eb37a276b..92a7c5e1a 100644 --- a/Subsurface/Source/Networking/NetworkMember.cs +++ b/Subsurface/Source/Networking/NetworkMember.cs @@ -45,7 +45,7 @@ namespace Barotrauma.Networking CHAT_MESSAGE, VOTE, ENTITY_POSITION, - ITEM_STATE, + ENTITY_STATE, ENTITY_SPAWN }