Client LuaCsForBarotrauma
BarotraumaClient/ClientSource/Networking/Voting.cs
2 using Microsoft.Xna.Framework;
3 using System.Collections.Generic;
4 using System.Linq;
6 using System;
7 
8 namespace Barotrauma
9 {
10  partial class Voting
11  {
12  private struct SubmarineVoteInfo
13  {
14  public SubmarineInfo SubmarineInfo { get; set; }
15  public bool TransferItems { get; set; }
16 
17  public SubmarineVoteInfo(SubmarineInfo submarineInfo, bool transferItems)
18  {
19  SubmarineInfo = submarineInfo;
20  TransferItems = transferItems;
21  }
22  }
23 
24  private readonly Dictionary<VoteType, int>
25  voteCountYes = new Dictionary<VoteType, int>(),
26  voteCountNo = new Dictionary<VoteType, int>(),
27  voteCountMax = new Dictionary<VoteType, int>();
28 
29  public int GetVoteCountYes(VoteType voteType)
30  {
31  voteCountYes.TryGetValue(voteType, out int value);
32  return value;
33  }
34  public int GetVoteCountNo(VoteType voteType)
35  {
36  voteCountNo.TryGetValue(voteType, out int value);
37  return value;
38  }
39  public int GetVoteCountMax(VoteType voteType)
40  {
41  voteCountMax.TryGetValue(voteType, out int value);
42  return value;
43  }
44  public void SetVoteCountYes(VoteType voteType, int value)
45  {
46  voteCountYes[voteType] = value;
47  }
48  public void SetVoteCountNo(VoteType voteType, int value)
49  {
50  voteCountNo[voteType] = value;
51  }
52  public void SetVoteCountMax(VoteType voteType, int value)
53  {
54  voteCountMax[voteType] = value;
55  }
56 
57  public void UpdateVoteTexts(IEnumerable<Client> clients, VoteType voteType)
58  {
59  switch (voteType)
60  {
61  case VoteType.Sub:
62  case VoteType.Mode:
63  GUIListBox listBox = (voteType == VoteType.Sub) ?
65 
66  foreach (GUIComponent comp in listBox.Content.Children)
67  {
68  if (comp.FindChild("votes") is GUITextBlock voteText) { comp.RemoveChild(voteText); }
69  }
70 
71  if (clients == null) { return; }
72 
73  IReadOnlyDictionary<object, int> voteList = GetVoteCounts<object>(voteType, clients);
74  foreach (KeyValuePair<object, int> votable in voteList)
75  {
76  SetVoteText(listBox, votable.Key, votable.Value);
77  }
78  break;
79  case VoteType.StartRound:
80  if (clients == null) { return; }
81  foreach (Client client in clients)
82  {
83  var clientReady = GameMain.NetLobbyScreen.PlayerList.Content.FindChild(client)?.FindChild("clientready");
84  if (clientReady != null)
85  {
86  clientReady.Visible = client.GetVote<bool>(VoteType.StartRound);
87  }
88  }
89  break;
90  }
91  }
92 
93  private void SetVoteText(GUIListBox listBox, object userData, int votes)
94  {
95  if (userData == null) { return; }
96  foreach (GUIComponent comp in listBox.Content.Children)
97  {
98  if (comp.UserData != userData) { continue; }
99  if (comp.FindChild("votes") is not GUITextBlock voteText)
100  {
101  voteText = new GUITextBlock(new RectTransform(new Point(GUI.IntScale(30), comp.Rect.Height), comp.RectTransform, Anchor.CenterRight),
102  "", textAlignment: Alignment.Center)
103  {
104  Padding = Vector4.Zero,
105  UserData = "votes"
106  };
107  }
108  voteText.Text = votes == 0 ? "" : votes.ToString();
109  }
110  }
111 
112  public void ResetVotes(IEnumerable<Client> connectedClients)
113  {
114  foreach (Client client in connectedClients)
115  {
116  client.ResetVotes();
117  }
118 
119  foreach (VoteType voteType in Enum.GetValues(typeof(VoteType)))
120  {
121  SetVoteCountYes(voteType, 0);
122  SetVoteCountNo(voteType, 0);
123  SetVoteCountMax(voteType, 0);
124  }
125  UpdateVoteTexts(connectedClients, VoteType.Mode);
126  UpdateVoteTexts(connectedClients, VoteType.Sub);
127  }
128 
134  public bool ClientWrite(IWriteMessage msg, VoteType voteType, object data)
135  {
136  msg.WriteByte((byte)voteType);
137 
138  switch (voteType)
139  {
140  case VoteType.Sub:
141  if (data is not SubmarineInfo sub) { return false; }
142  msg.WriteInt32(sub.EqualityCheckVal);
143  if (sub.EqualityCheckVal <= 0)
144  {
145  //sub doesn't exist client-side, use hash to let the server know which one we voted for
146  msg.WriteString(sub.MD5Hash.StringRepresentation);
147  }
148  break;
149  case VoteType.Mode:
150  if (data is not GameModePreset gameMode) { return false; }
151  msg.WriteIdentifier(gameMode.Identifier);
152  break;
153  case VoteType.EndRound:
154  if (data is not bool endRound) { return false; }
155  msg.WriteBoolean(endRound);
156  break;
157  case VoteType.Kick:
158  if (data is not Client votedClient) { return false; }
159 
160  msg.WriteByte(votedClient.SessionId);
161  break;
162  case VoteType.StartRound:
163  if (data is not bool startRound) { return false; }
164  msg.WriteBoolean(startRound);
165  break;
166  case VoteType.PurchaseAndSwitchSub:
167  case VoteType.PurchaseSub:
168  case VoteType.SwitchSub:
169  switch (data)
170  {
171  case (SubmarineInfo voteSub, bool transferItems):
172  //initiate sub vote
173  msg.WriteBoolean(true);
174  msg.WriteString(voteSub.Name);
175  msg.WriteBoolean(transferItems);
176  break;
177  case int vote:
178  // vote
179  msg.WriteBoolean(false);
180  msg.WriteInt32(vote);
181  break;
182  default:
183  return false;
184  }
185  break;
186  case VoteType.TransferMoney:
187  if (data is not int money) { return false; }
188  msg.WriteBoolean(false); //not initiating a vote
189  msg.WriteInt32(money);
190  break;
191  case VoteType.Traitor:
192  //use 0 to indicate we voted for no-one
193  msg.WriteInt32((data as Client)?.SessionId ?? 0);
194  break;
195  }
196 
197  msg.WritePadBits();
198  return true;
199  }
200 
201  public void ClientRead(IReadMessage inc)
202  {
203  GameMain.Client.ServerSettings.AllowSubVoting = inc.ReadBoolean();
204  if (GameMain.Client.ServerSettings.AllowSubVoting)
205  {
206  UpdateVoteTexts(null, VoteType.Sub);
207  int votableCount = inc.ReadByte();
208  for (int i = 0; i < votableCount; i++)
209  {
210  int votes = inc.ReadByte();
211  string subName = inc.ReadString();
212  List<SubmarineInfo> serversubs = new List<SubmarineInfo>();
213  if (GameMain.NetLobbyScreen?.SubList?.Content != null)
214  {
216  {
217  if (item.UserData != null && item.UserData is SubmarineInfo) { serversubs.Add(item.UserData as SubmarineInfo); }
218  }
219  }
220  SubmarineInfo sub = serversubs.FirstOrDefault(s => s.Name == subName);
221  SetVoteText(GameMain.NetLobbyScreen.SubList, sub, votes);
222  }
223  }
224  GameMain.Client.ServerSettings.AllowModeVoting = inc.ReadBoolean();
225  if (GameMain.Client.ServerSettings.AllowModeVoting)
226  {
227  UpdateVoteTexts(null, VoteType.Mode);
228  int votableCount = inc.ReadByte();
229  for (int i = 0; i < votableCount; i++)
230  {
231  int votes = inc.ReadByte();
232  string modeIdentifier = inc.ReadString();
233  GameModePreset mode = GameModePreset.List.Find(m => m.Identifier == modeIdentifier);
234  SetVoteText(GameMain.NetLobbyScreen.ModeList, mode, votes);
235  }
236  }
237  GameMain.Client.ServerSettings.AllowEndVoting = inc.ReadBoolean();
238  if (GameMain.Client.ServerSettings.AllowEndVoting)
239  {
240  SetVoteCountYes(VoteType.EndRound, inc.ReadByte());
241  SetVoteCountMax(VoteType.EndRound, inc.ReadByte());
242  }
243  GameMain.Client.ServerSettings.AllowVoteKick = inc.ReadBoolean();
244 
245  byte activeVoteStateByte = inc.ReadByte();
246 
247  VoteState activeVoteState = VoteState.None;
248  try { activeVoteState = (VoteState)activeVoteStateByte; }
249  catch (System.Exception e)
250  {
251  DebugConsole.ThrowError("Failed to cast vote type \"" + activeVoteStateByte + "\"", e);
252  }
253 
254  if (activeVoteState != VoteState.None)
255  {
256  byte voteTypeByte = inc.ReadByte();
257  VoteType voteType = VoteType.Unknown;
258  try { voteType = (VoteType)voteTypeByte; }
259  catch (System.Exception e)
260  {
261  DebugConsole.ThrowError("Failed to cast vote type \"" + voteTypeByte + "\"", e);
262  }
263 
264  int readVote(int value)
265  {
266  byte clientCount = inc.ReadByte();
267  for (int i = 0; i < clientCount; i++)
268  {
269  byte clientId = inc.ReadByte();
270  var matchingClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.SessionId == clientId);
271  matchingClient?.SetVote(voteType, value);
272  }
273 
274  return clientCount;
275  }
276 
277  int yesClientCount = readVote(value: 2);
278  int noClientCount = readVote(value: 1);
279 
280  byte maxClientCount = inc.ReadByte();
281 
282  SetVoteCountYes(voteType, yesClientCount);
283  SetVoteCountNo(voteType, noClientCount);
284  SetVoteCountMax(voteType, maxClientCount);
285 
286  switch (activeVoteState)
287  {
288  case VoteState.Started:
289  byte starterID = inc.ReadByte();
290  Client starterClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.SessionId == starterID);
291  float timeOut = inc.ReadByte();
292 
293  Client myClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.SessionId == GameMain.Client.SessionId);
294  if (myClient == null || !myClient.InGame) { return; }
295 
296  switch (voteType)
297  {
298  case VoteType.PurchaseSub:
299  case VoteType.PurchaseAndSwitchSub:
300  case VoteType.SwitchSub:
301  string subName1 = inc.ReadString();
302  bool transferItems = inc.ReadBoolean();
303  SubmarineInfo info = GameMain.GameSession.OwnedSubmarines.FirstOrDefault(s => s.Name == subName1) ?? GameMain.Client.ServerSubmarines.FirstOrDefault(s => s.Name == subName1);
304  if (info == null)
305  {
306  DebugConsole.ThrowError("Failed to find a matching submarine, vote aborted");
307  return;
308  }
309  GameMain.Client.ShowSubmarineChangeVoteInterface(starterClient, info, voteType, transferItems, timeOut);
310  break;
311  case VoteType.TransferMoney:
312  byte fromClientId = inc.ReadByte();
313  byte toClientId = inc.ReadByte();
314  int transferAmount = inc.ReadInt32();
315 
316  Client fromClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.SessionId == fromClientId);
317  Client toClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.SessionId == toClientId);
318  GameMain.Client.ShowMoneyTransferVoteInterface(starterClient, fromClient, transferAmount, toClient, timeOut);
319  break;
320  }
321  break;
322  case VoteState.Running:
323  // Nothing specific
324  break;
325  case VoteState.Passed:
326  case VoteState.Failed:
327  bool passed = inc.ReadBoolean();
328  SubmarineVoteInfo submarineVoteInfo = default;
329  switch (voteType)
330  {
331  case VoteType.PurchaseSub:
332  case VoteType.PurchaseAndSwitchSub:
333  case VoteType.SwitchSub:
334  string subName2 = inc.ReadString();
335  bool transferItems = inc.ReadBoolean();
336  if (GameMain.GameSession != null)
337  {
338  var submarineInfo = GameMain.GameSession.OwnedSubmarines.FirstOrDefault(s => s.Name == subName2) ?? GameMain.Client.ServerSubmarines.FirstOrDefault(s => s.Name == subName2);
339  if (submarineInfo == null)
340  {
341  DebugConsole.ThrowError("Failed to find a matching submarine, vote aborted");
342  return;
343  }
344  submarineVoteInfo = new SubmarineVoteInfo(submarineInfo, transferItems);
345  }
346  break;
347  }
348 
349  GameMain.Client.VotingInterface?.EndVote(passed, yesClientCount, noClientCount);
350  if (passed && submarineVoteInfo.SubmarineInfo is { } subInfo)
351  {
352  switch (voteType)
353  {
354  case VoteType.PurchaseAndSwitchSub:
356  {
357  GameMain.GameSession.SwitchSubmarine(subInfo, submarineVoteInfo.TransferItems);
358  }
359  break;
360  case VoteType.PurchaseSub:
362  break;
363  case VoteType.SwitchSub:
364  GameMain.GameSession.SwitchSubmarine(subInfo, submarineVoteInfo.TransferItems);
365  break;
366  }
367 
369  }
370  break;
371  }
372  }
373 
374  GameMain.NetworkMember.ConnectedClients.ForEach(c => c.SetVote(VoteType.StartRound, false));
375  byte readyClientCount = inc.ReadByte();
376  for (int i = 0; i < readyClientCount; i++)
377  {
378  byte clientId = inc.ReadByte();
379  var matchingClient = GameMain.NetworkMember.ConnectedClients.Find(c => c.SessionId == clientId);
380  matchingClient?.SetVote(VoteType.StartRound, true);
381  }
382  UpdateVoteTexts(GameMain.NetworkMember.ConnectedClients, VoteType.StartRound);
383 
384  inc.ReadPadBits();
385  }
386  }
387 }
GUIComponent FindChild(Func< GUIComponent, bool > predicate, bool recursive=false)
Definition: GUIComponent.cs:95
IEnumerable< GUIComponent > Children
Definition: GUIComponent.cs:29
override void RemoveChild(GUIComponent child)
Definition: GUIListBox.cs:1249
GUIFrame Content
A frame that contains the contents of the listbox. The frame itself is not rendered.
Definition: GUIListBox.cs:33
static GameSession?? GameSession
Definition: GameMain.cs:88
static NetLobbyScreen NetLobbyScreen
Definition: GameMain.cs:55
static NetworkMember NetworkMember
Definition: GameMain.cs:190
static GameClient Client
Definition: GameMain.cs:188
static List< GameModePreset > List
bool TryPurchaseSubmarine(SubmarineInfo newSubmarine, Client? client=null)
void SwitchSubmarine(SubmarineInfo newSubmarine, bool transferItems, Client? client=null)
Switch to another submarine. The sub is loaded when the next round starts.
readonly List< SubmarineInfo > ServerSubmarines
Definition: GameClient.cs:94
void ShowMoneyTransferVoteInterface(Client starter, Client from, int amount, Client to, float timeOut)
Definition: GameClient.cs:2728
void ShowSubmarineChangeVoteInterface(Client starter, SubmarineInfo info, VoteType type, bool transferItems, float timeOut)
Definition: GameClient.cs:2718
void SetVoteCountMax(VoteType voteType, int value)
void ResetVotes(IEnumerable< Client > connectedClients)
bool ClientWrite(IWriteMessage msg, VoteType voteType, object data)
Returns true if the given data is valid for the given vote type, returns false otherwise....
void SetVoteCountYes(VoteType voteType, int value)
void UpdateVoteTexts(IEnumerable< Client > clients, VoteType voteType)
void SetVoteCountNo(VoteType voteType, int value)
void EndVote(bool passed, int yesVoteFinal, int noVoteFinal)
void WriteIdentifier(Identifier val)