Client LuaCsForBarotrauma
LidgrenClientPeer.cs
1 #nullable enable
2 using System;
3 using System.Collections.Generic;
4 using System.Collections.Immutable;
5 using System.Linq;
6 using System.Text;
7 using System.Threading.Tasks;
9 using Lidgren.Network;
10 using Barotrauma.Steam;
11 using System.Net.Sockets;
12 
13 namespace Barotrauma.Networking
14 {
15  internal sealed class LidgrenClientPeer : ClientPeer<LidgrenEndpoint>
16  {
17  private NetClient? netClient;
18  private readonly NetPeerConfiguration netPeerConfiguration;
19 
20  private readonly List<NetIncomingMessage> incomingLidgrenMessages;
21 
22  public LidgrenClientPeer(LidgrenEndpoint endpoint, Callbacks callbacks, Option<int> ownerKey) : base(endpoint, ((Endpoint)endpoint).ToEnumerable().ToImmutableArray(), callbacks, ownerKey)
23  {
24  ServerConnection = null;
25 
26  netClient = null;
27  isActive = false;
28 
29  netPeerConfiguration = new NetPeerConfiguration("barotrauma")
30  {
31  DualStack = GameSettings.CurrentConfig.UseDualModeSockets
32  };
33  if (endpoint.NetEndpoint.Address.AddressFamily == AddressFamily.InterNetworkV6)
34  {
35  netPeerConfiguration.LocalAddress = System.Net.IPAddress.IPv6Any;
36  }
37 
38  netPeerConfiguration.DisableMessageType(
39  NetIncomingMessageType.DebugMessage
40  | NetIncomingMessageType.WarningMessage
41  | NetIncomingMessageType.Receipt
42  | NetIncomingMessageType.ErrorMessage
43  | NetIncomingMessageType.Error);
44 
45  incomingLidgrenMessages = new List<NetIncomingMessage>();
46  }
47 
48  public override void Start()
49  {
50  if (isActive) { return; }
51 
52  incomingLidgrenMessages.Clear();
53 
54  ContentPackageOrderReceived = false;
55 
56  netClient = new NetClient(netPeerConfiguration);
57 
58  initializationStep = ConnectionInitialization.AuthInfoAndVersion;
59 
60  if (ServerEndpoint is not { } lidgrenEndpointValue)
61  {
62  throw new InvalidCastException($"Endpoint is not {nameof(LidgrenEndpoint)}");
63  }
64 
65  if (ServerConnection != null)
66  {
67  throw new InvalidOperationException("ServerConnection is not null");
68  }
69 
70  netClient.Start();
71 
72  TaskPool.Add(
73  $"{nameof(LidgrenClientPeer)}.GetAuthTicket",
74  AuthenticationTicket.Create(ServerEndpoint),
75  t =>
76  {
77  if (!t.TryGetResult(out Option<AuthenticationTicket> authenticationTicket))
78  {
79  Close(PeerDisconnectPacket.WithReason(DisconnectReason.AuthenticationFailed));
80  return;
81  }
82  authTicket = authenticationTicket;
83 
84  var netConnection = netClient.Connect(lidgrenEndpointValue.NetEndpoint);
85 
86  ServerConnection = new LidgrenConnection(netConnection)
87  {
88  Status = NetworkConnectionStatus.Connected
89  };
90  });
91 
92  isActive = true;
93  }
94 
95  public override void Update(float deltaTime)
96  {
97  if (!isActive) { return; }
98 
99  ToolBox.ThrowIfNull(netClient);
100  ToolBox.ThrowIfNull(incomingLidgrenMessages);
101 
102  if (IsOwner && !ChildServerRelay.IsProcessAlive)
103  {
104  var gameClient = GameMain.Client;
105  Close(PeerDisconnectPacket.WithReason(DisconnectReason.ServerCrashed));
106  gameClient?.CreateServerCrashMessage();
107  return;
108  }
109 
110  incomingLidgrenMessages.Clear();
111  netClient.ReadMessages(incomingLidgrenMessages);
112 
113  GameMain.Client?.NetStats?.AddValue(NetStats.NetStatType.ReceivedBytes, netClient.Statistics.ReceivedBytes);
114  GameMain.Client?.NetStats?.AddValue(NetStats.NetStatType.SentBytes, netClient.Statistics.SentBytes);
115 
116  foreach (NetIncomingMessage inc in incomingLidgrenMessages)
117  {
118  var remoteEndpoint = new LidgrenEndpoint(inc.SenderEndPoint);
119 
120  if (remoteEndpoint != ServerEndpoint)
121  {
122  DebugConsole.AddWarning($"Mismatched endpoint: expected {ServerEndpoint.NetEndpoint}, got {inc.SenderConnection.RemoteEndPoint}");
123  continue;
124  }
125 
126  switch (inc.MessageType)
127  {
128  case NetIncomingMessageType.Data:
129  HandleDataMessage(inc);
130  break;
131  case NetIncomingMessageType.StatusChanged:
132  HandleStatusChanged(inc);
133  break;
134  }
135  }
136  }
137 
138  private void HandleDataMessage(NetIncomingMessage lidgrenMsg)
139  {
140  if (!isActive) { return; }
141 
142  ToolBox.ThrowIfNull(ServerConnection);
143 
144  IReadMessage inc = lidgrenMsg.ToReadMessage();
145 
146  var (_, packetHeader, initialization) = INetSerializableStruct.Read<PeerPacketHeaders>(inc);
147 
148  if (packetHeader.IsConnectionInitializationStep())
149  {
150  if (initializationStep == ConnectionInitialization.Success) { return; }
151 
152  ReadConnectionInitializationStep(new IncomingInitializationMessage
153  {
154  InitializationStep = initialization ?? throw new Exception("Initialization step missing"),
155  Message = inc
156  });
157  }
158  else
159  {
160  OnInitializationComplete();
161 
162  var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
163  callbacks.OnMessageReceived.Invoke(packet.GetReadMessage(packetHeader.IsCompressed(), ServerConnection));
164  }
165  }
166 
167  private void HandleStatusChanged(NetIncomingMessage inc)
168  {
169  if (!isActive) { return; }
170 
171  NetConnectionStatus status = inc.ReadHeader<NetConnectionStatus>();
172  switch (status)
173  {
174  case NetConnectionStatus.Disconnected:
175  string disconnectMsg = inc.ReadString();
176  var peerDisconnectPacket =
177  PeerDisconnectPacket.FromLidgrenStringRepresentation(disconnectMsg);
178  Close(peerDisconnectPacket.Fallback(PeerDisconnectPacket.WithReason(DisconnectReason.Unknown)));
179  break;
180  }
181  }
182 
183  public override void SendPassword(string password)
184  {
185  if (!isActive) { return; }
186 
187  ToolBox.ThrowIfNull(netClient);
188 
189  if (initializationStep != ConnectionInitialization.Password) { return; }
190 
191  var headers = new PeerPacketHeaders
192  {
193  DeliveryMethod = DeliveryMethod.Reliable,
194  PacketHeader = PacketHeader.IsConnectionInitializationStep,
195  Initialization = ConnectionInitialization.Password
196  };
197  var body = new ClientPeerPasswordPacket
198  {
199  Password = ServerSettings.SaltPassword(Encoding.UTF8.GetBytes(password), passwordSalt)
200  };
201 
202  SendMsgInternal(headers, body);
203  }
204 
205  public override void Close(PeerDisconnectPacket peerDisconnectPacket)
206  {
207  if (!isActive) { return; }
208 
209  ToolBox.ThrowIfNull(netClient);
210 
211  isActive = false;
212 
213  netClient.Shutdown(peerDisconnectPacket.ToLidgrenStringRepresentation());
214  netClient = null;
215 
216  callbacks.OnDisconnect.Invoke(peerDisconnectPacket);
217  }
218 
219  public override void Send(IWriteMessage msg, DeliveryMethod deliveryMethod, bool compressPastThreshold = true)
220  {
221  if (!isActive) { return; }
222 
223  ToolBox.ThrowIfNull(netClient);
224  ToolBox.ThrowIfNull(netPeerConfiguration);
225 
226 #if DEBUG
227  if (GameMain.Client != null)
228  {
229  netPeerConfiguration.SimulatedDuplicatesChance = GameMain.Client.SimulatedDuplicatesChance;
230  netPeerConfiguration.SimulatedMinimumLatency = GameMain.Client.SimulatedMinimumLatency;
231  netPeerConfiguration.SimulatedRandomLatency = GameMain.Client.SimulatedRandomLatency;
232  netPeerConfiguration.SimulatedLoss = GameMain.Client.SimulatedLoss;
233  }
234 #endif
235 
236  byte[] bufAux = msg.PrepareForSending(compressPastThreshold, out bool isCompressed, out _);
237 
238  var headers = new PeerPacketHeaders
239  {
240  DeliveryMethod = deliveryMethod,
241  PacketHeader = isCompressed ? PacketHeader.IsCompressed : PacketHeader.None,
242  Initialization = null
243  };
244  var body = new PeerPacketMessage
245  {
246  Buffer = bufAux
247  };
248 
249  SendMsgInternal(headers, body);
250  }
251 
252  protected override void SendMsgInternal(PeerPacketHeaders headers, INetSerializableStruct? body)
253  {
254  ToolBox.ThrowIfNull(netClient);
255 
256  IWriteMessage msg = new WriteOnlyMessage();
257  msg.WriteNetSerializableStruct(headers);
258  body?.Write(msg);
259 
260  NetSendResult result = ForwardToLidgren(msg, DeliveryMethod.Reliable);
261  if (result != NetSendResult.Queued && result != NetSendResult.Sent)
262  {
263  DebugConsole.NewMessage($"Failed to send message to host: {result}\n{Environment.StackTrace}");
264  }
265  }
266 
267  private NetSendResult ForwardToLidgren(IWriteMessage msg, DeliveryMethod deliveryMethod)
268  {
269  ToolBox.ThrowIfNull(netClient);
270 
271  return netClient.SendMessage(msg.ToLidgren(netClient), deliveryMethod.ToLidgren());
272  }
273 
274  protected override async Task<Option<AccountId>> GetAccountId()
275  {
276  if (!EosInterface.Core.IsInitialized) { return SteamManager.GetSteamId().Select(id => (AccountId)id); }
277 
278  var selfPuids = EosInterface.IdQueries.GetLoggedInPuids();
279  if (selfPuids.None()) { return Option.None; }
280  var accountIdsResult = await EosInterface.IdQueries.GetSelfExternalAccountIds(selfPuids.First());
281  return accountIdsResult.TryUnwrapSuccess(out var accountIds) && accountIds.Length > 0
282  ? Option.Some(accountIds[0])
283  : Option.None;
284  }
285 
286 #if DEBUG
287 
288 
289  public override void ForceTimeOut()
290  {
291  netClient?.ServerConnection?.ForceTimeOut();
292  }
293 
294  public override void DebugSendRawMessage(IWriteMessage msg)
295  => ForwardToLidgren(msg, DeliveryMethod.Reliable);
296 #endif
297  }
298 }