diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index e4f459224..20ae8951d 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -676,6 +676,12 @@ namespace Barotrauma.Networking byte id = inc.ReadByte(); Character newCharacter = ReadCharacterData(inc, id == myID); + if (id != myID) + { + var characterOwner = otherClients.Find(c => c.ID == id); + characterOwner.Character = newCharacter; + } + crew.Add(newCharacter); yield return CoroutineStatus.Running; @@ -758,15 +764,26 @@ namespace Barotrauma.Networking if (fileStreamReceiver != null && (fileStreamReceiver.Status == FileTransferStatus.Receiving || fileStreamReceiver.Status == FileTransferStatus.NotStarted)) { - Vector2 pos = Screen.Selected == GameMain.NetLobbyScreen ? - new Vector2(GameMain.NetLobbyScreen.SubList.Rect.X, GameMain.NetLobbyScreen.SubList.Rect.Bottom+5) : new Vector2(GameMain.GraphicsWidth / 2 - 200, 10); + //Vector2 pos = Screen.Selected == GameMain.NetLobbyScreen ? + // new Vector2(GameMain.NetLobbyScreen.SubList.Rect.X, GameMain.NetLobbyScreen.SubList.Rect.Bottom+5) : new Vector2(GameMain.GraphicsWidth / 2 - 200, 10); - GUI.DrawString(spriteBatch, pos, "Downloading " + fileStreamReceiver.FileName, Color.White); - GUI.DrawString(spriteBatch, pos + Vector2.UnitX*300, - MathUtils.GetBytesReadable((long)fileStreamReceiver.Received) + " / " + MathUtils.GetBytesReadable((long)fileStreamReceiver.FileSize), Color.White); - GUI.DrawProgressBar(spriteBatch, new Vector2(pos.X, -pos.Y - 20), new Vector2(300, 15), fileStreamReceiver.Progress, Color.Green); - if (GUI.DrawButton(spriteBatch, new Rectangle((int)pos.X + 310, (int)pos.Y + 20, 100, 15), "Cancel", new Color(0.88f, 0.25f, 0.15f, 0.8f))) + + Vector2 pos = new Vector2(GameMain.GraphicsWidth / 2 - 130, GameMain.NetLobbyScreen.InfoFrame.Rect.Y / 2 - 15); + + GUI.DrawString(spriteBatch, + pos, + "Downloading " + fileStreamReceiver.FileName, + Color.White, null, 0, GUI.SmallFont); + + + GUI.DrawProgressBar(spriteBatch, new Vector2(pos.X, -pos.Y - 12), new Vector2(200, 15), fileStreamReceiver.Progress, Color.Green); + + GUI.DrawString(spriteBatch, pos + new Vector2(5,12), + MathUtils.GetBytesReadable((long)fileStreamReceiver.Received) + " / " + MathUtils.GetBytesReadable((long)fileStreamReceiver.FileSize), + Color.White, null, 0, GUI.SmallFont); + + if (GUI.DrawButton(spriteBatch, new Rectangle((int)pos.X + 210, (int)pos.Y+12, 65, 15), "Cancel", new Color(0.47f, 0.13f, 0.15f, 0.08f))) { CancelFileTransfer(); } @@ -799,6 +816,24 @@ namespace Barotrauma.Networking GameMain.NetworkMember = null; } + + public override bool SelectCrewCharacter(GUIComponent component, object obj) + { + var characterFrame = component.Parent.Parent.FindChild("selectedcharacter"); + + Character character = obj as Character; + if (character == null) return false; + + if (character != myCharacter) + { + var kickButton = new GUIButton(new Rectangle(0, 0, 130, 20), "Vote to Kick", Alignment.BottomLeft, GUI.Style, characterFrame); + kickButton.UserData = character; + kickButton.OnClicked += VoteForKick; + } + + return true; + } + public void ReadCharacterSpawnMessage(NetIncomingMessage message) { string configPath = message.ReadString(); @@ -873,6 +908,17 @@ namespace Barotrauma.Networking client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); } + public bool VoteForKick(GUIButton button, object userdata) + { + var votedClient = otherClients.Find(c => c.Character == userdata); + + if (votedClient == null) return false; + + Vote(VoteType.Kick, votedClient); + + return true; + } + public void Vote(VoteType voteType, object userData) { NetOutgoingMessage msg = client.CreateMessage(); @@ -890,6 +936,12 @@ namespace Barotrauma.Networking case VoteType.EndRound: msg.Write((bool)userData); break; + case VoteType.Kick: + Client votedClient = userData as Client; + if (votedClient == null) return; + + msg.Write(votedClient.ID); + break; } client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 352369a62..a539ce4c1 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -1380,6 +1380,9 @@ namespace Barotrauma.Networking { if (server.Connections.Count == 0) return; + var clientsToKick = ConnectedClients.FindAll(c => c.KickVoteCount > ConnectedClients.Count * KickVoteRequiredRatio); + clientsToKick.ForEach(c => KickClient(c)); + try { NetOutgoingMessage msg = server.CreateMessage(); @@ -1417,6 +1420,8 @@ namespace Barotrauma.Networking public override bool SelectCrewCharacter(GUIComponent component, object obj) { + base.SelectCrewCharacter(component, obj); + var characterFrame = component.Parent.Parent.FindChild("selectedcharacter"); Character character = obj as Character; @@ -1424,13 +1429,13 @@ namespace Barotrauma.Networking if (character != myCharacter) { - var kickButton = new GUIButton(new Rectangle(0, 0, 100, 20), "Kick", Alignment.BottomLeft, GUI.Style, characterFrame); - kickButton.UserData = character.Name; - kickButton.OnClicked += GameMain.NetLobbyScreen.KickPlayer; - var banButton = new GUIButton(new Rectangle(0, 0, 100, 20), "Ban", Alignment.BottomRight, GUI.Style, characterFrame); banButton.UserData = character.Name; banButton.OnClicked += GameMain.NetLobbyScreen.BanPlayer; + + var kickButton = new GUIButton(new Rectangle(0, 0, 100, 20), "Kick", Alignment.BottomLeft, GUI.Style, characterFrame); + kickButton.UserData = character.Name; + kickButton.OnClicked += GameMain.NetLobbyScreen.KickPlayer; } return true; @@ -1808,6 +1813,10 @@ namespace Barotrauma.Networking public string version; public bool inGame; + + + private List kickVoters; + public bool ReadyToStart; private object[] votes; @@ -1823,6 +1832,11 @@ namespace Barotrauma.Networking public float deleteDisconnectedTimer; + public int KickVoteCount + { + get { return kickVoters.Count; } + } + public Client(NetPeer server, string name, byte ID) : this(name, ID) { @@ -1834,6 +1848,8 @@ namespace Barotrauma.Networking this.name = name; this.ID = ID; + kickVoters = new List(); + votes = new object[Enum.GetNames(typeof(VoteType)).Length]; jobPreferences = new List(JobPrefab.List.GetRange(0,3)); @@ -1857,6 +1873,26 @@ namespace Barotrauma.Networking } } + public void AddKickVote(Client voter) + { + if (!kickVoters.Contains(voter)) kickVoters.Add(voter); + } + + + public void RemoveKickVote(Client voter) + { + kickVoters.Remove(voter); + } + + + public static void UpdateKickVotes(List connectedClients) + { + foreach (Client client in connectedClients) + { + client.kickVoters.RemoveAll(voter => !connectedClients.Contains(voter)); + } + } + public void CancelTransfer() { if (FileStreamSender == null) return; diff --git a/Subsurface/Source/Networking/GameServerSettings.cs b/Subsurface/Source/Networking/GameServerSettings.cs index 764de02c0..d6298b495 100644 --- a/Subsurface/Source/Networking/GameServerSettings.cs +++ b/Subsurface/Source/Networking/GameServerSettings.cs @@ -95,6 +95,8 @@ namespace Barotrauma.Networking public float EndVoteRequiredRatio = 0.5f; + public float KickVoteRequiredRatio = 0.5f; + private void SaveSettings() { XmlWriterSettings settings = new XmlWriterSettings(); diff --git a/Subsurface/Source/Networking/NetworkMember.cs b/Subsurface/Source/Networking/NetworkMember.cs index 6d9a3ea0d..50a9ef27a 100644 --- a/Subsurface/Source/Networking/NetworkMember.cs +++ b/Subsurface/Source/Networking/NetworkMember.cs @@ -47,7 +47,8 @@ namespace Barotrauma.Networking Unknown, Sub, Mode, - EndRound + EndRound, + Kick } class NetworkMember @@ -351,7 +352,6 @@ namespace Barotrauma.Networking "Votes (y/n): " + EndVoteCount + "/" + (EndVoteMax - EndVoteCount), Color.White, null, 0, GUI.SmallFont); } } - } public virtual bool SelectCrewCharacter(GUIComponent component, object obj) diff --git a/Subsurface/Source/Screens/NetLobbyVoting.cs b/Subsurface/Source/Screens/NetLobbyVoting.cs index acafcfb9e..82b55f4e8 100644 --- a/Subsurface/Source/Screens/NetLobbyVoting.cs +++ b/Subsurface/Source/Screens/NetLobbyVoting.cs @@ -64,10 +64,12 @@ namespace Barotrauma { voteType = (VoteType)voteTypeByte; } - catch + catch (Exception e) { + DebugConsole.ThrowError("Failed to cast vote type ''"+voteTypeByte+"''", e); return; } + Client sender = connectedClients.Find(x => x.Connection == inc.SenderConnection); switch (voteType) { @@ -89,7 +91,17 @@ namespace Barotrauma sender.SetVote(voteType, inc.ReadBoolean()); GameMain.NetworkMember.EndVoteCount = connectedClients.Count(c => c.Character != null && c.GetVote(VoteType.EndRound)); - GameMain.NetworkMember.EndVoteMax = connectedClients.Count(c => c.Character != null); + GameMain.NetworkMember.EndVoteMax = connectedClients.Count(c => c.Character != null); + + break; + case VoteType.Kick: + + byte kickedClientID = inc.ReadByte(); + + Client kicked = connectedClients.Find(x => x.ID == kickedClientID); + if (kicked == null) return; + + kicked.AddKickVote(sender); break; }