diff --git a/Subsurface/Barotrauma.csproj b/Subsurface/Barotrauma.csproj index 3e95f82d0..e3a848d2e 100644 --- a/Subsurface/Barotrauma.csproj +++ b/Subsurface/Barotrauma.csproj @@ -150,6 +150,7 @@ + diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index a7a61d1a2..8705f28dc 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -191,7 +191,8 @@ namespace Barotrauma NewMessage("kick [name]: kick a player out from the server", Color.Cyan); - NewMessage("ban [name]: kick and ban the player", Color.Cyan); + NewMessage("ban [name]: kick and ban the player from the server", Color.Cyan); + NewMessage("banip [IP address]: ban the IP address from the server", Color.Cyan); NewMessage("debugdraw: toggles the ''debug draw mode''", Color.Cyan); NewMessage("netstats: toggles the visibility of the network statistics panel", Color.Cyan); @@ -289,6 +290,19 @@ namespace Barotrauma if (GameMain.Server == null || commands.Length < 2) break; GameMain.Server.KickPlayer(string.Join(" ", commands.Skip(1)), true); break; + case "banip": + if (GameMain.Server == null || commands.Length < 2) break; + + var client = GameMain.Server.ConnectedClients.Find(c => c.Connection.RemoteEndPoint.Address.ToString() == commands[1]); + if (client == null) + { + GameMain.Server.BanList.BanPlayer("Unnamed", commands[1]); + } + else + { + GameMain.Server.KickClient(client, true); + } + break; case "startclient": if (commands.Length == 1) return; if (GameMain.Client == null) diff --git a/Subsurface/Source/Networking/BanList.cs b/Subsurface/Source/Networking/BanList.cs index 310e99084..3908ba0bf 100644 --- a/Subsurface/Source/Networking/BanList.cs +++ b/Subsurface/Source/Networking/BanList.cs @@ -66,7 +66,10 @@ namespace Barotrauma.Networking { if (bannedPlayers.Any(bp => bp.IP == ip)) return; + DebugConsole.Log("Banned " + name); + bannedPlayers.Add(new BannedPlayer(name, ip)); + Save(); } public bool IsBanned(string IP) @@ -108,10 +111,13 @@ namespace Barotrauma.Networking BannedPlayer banned = obj as BannedPlayer; if (banned == null) return false; + DebugConsole.Log("Removing ban from " + banned.Name); GameServer.Log("Removing ban from " + banned.Name, null); bannedPlayers.Remove(banned); + Save(); + if (banFrame != null) { banFrame.Parent.RemoveChild(banFrame); @@ -130,7 +136,6 @@ namespace Barotrauma.Networking public void Save() { - GameServer.Log("Saving banlist", null); List lines = new List(); diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index a0bbfdbe3..f296de32c 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -108,21 +108,13 @@ namespace Barotrauma.Networking config.DisableMessageType(NetIncomingMessageType.DebugMessage | NetIncomingMessageType.WarningMessage | NetIncomingMessageType.Receipt | NetIncomingMessageType.ErrorMessage | NetIncomingMessageType.Error); - // Create new client, with previously created configs client = new NetClient(config); netPeer = client; reliableChannel = new ReliableChannel(client); - - NetOutgoingMessage outmsg = client.CreateMessage(); client.Start(); + NetOutgoingMessage outmsg = client.CreateMessage(); outmsg.Write((byte)PacketTypes.Login); - outmsg.Write(myID); - outmsg.Write(password); - outmsg.Write(GameMain.Version.ToString()); - outmsg.Write(GameMain.SelectedPackage.Name); - outmsg.Write(GameMain.SelectedPackage.MD5hash.Hash); - outmsg.Write(name); System.Net.IPEndPoint IPEndPoint = null; @@ -141,6 +133,7 @@ namespace Barotrauma.Networking try { client.Connect(IPEndPoint, outmsg); + } catch (Exception e) { @@ -150,17 +143,9 @@ namespace Barotrauma.Networking GameMain.ServerListScreen.Select(); return; } - - + updateInterval = new TimeSpan(0, 0, 0, 0, 150); - // Set timer to tick every 50ms - //update = new System.Timers.startTimer(50); - - // When time has elapsed ( 50ms in this case ), call "update_Elapsed" funtion - //update.Elapsed += new System.Timers.ElapsedEventHandler(Update); - - // Funtion that waits for connection approval info from server if (reconnectBox==null) { reconnectBox = new GUIMessageBox("CONNECTING", "Connecting to " + serverIP, new string[] { "Cancel" }); @@ -169,7 +154,7 @@ namespace Barotrauma.Networking reconnectBox.Buttons[0].OnClicked += reconnectBox.Close; } - CoroutineManager.StartCoroutine(WaitForStartingInfo()); + CoroutineManager.StartCoroutine(WaitForStartingInfo(password)); // Start the timer //update.Start(); @@ -203,7 +188,7 @@ namespace Barotrauma.Networking } // Before main looping starts, we loop here and wait for approval message - private IEnumerable WaitForStartingInfo() + private IEnumerable WaitForStartingInfo(string password) { connectCanceled = false; // When this is set to true, we are approved and ready to go @@ -303,18 +288,12 @@ namespace Barotrauma.Networking lobbyUpdateRequest.Write((byte)PacketTypes.RequestNetLobbyUpdate); client.SendMessage(lobbyUpdateRequest, NetDeliveryMethod.ReliableUnordered); } - else if (packetType == (byte)PacketTypes.KickedOut) - { - string msg = inc.ReadString(); - DebugConsole.ThrowError(msg); - - GameMain.MainMenuScreen.Select(); - } break; case NetIncomingMessageType.StatusChanged: - DebugConsole.NewMessage("Connection status changed: " + client.ConnectionStatus.ToString(), Color.Orange); NetConnectionStatus connectionStatus = (NetConnectionStatus)inc.ReadByte(); + DebugConsole.NewMessage("Connection status changed: " + connectionStatus.ToString(), Color.Orange); + if (connectionStatus == NetConnectionStatus.Disconnected) { string denyMessage = inc.ReadString(); @@ -330,6 +309,25 @@ namespace Barotrauma.Networking connectCanceled = true; } + else if (connectionStatus == NetConnectionStatus.Connected) + { + int nonce = inc.SenderConnection.RemoteHailMessage.ReadInt32(); + + var outmsg = client.CreateMessage(); + + NetEncryption algo = new NetXtea(client, password); + outmsg.Write((byte)PacketTypes.Login); + outmsg.Write(nonce); + outmsg.Write(myID); + outmsg.Write(GameMain.Version.ToString()); + outmsg.Write(GameMain.SelectedPackage.Name); + outmsg.Write(GameMain.SelectedPackage.MD5hash.Hash); + outmsg.Write(name); + + outmsg.Encrypt(algo); + + client.SendMessage(outmsg, NetDeliveryMethod.ReliableUnordered); + } break; default: @@ -394,19 +392,6 @@ namespace Barotrauma.Networking if (!connected) return; - if (client.ConnectionStatus == NetConnectionStatus.Disconnected) - { - //GameMain.NetLobbyScreen.RemovePlayer(myID); - if (reconnectBox==null) - { - reconnectBox = new GUIMessageBox("CONNECTION LOST", "You have been disconnected from the server. Reconnecting...", new string[0]); - connected = false; - ConnectToServer(serverIP); - } - - return; - } - if (reconnectBox!=null) { reconnectBox.Close(null,null); @@ -488,6 +473,33 @@ namespace Barotrauma.Networking while ((inc = client.ReadMessage()) != null) { + if (inc.MessageType == NetIncomingMessageType.StatusChanged) + { + NetConnectionStatus connectionStatus = (NetConnectionStatus)inc.ReadByte(); + DebugConsole.NewMessage("Connection status changed: " + connectionStatus.ToString(), Color.Orange); + + if (connectionStatus == NetConnectionStatus.Disconnected) + { + string disconnectMessage = inc.ReadString(); + if (string.IsNullOrEmpty(disconnectMessage) || disconnectMessage == "Connection timed out") + { + if (reconnectBox == null) + { + reconnectBox = new GUIMessageBox("CONNECTION LOST", "You have been disconnected from the server. Reconnecting...", new string[0]); + connected = false; + ConnectToServer(serverIP); + } + } + else + { + new GUIMessageBox("Disconnected", disconnectMessage); + + Disconnect(); + GameMain.ServerListScreen.Select(); + } + } + } + if (inc.MessageType != NetIncomingMessageType.Data) continue; byte packetType = inc.ReadByte(); @@ -590,16 +602,6 @@ namespace Barotrauma.Networking //GameMain.GameSession.CrewManager.CreateCrewFrame(crew); - break; - - case (byte)PacketTypes.KickedOut: - string msg = inc.ReadString(); - - new GUIMessageBox("Disconnected from server", msg); - - Disconnect(); - GameMain.MainMenuScreen.Select(); - break; case (byte)PacketTypes.Chatmessage: //ChatMessageType messageType = (ChatMessageType)inc.ReadByte(); @@ -784,16 +786,6 @@ namespace Barotrauma.Networking { secondsLeft -= CoroutineManager.UnscaledDeltaTime; - //float camAngle = (float)((DateTime.Now - endTime).TotalSeconds / endPreviewLength) * MathHelper.TwoPi; - //Vector2 offset = (new Vector2( - // (float)Math.Cos(camAngle) * (Submarine.Borders.Width / 2.0f), - // (float)Math.Sin(camAngle) * (Submarine.Borders.Height / 2.0f))); - - //GameMain.GameScreen.Cam.TargetPos = Submarine.Loaded.Position + offset * 0.8f; - //Game1.GameScreen.Cam.MoveCamera((float)deltaTime); - - //messageBox.Text = endMessage + "\nReturning to lobby in " + (int)secondsLeft + " s"; - yield return CoroutineStatus.Running; } while (secondsLeft > 0.0f); } @@ -802,8 +794,6 @@ namespace Barotrauma.Networking GameMain.NetLobbyScreen.Select(); - //if (GameMain.GameSession!=null) GameMain.GameSession.EndShift(""); - myCharacter = null; foreach (Client c in otherClients) { @@ -948,16 +938,23 @@ namespace Barotrauma.Networking switch (receiver.FileType) { case FileTransferMessageType.Submarine: - var textBlock = GameMain.NetLobbyScreen.SubList.children.Find(c => (c.UserData as Submarine).Name+".sub" == receiver.FileName); - if (textBlock != null) - { Submarine.SavedSubmarines.RemoveAll(s => s.Name + ".sub" == receiver.FileName); + for (int i = 0; i<2; i++) + { + + var textBlock = (i == 0) ? + GameMain.NetLobbyScreen.ShuttleList.ListBox.children.Find(c => (c.UserData as Submarine).Name+".sub" == receiver.FileName) : + GameMain.NetLobbyScreen.SubList.children.Find(c => (c.UserData as Submarine).Name+".sub" == receiver.FileName); + if (textBlock == null) continue; + (textBlock as GUITextBlock).TextColor = Color.White; + var newSub = new Submarine(receiver.FilePath); Submarine.SavedSubmarines.Add(newSub); textBlock.UserData = newSub; } + break; } } @@ -1063,7 +1060,7 @@ namespace Barotrauma.Networking var jobPreferences = GameMain.NetLobbyScreen.JobPreferences; int count = Math.Min(jobPreferences.Count, 3); - msg.Write(count); + msg.Write((byte)count); for (int i = 0; i < count; i++ ) { msg.Write(jobPreferences[i].Name); @@ -1094,8 +1091,7 @@ namespace Barotrauma.Networking GameMain.GameSession.CrewManager.characters.Add(character); character.ID = ID; - - + Item.Spawner.ReadNetworkData(inc); if (isMyCharacter) @@ -1112,8 +1108,6 @@ namespace Barotrauma.Networking public override void SendChatMessage(string message, ChatMessageType? type = null) { - //AddChatMessage(message); - if (client.ServerConnection == null) return; type = ChatMessageType.Default; diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 00357a32a..cba549500 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -108,7 +108,7 @@ namespace Barotrauma.Networking banList = new BanList(); LoadSettings(); - + //---------------------------------------- @@ -277,6 +277,17 @@ namespace Barotrauma.Networking if (!started) return; base.Update(deltaTime); + + foreach (UnauthenticatedClient unauthClient in unauthenticatedClients) + { + unauthClient.AuthTimer -= deltaTime; + if (unauthClient.AuthTimer <= 0.0f) + { + unauthClient.Connection.Disconnect("Connection timed out"); + } + } + + unauthenticatedClients.RemoveAll(uc => uc.AuthTimer <= 0.0f); if (gameStarted) { @@ -430,6 +441,8 @@ namespace Barotrauma.Networking private void ReadMessage(NetIncomingMessage inc) { + Client sender = connectedClients.Find(x => x.Connection == inc.SenderConnection); + switch (inc.MessageType) { case NetIncomingMessageType.ConnectionApproval: @@ -437,76 +450,7 @@ namespace Barotrauma.Networking break; case NetIncomingMessageType.StatusChanged: Debug.WriteLine(inc.SenderConnection + " status changed. " + (NetConnectionStatus)inc.SenderConnection.Status); - if (inc.SenderConnection.Status == NetConnectionStatus.Connected) - { - Client sender = connectedClients.Find(x => x.Connection == inc.SenderConnection); - - if (sender == null) break; - - if (sender.version != GameMain.Version.ToString()) - { - DisconnectClient(sender, sender.name+" was unable to connect to the server (nonmatching game version)", - "Version " + GameMain.Version + " required to connect to the server (Your version: " + sender.version + ")"); - } - else if (connectedClients.Find(x => x.name == sender.name && x != sender)!=null) - { - DisconnectClient(sender, sender.name + " was unable to connect to the server (name already in use)", - "The name ''"+sender.name+"'' is already in use. Please choose another name."); - } - else - { - //AssignJobs(); - - GameMain.NetLobbyScreen.RemovePlayer(sender.name); - GameMain.NetLobbyScreen.AddPlayer(sender.name); - - // Notify the client that they have logged in - var outmsg = server.CreateMessage(); - - outmsg.Write((byte)PacketTypes.LoggedIn); - outmsg.Write(sender.ID); - outmsg.Write(gameStarted); - outmsg.Write(gameStarted && sender.Character != null && !sender.Character.IsDead); - outmsg.Write(AllowSpectating); - - //notify the client about other clients already logged in - outmsg.Write((byte)((characterInfo == null) ? connectedClients.Count - 1 : connectedClients.Count)); - foreach (Client c in connectedClients) - { - if (c.Connection == inc.SenderConnection) continue; - outmsg.Write(c.name); - outmsg.Write(c.ID); - } - - if (characterInfo != null) - { - outmsg.Write(characterInfo.Name); - outmsg.Write((byte)0); - } - - var subs = GameMain.NetLobbyScreen.GetSubList(); - outmsg.Write((byte)subs.Count); - foreach (Submarine sub in subs) - { - outmsg.Write(sub.Name); - outmsg.Write(sub.MD5Hash.Hash); - } - - server.SendMessage(outmsg, inc.SenderConnection, NetDeliveryMethod.ReliableUnordered, 0); - - //notify other clients about the new client - outmsg = server.CreateMessage(); - outmsg.Write((byte)PacketTypes.PlayerJoined); - outmsg.Write(sender.name); - outmsg.Write(sender.ID); - - //send the message to everyone except the client who just logged in - SendMessage(outmsg, NetDeliveryMethod.ReliableUnordered, inc.SenderConnection); - - AddChatMessage(sender.name + " has joined the server", ChatMessageType.Server); - } - } - else if (inc.SenderConnection.Status == NetConnectionStatus.Disconnected) + if (inc.SenderConnection.Status == NetConnectionStatus.Disconnected) { var connectedClient = connectedClients.Find(c => c.Connection == inc.SenderConnection); if (connectedClient != null && !disconnectedClients.Contains(connectedClient)) @@ -515,21 +459,32 @@ namespace Barotrauma.Networking disconnectedClients.Add(connectedClient); } - DisconnectClient(inc.SenderConnection, - connectedClient != null ? connectedClient.name+" has disconnected" : ""); + DisconnectClient(inc.SenderConnection, + connectedClient != null ? connectedClient.name + " has disconnected" : ""); } - break; case NetIncomingMessageType.Data: - Client dataSender = connectedClients.Find(c => c.Connection == inc.SenderConnection); - if (dataSender == null) return; - byte packetType = inc.ReadByte(); + if (sender == null) + { + var authUser = unauthenticatedClients.Find(c => c.Connection == inc.SenderConnection); + if (authUser == null) + { + unauthenticatedClients.Remove(authUser); + inc.SenderConnection.Disconnect("Disconnected"); + } + else + { + CheckAuthentication(inc); + } + return; + } + if (packetType == (byte)PacketTypes.ReliableMessage) { - if (!dataSender.ReliableChannel.CheckMessage(inc)) return; + if (!sender.ReliableChannel.CheckMessage(inc)) return; packetType = inc.ReadByte(); } @@ -551,7 +506,7 @@ namespace Barotrauma.Networking DisconnectClient(inc.SenderConnection); break; case (byte)PacketTypes.StartGame: - dataSender.ReadyToStart = true; + sender.ReadyToStart = true; break; case (byte)PacketTypes.CharacterInfo: ReadCharacterData(inc); @@ -560,7 +515,7 @@ namespace Barotrauma.Networking if (!AllowFileTransfers) { - SendCancelTransferMessage(dataSender, "File transfers have been disabled by the server."); + SendCancelTransferMessage(sender, "File transfers have been disabled by the server."); break; } @@ -579,16 +534,16 @@ namespace Barotrauma.Networking } else { - if (dataSender.FileStreamSender != null) dataSender.FileStreamSender.CancelTransfer(); + if (sender.FileStreamSender != null) sender.FileStreamSender.CancelTransfer(); - var fileStreamSender = FileStreamSender.Create(dataSender.Connection, requestedSubmarine.FilePath, FileTransferMessageType.Submarine); - if (fileStreamSender != null) dataSender.FileStreamSender = fileStreamSender; + var fileStreamSender = FileStreamSender.Create(sender.Connection, requestedSubmarine.FilePath, FileTransferMessageType.Submarine); + if (fileStreamSender != null) sender.FileStreamSender = fileStreamSender; } break; case (byte)FileTransferMessageType.Cancel: - if (dataSender.FileStreamSender != null) + if (sender.FileStreamSender != null) { - dataSender.FileStreamSender.CancelTransfer(); + sender.FileStreamSender.CancelTransfer(); } break; default: @@ -596,14 +551,12 @@ namespace Barotrauma.Networking break; } - break; case (byte)PacketTypes.ResendRequest: - - dataSender.ReliableChannel.HandleResendRequest(inc); + sender.ReliableChannel.HandleResendRequest(inc); break; case (byte)PacketTypes.LatestMessageID: - dataSender.ReliableChannel.HandleLatestMessageID(inc); + sender.ReliableChannel.HandleLatestMessageID(inc); break; case (byte)PacketTypes.Vote: Voting.RegisterVote(inc, connectedClients); @@ -625,8 +578,8 @@ namespace Barotrauma.Networking var startMessage = CreateStartMessage(roundStartSeed, Submarine.MainSub, GameMain.GameSession.gameMode.Preset); server.SendMessage(startMessage, inc.SenderConnection, NetDeliveryMethod.ReliableOrdered); - dataSender.Spectating = true; - CoroutineManager.StartCoroutine(SyncSpectator(dataSender)); + sender.Spectating = true; + CoroutineManager.StartCoroutine(SyncSpectator(sender)); } break; } @@ -637,127 +590,6 @@ namespace Barotrauma.Networking } } - private void HandleConnectionApproval(NetIncomingMessage inc) - { - if ((PacketTypes)inc.ReadByte() != PacketTypes.Login) return; - - DebugConsole.NewMessage("New player has joined the server", Color.White); - - if (banList.IsBanned(inc.SenderEndPoint.Address.ToString())) - { - inc.SenderConnection.Deny("You have been banned from the server"); - DebugConsole.NewMessage("Banned player tried to join the server", Color.Red); - return; - } - - if (connectedClients.Find(c => c.Connection == inc.SenderConnection)!=null) - { - inc.SenderConnection.Deny("Connection error - already joined"); - return; - } - - byte userID; - string userPassword = "", version = "", packageName = "", packageHash = "", name = ""; - try - { - userID = inc.ReadByte(); - userPassword = inc.ReadString(); - version = inc.ReadString(); - packageName = inc.ReadString(); - packageHash = inc.ReadString(); - name = inc.ReadString(); - } - catch - { - inc.SenderConnection.Deny("Connection error - server failed to read your ConnectionApproval message"); - DebugConsole.NewMessage("Connection error - server failed to read the ConnectionApproval message", Color.Red); - return; - } - -#if !DEBUG - if (!string.IsNullOrWhiteSpace(password) && string.IsNullOrWhiteSpace(userPassword)) - { - inc.SenderConnection.Deny("Password required!"); - DebugConsole.NewMessage(name + " couldn't join the server (no password)", Color.Red); - return; - } - else if (userPassword != password) - { - inc.SenderConnection.Deny("Wrong password!"); - DebugConsole.NewMessage(name + " couldn't join the server (wrong password)", Color.Red); - return; - } - else if (version != GameMain.Version.ToString()) - { - inc.SenderConnection.Deny("Version " + GameMain.Version + " required to connect to the server (Your version: " + version + ")"); - DebugConsole.NewMessage(name + " couldn't join the server (wrong game version)", Color.Red); - return; - } - else if (packageName != GameMain.SelectedPackage.Name) - { - inc.SenderConnection.Deny("Your content package (" + packageName + ") doesn't match the server's version (" + GameMain.SelectedPackage.Name + ")"); - DebugConsole.NewMessage(name + " couldn't join the server (wrong content package name)", Color.Red); - return; - } - else if (packageHash != GameMain.SelectedPackage.MD5hash.Hash) - { - inc.SenderConnection.Deny("Your content package (MD5: " + packageHash + ") doesn't match the server's version (MD5: " + GameMain.SelectedPackage.MD5hash.Hash + ")"); - DebugConsole.NewMessage(name + " couldn't join the server (wrong content package hash)", Color.Red); - return; - } - else if (connectedClients.Find(c => c.name.ToLower() == name.ToLower() && c.ID != userID) != null) - { - inc.SenderConnection.Deny("The name ''" + name + "'' is already in use. Please choose another name."); - DebugConsole.NewMessage(name + " couldn't join the server (name already in use)", Color.Red); - return; - } - -#endif - - //existing user re-joining - if (userID > 0) - { - Client existingClient = connectedClients.Find(c => c.ID == userID); - if (existingClient == null) - { - existingClient = disconnectedClients.Find(c => c.ID == userID); - if (existingClient != null) - { - disconnectedClients.Remove(existingClient); - connectedClients.Add(existingClient); - - UpdateCrewFrame(); - } - } - if (existingClient != null) - { - existingClient.Connection = inc.SenderConnection; - existingClient.ReliableChannel = new ReliableChannel(server); - inc.SenderConnection.Approve(); - return; - } - } - - userID = 1; - while (connectedClients.Any(c => c.ID == userID)) - { - userID++; - } - - Client newClient = new Client(server, name, userID); - newClient.Connection = inc.SenderConnection; - newClient.version = version; - - connectedClients.Add(newClient); - - UpdateCrewFrame(); - - inc.SenderConnection.Approve(); - - refreshMasterTimer = DateTime.Now; - } - - private void SendMessage(NetOutgoingMessage msg, NetDeliveryMethod deliveryMethod, NetConnection excludedConnection = null) { List recipients = new List(); @@ -1159,38 +991,14 @@ namespace Barotrauma.Networking msg.Write(true); msg.Write((byte)0); } - else{ + else + { msg.Write(false); } WriteCharacterData(msg, c.Name, c); } } - // message.Write((byte)PacketTypes.NewCharacter); - - //message.Write(character.ConfigPath); - - //message.Write(character.ID); - - //message.Write(character.Position.X); - //message.Write(character.Position.Y); - - - //List playingClients = connectedClients.FindAll(c => c.Character != null); - - //msg.Write((myCharacter == null) ? (byte)playingClients.Count : (byte)(playingClients.Count + 1)); - //foreach (Client client in playingClients) - //{ - // msg.Write(client.ID); - // WriteCharacterData(msg, client.Character.Name, client.Character); - //} - - //if (myCharacter != null) - //{ - // msg.Write((byte)0); - // WriteCharacterData(msg, myCharacter.Info.Name, myCharacter); - //} - return msg; } @@ -1310,14 +1118,10 @@ namespace Barotrauma.Networking Log(msg, ChatMessage.MessageColor[(int)ChatMessageType.Server]); + client.Connection.Disconnect(targetmsg); + + //notify other players about the disconnected client NetOutgoingMessage outmsg = server.CreateMessage(); - outmsg.Write((byte)PacketTypes.KickedOut); - outmsg.Write(targetmsg); - server.SendMessage(outmsg, client.Connection, NetDeliveryMethod.ReliableUnordered, 0); - - connectedClients.Remove(client); - - outmsg = server.CreateMessage(); outmsg.Write((byte)PacketTypes.PlayerLeft); outmsg.Write(client.ID); outmsg.Write(msg); @@ -1329,6 +1133,7 @@ namespace Barotrauma.Networking server.SendMessage(outmsg, server.Connections, NetDeliveryMethod.ReliableUnordered, 0); } + connectedClients.Remove(client); if (client.FileStreamSender != null) { client.FileStreamSender.Dispose(); @@ -1362,15 +1167,14 @@ namespace Barotrauma.Networking { playerName = playerName.ToLowerInvariant(); - Client client = connectedClients.Find(c => c.name.ToLowerInvariant() == playerName || - (c.Character != null && c.Character.Name.ToLowerInvariant() == playerName)); - - if (client == null) return; + Client client = connectedClients.Find(c => + c.name.ToLowerInvariant() == playerName || + (c.Character != null && c.Character.Name.ToLowerInvariant() == playerName)); KickClient(client, ban); } - private void KickClient(Client client, bool ban = false) + public void KickClient(Client client, bool ban = false) { if (client == null) return; @@ -1519,10 +1323,10 @@ namespace Barotrauma.Networking if (y >= startY && y < startY + height - 120) { - spriteBatch.DrawString(GUI.SmallFont, c.name + ":", new Vector2(x + 10, y), clientColor); - spriteBatch.DrawString(GUI.SmallFont, "Ping: " + (int)(c.Connection.AverageRoundtripTime * 1000.0f) + " ms", new Vector2(x + width - 100, y), clientColor); + spriteBatch.DrawString(GUI.SmallFont, c.name + " ("+c.Connection.RemoteEndPoint.Address.ToString()+")", new Vector2(x + 10, y), clientColor); + spriteBatch.DrawString(GUI.SmallFont, "Ping: " + (int)(c.Connection.AverageRoundtripTime * 1000.0f) + " ms", new Vector2(x+20, y+10), clientColor); } - if (y + 10 >= startY && y < startY + height - 130) spriteBatch.DrawString(GUI.SmallFont, "Resent messages: " + c.Connection.Statistics.ResentMessages, new Vector2(x + 10, y + 10), clientColor); + if (y + 25 >= startY && y < startY + height - 130) spriteBatch.DrawString(GUI.SmallFont, "Resent messages: " + c.Connection.Statistics.ResentMessages, new Vector2(x + 20, y + 20), clientColor); resentMessages += (int)c.Connection.Statistics.ResentMessages; @@ -1741,8 +1545,8 @@ namespace Barotrauma.Networking List jobPreferences = new List(); - int count = message.ReadInt32(); - for (int i = 0; i < count; i++) + int count = message.ReadByte(); + for (int i = 0; i < Math.Min(count, 3); i++) { string jobName = message.ReadString(); JobPrefab jobPrefab = JobPrefab.List.Find(jp => jp.Name == jobName); @@ -1960,7 +1764,6 @@ namespace Barotrauma.Networking { banList.Save(); - if (registeredToMaster && restClient != null) { var request = new RestRequest("masterserver2.php", Method.GET); @@ -1996,8 +1799,6 @@ namespace Barotrauma.Networking public string version; public bool inGame; - - private List kickVoters; public bool ReadyToStart; @@ -2039,6 +1840,27 @@ namespace Barotrauma.Networking jobPreferences = new List(JobPrefab.List.GetRange(0,3)); } + public static bool IsValidName(string name) + { + if (name.Contains("\n") || name.Contains("\r\n")) return false; + + return (name.All(c => + c != ';' && + c != ',' && + c != '<' && + c != '/')); + } + + public static string SanitizeName(string name) + { + if (name.Length > 20) + { + name = name.Substring(0, 20); + } + + return name; + } + public T GetVote(VoteType voteType) { return (votes[(int)voteType] is T) ? (T)votes[(int)voteType] : default(T); diff --git a/Subsurface/Source/Networking/GameServerLogin.cs b/Subsurface/Source/Networking/GameServerLogin.cs new file mode 100644 index 000000000..45b20e0f9 --- /dev/null +++ b/Subsurface/Source/Networking/GameServerLogin.cs @@ -0,0 +1,261 @@ +using Barotrauma.Networking.ReliableMessages; +using Lidgren.Network; +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Barotrauma.Networking +{ + class UnauthenticatedClient + { + public NetConnection Connection; + public int Nonce; + + public float AuthTimer; + + public UnauthenticatedClient(NetConnection connection, int nonce) + { + Connection = connection; + Nonce = nonce; + + AuthTimer = 5.0f; + } + } + + partial class GameServer : NetworkMember, IPropertyObject + { + List unauthenticatedClients = new List(); + + private void HandleConnectionApproval(NetIncomingMessage inc) + { + if ((PacketTypes)inc.ReadByte() != PacketTypes.Login) return; + + if (banList.IsBanned(inc.SenderEndPoint.Address.ToString())) + { + inc.SenderConnection.Deny("You have been banned from the server"); + DebugConsole.NewMessage("Banned player tried to join the server", Color.Red); + return; + } + + if (connectedClients.Any(c => c.Connection == inc.SenderConnection)) + { + inc.SenderConnection.Deny("Connection error - already joined"); + return; + } + + int nonce = CryptoRandom.Instance.Next(); + var msg = server.CreateMessage(); + msg.Write(nonce); + + unauthenticatedClients.Add(new UnauthenticatedClient(inc.SenderConnection, nonce)); + + inc.SenderConnection.Approve(msg); + } + + private void CheckAuthentication(NetIncomingMessage inc) + { + var unauthenticatedClient = unauthenticatedClients.Find(uc => uc.Connection == inc.SenderConnection); + if (unauthenticatedClient != null) + { + unauthenticatedClients.Remove(unauthenticatedClient); + + NetEncryption algo = new NetXtea(server, password); + inc.Decrypt(algo); + + int nonce = inc.ReadInt32(); + if (nonce != unauthenticatedClient.Nonce) + { + inc.SenderConnection.Disconnect("Wrong password!"); + return; + } + } + else + { + inc.SenderConnection.Disconnect("Authentication failed"); + } + + DebugConsole.NewMessage("New player has joined the server", Color.White); + + byte userID; + string version = "", packageName = "", packageHash = "", name = ""; + try + { + userID = inc.ReadByte(); + version = inc.ReadString(); + packageName = inc.ReadString(); + packageHash = inc.ReadString(); + name = Client.SanitizeName(inc.ReadString()); + } + catch + { + inc.SenderConnection.Disconnect("Connection error - server failed to read your ConnectionApproval message"); + DebugConsole.NewMessage("Connection error - server failed to read the ConnectionApproval message", Color.Red); + return; + } + +#if !DEBUG + if (string.IsNullOrWhiteSpace(name)) + { + inc.SenderConnection.Disconnect("Invalid username"); + DebugConsole.NewMessage(name + " couldn't join the server (name empty)", Color.Red); + return; + } + else if (!Client.IsValidName(name)) + { + inc.SenderConnection.Disconnect("Username contains illegal symbols"); + DebugConsole.NewMessage(name + " couldn't join the server (username contains illegal symbols)", Color.Red); + return; + + } + else if (version != GameMain.Version.ToString()) + { + inc.SenderConnection.Disconnect("Version " + GameMain.Version + " required to connect to the server (Your version: " + version + ")"); + DebugConsole.NewMessage(name + " couldn't join the server (wrong game version)", Color.Red); + return; + } + else if (packageName != GameMain.SelectedPackage.Name) + { + inc.SenderConnection.Disconnect("Your content package (" + packageName + ") doesn't match the server's version (" + GameMain.SelectedPackage.Name + ")"); + DebugConsole.NewMessage(name + " couldn't join the server (wrong content package name)", Color.Red); + return; + } + else if (packageHash != GameMain.SelectedPackage.MD5hash.Hash) + { + inc.SenderConnection.Disconnect("Your content package (MD5: " + packageHash + ") doesn't match the server's version (MD5: " + GameMain.SelectedPackage.MD5hash.Hash + ")"); + DebugConsole.NewMessage(name + " couldn't join the server (wrong content package hash)", Color.Red); + return; + } + else if (connectedClients.Any(c => c.name.ToLower() == name.ToLower() && c.Connection != inc.SenderConnection)) + { + inc.SenderConnection.Disconnect("The name ''" + name + "'' is already in use. Please choose another name."); + DebugConsole.NewMessage(name + " couldn't join the server (name already in use)", Color.Red); + return; + } + +#endif + + //existing user re-joining + if (userID > 0) + { + Client existingClient = connectedClients.Find(c => + c.ID == userID && + c.Connection == inc.SenderConnection); + + if (existingClient == null) + { + existingClient = disconnectedClients.Find(c => + c.ID == userID && + c.Connection == inc.SenderConnection); + + if (existingClient != null) + { + disconnectedClients.Remove(existingClient); + connectedClients.Add(existingClient); + + UpdateCrewFrame(); + } + } + if (existingClient != null) + { + existingClient.Connection = inc.SenderConnection; + existingClient.ReliableChannel = new ReliableChannel(server); + LogClientIn(inc); + return; + } + } + + userID = 1; + while (connectedClients.Any(c => c.ID == userID)) + { + userID++; + } + + Client newClient = new Client(server, name, userID); + newClient.Connection = inc.SenderConnection; + newClient.version = version; + + connectedClients.Add(newClient); + + UpdateCrewFrame(); + + LogClientIn(inc); + + refreshMasterTimer = DateTime.Now; + } + + private void LogClientIn(NetIncomingMessage inc) + { + Client sender = connectedClients.Find(x => x.Connection == inc.SenderConnection); + + if (sender == null) return; + + if (sender.version != GameMain.Version.ToString()) + { + DisconnectClient(sender, sender.name + " was unable to connect to the server (nonmatching game version)", + "Version " + GameMain.Version + " required to connect to the server (Your version: " + sender.version + ")"); + } + else if (connectedClients.Find(x => x.name == sender.name && x != sender) != null) + { + DisconnectClient(sender, sender.name + " was unable to connect to the server (name already in use)", + "The name ''" + sender.name + "'' is already in use. Please choose another name."); + } + else + { + //AssignJobs(); + + GameMain.NetLobbyScreen.RemovePlayer(sender.name); + GameMain.NetLobbyScreen.AddPlayer(sender.name); + + // Notify the client that they have logged in + var outmsg = server.CreateMessage(); + + outmsg.Write((byte)PacketTypes.LoggedIn); + outmsg.Write(sender.ID); + outmsg.Write(gameStarted); + outmsg.Write(gameStarted && sender.Character != null && !sender.Character.IsDead); + outmsg.Write(AllowSpectating); + + //notify the client about other clients already logged in + outmsg.Write((byte)((characterInfo == null) ? connectedClients.Count - 1 : connectedClients.Count)); + foreach (Client c in connectedClients) + { + if (c.Connection == inc.SenderConnection) continue; + outmsg.Write(c.name); + outmsg.Write(c.ID); + } + + if (characterInfo != null) + { + outmsg.Write(characterInfo.Name); + outmsg.Write((byte)0); + } + + var subs = GameMain.NetLobbyScreen.GetSubList(); + outmsg.Write((byte)subs.Count); + foreach (Submarine sub in subs) + { + outmsg.Write(sub.Name); + outmsg.Write(sub.MD5Hash.Hash); + } + + server.SendMessage(outmsg, inc.SenderConnection, NetDeliveryMethod.ReliableUnordered, 0); + + //notify other clients about the new client + outmsg = server.CreateMessage(); + outmsg.Write((byte)PacketTypes.PlayerJoined); + outmsg.Write(sender.name); + outmsg.Write(sender.ID); + + //send the message to everyone except the client who just logged in + SendMessage(outmsg, NetDeliveryMethod.ReliableUnordered, inc.SenderConnection); + + AddChatMessage(sender.name + " has joined the server", ChatMessageType.Server); + } + } + + + } +} diff --git a/Subsurface/Source/Networking/NetworkMember.cs b/Subsurface/Source/Networking/NetworkMember.cs index 33dbd6a2d..19388ae4b 100644 --- a/Subsurface/Source/Networking/NetworkMember.cs +++ b/Subsurface/Source/Networking/NetworkMember.cs @@ -13,9 +13,9 @@ namespace Barotrauma.Networking { Unknown, - Login, LoggedIn, LogOut, + Login, LoggedIn, - PlayerJoined, PlayerLeft, KickedOut, + PlayerJoined, PlayerLeft, RequestNetLobbyUpdate,