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