diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index fe7deda61..d4d1aef3a 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -28,8 +28,14 @@ namespace Barotrauma.Networking private List otherClients; - private string serverIP; - + private string serverIP; + + private bool needAuth; + private bool requiresPw; + private int nonce; + private string saltedPw; + NetEncryption algo; + public byte ID { get { return myID; } @@ -77,7 +83,7 @@ namespace Barotrauma.Networking GameMain.NetLobbyScreen = new NetLobbyScreen(); } - public void ConnectToServer(string hostIP, string password = "") + public void ConnectToServer(string hostIP) { string[] address = hostIP.Split(':'); if (address.Length==1) @@ -126,11 +132,13 @@ namespace Barotrauma.Networking return; } + NetOutgoingMessage outmsg = client.CreateMessage(); + outmsg.Write((byte)ClientPacketHeader.REQUEST_AUTH); // Connect client, to ip previously requested from user try { - + client.Connect(IPEndPoint, outmsg); } catch (Exception e) { @@ -150,13 +158,8 @@ namespace Barotrauma.Networking reconnectBox.Buttons[0].OnClicked += CancelConnect; reconnectBox.Buttons[0].OnClicked += reconnectBox.Close; } - - String sendPw = ""; - if (password.Length>0) - { - sendPw = Encoding.UTF8.GetString(NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(password))); - } - CoroutineManager.StartCoroutine(WaitForStartingInfo(sendPw)); + + CoroutineManager.StartCoroutine(WaitForStartingInfo()); // Start the timer //update.Start(); @@ -181,25 +184,30 @@ namespace Barotrauma.Networking return true; } - private bool connectCanceled; + private bool connectCancelled; private bool CancelConnect(GUIButton button, object obj) { - connectCanceled = true; + connectCancelled = true; return true; } // Before main looping starts, we loop here and wait for approval message - private IEnumerable WaitForStartingInfo(string password) + private IEnumerable WaitForStartingInfo() { - connectCanceled = false; + requiresPw = false; + needAuth = true; + saltedPw = ""; + + connectCancelled = false; // When this is set to true, we are approved and ready to go bool CanStart = false; DateTime timeOut = DateTime.Now + new TimeSpan(0,0,20); + DateTime reqAuthTime = DateTime.Now + new TimeSpan(0, 0, 3); // Loop until we are approved - while (!CanStart && !connectCanceled) + while (!CanStart && !connectCancelled) { int seconds = DateTime.Now.Second; @@ -210,6 +218,44 @@ namespace Barotrauma.Networking } reconnectBox.Text = connectingText; + if (DateTime.Now > reqAuthTime) + { + if (needAuth) + { + //request auth again + NetOutgoingMessage reqAuthMsg = client.CreateMessage(); + reqAuthMsg.Write((byte)ClientPacketHeader.REQUEST_AUTH); + client.SendMessage(reqAuthMsg, NetDeliveryMethod.Unreliable); + } + else + { + //request init again + if (!requiresPw) + { + NetOutgoingMessage outmsg = client.CreateMessage(); + outmsg.Write((byte)ClientPacketHeader.REQUEST_INIT); + outmsg.Write(GameMain.Version.ToString()); + outmsg.Write(GameMain.SelectedPackage.Name); + outmsg.Write(GameMain.SelectedPackage.MD5hash.Hash); + outmsg.Write(name); + client.SendMessage(outmsg, NetDeliveryMethod.Unreliable); + } + else + { + NetOutgoingMessage outmsg = client.CreateMessage(); + outmsg.Write((byte)ClientPacketHeader.REQUEST_INIT); + outmsg.Write(saltedPw); + 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.Unreliable); + } + } + reqAuthTime = DateTime.Now + new TimeSpan(0, 0, 3); + } + yield return CoroutineStatus.Running; if (DateTime.Now > timeOut) break; @@ -217,10 +263,47 @@ namespace Barotrauma.Networking NetIncomingMessage inc; // If new messages arrived if ((inc = client.ReadMessage()) == null) continue; + + string pwMsg = "Password required"; try - { - //TODO: read message data + { + switch (inc.MessageType) + { + case NetIncomingMessageType.Data: + ServerPacketHeader header = (ServerPacketHeader)inc.ReadByte(); + switch (header) + { + case ServerPacketHeader.AUTH_RESPONSE: + if (inc.ReadBoolean()) + { + //requires password + nonce = inc.ReadInt32(); + requiresPw = true; + } + else + { + requiresPw = false; + } + break; + case ServerPacketHeader.AUTH_FAILURE: + //failed to authenticate, can still use same nonce + pwMsg = inc.ReadString(); + requiresPw = true; + break; + } + break; + case NetIncomingMessageType.StatusChanged: + if (client.ConnectionStatus == NetConnectionStatus.Disconnected) + { + string denyMessage = inc.ReadString(); + + new GUIMessageBox("Couldn't connect to server", denyMessage); + + connectCancelled = true; + } + break; + } } catch (Exception e) @@ -228,16 +311,55 @@ namespace Barotrauma.Networking DebugConsole.ThrowError("Error while connecting to server", e); break; } + + if (requiresPw && needAuth) + { + var msgBox = new GUIMessageBox(pwMsg, "", new string[] { "OK", "Cancel" }); + var passwordBox = new GUITextBox(new Rectangle(0, 40, 150, 25), Alignment.TopLeft, GUI.Style, msgBox.children[0]); + passwordBox.UserData = "password"; + + var okButton = msgBox.Buttons[0]; + var cancelButton = msgBox.Buttons[1]; + + while (GUIMessageBox.MessageBoxes.Contains(msgBox)) + { + while (client.ReadMessage() != null) {} //clear incoming message queue until client sends password request + okButton.Enabled = !string.IsNullOrWhiteSpace(passwordBox.Text); + + if (okButton.Selected) + { + saltedPw = Encoding.UTF8.GetString(NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(passwordBox.Text))); + saltedPw = saltedPw + Convert.ToString(nonce); + saltedPw = Encoding.UTF8.GetString(NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(saltedPw))); + algo = new NetXtea(client, saltedPw); + + timeOut = DateTime.Now + new TimeSpan(0, 0, 20); + reqAuthTime = DateTime.Now + new TimeSpan(0, 0, 0); + needAuth = false; + + msgBox.Close(null, null); + break; + } + else if (cancelButton.Selected) + { + msgBox.Close(null, null); + connectCancelled = true; + } + else + { + yield return CoroutineStatus.Running; + } + } + } } - - + if (reconnectBox != null) { reconnectBox.Close(null, null); reconnectBox = null; } - if (connectCanceled) yield return CoroutineStatus.Success; + if (connectCancelled) yield return CoroutineStatus.Success; if (client.ConnectionStatus != NetConnectionStatus.Connected) { diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 0c875321b..8eea8577a 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -366,51 +366,75 @@ namespace Barotrauma.Networking { try { - switch (inc.MessageType) - { - case NetIncomingMessageType.Data: - if (banList.IsBanned(inc.SenderEndPoint.Address.ToString())) + switch (inc.MessageType) + { + case NetIncomingMessageType.Data: + if (banList.IsBanned(inc.SenderEndPoint.Address.ToString())) + { + KickClient(inc.SenderConnection,true); + } + else + { + ClientPacketHeader header = (ClientPacketHeader)inc.ReadByte(); + switch (header) + { + case ClientPacketHeader.REQUEST_AUTH: + ClientAuthRequest(inc.SenderConnection); + break; + case ClientPacketHeader.REQUEST_INIT: + ClientInitialize(inc); + break; + case ClientPacketHeader.UPDATE_LOBBY: + //TODO + break; + case ClientPacketHeader.UPDATE_INGAME_ALIVE: + //TODO + break; + case ClientPacketHeader.UPDATE_INGAME_SPECTATING: + //TODO + break; + } + } + break; + case NetIncomingMessageType.StatusChanged: + switch (inc.SenderConnection.Status) { - inc.SenderConnection.Disconnect("You have been banned from the server"); - } - else - { - ClientPacketHeader header = (ClientPacketHeader)inc.ReadByte(); - switch (header) + case NetConnectionStatus.Disconnected: + var connectedClient = connectedClients.Find(c => c.Connection == inc.SenderConnection); + /*if (connectedClient != null && !disconnectedClients.Contains(connectedClient)) + { + connectedClient.deleteDisconnectedTimer = NetConfig.DeleteDisconnectedTime; + disconnectedClients.Add(connectedClient); + } + */ + DisconnectClient(inc.SenderConnection, + connectedClient != null ? connectedClient.name + " has disconnected" : ""); + break; + } + break; + case NetIncomingMessageType.ConnectionApproval: + if (banList.IsBanned(inc.SenderEndPoint.Address.ToString())) + { + inc.SenderConnection.Deny("You have been banned from the server"); + } + else if (ConnectedClients.Count >= MaxPlayers) + { + inc.SenderConnection.Deny("Server full"); + } + else + { + if ((ClientPacketHeader)inc.ReadByte() == ClientPacketHeader.REQUEST_AUTH) { - case ClientPacketHeader.REQUEST_AUTH: - ClientAuthRequest(inc.SenderConnection); - break; - case ClientPacketHeader.REQUEST_INIT: - ClientInitialize(inc); - break; - case ClientPacketHeader.UPDATE_LOBBY: - //TODO - break; - case ClientPacketHeader.UPDATE_INGAME_ALIVE: - //TODO - break; - case ClientPacketHeader.UPDATE_INGAME_SPECTATING: - //TODO - break; - } - } - break; - case NetIncomingMessageType.ConnectionApproval: - if (banList.IsBanned(inc.SenderEndPoint.Address.ToString())) - { - inc.SenderConnection.Deny("You have been banned from the server"); - } - else if (ConnectedClients.Count >= MaxPlayers) - { - inc.SenderConnection.Deny("Server full"); - } - else - { - inc.SenderConnection.Approve(); - } - break; - } + inc.SenderConnection.Approve(); + ClientAuthRequest(inc.SenderConnection); + } + else + { + inc.SenderConnection.Deny("GAY"); + } + } + break; + } } catch (Exception e) @@ -494,14 +518,14 @@ namespace Barotrauma.Networking sparseUpdateTimer = DateTime.Now + sparseUpdateInterval; } - private byte GetNewClientID() - { + private byte GetNewClientID() + { byte userID = 1; while (connectedClients.Any(c => c.ID == userID)) { userID++; - } - return userID; + } + return userID; } public bool StartGameClicked(GUIButton button, object obj) @@ -609,8 +633,93 @@ namespace Barotrauma.Networking GameMain.NetLobbyScreen.Select(); yield return CoroutineStatus.Success; + } + + public override void KickPlayer(string playerName, bool ban) + { + playerName = playerName.ToLowerInvariant(); + + Client client = connectedClients.Find(c => + c.name.ToLowerInvariant() == playerName || + (c.Character != null && c.Character.Name.ToLowerInvariant() == playerName)); + + KickClient(client, ban); } - + + public void KickClient(NetConnection conn, bool ban = false) + { + Client client = connectedClients.Find(c => c.Connection == conn); + if (client == null) + { + conn.Disconnect(ban ? "You have been banned from the server" : "You have been kicked from the server"); + if (ban) + { + if (!banList.IsBanned(conn.RemoteEndPoint.Address.ToString())) + { + banList.BanPlayer("Unnamed", conn.RemoteEndPoint.Address.ToString()); + } + } + } + else + { + KickClient(client, ban); + } + } + + public void KickClient(Client client, bool ban = false) + { + if (client == null) return; + + if (ban) + { + DisconnectClient(client, client.name + " has been banned from the server", "You have been banned from the server"); + banList.BanPlayer(client.name, client.Connection.RemoteEndPoint.Address.ToString()); + } + else + { + DisconnectClient(client, client.name + " has been kicked from the server", "You have been kicked from the server"); + } + } + + private void DisconnectClient(NetConnection senderConnection, string msg = "", string targetmsg = "") + { + Client client = connectedClients.Find(x => x.Connection == senderConnection); + if (client == null) return; + + DisconnectClient(client, msg, targetmsg); + } + + private void DisconnectClient(Client client, string msg = "", string targetmsg = "") + { + if (client == null) return; + + if (gameStarted && client.Character != null) + { + client.Character.ClearInputs(); + client.Character.Kill(CauseOfDeath.Disconnected, true); + } + + client.Character = null; + client.inGame = false; + + if (string.IsNullOrWhiteSpace(msg)) msg = client.name + " has left the server"; + if (string.IsNullOrWhiteSpace(targetmsg)) targetmsg = "You have left the server"; + + Log(msg, ChatMessage.MessageColor[(int)ChatMessageType.Server]); + + client.Connection.Disconnect(targetmsg); + + GameMain.NetLobbyScreen.RemovePlayer(client.name); + + connectedClients.Remove(client); + + AddChatMessage(msg, ChatMessageType.Server); + + UpdateCrewFrame(); + + refreshMasterTimer = DateTime.Now; + } + private void UpdateCrewFrame() { List crew = new List(); diff --git a/Subsurface/Source/Networking/GameServerLogin.cs b/Subsurface/Source/Networking/GameServerLogin.cs index 411b763fb..cc554f5fe 100644 --- a/Subsurface/Source/Networking/GameServerLogin.cs +++ b/Subsurface/Source/Networking/GameServerLogin.cs @@ -66,7 +66,7 @@ namespace Barotrauma.Networking else { nonceMsg.Write(true); //true = password - nonceMsg.Write(unauthClient.Nonce); //here's nonce, encrypt with this + nonceMsg.Write((Int32)unauthClient.Nonce); //here's nonce, encrypt with this } server.SendMessage(nonceMsg, conn, NetDeliveryMethod.Unreliable); } diff --git a/Subsurface/Source/Screens/ServerListScreen.cs b/Subsurface/Source/Screens/ServerListScreen.cs index 6380ffd5c..c8474dbf7 100644 --- a/Subsurface/Source/Screens/ServerListScreen.cs +++ b/Subsurface/Source/Screens/ServerListScreen.cs @@ -288,7 +288,7 @@ namespace Barotrauma return false; } - CoroutineManager.StartCoroutine(ConnectToServer(ip, serverList.Selected != null && (serverList.Selected.GetChild("password") as GUITickBox).Selected)); + CoroutineManager.StartCoroutine(ConnectToServer(ip)); return true; @@ -296,47 +296,15 @@ namespace Barotrauma public void JoinServer(string ip, bool hasPassword, string msg = "Password required") { - CoroutineManager.StartCoroutine(ConnectToServer(ip, hasPassword, msg)); + CoroutineManager.StartCoroutine(ConnectToServer(ip)); } - private IEnumerable ConnectToServer(string ip, bool hasPassword, string msg = "Password required") + private IEnumerable ConnectToServer(string ip) { - string selectedPassword = ""; - - if (hasPassword) - { - var msgBox = new GUIMessageBox(msg, "", new string[] { "OK", "Cancel" }); - var passwordBox = new GUITextBox(new Rectangle(0,40,150,25), Alignment.TopLeft, GUI.Style, msgBox.children[0]); - passwordBox.UserData = "password"; - - var okButton = msgBox.Buttons[0]; - var cancelButton = msgBox.Buttons[1]; - - while (GUIMessageBox.MessageBoxes.Contains(msgBox)) - { - okButton.Enabled = !string.IsNullOrWhiteSpace(passwordBox.Text); - - if (okButton.Selected) - { - msgBox.Close(null,null); - break; - } - else if (cancelButton.Selected) - { - msgBox.Close(null, null); - yield return CoroutineStatus.Success; - } - - yield return CoroutineStatus.Running; - } - - selectedPassword = passwordBox.Text; - } - try { GameMain.NetworkMember = new GameClient(clientNameBox.Text); - GameMain.Client.ConnectToServer(ip, selectedPassword); + GameMain.Client.ConnectToServer(ip); } catch (Exception e)