Client LuaCsForBarotrauma
ClientPeer.cs
1 #nullable enable
2 using System.Collections.Immutable;
3 using System.Linq;
4 using System.Threading.Tasks;
5 using Microsoft.Xna.Framework;
6 
7 namespace Barotrauma.Networking
8 {
9  internal abstract class ClientPeer<TEndpoint> : ClientPeer where TEndpoint : Endpoint
10  {
11  public new TEndpoint ServerEndpoint => (base.ServerEndpoint as TEndpoint)!;
12  protected ClientPeer(TEndpoint serverEndpoint, ImmutableArray<Endpoint> allServerEndpoints, Callbacks callbacks, Option<int> ownerKey)
13  : base(serverEndpoint, allServerEndpoints, callbacks, ownerKey) { }
14  }
15 
16  internal abstract class ClientPeer
17  {
18  public ImmutableArray<ServerContentPackage> ServerContentPackages { get; set; } =
19  ImmutableArray<ServerContentPackage>.Empty;
20 
21  public bool AllowModDownloads { get; private set; } = true;
22 
23  public string AutomaticallyAttemptedPassword = string.Empty;
24 
25  public readonly record struct Callbacks(
26  Callbacks.MessageCallback OnMessageReceived,
27  Callbacks.DisconnectCallback OnDisconnect,
28  Callbacks.InitializationCompleteCallback OnInitializationComplete)
29  {
30  public delegate void MessageCallback(IReadMessage message);
31  public delegate void DisconnectCallback(PeerDisconnectPacket disconnectPacket);
32  public delegate void InitializationCompleteCallback();
33  }
34 
35  protected readonly Callbacks callbacks;
36 
37  public readonly Endpoint ServerEndpoint;
38  public readonly ImmutableArray<Endpoint> AllServerEndpoints;
39  public NetworkConnection? ServerConnection { get; protected set; }
40 
41  protected bool IsOwner => ownerKey.IsSome();
42  protected readonly Option<int> ownerKey;
43 
44  public bool IsActive => isActive;
45 
46  protected bool isActive;
47 
48  protected ClientPeer(Endpoint serverEndpoint, ImmutableArray<Endpoint> allServerEndpoints, Callbacks callbacks, Option<int> ownerKey)
49  {
50  ServerEndpoint = serverEndpoint;
51  AllServerEndpoints = allServerEndpoints;
52  this.callbacks = callbacks;
53  this.ownerKey = ownerKey;
54  }
55 
56  public abstract void Start();
57  public abstract void Close(PeerDisconnectPacket peerDisconnectPacket);
58  public abstract void Update(float deltaTime);
59  public abstract void Send(IWriteMessage msg, DeliveryMethod deliveryMethod, bool compressPastThreshold = true);
60  public abstract void SendPassword(string password);
61 
62  protected abstract void SendMsgInternal(PeerPacketHeaders headers, INetSerializableStruct? body);
63 
64  protected ConnectionInitialization initializationStep;
65  public bool ContentPackageOrderReceived { get; set; }
66  protected int passwordSalt;
67  protected Option<AuthenticationTicket> authTicket;
68  private GUIMessageBox? passwordMsgBox;
69 
70  public bool WaitingForPassword
71  => isActive && initializationStep == ConnectionInitialization.Password
72  && passwordMsgBox != null
73  && GUIMessageBox.MessageBoxes.Contains(passwordMsgBox);
74 
76  {
77  public ConnectionInitialization InitializationStep;
79  }
80 
81  protected abstract Task<Option<AccountId>> GetAccountId();
82 
83  protected void OnInitializationComplete()
84  {
85  passwordMsgBox?.Close();
86  if (initializationStep == ConnectionInitialization.Success) { return; }
87 
88  callbacks.OnInitializationComplete.Invoke();
89  initializationStep = ConnectionInitialization.Success;
90  }
91 
92  protected void ReadConnectionInitializationStep(IncomingInitializationMessage inc)
93  {
94  if (inc.InitializationStep != ConnectionInitialization.Password)
95  {
96  passwordMsgBox?.Close();
97  }
98 
99  switch (inc.InitializationStep)
100  {
101  case ConnectionInitialization.AuthInfoAndVersion:
102  {
103  if (initializationStep != ConnectionInitialization.AuthInfoAndVersion) { return; }
104 
105  TaskPool.Add($"{GetType().Name}.{nameof(GetAccountId)}", GetAccountId(), t =>
106  {
107  // FIXME what to do with this?
108  //if (GameMain.Client?.ClientPeer is null) { return; }
109 
110  if (!t.TryGetResult(out Option<AccountId> accountId))
111  {
112  Close(PeerDisconnectPacket.WithReason(DisconnectReason.AuthenticationFailed));
113  }
114 
115  var headers = new PeerPacketHeaders
116  {
117  DeliveryMethod = DeliveryMethod.Reliable,
118  PacketHeader = PacketHeader.IsConnectionInitializationStep,
119  Initialization = ConnectionInitialization.AuthInfoAndVersion
120  };
121 
122  var body = new ClientAuthTicketAndVersionPacket
123  {
124  Name = GameMain.Client?.Name ?? "Unknown",
125  OwnerKey = ownerKey,
126  AccountId = accountId,
127  AuthTicket = authTicket,
128  GameVersion = GameMain.Version.ToString(),
129  Language = GameSettings.CurrentConfig.Language.Value
130  };
131 
132  SendMsgInternal(headers, body);
133  });
134  break;
135  }
136  case ConnectionInitialization.ContentPackageOrder:
137  {
138  if (initializationStep
139  is ConnectionInitialization.AuthInfoAndVersion
140  or ConnectionInitialization.Password)
141  {
142  initializationStep = ConnectionInitialization.ContentPackageOrder;
143  }
144 
145  if (initializationStep != ConnectionInitialization.ContentPackageOrder) { return; }
146 
147  PeerPacketHeaders headers = new PeerPacketHeaders
148  {
149  DeliveryMethod = DeliveryMethod.Reliable,
150  PacketHeader = PacketHeader.IsConnectionInitializationStep,
151  Initialization = ConnectionInitialization.ContentPackageOrder
152  };
153 
154  var orderPacket = INetSerializableStruct.Read<ServerPeerContentPackageOrderPacket>(inc.Message);
155 
156  if (!ContentPackageOrderReceived)
157  {
158  ServerContentPackages = orderPacket.ContentPackages;
159  AllowModDownloads = orderPacket.AllowModDownloads;
160  if (ServerContentPackages.Length == 0)
161  {
162  string errorMsg = "Error in ContentPackageOrder message: list of content packages enabled on the server was empty.";
163  GameAnalyticsManager.AddErrorEventOnce("ClientPeer.ReadConnectionInitializationStep:NoContentPackages", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
164  DebugConsole.ThrowError(errorMsg);
165  }
166  ContentPackageOrderReceived = true;
167  }
168  SendMsgInternal(headers, null);
169 
170  break;
171  }
172  case ConnectionInitialization.Password:
173  if (initializationStep == ConnectionInitialization.AuthInfoAndVersion)
174  {
175  initializationStep = ConnectionInitialization.Password;
176  }
177 
178  if (initializationStep != ConnectionInitialization.Password) { return; }
179 
180  var passwordPacket = INetSerializableStruct.Read<ServerPeerPasswordPacket>(inc.Message);
181 
182  if (WaitingForPassword) { return; }
183 
184  passwordPacket.Salt.TryUnwrap(out passwordSalt);
185  passwordPacket.RetriesLeft.TryUnwrap(out var retries);
186 
187  if (!string.IsNullOrWhiteSpace(AutomaticallyAttemptedPassword))
188  {
189  SendPassword(AutomaticallyAttemptedPassword);
190  return;
191  }
192 
193  LocalizedString pwMsg = TextManager.Get("PasswordRequired");
194 
195  passwordMsgBox?.Close();
196  passwordMsgBox = new GUIMessageBox(pwMsg, "", new LocalizedString[] { TextManager.Get("OK"), TextManager.Get("Cancel") },
197  relativeSize: new Vector2(0.25f, 0.1f), minSize: new Point(400, GUI.IntScale(170)));
198  var passwordHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), passwordMsgBox.Content.RectTransform), childAnchor: Anchor.TopCenter);
199  var passwordBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1f), passwordHolder.RectTransform) { MinSize = new Point(0, 20) })
200  {
201  Censor = true
202  };
203 
204  if (retries > 0)
205  {
206  var incorrectPasswordText = new GUITextBlock(new RectTransform(new Vector2(1f, 0.0f), passwordHolder.RectTransform), TextManager.Get("incorrectpassword"), GUIStyle.Red, GUIStyle.Font, textAlignment: Alignment.Center);
207  incorrectPasswordText.RectTransform.MinSize = new Point(0, (int)incorrectPasswordText.TextSize.Y);
208  passwordHolder.Recalculate();
209  }
210 
211  passwordMsgBox.Content.Recalculate();
212  passwordMsgBox.Content.RectTransform.MinSize = new Point(0, passwordMsgBox.Content.RectTransform.Children.Sum(c => c.Rect.Height));
213  passwordMsgBox.Content.Parent.RectTransform.MinSize = new Point(0, (int)(passwordMsgBox.Content.RectTransform.MinSize.Y / passwordMsgBox.Content.RectTransform.RelativeSize.Y));
214 
215  var okButton = passwordMsgBox.Buttons[0];
216  okButton.OnClicked += (_, __) =>
217  {
218  SendPassword(passwordBox.Text);
219  return true;
220  };
221  okButton.OnClicked += passwordMsgBox.Close;
222 
223  var cancelButton = passwordMsgBox.Buttons[1];
224  cancelButton.OnClicked = (_, __) =>
225  {
226  Close(PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
227  passwordMsgBox?.Close(); passwordMsgBox = null;
228 
229  return true;
230  };
231 
232  passwordBox.OnEnterPressed += (_, __) =>
233  {
234  okButton.OnClicked.Invoke(okButton, okButton.UserData);
235  return true;
236  };
237 
238  passwordBox.Select();
239 
240  break;
241  }
242  }
243 
244 #if DEBUG
245  public abstract void ForceTimeOut();
246 
247  public abstract void DebugSendRawMessage(IWriteMessage msg);
248 #endif
249  }
250 }