diff --git a/Lidgren.Network/Lidgren.Network.csproj b/Lidgren.Network/Lidgren.Network.csproj index b5ea57927..80fec2436 100644 --- a/Lidgren.Network/Lidgren.Network.csproj +++ b/Lidgren.Network/Lidgren.Network.csproj @@ -45,7 +45,7 @@ pdbonly true bin\Release\ - TRACE + TRACE;USE_RELEASE_STATISTICS prompt 4 AllRules.ruleset diff --git a/Subsurface/Barotrauma.csproj b/Subsurface/Barotrauma.csproj index 2f4419f25..b825317a0 100644 --- a/Subsurface/Barotrauma.csproj +++ b/Subsurface/Barotrauma.csproj @@ -100,6 +100,7 @@ + diff --git a/Subsurface/Properties/AssemblyInfo.cs b/Subsurface/Properties/AssemblyInfo.cs index 32dd7fc3b..43a9e6164 100644 --- a/Subsurface/Properties/AssemblyInfo.cs +++ b/Subsurface/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.2.0.0")] -[assembly: AssemblyFileVersion("0.2.0.0")] +[assembly: AssemblyVersion("0.2.2.0")] +[assembly: AssemblyFileVersion("0.2.2.0")] diff --git a/Subsurface/Source/Characters/AICharacter.cs b/Subsurface/Source/Characters/AICharacter.cs index 0642b529d..69dd89a79 100644 --- a/Subsurface/Source/Characters/AICharacter.cs +++ b/Subsurface/Source/Characters/AICharacter.cs @@ -83,11 +83,11 @@ namespace Barotrauma return result; } - public override void FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) + public override bool FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) { if (type == NetworkEventType.KillCharacter) { - return; + return true; } message.Write((float)NetTime.Now); @@ -127,6 +127,9 @@ namespace Barotrauma LargeUpdateTimer = Math.Max(0, LargeUpdateTimer - 1); } + + + return true; } public override void ReadNetworkData(NetworkEventType type, NetIncomingMessage message) diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index 2a557fe23..a36ce84b1 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -1084,21 +1084,21 @@ namespace Barotrauma } } - public override void FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) + public override bool FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) { if (type == NetworkEventType.PickItem) { message.Write((int)data); - return; + return true; } else if (type== NetworkEventType.SelectCharacter) { message.Write((int)data); - return; + return true; } else if (type == NetworkEventType.KillCharacter) { - return; + return true; } var hasInputs = @@ -1160,7 +1160,9 @@ namespace Barotrauma message.Write(AnimController.RefLimb.SimPosition.Y); LargeUpdateTimer = Math.Max(0, LargeUpdateTimer-1); - } + } + + return true; } public override void ReadNetworkData(NetworkEventType type, NetIncomingMessage message) diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index 4ed836b2b..d132e6a6c 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -325,6 +325,11 @@ namespace Barotrauma //Ragdoll.DebugDraw = !Ragdoll.DebugDraw; GameMain.DebugDraw = !GameMain.DebugDraw; break; + case "netstats": + if (GameMain.Server == null) return; + + GameMain.Server.ShowNetStats = !GameMain.Server.ShowNetStats; + break; default: NewMessage("Command not found", Color.Red); break; diff --git a/Subsurface/Source/GameSession/GameModes/TutorialMode.cs b/Subsurface/Source/GameSession/GameModes/TutorialMode.cs index 5cadb67c0..6b49c7be9 100644 --- a/Subsurface/Source/GameSession/GameModes/TutorialMode.cs +++ b/Subsurface/Source/GameSession/GameModes/TutorialMode.cs @@ -613,6 +613,8 @@ namespace Barotrauma { do { + if (enemy == null) break; + enemy.Health = 50.0f; enemy.AIController.State = AIController.AiState.None; diff --git a/Subsurface/Source/Items/Inventory.cs b/Subsurface/Source/Items/Inventory.cs index 8cbff3e8f..70df9f8f4 100644 --- a/Subsurface/Source/Items/Inventory.cs +++ b/Subsurface/Source/Items/Inventory.cs @@ -279,12 +279,14 @@ namespace Barotrauma spriteBatch.DrawString(GUI.Font, (int)item.Condition + " %", new Vector2(rect.X + rect.Width / 2, rect.Y + rect.Height / 2), Color.Red); } - public override void FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) + public override bool FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) { for (int i = 0; i= components.Count) return; + if (componentIndex < 0 || componentIndex >= components.Count) return false; message.Write((byte)componentIndex); components[componentIndex].FillNetworkData(type, message); @@ -1214,6 +1214,8 @@ namespace Barotrauma break; } + + return true; } public override void ReadNetworkData(NetworkEventType type, NetIncomingMessage message) diff --git a/Subsurface/Source/Map/Entity.cs b/Subsurface/Source/Map/Entity.cs index 48d868ce2..835ccdcec 100644 --- a/Subsurface/Source/Map/Entity.cs +++ b/Subsurface/Source/Map/Entity.cs @@ -64,7 +64,10 @@ namespace Barotrauma dictionary.Add(id, this); } - public virtual void FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) { } + public virtual bool FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) + { + return false; + } public virtual void ReadNetworkData(NetworkEventType type, NetIncomingMessage message) { } /// diff --git a/Subsurface/Source/Map/Hull.cs b/Subsurface/Source/Map/Hull.cs index 36142d8a3..b75ef5671 100644 --- a/Subsurface/Source/Map/Hull.cs +++ b/Subsurface/Source/Map/Hull.cs @@ -471,9 +471,11 @@ namespace Barotrauma h.ID = int.Parse(element.Attribute("ID").Value); } - public override void FillNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetOutgoingMessage message, object data) + public override bool FillNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetOutgoingMessage message, object data) { message.Write(volume); + + return true; } public override void ReadNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetIncomingMessage message) diff --git a/Subsurface/Source/Map/Structure.cs b/Subsurface/Source/Map/Structure.cs index a5d541f40..d8858e61b 100644 --- a/Subsurface/Source/Map/Structure.cs +++ b/Subsurface/Source/Map/Structure.cs @@ -636,7 +636,7 @@ namespace Barotrauma } - public override void FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) + public override bool FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) { int sectionIndex = 0; byte byteIndex = 0; @@ -648,12 +648,14 @@ namespace Barotrauma } catch { - return; + return false; } message.Write((float)NetTime.Now); message.Write(byteIndex); message.Write(sections[sectionIndex].damage); + + return true; } public override void ReadNetworkData(NetworkEventType type, NetIncomingMessage message) diff --git a/Subsurface/Source/Map/Submarine.cs b/Subsurface/Source/Map/Submarine.cs index 21ed6e856..b52386284 100644 --- a/Subsurface/Source/Map/Submarine.cs +++ b/Subsurface/Source/Map/Submarine.cs @@ -149,7 +149,7 @@ namespace Barotrauma } base.Remove(); - ID = -1; + ID = -5; } //drawing ---------------------------------------------------- @@ -378,14 +378,18 @@ namespace Barotrauma Level.Loaded.Move(-amount); } - public override void FillNetworkData(Networking.NetworkEventType type, NetOutgoingMessage message, object data) + public override bool FillNetworkData(Networking.NetworkEventType type, NetOutgoingMessage message, object data) { + if (subBody == null) return false; + message.Write((float)NetTime.Now); message.Write(Position.X); message.Write(Position.Y); message.Write(Speed.X); message.Write(Speed.Y); + + return true; } public override void ReadNetworkData(Networking.NetworkEventType type, NetIncomingMessage message) diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 625a41121..26ef45aa0 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using Lidgren.Network; using Microsoft.Xna.Framework; using System.Collections.Generic; +using Barotrauma.Networking.ReliableMessages; namespace Barotrauma.Networking { @@ -12,6 +13,8 @@ namespace Barotrauma.Networking private GUIMessageBox reconnectBox; + private ReliableChannel reliableChannel; + private bool connected; private int myID; @@ -34,6 +37,7 @@ namespace Barotrauma.Networking otherClients = new List(); + } public void ConnectToServer(string hostIP, string password = "") @@ -63,6 +67,7 @@ namespace Barotrauma.Networking #if DEBUG config.SimulatedLoss = 0.1f; config.SimulatedMinimumLatency = 0.3f; + config.SimulatedRandomLatency = 0.5f; #endif config.DisableMessageType(NetIncomingMessageType.DebugMessage | NetIncomingMessageType.WarningMessage | NetIncomingMessageType.Receipt @@ -70,6 +75,7 @@ namespace Barotrauma.Networking // Create new client, with previously created configs client = new NetClient(config); + reliableChannel = new ReliableChannel(client); NetOutgoingMessage outmsg = client.CreateMessage(); client.Start(); @@ -329,6 +335,8 @@ namespace Barotrauma.Networking new NetworkEvent(myCharacter.ID, true); } } + + reliableChannel.Update(deltaTime); foreach (NetworkEvent networkEvent in NetworkEvent.events) { @@ -368,8 +376,17 @@ namespace Barotrauma.Networking while ((inc = client.ReadMessage()) != null) { if (inc.MessageType != NetIncomingMessageType.Data) continue; - - switch (inc.ReadByte()) + + //todo: exception handling + byte packetType = inc.ReadByte(); + + if (packetType == (byte)PacketTypes.ReliableMessage) + { + if (!reliableChannel.CheckMessage(inc)) continue; + packetType = inc.ReadByte(); + } + + switch (packetType) { case (byte)PacketTypes.StartGame: if (gameStarted) continue; @@ -427,6 +444,12 @@ namespace Barotrauma.Networking new GUIMessageBox("You are the Traitor!", "Your secret task is to assassinate " + targetName + "!"); + break; + case (byte)PacketTypes.ResendRequest: + reliableChannel.HandleResendRequest(inc); + break; + case (byte)PacketTypes.Ack: + reliableChannel.HandleAckMessage(inc); break; } } @@ -652,13 +675,13 @@ namespace Barotrauma.Networking //AddChatMessage(message); type = (gameStarted && myCharacter != null && myCharacter.IsDead) ? ChatMessageType.Dead : ChatMessageType.Default; + + ReliableMessage msg = reliableChannel.CreateMessage(); + msg.InnerMessage.Write((byte)PacketTypes.Chatmessage); + msg.InnerMessage.Write((byte)type); + msg.InnerMessage.Write(message); - NetOutgoingMessage msg = client.CreateMessage(); - msg.Write((byte)PacketTypes.Chatmessage); - msg.Write((byte)type); - msg.Write(message); - - client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); + reliableChannel.SendMessage(msg, client.ServerConnection); } /// diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 2a9df35c1..19fb18d3d 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -5,11 +5,13 @@ using System.Diagnostics; using Lidgren.Network; using Microsoft.Xna.Framework; using RestSharp; +using Barotrauma.Networking.ReliableMessages; namespace Barotrauma.Networking { class GameServer : NetworkMember { + public bool ShowNetStats; public List connectedClients = new List(); @@ -64,7 +66,7 @@ namespace Barotrauma.Networking #if DEBUG config.SimulatedLoss = 0.2f; - config.SimulatedMinimumLatency = 0.3f; + config.SimulatedRandomLatency = 0.6f; config.SimulatedDuplicatesChance = 0.05f; config.SimulatedMinimumLatency = 0.1f; #endif @@ -96,9 +98,9 @@ namespace Barotrauma.Networking } catch (Exception e) { - DebugConsole.ThrowError("Couldn't start the server", e); + DebugConsole.ThrowError("Couldn't start the server", e); } - + if (config.EnableUPnP) { @@ -225,7 +227,7 @@ namespace Barotrauma.Networking public override void Update(float deltaTime) { - if (GameMain.DebugDraw) netStats.Update(deltaTime); + if (ShowNetStats) netStats.Update(deltaTime); if (!started) return; @@ -268,8 +270,13 @@ namespace Barotrauma.Networking disconnectedClients.RemoveAt(i); } - NetIncomingMessage inc = server.ReadMessage(); - if (inc != null) + foreach (Client c in connectedClients) + { + c.ReliableChannel.Update(deltaTime); + } + + NetIncomingMessage inc = null; + while ((inc = server.ReadMessage()) != null) { try { @@ -277,7 +284,11 @@ namespace Barotrauma.Networking } catch { +#if DEBUG + DebugConsole.ThrowError("Failed to read incoming message"); +#endif + continue; } } @@ -416,7 +427,26 @@ namespace Barotrauma.Networking break; case NetIncomingMessageType.Data: - switch (inc.ReadByte()) + Client dataSender = connectedClients.Find(c => c.Connection == inc.SenderConnection); + if (dataSender == null) return; + + byte packetType = 0; + try + { + packetType = inc.ReadByte(); + } + catch + { + return; + } + + if (packetType == (byte)PacketTypes.ReliableMessage) + { + if (!dataSender.ReliableChannel.CheckMessage(inc)) return; + packetType = inc.ReadByte(); + } + + switch (packetType) { case (byte)PacketTypes.NetworkEvent: if (!gameStarted) break; @@ -452,6 +482,13 @@ namespace Barotrauma.Networking case (byte)PacketTypes.CharacterInfo: ReadCharacterData(inc); break; + case (byte)PacketTypes.ResendRequest: + + dataSender.ReliableChannel.HandleResendRequest(inc); + break; + case (byte)PacketTypes.Ack: + dataSender.ReliableChannel.HandleAckMessage(inc); + break; } break; case NetIncomingMessageType.WarningMessage: @@ -552,7 +589,7 @@ namespace Barotrauma.Networking userID++; } - Client newClient = new Client(name, userID); + Client newClient = new Client(server, name, userID); newClient.Connection = inc.SenderConnection; newClient.version = version; @@ -581,26 +618,23 @@ namespace Barotrauma.Networking { if (NetworkEvent.events.Count == 0) return; + List recipients = new List(); + foreach (Client c in connectedClients) + { + if (c.character == null) continue; + //if (networkEvent.Type == NetworkEventType.UpdateEntity && + // Vector2.Distance(e.SimPosition, c.character.SimPosition) > NetConfig.UpdateEntityDistance) continue; + + recipients.Add(c.Connection); + } + + if (recipients.Count == 0) return; + foreach (NetworkEvent networkEvent in NetworkEvent.events) { - - List recipients = new List(); - Entity e = Entity.FindEntityByID(networkEvent.ID); if (e == null) continue; - foreach (Client c in connectedClients) - { - if (c.character == null) continue; - //if (networkEvent.Type == NetworkEventType.UpdateEntity && - // Vector2.Distance(e.SimPosition, c.character.SimPosition) > NetConfig.UpdateEntityDistance) continue; - - recipients.Add(c.Connection); - } - - - if (recipients.Count == 0) return; - NetOutgoingMessage message = server.CreateMessage(); message.Write((byte)PacketTypes.NetworkEvent); //if (!networkEvent.IsClient) continue; @@ -872,7 +906,7 @@ namespace Barotrauma.Networking { base.Draw(spriteBatch); - if (!GameMain.DebugDraw) return; + if (!ShowNetStats) return; int width = 200, height = 300; int x = GameMain.GraphicsWidth - width, y = (int)(GameMain.GraphicsHeight*0.3f); @@ -934,27 +968,22 @@ namespace Barotrauma.Networking if (server.Connections.Count == 0) return; - NetOutgoingMessage msg = server.CreateMessage(); - msg.Write((byte)PacketTypes.Chatmessage); - msg.Write((byte)type); - msg.Write(message); + List recipients = new List(); - if (type==ChatMessageType.Dead) + foreach (Client c in connectedClients) { - List recipients = new List(); - foreach (Client c in connectedClients) - { - if (c.character != null && c.character.IsDead) recipients.Add(c.Connection); - } - if (recipients.Count>0) - { - server.SendMessage(msg, recipients, NetDeliveryMethod.ReliableUnordered, 0); - } + if (type!=ChatMessageType.Dead || (c.character != null && c.character.IsDead)) recipients.Add(c); } - else + + foreach (Client c in recipients) { - server.SendMessage(msg, server.Connections, NetDeliveryMethod.ReliableUnordered, 0); - } + ReliableMessage msg = c.ReliableChannel.CreateMessage(); + msg.InnerMessage.Write((byte)PacketTypes.Chatmessage); + msg.InnerMessage.Write((byte)type); + msg.InnerMessage.Write(message); + + c.ReliableChannel.SendMessage(msg, c.Connection); + } } @@ -1177,13 +1206,21 @@ namespace Barotrauma.Networking public List jobPreferences; public JobPrefab assignedJob; + public ReliableChannel ReliableChannel; + public float deleteDisconnectedTimer; + public Client(NetPeer server, string name, int ID) + : this(name, ID) + { + ReliableChannel = new ReliableChannel(server); + } + public Client(string name, int ID) { this.name = name; this.ID = ID; - + jobPreferences = new List(JobPrefab.List.GetRange(0,3)); } } diff --git a/Subsurface/Source/Networking/NetStats.cs b/Subsurface/Source/Networking/NetStats.cs index 038bde271..709796eb6 100644 --- a/Subsurface/Source/Networking/NetStats.cs +++ b/Subsurface/Source/Networking/NetStats.cs @@ -79,13 +79,13 @@ namespace Barotrauma.Networking graphs[(int)NetStatType.ResentMessages].Draw(spriteBatch, rect, null, 0.0f, Color.Red); - spriteBatch.DrawString(GUI.SmallFont, "Max received: "+graphs[(int)NetStatType.ReceivedBytes].LargestValue()+" bytes/s", + spriteBatch.DrawString(GUI.SmallFont, "Peak received: "+graphs[(int)NetStatType.ReceivedBytes].LargestValue()+" bytes/s", new Vector2(rect.X + 10, rect.Y+10), Color.Cyan); - spriteBatch.DrawString(GUI.SmallFont, "Max sent: " + graphs[(int)NetStatType.SentBytes].LargestValue() + " bytes/s", + spriteBatch.DrawString(GUI.SmallFont, "Peak sent: " + graphs[(int)NetStatType.SentBytes].LargestValue() + " bytes/s", new Vector2(rect.X + 10, rect.Y + 30), Color.Orange); - spriteBatch.DrawString(GUI.SmallFont, "Max resent: " + graphs[(int)NetStatType.ResentMessages].LargestValue() + " messages/s", + spriteBatch.DrawString(GUI.SmallFont, "Peak resent: " + graphs[(int)NetStatType.ResentMessages].LargestValue() + " messages/s", new Vector2(rect.X + 10, rect.Y + 50), Color.Red); } } diff --git a/Subsurface/Source/Networking/NetworkEvent.cs b/Subsurface/Source/Networking/NetworkEvent.cs index 021ce3974..0f97eeea5 100644 --- a/Subsurface/Source/Networking/NetworkEvent.cs +++ b/Subsurface/Source/Networking/NetworkEvent.cs @@ -93,7 +93,15 @@ namespace Barotrauma.Networking message.Write(id); - e.FillNetworkData(eventType, message, data); + try + { + if (!e.FillNetworkData(eventType, message, data)) return false; + } + + catch + { + return false; + } return true; } diff --git a/Subsurface/Source/Networking/NetworkMember.cs b/Subsurface/Source/Networking/NetworkMember.cs index bb9d01277..729d14b98 100644 --- a/Subsurface/Source/Networking/NetworkMember.cs +++ b/Subsurface/Source/Networking/NetworkMember.cs @@ -8,6 +8,8 @@ namespace Barotrauma.Networking { enum PacketTypes { + Unknown, + Login, LoggedIn, LogOut, @@ -26,7 +28,12 @@ namespace Barotrauma.Networking NetworkEvent, - Traitor + Traitor, + + ResendRequest, + ReliableMessage, + Ack + } class NetworkMember diff --git a/Subsurface/Source/Networking/ReliableSender.cs b/Subsurface/Source/Networking/ReliableSender.cs new file mode 100644 index 000000000..a16ed7b1d --- /dev/null +++ b/Subsurface/Source/Networking/ReliableSender.cs @@ -0,0 +1,404 @@ +using Lidgren.Network; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; + +namespace Barotrauma.Networking.ReliableMessages +{ + + class ReliableChannel + { + ReliableSender sender; + ReliableReceiver receiver; + + public ReliableChannel(NetPeer host) + { + sender = new ReliableSender(host); + receiver = new ReliableReceiver(host); + } + + public ReliableMessage CreateMessage(int lengthBytes = 0) + { + return sender.CreateMessage(); + } + + public void SendMessage(ReliableMessage message, NetConnection receiver) + { + sender.SendMessage(message, receiver); + } + + public void HandleResendRequest(NetIncomingMessage inc) + { + sender.HandleResendRequest(inc); + } + + public void HandleAckMessage(NetIncomingMessage inc) + { + //make sure we've received what's been sent to us, if not, rerequest + receiver.HandleAckMessage(inc); + } + + public bool CheckMessage(NetIncomingMessage inc) + { + return receiver.CheckMessage(inc); + } + + public void Update(float deltaTime) + { + sender.Update(deltaTime); + //update receiver to rerequest missed messages + receiver.Update(deltaTime); + } + + } + + internal class ReliableSender + { + private List messageBuffer; + + private ushort messageCount; + + private NetPeer sender; + + private NetConnection recipient; + + private float ackTimer; + + public ReliableSender(NetPeer sender) + { + this.sender = sender; + + messageBuffer = new List(); + } + + public ReliableMessage CreateMessage() + { + if (messageCount == ushort.MaxValue) messageCount = 0; + messageCount++; + + NetOutgoingMessage message = sender.CreateMessage(); + + var reliableMessage = new ReliableMessage(message, messageCount); + messageBuffer.Add(reliableMessage); + + message.Write((byte)PacketTypes.ReliableMessage); + message.Write(messageCount); + + while (messageBuffer.Count>100) + { + messageBuffer.RemoveAt(0); + } + + return reliableMessage; + + //server.SendMessage(msg, server.Connections, NetDeliveryMethod.Unreliable, 0); + } + + public void SendMessage(ReliableMessage message, NetConnection connection) + { + message.SaveInnerMessage(); + + sender.SendMessage(message.InnerMessage, connection, NetDeliveryMethod.Unreliable, 0); + + recipient = connection; + } + + // NetOutgoingMessage msg = server.CreateMessage(); + //reliableSender.CreateMessage(msg); + //msg.Write((byte)PacketTypes.Chatmessage); + //msg.Write((byte)type); + //msg.Write(message); + + + + public void HandleResendRequest(NetIncomingMessage inc) + { + ushort messageId = inc.ReadUInt16(); + + Debug.WriteLine("received resend request for msg id "+messageId); + + ResendMessage(messageId, inc.SenderConnection); + } + + private void ResendMessage(ushort messageId, NetConnection connection) + { + ReliableMessage message = messageBuffer.Find(m => m.ID == messageId); + if (message == null) return; + + Debug.WriteLine("resending " + messageId); + + + NetOutgoingMessage resendMessage = sender.CreateMessage(); + message.RestoreInnerMessage(resendMessage); + + sender.SendMessage(resendMessage, connection, NetDeliveryMethod.Unreliable); + } + + public void Update(float deltaTime) + { + if (recipient == null) return; + + ackTimer -= deltaTime; + + if (ackTimer > 0.0f) return; + + Debug.WriteLine("Sending ack message: "+messageCount); + + NetOutgoingMessage message = sender.CreateMessage(); + message.Write((byte)PacketTypes.Ack); + message.Write(messageCount); + + sender.SendMessage(message, recipient, NetDeliveryMethod.Unreliable); + + ackTimer = Math.Max(recipient.AverageRoundtripTime, 1.0f); + } + } + + internal class ReliableReceiver + { + ushort lastMessageID; + + Dictionary missingMessages; + + private NetPeer receiver; + + private NetConnection recipient; + + public ReliableReceiver(NetPeer receiver) + { + this.receiver = receiver; + + missingMessages = new Dictionary(); + } + + public void Update(float deltaTime) + { + foreach (var message in missingMessages.Where(m => m.Value.ResendRequestsSent>10).ToList()) + { + missingMessages.Remove(message.Key); + } + + foreach (KeyValuePair valuePair in missingMessages) + { + MissingMessage missingMessage = valuePair.Value; + + missingMessage.ResendTimer -= deltaTime; + + if (missingMessage.ResendRequestsSent==0 + || missingMessage.ResendTimer<0.0f) + { + Debug.WriteLine("rerequest "+missingMessage.ID+" (try #"+missingMessage.ResendRequestsSent+")"); + + NetOutgoingMessage resendRequest = receiver.CreateMessage(); + resendRequest.Write((byte)PacketTypes.ResendRequest); + resendRequest.Write(missingMessage.ID); + + receiver.SendMessage(resendRequest, recipient, NetDeliveryMethod.Unreliable); + + + missingMessage.ResendTimer = Math.Max(recipient.AverageRoundtripTime, 0.2f); + missingMessage.ResendRequestsSent++; + } + } + + } + + public bool CheckMessage(NetIncomingMessage message) + { + recipient = message.SenderConnection; + + ushort id = message.ReadUInt16(); + + Debug.WriteLine("received message ID " + id + " - last id: " + lastMessageID); + + //wrapped around + if (Math.Abs((int)lastMessageID - (int)id) > ushort.MaxValue / 2) + { + //id wrapped around and we missed some messages in between, rerequest them + if (lastMessageID<=ushort.MaxValue && id>1) + { + for (ushort i = (ushort)(Math.Min(lastMessageID, (ushort)(ushort.MaxValue-1)) + 1); i < ushort.MaxValue; i++) + { + //message already marked as missed, continue + if (missingMessages.ContainsKey((i))) continue; + + Debug.WriteLine("added " + i + " to missed"); + missingMessages.Add(i, new MissingMessage((ushort)i)); + } + for (ushort i = 1; i < id; i++) + { + //message already marked as missed, continue + if (missingMessages.ContainsKey((i))) continue; + + Debug.WriteLine("added " + i + " to missed"); + missingMessages.Add(i, new MissingMessage((ushort)i)); + } + + lastMessageID = id; + } + //we already wrapped around but the message hasn't, check if it's a duplicate + else if (lastMessageID < ushort.MaxValue / 2 && id > ushort.MaxValue / 2 && !missingMessages.ContainsKey(id)) + { + Debug.WriteLine("old already received message, ignore"); + return false; + } + else + { + if (missingMessages.ContainsKey(id)) + { + Debug.WriteLine("remove " + id + " from missed"); + missingMessages.Remove(id); + } + } + } + else + { + if (id>lastMessageID+1) + { + for (ushort i = (ushort)(lastMessageID+1); i < id; i++ ) + { + //message already marked as missed, continue + if (missingMessages.ContainsKey((i))) continue; + + Debug.WriteLine("added "+i+" to missed"); + missingMessages.Add(i, new MissingMessage((ushort)i)); + } + + } + //received an old message and it wasn't marked as missed, lets ignore it + else if (id<=lastMessageID && !missingMessages.ContainsKey(id)) + { + Debug.WriteLine("old already received message, ignore"); + return false; + } + else + { + if (missingMessages.ContainsKey(id)) + { + Debug.WriteLine("remove "+id+" from missed"); + missingMessages.Remove(id); + } + } + + lastMessageID = Math.Max(lastMessageID, id); + } + + return true; + } + + public void HandleAckMessage(NetIncomingMessage inc) + { + int messageId = inc.ReadUInt16(); + + recipient = inc.SenderConnection; + + //id matches, all good + if (messageId == lastMessageID) + { + + Debug.WriteLine("Received ack message: " + messageId + ", all good"); + return; + } + + if (lastMessageID > messageId) + { + //shouldn't happen: we have somehow received messages that the other end hasn't sent + Debug.WriteLine("Reliable message error - recipient last sent: " + messageId + " (current count " + lastMessageID + ")"); + return; + } + + Debug.WriteLine("Received ack message: " + messageId + ", need to rerequest messages"); + + if (lastMessageID > ushort.MaxValue / 2 && messageId < short.MaxValue / 2) + { + for (ushort i = (ushort)Math.Min((int)lastMessageID + 1, ushort.MaxValue); i <= ushort.MaxValue; i++) + { + + if (!missingMessages.ContainsKey(i)) missingMessages.Add(i, new MissingMessage(i)); + } + + for (ushort i = 1; i <= messageId; i++) + { + if (!missingMessages.ContainsKey(i)) missingMessages.Add(i, new MissingMessage(i)); + } + } + else + { + for (ushort i = (ushort)Math.Min((int)lastMessageID+1, ushort.MaxValue); i <= messageId; i++) + { + + if (!missingMessages.ContainsKey(i)) missingMessages.Add(i, new MissingMessage(i)); + } + } + + + + // Debug.WriteLine("received recent request for msg id " + messageId); + + //ReliableMessage message = messageBuffer.Find(m => m.ID == messageId); + //if (message == null) return; + + //NetOutgoingMessage resendMessage = sender.CreateMessage(); + //message.RestoreInnerMessage(resendMessage); + + //sender.SendMessage(resendMessage, inc.SenderConnection, NetDeliveryMethod.Unreliable); + } + } + + internal class MissingMessage + { + private ushort id; + + public byte ResendRequestsSent; + + public float ResendTimer; + + public ushort ID + { + get { return id; } + } + + public MissingMessage(ushort id) + { + this.id = id; + } + } + + class ReliableMessage + { + private NetOutgoingMessage innerMessage; + private ushort id; + + private byte[] innerMessageBytes; + + public NetOutgoingMessage InnerMessage + { + get { return innerMessage; } + } + + public ushort ID + { + get { return id; } + } + + + public ReliableMessage(NetOutgoingMessage message, ushort id) + { + this.innerMessage = message; + this.id = id; + } + + public void SaveInnerMessage() + { + innerMessageBytes = innerMessage.PeekBytes(innerMessage.LengthBytes); + //innerMessage = null; + } + + public void RestoreInnerMessage(NetOutgoingMessage message) + { + message.Write(innerMessageBytes); + } + } +} diff --git a/Subsurface/Source/Screens/ServerListScreen.cs b/Subsurface/Source/Screens/ServerListScreen.cs index 33e334f9e..379ffcfbb 100644 --- a/Subsurface/Source/Screens/ServerListScreen.cs +++ b/Subsurface/Source/Screens/ServerListScreen.cs @@ -287,8 +287,8 @@ namespace Barotrauma if (serverList.Selected!=null && (serverList.Selected.GetChild("password") as GUITickBox).Selected) { - var msgBox = new GUIMessageBox("Password required", ""); - var passwordBox = new GUITextBox(new Rectangle(0,0,150,20), Alignment.BottomCenter, GUI.Style, msgBox); + var msgBox = new GUIMessageBox("Password required:", ""); + var passwordBox = new GUITextBox(new Rectangle(0,40,150,25), Alignment.TopLeft, GUI.Style, msgBox); passwordBox.UserData = "password"; var okButton = msgBox.GetChild(); diff --git a/Subsurface/changelog.txt b/Subsurface/changelog.txt index 8c9517226..c6d7790bc 100644 --- a/Subsurface/changelog.txt +++ b/Subsurface/changelog.txt @@ -1,3 +1,24 @@ +--------------------------------------------------------------------------------------------------------- +v0.2.2 +--------------------------------------------------------------------------------------------------------- + +Multiplayer: + - network statistics view which can be enabled by opening the debug console (F3) and entering "netstats" + (only works if you're running a server) + - updated to latest version of Lidgren networking library, which may or may not have an effect + on the chat lag issues + +Items: + - fixed some game-crashing bugs related to detaching and attaching items (such as buttons) + - railgun shells can be bought in single player + +Submarine: + - more tools, diving suits and misc supplies in both default subs + +Misc: + - fixed Moloch spawning inside the level in the tutorial + - the launcher shows an error message instead of crashing if it can't connect to the update server + --------------------------------------------------------------------------------------------------------- v0.2.1 --------------------------------------------------------------------------------------------------------- diff --git a/Subsurface_Solution.v12.suo b/Subsurface_Solution.v12.suo index cb424628c..45a8ce9b4 100644 Binary files a/Subsurface_Solution.v12.suo and b/Subsurface_Solution.v12.suo differ