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  netPeerConfiguration.SimulatedDuplicatesChance = GameMain.Client.SimulatedDuplicatesChance;
228  netPeerConfiguration.SimulatedMinimumLatency = GameMain.Client.SimulatedMinimumLatency;
229  netPeerConfiguration.SimulatedRandomLatency = GameMain.Client.SimulatedRandomLatency;
230  netPeerConfiguration.SimulatedLoss = GameMain.Client.SimulatedLoss;
231 #endif
232 
233  byte[] bufAux = msg.PrepareForSending(compressPastThreshold, out bool isCompressed, out _);
234 
235  var headers = new PeerPacketHeaders
236  {
237  DeliveryMethod = deliveryMethod,
238  PacketHeader = isCompressed ? PacketHeader.IsCompressed : PacketHeader.None,
239  Initialization = null
240  };
241  var body = new PeerPacketMessage
242  {
243  Buffer = bufAux
244  };
245 
246  SendMsgInternal(headers, body);
247  }
248 
249  protected override void SendMsgInternal(PeerPacketHeaders headers, INetSerializableStruct? body)
250  {
251  ToolBox.ThrowIfNull(netClient);
252 
253  IWriteMessage msg = new WriteOnlyMessage();
254  msg.WriteNetSerializableStruct(headers);
255  body?.Write(msg);
256 
257  NetSendResult result = ForwardToLidgren(msg, DeliveryMethod.Reliable);
258  if (result != NetSendResult.Queued && result != NetSendResult.Sent)
259  {
260  DebugConsole.NewMessage($"Failed to send message to host: {result}\n{Environment.StackTrace}");
261  }
262  }
263 
264  private NetSendResult ForwardToLidgren(IWriteMessage msg, DeliveryMethod deliveryMethod)
265  {
266  ToolBox.ThrowIfNull(netClient);
267 
268  return netClient.SendMessage(msg.ToLidgren(netClient), deliveryMethod.ToLidgren());
269  }
270 
271  protected override async Task<Option<AccountId>> GetAccountId()
272  {
273  if (!EosInterface.Core.IsInitialized) { return SteamManager.GetSteamId().Select(id => (AccountId)id); }
274 
275  var selfPuids = EosInterface.IdQueries.GetLoggedInPuids();
276  if (selfPuids.None()) { return Option.None; }
277  var accountIdsResult = await EosInterface.IdQueries.GetSelfExternalAccountIds(selfPuids.First());
278  return accountIdsResult.TryUnwrapSuccess(out var accountIds) && accountIds.Length > 0
279  ? Option.Some(accountIds[0])
280  : Option.None;
281  }
282 
283 #if DEBUG
284 
285 
286  public override void ForceTimeOut()
287  {
288  netClient?.ServerConnection?.ForceTimeOut();
289  }
290 
291  public override void DebugSendRawMessage(IWriteMessage msg)
292  => ForwardToLidgren(msg, DeliveryMethod.Reliable);
293 #endif
294  }
295 }