From e3433c725eebfb30628f2b193fc46075cb180be5 Mon Sep 17 00:00:00 2001 From: juanjp600 Date: Fri, 2 Sep 2016 22:24:14 -0300 Subject: [PATCH] Server name, message and submarine list syncing --- Subsurface/Source/Networking/Client.cs | 10 +- Subsurface/Source/Networking/GameClient.cs | 153 +++++++----- Subsurface/Source/Networking/GameServer.cs | 229 ++++++++++-------- .../Source/Networking/GameServerLogin.cs | 12 +- Subsurface/Source/Networking/NetworkMember.cs | 30 +-- Subsurface/Source/Screens/NetLobbyScreen.cs | 70 +++--- 6 files changed, 294 insertions(+), 210 deletions(-) diff --git a/Subsurface/Source/Networking/Client.cs b/Subsurface/Source/Networking/Client.cs index 0b3496b70..3d10e492b 100644 --- a/Subsurface/Source/Networking/Client.cs +++ b/Subsurface/Source/Networking/Client.cs @@ -27,8 +27,10 @@ namespace Barotrauma.Networking public CharacterInfo characterInfo; public NetConnection Connection { get; set; } public string version; - public bool inGame; + public bool inGame; + public UInt32 lastRecvLobbyUpdate = 0; + public bool hasLobbyData = false; public UInt32 lastSentChatMsgID = 0; //last msg this client said public UInt32 lastRecvChatMsgID = 0; //last msg this client knows about @@ -50,6 +52,12 @@ namespace Barotrauma.Networking public ClientPermissions Permissions; + public void InitClientSync() + { + lastSentChatMsgID = 0; + lastRecvChatMsgID = ChatMessage.LastID; + } + public int KickVoteCount { get { return kickVoters.Count; } diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index b0eff72ff..c8159fb15 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -300,6 +300,7 @@ namespace Barotrauma.Networking break; case ServerPacketHeader.UPDATE_LOBBY: //server accepted client + ReadLobbyUpdate(inc); CanStart = true; break; } @@ -480,9 +481,9 @@ namespace Barotrauma.Networking } } - if (!gameStarted) - { - SendLobbyUpdate(); + if (!gameStarted) + { + SendLobbyUpdate(); } // Update current time @@ -502,66 +503,100 @@ namespace Barotrauma.Networking if (startGameCoroutine != null && CoroutineManager.IsCoroutineRunning(startGameCoroutine)) return; while ((inc = client.ReadMessage()) != null) - { - switch (inc.MessageType) - { - case NetIncomingMessageType.Data: - ServerPacketHeader header = (ServerPacketHeader)inc.ReadByte(); - switch (header) - { - case ServerPacketHeader.UPDATE_LOBBY: - ReadLobbyUpdate(inc); - break; - } - break; + { + switch (inc.MessageType) + { + case NetIncomingMessageType.Data: + ServerPacketHeader header = (ServerPacketHeader)inc.ReadByte(); + switch (header) + { + case ServerPacketHeader.UPDATE_LOBBY: + ReadLobbyUpdate(inc); + break; + } + break; } } } - private void ReadLobbyUpdate(NetIncomingMessage inc) - { - lastRecvChatMsgID = inc.ReadUInt32(); - - ServerNetObject objHeader; - while ((objHeader = (ServerNetObject)inc.ReadByte()) != ServerNetObject.END_OF_MESSAGE) - { - switch (objHeader) - { - case ServerNetObject.CHAT_MESSAGE: - UInt32 ID = inc.ReadUInt32(); - ChatMessageType type = (ChatMessageType)inc.ReadByte(); - string senderName = inc.ReadString(); - string msg = inc.ReadString(); - if (ID > lastSentChatMsgID) + private void ReadLobbyUpdate(NetIncomingMessage inc) + { + ServerNetObject objHeader; + while ((objHeader = (ServerNetObject)inc.ReadByte()) != ServerNetObject.END_OF_MESSAGE) + { + switch (objHeader) + { + case ServerNetObject.SYNC_IDS: + bool lobbyUpdated = inc.ReadBoolean(); + inc.ReadPadBits(); + if (lobbyUpdated) { - AddChatMessage(msg, type, senderName); - lastSentChatMsgID = ID; - } - break; - } - } + GameMain.NetLobbyScreen.LastUpdateID = inc.ReadUInt32(); + GameMain.NetLobbyScreen.ServerName = inc.ReadString(); + GameMain.NetLobbyScreen.ServerMessage = inc.ReadString(); + + UInt16 subListCount = inc.ReadUInt16(); + if (subListCount > 0) + { + List submarines = new List(); + for (int i = 0; i < subListCount; i++) + { + string subName = inc.ReadString(); + string subHash = inc.ReadString(); + + var matchingSub = Submarine.SavedSubmarines.Find(s => s.Name == subName); + if (matchingSub != null) + { + submarines.Add(matchingSub); + } + else + { + submarines.Add(new Submarine(Path.Combine(Submarine.SavePath, subName), subHash, false)); + } + } + GameMain.NetLobbyScreen.UpdateSubList(GameMain.NetLobbyScreen.SubList, submarines); + GameMain.NetLobbyScreen.UpdateSubList(GameMain.NetLobbyScreen.ShuttleList.ListBox, submarines); + } + } + lastRecvChatMsgID = inc.ReadUInt32(); + break; + case ServerNetObject.CHAT_MESSAGE: + UInt32 ID = inc.ReadUInt32(); + ChatMessageType type = (ChatMessageType)inc.ReadByte(); + string senderName = inc.ReadString(); + string msg = inc.ReadString(); + if (ID > lastSentChatMsgID) + { + AddChatMessage(msg, type, senderName); + lastSentChatMsgID = ID; + } + break; + } + } } - private void SendLobbyUpdate() - { - NetOutgoingMessage outmsg = client.CreateMessage(); - outmsg.Write((byte)ClientPacketHeader.UPDATE_LOBBY); - - outmsg.Write(lastSentChatMsgID); - ChatMessage removeMsg; - while ((removeMsg=chatMsgQueue.Find(cMsg => cMsg.ID <= lastRecvChatMsgID)) != null) - { - chatMsgQueue.Remove(removeMsg); - } - - foreach (ChatMessage cMsg in chatMsgQueue) - { - outmsg.Write((byte)ClientNetObject.CHAT_MESSAGE); - outmsg.Write(cMsg.ID); - outmsg.Write(cMsg.Text); - } - outmsg.Write((byte)ClientNetObject.END_OF_MESSAGE); - client.SendMessage(outmsg, NetDeliveryMethod.Unreliable); + private void SendLobbyUpdate() + { + NetOutgoingMessage outmsg = client.CreateMessage(); + outmsg.Write((byte)ClientPacketHeader.UPDATE_LOBBY); + + outmsg.Write((byte)ClientNetObject.SYNC_IDS); + outmsg.Write(GameMain.NetLobbyScreen.LastUpdateID); + outmsg.Write(lastSentChatMsgID); + ChatMessage removeMsg; + while ((removeMsg=chatMsgQueue.Find(cMsg => cMsg.ID <= lastRecvChatMsgID)) != null) + { + chatMsgQueue.Remove(removeMsg); + } + + foreach (ChatMessage cMsg in chatMsgQueue) + { + outmsg.Write((byte)ClientNetObject.CHAT_MESSAGE); + outmsg.Write(cMsg.ID); + outmsg.Write(cMsg.Text); + } + outmsg.Write((byte)ClientNetObject.END_OF_MESSAGE); + client.SendMessage(outmsg, NetDeliveryMethod.Unreliable); } public override void SendChatMessage(string message, ChatMessageType? type = null) @@ -572,9 +607,9 @@ namespace Barotrauma.Networking ChatMessage chatMessage = ChatMessage.Create( gameStarted && myCharacter != null ? myCharacter.Name : name, - message, (ChatMessageType)type, gameStarted ? myCharacter : null); - - chatMsgQueue.Add(chatMessage); + message, (ChatMessageType)type, gameStarted ? myCharacter : null); + + chatMsgQueue.Add(chatMessage); } public bool HasPermission(ClientPermissions permission) diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 3b2367894..2fc1700a5 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -81,7 +81,7 @@ namespace Barotrauma.Networking config.EnableUPnP = true; } - config.MaximumConnections = maxPlayers; + config.MaximumConnections = maxPlayers*2; //double the lidgren connections for unauthenticated players MaxPlayers = maxPlayers; config.DisableMessageType(NetIncomingMessageType.DebugMessage | @@ -466,25 +466,25 @@ namespace Barotrauma.Networking if (server.ConnectionsCount > 0) { - if (sparseUpdateTimer < DateTime.Now) SparseUpdate(); - - foreach (Client c in ConnectedClients) - { - if (gameStarted) - { - if (c.inGame) - { - ClientWriteIngame(c); - } - else - { - ClientWriteLobby(c); - } - } - else - { - ClientWriteLobby(c); - } + if (sparseUpdateTimer < DateTime.Now) SparseUpdate(); + + foreach (Client c in ConnectedClients) + { + if (gameStarted) + { + if (c.inGame) + { + ClientWriteIngame(c); + } + else + { + ClientWriteLobby(c); + } + } + else + { + ClientWriteLobby(c); + } } } @@ -535,90 +535,129 @@ namespace Barotrauma.Networking userID++; } return userID; - } - - private void ClientReadLobby(NetIncomingMessage inc) - { - Client c = ConnectedClients.Find(x => x.Connection == inc.SenderConnection); - if (c == null) - { - inc.SenderConnection.Disconnect("You're not a connected client."); - return; - } - - UInt32 ID = inc.ReadUInt32(); - if (ID > c.lastRecvChatMsgID) - { - c.lastRecvChatMsgID = ID; - } - - ClientNetObject objHeader; - while ((objHeader=(ClientNetObject)inc.ReadByte()) != ClientNetObject.END_OF_MESSAGE) - { - switch (objHeader) - { - case ClientNetObject.CHAT_MESSAGE: - ID = inc.ReadUInt32(); - string msg = inc.ReadString(); - if (c.lastSentChatMsgID x.Connection == inc.SenderConnection); + if (c == null) + { + inc.SenderConnection.Disconnect("You're not a connected client."); + return; + } + + ClientNetObject objHeader; + while ((objHeader=(ClientNetObject)inc.ReadByte()) != ClientNetObject.END_OF_MESSAGE) + { + switch (objHeader) + { + case ClientNetObject.SYNC_IDS: + //TODO: might want to use a clever class for this + UInt32 lastLobbyUpdID = inc.ReadUInt32(); + if (lastLobbyUpdID > c.lastRecvLobbyUpdate) { - //this chat message is new to the server - AddChatMessage(msg, ChatMessageType.Default, c.name); - c.lastSentChatMsgID = ID; - } - break; - } - } - + c.lastRecvLobbyUpdate = lastLobbyUpdID; + } + UInt32 lastChatID = inc.ReadUInt32(); + if (lastChatID > c.lastRecvChatMsgID) + { + c.lastRecvChatMsgID = lastChatID; + } + break; + case ClientNetObject.CHAT_MESSAGE: + UInt32 ID = inc.ReadUInt32(); + string msg = inc.ReadString(); + if (c.lastSentChatMsgID x.Connection == inc.SenderConnection); - if (c == null) - { - inc.SenderConnection.Disconnect("You're not a connected client."); - return; - } + private void ClientReadIngame(NetIncomingMessage inc) + { + Client c = ConnectedClients.Find(x => x.Connection == inc.SenderConnection); + if (c == null) + { + inc.SenderConnection.Disconnect("You're not a connected client."); + return; + } } - private void ClientWriteIngame(Client c) - { - if (c.Character != null && !c.Character.IsDead) - { + private void ClientWriteIngame(Client c) + { + if (c.Character != null && !c.Character.IsDead) + { + + } + else + { + + } + } + + private void ClientWriteLobby(Client c) + { + NetOutgoingMessage outmsg = server.CreateMessage(); + outmsg.Write((byte)ServerPacketHeader.UPDATE_LOBBY); - } + outmsg.Write((byte)ServerNetObject.SYNC_IDS); + + if (c.lastRecvLobbyUpdate c.lastRecvChatMsgID) - { - outmsg.Write((byte)ServerNetObject.CHAT_MESSAGE); - outmsg.Write(cMsg.ID); - outmsg.Write((byte)cMsg.Type); - outmsg.Write(cMsg.SenderName); - outmsg.Write(cMsg.Text); - } - } - } - } - outmsg.Write((byte)ServerNetObject.END_OF_MESSAGE); - server.SendMessage(outmsg,c.Connection,NetDeliveryMethod.Unreliable); + outmsg.Write(c.lastSentChatMsgID); //send this to client so they know which chat messages weren't received by the server + + foreach (GUIComponent gc in GameMain.NetLobbyScreen.ChatBox.children) + { + if (gc is GUITextBlock) + { + if (gc.UserData is ChatMessage) + { + ChatMessage cMsg = (ChatMessage)gc.UserData; + if (cMsg.ID > c.lastRecvChatMsgID) + { + outmsg.Write((byte)ServerNetObject.CHAT_MESSAGE); + outmsg.Write(cMsg.ID); + outmsg.Write((byte)cMsg.Type); + outmsg.Write(cMsg.SenderName); + outmsg.Write(cMsg.Text); + } + } + } + } + outmsg.Write((byte)ServerNetObject.END_OF_MESSAGE); + server.SendMessage(outmsg,c.Connection,NetDeliveryMethod.Unreliable); } public bool StartGameClicked(GUIButton button, object obj) diff --git a/Subsurface/Source/Networking/GameServerLogin.cs b/Subsurface/Source/Networking/GameServerLogin.cs index 00032472c..b6c0dadbc 100644 --- a/Subsurface/Source/Networking/GameServerLogin.cs +++ b/Subsurface/Source/Networking/GameServerLogin.cs @@ -27,7 +27,7 @@ namespace Barotrauma.Networking failedAttempts = 0; } } - + partial class GameServer : NetworkMember, IPropertyObject { List unauthenticatedClients = new List(); @@ -101,9 +101,10 @@ namespace Barotrauma.Networking unauthClient.failedAttempts++; if (unauthClient.failedAttempts > 3) { - //disconnect after too many failed attempts - unauthClient.Connection.Disconnect("Too many failed login attempts."); + //disconnect and ban after too many failed attempts + unauthClient.Connection.Disconnect("Too many failed login attempts. You have been automatically banned from the server."); unauthenticatedClients.Remove(unauthClient); + banList.BanPlayer("Unnamed", unauthClient.Connection.RemoteEndPoint.Address.ToString()); unauthClient = null; return; } @@ -112,7 +113,7 @@ namespace Barotrauma.Networking //not disconnecting the player here, because they'll still use the same connection and nonce if they try logging in again NetOutgoingMessage reject = server.CreateMessage(); reject.Write((byte)ServerPacketHeader.AUTH_FAILURE); - reject.Write("Wrong password!"); + reject.Write("Wrong password! You have "+Convert.ToString(4-unauthClient.failedAttempts)+" more attempts before you're banned from the server."); server.SendMessage(reject, unauthClient.Connection, NetDeliveryMethod.Unreliable); unauthClient.AuthTimer = 10.0f; return; @@ -171,6 +172,7 @@ namespace Barotrauma.Networking //both name and IP address match, replace this player's connection nameTaken.Connection.Disconnect("Your session was taken by a new connection on the same IP address."); nameTaken.Connection = unauthClient.Connection; + nameTaken.InitClientSync(); //reinitialize sync ids because this is a new connection unauthenticatedClients.Remove(unauthClient); unauthClient = null; return; @@ -187,7 +189,7 @@ namespace Barotrauma.Networking //new client Client newClient = new Client(clName, GetNewClientID()); - newClient.lastRecvChatMsgID = ChatMessage.LastID; + newClient.InitClientSync(); newClient.Connection = unauthClient.Connection; unauthenticatedClients.Remove(unauthClient); unauthClient = null; diff --git a/Subsurface/Source/Networking/NetworkMember.cs b/Subsurface/Source/Networking/NetworkMember.cs index 80f327432..87adf7148 100644 --- a/Subsurface/Source/Networking/NetworkMember.cs +++ b/Subsurface/Source/Networking/NetworkMember.cs @@ -16,13 +16,14 @@ namespace Barotrauma.Networking UPDATE_LOBBY, //update state in lobby UPDATE_INGAME, //update state ingame while alive } - enum ClientNetObject - { - END_OF_MESSAGE, //self-explanatory - CHAT_MESSAGE, //also self-explanatory - VOTE, //you get the idea - CHARACTER_INPUT, - ITEM_INTERACTION + enum ClientNetObject + { + END_OF_MESSAGE, //self-explanatory + SYNC_IDS, //ids of the last changes the client knows about + CHAT_MESSAGE, //also self-explanatory + VOTE, //you get the idea + CHARACTER_INPUT, + ITEM_INTERACTION } enum ServerPacketHeader @@ -32,13 +33,14 @@ namespace Barotrauma.Networking UPDATE_LOBBY, //update state in lobby (votes and chat messages) UPDATE_INGAME, //update state ingame while alive (character input and chat messages) } - enum ServerNetObject - { - END_OF_MESSAGE, - CHAT_MESSAGE, - VOTE, - CHARACTER_POSITION, - ITEM_STATE + enum ServerNetObject + { + END_OF_MESSAGE, + SYNC_IDS, + CHAT_MESSAGE, + VOTE, + CHARACTER_POSITION, + ITEM_STATE } enum VoteType diff --git a/Subsurface/Source/Screens/NetLobbyScreen.cs b/Subsurface/Source/Screens/NetLobbyScreen.cs index 9e8539d79..7cf4cf460 100644 --- a/Subsurface/Source/Screens/NetLobbyScreen.cs +++ b/Subsurface/Source/Screens/NetLobbyScreen.cs @@ -23,9 +23,9 @@ namespace Barotrauma private GUIListBox subList, modeList, chatBox; public GUIListBox ChatBox { - get - { - return chatBox; + get + { + return chatBox; } } @@ -54,11 +54,21 @@ namespace Barotrauma const float NetworkUpdateInterval = 1.0f; private float networkUpdateTimer; - private bool valueChanged; + private UInt32 lastUpdateID; + public UInt32 LastUpdateID + { + get { if (GameMain.Server != null && lastUpdateID < 1) lastUpdateID++; return lastUpdateID; } + set { if (GameMain.Server != null) return; lastUpdateID = value; } + } private Sprite backgroundSprite; private GUITextBox serverMessage; + public string ServerMessage + { + get { return serverMessage.Text; } + set { if (GameMain.Server != null) return; serverMessage.Text = value; } + } public GUIListBox SubList { @@ -559,10 +569,10 @@ namespace Barotrauma { if (GameMain.Server == null) return false; - GameMain.Server.AutoRestart = tickBox.Selected; - - valueChanged = true; - + GameMain.Server.AutoRestart = tickBox.Selected; + + lastUpdateID++; + return true; } @@ -573,7 +583,7 @@ namespace Barotrauma missionTypeBlock.GetChild().Text = Mission.MissionTypes[missionTypeIndex]; missionTypeBlock.UserData = missionTypeIndex; - valueChanged = true; + lastUpdateID++; } public bool ToggleMissionType(GUIButton button, object userData) @@ -586,7 +596,7 @@ namespace Barotrauma SetMissionType(missionTypeIndex); - valueChanged = true; + lastUpdateID++; return true; } @@ -601,9 +611,9 @@ namespace Barotrauma if (index < 0) index = 2; if (index > 2) index = 0; - SetTraitorsEnabled((YesNoMaybe)index); - - valueChanged = true; + SetTraitorsEnabled((YesNoMaybe)index); + + lastUpdateID++; return true; } @@ -628,7 +638,7 @@ namespace Barotrauma private bool SelectSub(GUIComponent component, object obj) { - valueChanged = true; + lastUpdateID++; var hash = obj is Submarine ? ((Submarine)obj).MD5Hash.Hash : ""; @@ -654,7 +664,7 @@ namespace Barotrauma subList.ClearChildren(); - if (submarines.Count == 0) + if (submarines.Count == 0 && GameMain.Server != null) { DebugConsole.ThrowError("No submarines found!"); } @@ -734,7 +744,7 @@ namespace Barotrauma { if (GameMain.Server == null) return false; ServerName = text; - valueChanged = true; + lastUpdateID++; return true; } @@ -742,7 +752,7 @@ namespace Barotrauma public bool UpdateServerMessage(GUITextBox textBox, string text) { if (GameMain.Server == null) return false; - valueChanged = true; + lastUpdateID++; return true; } @@ -940,18 +950,6 @@ namespace Barotrauma { autoRestartTimer = Math.Max(autoRestartTimer - (float)deltaTime, 0.0f); } - - if (valueChanged && GameMain.Server != null) - { - networkUpdateTimer -= (float)deltaTime; - if (networkUpdateTimer <= 0.0f) - { - GameMain.Server.UpdateNetLobby(null); - - valueChanged = false; - networkUpdateTimer = NetworkUpdateInterval; - } - } //durationBar.BarScroll = Math.Max(durationBar.BarScroll, 1.0f / 60.0f); } @@ -1050,7 +1048,7 @@ namespace Barotrauma if (modePreset == null) return false; - valueChanged = true; + lastUpdateID++; return true; } @@ -1061,12 +1059,12 @@ namespace Barotrauma if (!string.IsNullOrWhiteSpace(seed)) { LevelSeed = seed; - } - - //textBox.Text = LevelSeed; - //textBox.Selected = false; - - valueChanged = true; + } + + //textBox.Text = LevelSeed; + //textBox.Selected = false; + + lastUpdateID++; return true; }