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 if (GameMain.Client !=
null)
229 netPeerConfiguration.SimulatedDuplicatesChance = GameMain.Client.SimulatedDuplicatesChance;
230 netPeerConfiguration.SimulatedMinimumLatency = GameMain.Client.SimulatedMinimumLatency;
231 netPeerConfiguration.SimulatedRandomLatency = GameMain.Client.SimulatedRandomLatency;
232 netPeerConfiguration.SimulatedLoss = GameMain.Client.SimulatedLoss;
236 byte[] bufAux = msg.PrepareForSending(compressPastThreshold, out
bool isCompressed, out _);
238 var headers =
new PeerPacketHeaders
240 DeliveryMethod = deliveryMethod,
241 PacketHeader = isCompressed ? PacketHeader.IsCompressed : PacketHeader.None,
242 Initialization =
null
244 var body =
new PeerPacketMessage
249 SendMsgInternal(headers, body);
252 protected override void SendMsgInternal(PeerPacketHeaders headers, INetSerializableStruct? body)
254 ToolBox.ThrowIfNull(netClient);
256 IWriteMessage msg =
new WriteOnlyMessage();
257 msg.WriteNetSerializableStruct(headers);
260 NetSendResult result = ForwardToLidgren(msg, DeliveryMethod.Reliable);
261 if (result != NetSendResult.Queued && result != NetSendResult.Sent)
263 DebugConsole.NewMessage($
"Failed to send message to host: {result}\n{Environment.StackTrace}");
267 private NetSendResult ForwardToLidgren(IWriteMessage msg, DeliveryMethod deliveryMethod)
269 ToolBox.ThrowIfNull(netClient);
271 return netClient.SendMessage(msg.ToLidgren(netClient), deliveryMethod.ToLidgren());
274 protected override async Task<Option<AccountId>> GetAccountId()
276 if (!EosInterface.Core.IsInitialized) {
return SteamManager.GetSteamId().Select(
id => (AccountId)
id); }
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])
289 public override void ForceTimeOut()
291 netClient?.ServerConnection?.ForceTimeOut();
294 public override void DebugSendRawMessage(IWriteMessage msg)
295 => ForwardToLidgren(msg, DeliveryMethod.Reliable);