3 using System.Collections.Generic;
4 using System.Collections.Immutable;
7 using System.Threading.Tasks;
11 using System.Net.Sockets;
15 internal sealed
class LidgrenClientPeer : ClientPeer<LidgrenEndpoint>
17 private NetClient? netClient;
18 private readonly NetPeerConfiguration netPeerConfiguration;
20 private readonly List<NetIncomingMessage> incomingLidgrenMessages;
22 public LidgrenClientPeer(LidgrenEndpoint endpoint, Callbacks callbacks, Option<int> ownerKey) : base(endpoint, ((
Endpoint)endpoint).ToEnumerable().ToImmutableArray(), callbacks, ownerKey)
24 ServerConnection =
null;
29 netPeerConfiguration =
new NetPeerConfiguration(
"barotrauma")
31 DualStack = GameSettings.CurrentConfig.UseDualModeSockets
33 if (endpoint.NetEndpoint.Address.AddressFamily == AddressFamily.InterNetworkV6)
35 netPeerConfiguration.LocalAddress = System.Net.IPAddress.IPv6Any;
38 netPeerConfiguration.DisableMessageType(
39 NetIncomingMessageType.DebugMessage
40 | NetIncomingMessageType.WarningMessage
41 | NetIncomingMessageType.Receipt
42 | NetIncomingMessageType.ErrorMessage
43 | NetIncomingMessageType.Error);
45 incomingLidgrenMessages =
new List<NetIncomingMessage>();
48 public override void Start()
50 if (isActive) {
return; }
52 incomingLidgrenMessages.Clear();
54 ContentPackageOrderReceived =
false;
56 netClient =
new NetClient(netPeerConfiguration);
58 initializationStep = ConnectionInitialization.AuthInfoAndVersion;
60 if (ServerEndpoint is not { } lidgrenEndpointValue)
62 throw new InvalidCastException($
"Endpoint is not {nameof(LidgrenEndpoint)}");
65 if (ServerConnection !=
null)
67 throw new InvalidOperationException(
"ServerConnection is not null");
73 $
"{nameof(LidgrenClientPeer)}.GetAuthTicket",
74 AuthenticationTicket.Create(ServerEndpoint),
77 if (!t.TryGetResult(out Option<AuthenticationTicket> authenticationTicket))
79 Close(PeerDisconnectPacket.WithReason(DisconnectReason.AuthenticationFailed));
82 authTicket = authenticationTicket;
84 var netConnection = netClient.Connect(lidgrenEndpointValue.NetEndpoint);
86 ServerConnection =
new LidgrenConnection(netConnection)
95 public override void Update(
float deltaTime)
97 if (!isActive) {
return; }
99 ToolBox.ThrowIfNull(netClient);
100 ToolBox.ThrowIfNull(incomingLidgrenMessages);
102 if (IsOwner && !ChildServerRelay.IsProcessAlive)
104 var gameClient = GameMain.Client;
105 Close(PeerDisconnectPacket.WithReason(DisconnectReason.ServerCrashed));
106 gameClient?.CreateServerCrashMessage();
110 incomingLidgrenMessages.Clear();
111 netClient.ReadMessages(incomingLidgrenMessages);
113 GameMain.Client?.NetStats?.AddValue(NetStats.NetStatType.ReceivedBytes, netClient.Statistics.ReceivedBytes);
114 GameMain.Client?.NetStats?.AddValue(NetStats.NetStatType.SentBytes, netClient.Statistics.SentBytes);
116 foreach (NetIncomingMessage inc
in incomingLidgrenMessages)
118 var remoteEndpoint =
new LidgrenEndpoint(inc.SenderEndPoint);
120 if (remoteEndpoint != ServerEndpoint)
122 DebugConsole.AddWarning($
"Mismatched endpoint: expected {ServerEndpoint.NetEndpoint}, got {inc.SenderConnection.RemoteEndPoint}");
126 switch (inc.MessageType)
128 case NetIncomingMessageType.Data:
129 HandleDataMessage(inc);
131 case NetIncomingMessageType.StatusChanged:
132 HandleStatusChanged(inc);
138 private void HandleDataMessage(NetIncomingMessage lidgrenMsg)
140 if (!isActive) {
return; }
142 ToolBox.ThrowIfNull(ServerConnection);
144 IReadMessage inc = lidgrenMsg.ToReadMessage();
146 var (_, packetHeader, initialization) = INetSerializableStruct.Read<PeerPacketHeaders>(inc);
148 if (packetHeader.IsConnectionInitializationStep())
150 if (initializationStep == ConnectionInitialization.Success) {
return; }
152 ReadConnectionInitializationStep(
new IncomingInitializationMessage
154 InitializationStep = initialization ??
throw new Exception(
"Initialization step missing"),
160 OnInitializationComplete();
162 var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
163 callbacks.OnMessageReceived.Invoke(packet.GetReadMessage(packetHeader.IsCompressed(), ServerConnection));
167 private void HandleStatusChanged(NetIncomingMessage inc)
169 if (!isActive) {
return; }
171 NetConnectionStatus status = inc.ReadHeader<NetConnectionStatus>();
174 case NetConnectionStatus.Disconnected:
175 string disconnectMsg = inc.ReadString();
176 var peerDisconnectPacket =
177 PeerDisconnectPacket.FromLidgrenStringRepresentation(disconnectMsg);
178 Close(peerDisconnectPacket.Fallback(PeerDisconnectPacket.WithReason(DisconnectReason.Unknown)));
183 public override void SendPassword(
string password)
185 if (!isActive) {
return; }
187 ToolBox.ThrowIfNull(netClient);
189 if (initializationStep != ConnectionInitialization.Password) {
return; }
191 var headers =
new PeerPacketHeaders
193 DeliveryMethod = DeliveryMethod.Reliable,
194 PacketHeader = PacketHeader.IsConnectionInitializationStep,
195 Initialization = ConnectionInitialization.Password
197 var body =
new ClientPeerPasswordPacket
199 Password = ServerSettings.SaltPassword(Encoding.UTF8.GetBytes(password), passwordSalt)
202 SendMsgInternal(headers, body);
205 public override void Close(PeerDisconnectPacket peerDisconnectPacket)
207 if (!isActive) {
return; }
209 ToolBox.ThrowIfNull(netClient);
213 netClient.Shutdown(peerDisconnectPacket.ToLidgrenStringRepresentation());
216 callbacks.OnDisconnect.Invoke(peerDisconnectPacket);
219 public override void Send(IWriteMessage msg, DeliveryMethod deliveryMethod,
bool compressPastThreshold =
true)
221 if (!isActive) {
return; }
223 ToolBox.ThrowIfNull(netClient);
224 ToolBox.ThrowIfNull(netPeerConfiguration);
227 netPeerConfiguration.SimulatedDuplicatesChance = GameMain.Client.SimulatedDuplicatesChance;
228 netPeerConfiguration.SimulatedMinimumLatency = GameMain.Client.SimulatedMinimumLatency;
229 netPeerConfiguration.SimulatedRandomLatency = GameMain.Client.SimulatedRandomLatency;
230 netPeerConfiguration.SimulatedLoss = GameMain.Client.SimulatedLoss;
233 byte[] bufAux = msg.PrepareForSending(compressPastThreshold, out
bool isCompressed, out _);
235 var headers =
new PeerPacketHeaders
237 DeliveryMethod = deliveryMethod,
238 PacketHeader = isCompressed ? PacketHeader.IsCompressed : PacketHeader.None,
239 Initialization =
null
241 var body =
new PeerPacketMessage
246 SendMsgInternal(headers, body);
249 protected override void SendMsgInternal(PeerPacketHeaders headers, INetSerializableStruct? body)
251 ToolBox.ThrowIfNull(netClient);
253 IWriteMessage msg =
new WriteOnlyMessage();
254 msg.WriteNetSerializableStruct(headers);
257 NetSendResult result = ForwardToLidgren(msg, DeliveryMethod.Reliable);
258 if (result != NetSendResult.Queued && result != NetSendResult.Sent)
260 DebugConsole.NewMessage($
"Failed to send message to host: {result}\n{Environment.StackTrace}");
264 private NetSendResult ForwardToLidgren(IWriteMessage msg, DeliveryMethod deliveryMethod)
266 ToolBox.ThrowIfNull(netClient);
268 return netClient.SendMessage(msg.ToLidgren(netClient), deliveryMethod.ToLidgren());
271 protected override async Task<Option<AccountId>> GetAccountId()
273 if (!EosInterface.Core.IsInitialized) {
return SteamManager.GetSteamId().Select(
id => (AccountId)
id); }
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])
286 public override void ForceTimeOut()
288 netClient?.ServerConnection?.ForceTimeOut();
291 public override void DebugSendRawMessage(IWriteMessage msg)
292 => ForwardToLidgren(msg, DeliveryMethod.Reliable);