diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 1c98d5221..652d17c7a 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -67,7 +67,7 @@ namespace Barotrauma.Networking { if (!permissions.HasFlag(ClientPermissions.EndRound)) return false; - //TODO: tell server that client requested round end + RequestRoundEnd(); return true; }; @@ -539,6 +539,9 @@ namespace Barotrauma.Networking } CoroutineManager.StartCoroutine(EndGame(endMessage)); break; + case ServerPacketHeader.PERMISSIONS: + ReadPermissions(inc); + break; } break; case NetIncomingMessageType.StatusChanged: @@ -573,6 +576,48 @@ namespace Barotrauma.Networking } } + private void ReadPermissions(NetIncomingMessage inc) + { + ClientPermissions newPermissions = (ClientPermissions)inc.ReadByte(); + if (newPermissions != permissions) + { + SetPermissions(newPermissions); + } + } + + private void SetPermissions(ClientPermissions newPermissions) + { + if (GUIMessageBox.MessageBoxes.Count > 0) + { + var existingMsgBox = GUIMessageBox.MessageBoxes.Peek(); + if (existingMsgBox.UserData as string == "permissions") + { + GUIMessageBox.MessageBoxes.Dequeue(); + } + } + + string msg = ""; + if (newPermissions == ClientPermissions.None) + { + msg = "The host has removed all your special permissions."; + } + else + { + msg = "Your current permissions:\n"; + foreach (ClientPermissions permission in Enum.GetValues(typeof(ClientPermissions))) + { + if (!newPermissions.HasFlag(permission) || permission == ClientPermissions.None) continue; + System.Reflection.FieldInfo fi = typeof(ClientPermissions).GetField(permission.ToString()); + DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); + msg += " - " + attributes[0].Description+"\n"; + } + } + permissions = newPermissions; + new GUIMessageBox("Permissions changed", msg).UserData = "permissions"; + + endRoundButton.Visible = HasPermission(ClientPermissions.EndRound); + } + private IEnumerable StartGame(NetIncomingMessage inc) { if (Character != null) Character.Remove(); @@ -705,6 +750,8 @@ namespace Barotrauma.Networking gameStarted = inc.ReadBoolean(); bool allowSpectating = inc.ReadBoolean(); + SetPermissions((ClientPermissions)inc.ReadByte()); + if (gameStarted && !isDuplicate) { new GUIMessageBox("Please wait", @@ -997,7 +1044,6 @@ namespace Barotrauma.Networking } } - public override bool SelectCrewCharacter(GUIComponent component, object obj) { var characterFrame = component.Parent.Parent.FindChild("selectedcharacter"); @@ -1065,7 +1111,26 @@ namespace Barotrauma.Networking return true; } - + + public override void KickPlayer(string kickedName, bool ban, bool range = false) + { + NetOutgoingMessage msg = client.CreateMessage(); + msg.Write((byte)ClientPacketHeader.SERVER_COMMAND); + msg.Write((byte)(ban ? ClientPermissions.Ban : ClientPermissions.Kick)); + msg.Write(kickedName); + + client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); + } + + public void RequestRoundEnd() + { + NetOutgoingMessage msg = client.CreateMessage(); + msg.Write((byte)ClientPacketHeader.SERVER_COMMAND); + msg.Write((byte)ClientPermissions.EndRound); + + client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); + } + public bool SpectateClicked(GUIButton button, object userData) { if (button != null) button.Enabled = false; diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 0f7409c42..325d11962 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -560,6 +560,9 @@ namespace Barotrauma.Networking ClientReadIngame(inc); break; + case ClientPacketHeader.SERVER_COMMAND: + ClientReadServerCommand(inc); + break; } } @@ -618,7 +621,7 @@ namespace Barotrauma.Networking } ClientNetObject objHeader; - while ((objHeader=(ClientNetObject)inc.ReadByte()) != ClientNetObject.END_OF_MESSAGE) + while ((objHeader = (ClientNetObject)inc.ReadByte()) != ClientNetObject.END_OF_MESSAGE) { switch (objHeader) { @@ -706,6 +709,64 @@ namespace Barotrauma.Networking } } + private void ClientReadServerCommand(NetIncomingMessage inc) + { + Client c = ConnectedClients.Find(x => x.Connection == inc.SenderConnection); + if (c == null) + { + inc.SenderConnection.Disconnect("You're not a connected client."); + return; + } + + ClientPermissions command = ClientPermissions.None; + try + { + command = (ClientPermissions)inc.ReadByte(); + } + + catch + { + return; + } + + if (!c.HasPermission(command)) + { + Log("Client \""+c.name+"\" sent a server command \""+command+"\". Permission denied.", Color.Red); + return; + } + + switch (command) + { + case ClientPermissions.Kick: + string kickedName = inc.ReadString(); + var kickedClient = connectedClients.Find(cl => cl != c && cl.name == kickedName); + if (kickedClient != null) + { + Log("Client \"" + c.name + "\" kicked \"" + kickedClient.name + "\".", Color.Red); + KickClient(kickedClient, false, false); + } + break; + case ClientPermissions.Ban: + string bannedName = inc.ReadString(); + var bannedClient = connectedClients.Find(cl => cl != c && cl.name == bannedName); + if (bannedClient != null) + { + Log("Client \"" + c.name + "\" banned \"" + bannedClient.name + "\".", Color.Red); + KickClient(bannedClient, true, false); + } + break; + case ClientPermissions.EndRound: + if (gameStarted) + { + Log("Client \"" + c.name + "\" ended the round.", Color.Cyan); + EndGame(); + } + break; + } + + inc.ReadPadBits(); + } + /// /// Write info that the client needs when joining the server /// @@ -723,6 +784,8 @@ namespace Barotrauma.Networking outmsg.Write(GameStarted); outmsg.Write(AllowSpectating); + + outmsg.Write((byte)c.Permissions); } private void ClientWriteIngame(Client c) @@ -1644,8 +1707,7 @@ namespace Barotrauma.Networking } public void UpdateClientPermissions(Client client) - { - + { clientPermissions.RemoveAll(cp => cp.IP == client.Connection.RemoteEndPoint.Address.ToString()); if (client.Permissions != ClientPermissions.None) @@ -1656,6 +1718,11 @@ namespace Barotrauma.Networking client.Permissions)); } + var msg = server.CreateMessage(); + msg.Write((byte)ServerPacketHeader.PERMISSIONS); + msg.Write((byte)client.Permissions); + server.SendMessage(msg, client.Connection, NetDeliveryMethod.ReliableUnordered); + SaveClientPermissions(); } diff --git a/Subsurface/Source/Networking/GameServerLogin.cs b/Subsurface/Source/Networking/GameServerLogin.cs index d3ab37f45..9aa42debf 100644 --- a/Subsurface/Source/Networking/GameServerLogin.cs +++ b/Subsurface/Source/Networking/GameServerLogin.cs @@ -205,6 +205,16 @@ namespace Barotrauma.Networking GameMain.NetLobbyScreen.AddPlayer(newClient.name); GameMain.Server.SendChatMessage(clName + " has joined the server.", ChatMessageType.Server, null); + + var savedPermissions = clientPermissions.Find(cp => cp.IP == newClient.Connection.RemoteEndPoint.Address.ToString()); + if (savedPermissions != null) + { + newClient.SetPermissions(savedPermissions.Permissions); + } + else + { + newClient.SetPermissions(ClientPermissions.None); + } } } } diff --git a/Subsurface/Source/Networking/NetworkMember.cs b/Subsurface/Source/Networking/NetworkMember.cs index 3f910d2ac..7a93d58e0 100644 --- a/Subsurface/Source/Networking/NetworkMember.cs +++ b/Subsurface/Source/Networking/NetworkMember.cs @@ -16,7 +16,8 @@ namespace Barotrauma.Networking UPDATE_LOBBY, //update state in lobby UPDATE_INGAME, //update state ingame - RESPONSE_STARTGAME //tell the server whether you're ready to start + RESPONSE_STARTGAME, //tell the server whether you're ready to start + SERVER_COMMAND //tell the server to end a round or kick/ban someone (special permissions required) } enum ClientNetObject { @@ -25,7 +26,6 @@ namespace Barotrauma.Networking CHAT_MESSAGE, //also self-explanatory VOTE, //you get the idea CHARACTER_INPUT, - ITEM_INTERACTION, ENTITY_STATE } @@ -36,6 +36,8 @@ namespace Barotrauma.Networking UPDATE_LOBBY, //update state in lobby (votes and chat messages) UPDATE_INGAME, //update state ingame (character input and chat messages) + PERMISSIONS, //tell the client which special permissions they have (if any) + QUERY_STARTGAME, //ask the clients whether they're ready to start STARTGAME, //start a new round ENDGAME