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