Merge branch 'master' of https://github.com/Regalis11/Barotrauma into develop

This commit is contained in:
EvilFactory
2024-03-28 14:26:18 -03:00
271 changed files with 13174 additions and 3021 deletions
@@ -174,6 +174,22 @@ namespace Barotrauma.Networking
return bannedPlayer != null;
}
public bool IsBanned(AccountInfo accountInfo, out string reason)
{
if (accountInfo.AccountId.TryUnwrap(out var accountId) && IsBanned(accountId, out reason))
{
return true;
}
foreach (var otherId in accountInfo.OtherMatchingIds)
{
if (IsBanned(otherId, out reason)) { return true; }
}
reason = "";
return false;
}
public void BanPlayer(string name, Endpoint endpoint, string reason, TimeSpan? duration)
=> BanPlayer(name, endpoint.Address, reason, duration);
@@ -305,7 +321,7 @@ namespace Barotrauma.Networking
else
{
outMsg.WriteBoolean(false); outMsg.WritePadBits();
outMsg.WriteString(((SteamId)bannedPlayer.AddressOrAccountId).StringRepresentation);
outMsg.WriteString(((AccountId)bannedPlayer.AddressOrAccountId).StringRepresentation);
}
}
}
@@ -54,7 +54,7 @@ namespace Barotrauma.Networking
private DateTime refreshMasterTimer;
private readonly TimeSpan refreshMasterInterval = new TimeSpan(0, 0, 60);
private bool registeredToMaster;
private bool registeredToSteamMaster;
private DateTime roundStartTime;
@@ -123,7 +123,7 @@ namespace Barotrauma.Networking
public NetworkConnection OwnerConnection { get; private set; }
private readonly Option<int> ownerKey;
private readonly Option<SteamId> ownerSteamId;
private readonly Option<P2PEndpoint> ownerEndpoint;
public GameServer(
string name,
@@ -135,7 +135,7 @@ namespace Barotrauma.Networking
bool attemptUPnP,
int maxPlayers,
Option<int> ownerKey,
Option<SteamId> ownerSteamId)
Option<P2PEndpoint> ownerEndpoint)
{
if (name.Length > NetConfig.ServerNameMaxLength)
{
@@ -153,7 +153,7 @@ namespace Barotrauma.Networking
this.ownerKey = ownerKey;
this.ownerSteamId = ownerSteamId;
this.ownerEndpoint = ownerEndpoint;
entityEventManager = new ServerEntityEventManager(this);
}
@@ -168,16 +168,18 @@ namespace Barotrauma.Networking
OnInitializationComplete,
GameMain.Instance.CloseServer,
OnOwnerDetermined);
if (ownerSteamId.TryUnwrap(out var steamId))
if (ownerEndpoint.TryUnwrap(out var endpoint))
{
Log("Using SteamP2P networking.", ServerLog.MessageType.ServerMessage);
serverPeer = new SteamP2PServerPeer(steamId, ownerKey.Fallback(0), ServerSettings, callbacks);
Log("Using P2P networking.", ServerLog.MessageType.ServerMessage);
serverPeer = new P2PServerPeer(endpoint, ownerKey.Fallback(0), ServerSettings, callbacks);
}
else
{
Log("Using Lidgren networking. Manual port forwarding may be required. If players cannot connect to the server, you may want to use the in-game hosting menu (which uses SteamP2P networking and does not require port forwarding).", ServerLog.MessageType.ServerMessage);
Log("Using Lidgren networking. Manual port forwarding may be required. If players cannot connect to the server, you may want to use the in-game hosting menu (which uses Steamworks and EOS networking and does not require port forwarding).", ServerLog.MessageType.ServerMessage);
serverPeer = new LidgrenServerPeer(ownerKey, ServerSettings, callbacks);
registeredToSteamMaster = SteamManager.CreateServer(this, ServerSettings.IsPublic);
Eos.EosSessionManager.UpdateOwnedSession(Option.None, ServerSettings);
}
FileSender = new FileSender(serverPeer, MsgConstants.MTU);
@@ -190,15 +192,7 @@ namespace Barotrauma.Networking
VoipServer = new VoipServer(serverPeer);
if (serverPeer is LidgrenServerPeer)
{
#if USE_STEAM
registeredToMaster = SteamManager.CreateServer(this, ServerSettings.IsPublic);
#endif
}
GameMain.LuaCs.Initialize();
Log("Server started", ServerLog.MessageType.ServerMessage);
GameMain.NetLobbyScreen.Select();
@@ -658,20 +652,23 @@ namespace Barotrauma.Networking
updateTimer = DateTime.Now + UpdateInterval;
}
if (registeredToMaster && (DateTime.Now > refreshMasterTimer || ServerSettings.ServerDetailsChanged))
if (DateTime.Now > refreshMasterTimer || ServerSettings.ServerDetailsChanged)
{
if (GameSettings.CurrentConfig.UseSteamMatchmaking)
if (registeredToSteamMaster)
{
bool refreshSuccessful = SteamManager.RefreshServerDetails(this);
if (GameSettings.CurrentConfig.VerboseLogging)
{
Log(refreshSuccessful ?
"Refreshed server info on the server list." :
"Refreshing server info on the server list failed.", ServerLog.MessageType.ServerMessage);
"Refreshed server info on the Steam server list." :
"Refreshing server info on the Steam server list failed.", ServerLog.MessageType.ServerMessage);
}
}
refreshMasterTimer = DateTime.Now + refreshMasterInterval;
Eos.EosSessionManager.UpdateOwnedSession(Option.None, ServerSettings);
ServerSettings.ServerDetailsChanged = false;
refreshMasterTimer = DateTime.Now + refreshMasterInterval;
}
}
@@ -3082,8 +3079,6 @@ namespace Barotrauma.Networking
client.WaitForNextRoundRespawn = null;
client.InGame = false;
if (client.AccountId.TryUnwrap<SteamId>(out var steamId)) { SteamManager.StopAuthSession(steamId); }
var previousPlayer = previousPlayers.Find(p => p.MatchesClient(client));
if (previousPlayer == null)
{
@@ -3416,7 +3411,7 @@ namespace Barotrauma.Networking
msg.WriteByte((byte)ServerPacketHeader.FILE_TRANSFER);
msg.WriteByte((byte)FileTransferMessageType.Cancel);
msg.WriteByte((byte)transfer.ID);
serverPeer.Send(msg, transfer.Connection, DeliveryMethod.ReliableOrdered);
serverPeer.Send(msg, transfer.Connection, DeliveryMethod.Reliable);
}
public void UpdateVoteStatus(bool checkActiveVote = true)
@@ -3602,13 +3597,13 @@ namespace Barotrauma.Networking
}
}
public void IncrementStat(Character character, Identifier achievementIdentifier, int amount)
public void IncrementStat(Character character, AchievementStat stat, int amount)
{
foreach (Client client in connectedClients)
{
if (client.Character == character)
{
IncrementStat(client, achievementIdentifier, amount);
IncrementStat(client, stat, amount);
return;
}
}
@@ -3622,19 +3617,17 @@ namespace Barotrauma.Networking
IWriteMessage msg = new WriteOnlyMessage();
msg.WriteByte((byte)ServerPacketHeader.ACHIEVEMENT);
msg.WriteIdentifier(achievementIdentifier);
msg.WriteInt32(0);
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
}
public void IncrementStat(Client client, Identifier achievementIdentifier, int amount)
public void IncrementStat(Client client, AchievementStat stat, int amount)
{
if (client.GivenAchievements.Contains(achievementIdentifier)) { return; }
IWriteMessage msg = new WriteOnlyMessage();
msg.WriteByte((byte)ServerPacketHeader.ACHIEVEMENT);
msg.WriteIdentifier(achievementIdentifier);
msg.WriteInt32(amount);
msg.WriteByte((byte)ServerPacketHeader.ACHIEVEMENT_STAT);
INetSerializableStruct incrementedStat = new NetIncrementedStat(stat, amount);
incrementedStat.Write(msg);
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
}
@@ -3642,7 +3635,7 @@ namespace Barotrauma.Networking
public void SendTraitorMessage(WriteOnlyMessage msg, Client client)
{
if (client == null) { return; };
serverPeer.Send(msg, client.Connection, DeliveryMethod.ReliableOrdered);
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
}
public void UpdateCheatsEnabled()
@@ -1,24 +1,26 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Net;
using System.Linq;
using Barotrauma.Extensions;
using Barotrauma.Steam;
using Lidgren.Network;
namespace Barotrauma.Networking
{
internal sealed class LidgrenServerPeer : ServerPeer
internal sealed class LidgrenServerPeer : ServerPeer<LidgrenConnection>
{
private readonly NetPeerConfiguration netPeerConfiguration;
private ImmutableDictionary<AuthenticationTicketKind, Authenticator>? authenticators;
private NetServer? netServer;
private readonly List<NetIncomingMessage> incomingLidgrenMessages;
public LidgrenServerPeer(Option<int> ownKey, ServerSettings settings, Callbacks callbacks) : base(callbacks)
public LidgrenServerPeer(Option<int> ownKey, ServerSettings settings, Callbacks callbacks) : base(callbacks, settings)
{
serverSettings = settings;
authenticators = null;
netServer = null;
netPeerConfiguration = new NetPeerConfiguration("barotrauma")
@@ -42,9 +44,6 @@ namespace Barotrauma.Networking
netPeerConfiguration.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
connectedClients = new List<NetworkConnection>();
pendingClients = new List<PendingClient>();
incomingLidgrenMessages = new List<NetIncomingMessage>();
ownerKey = ownKey;
@@ -54,6 +53,8 @@ namespace Barotrauma.Networking
{
if (netServer != null) { return; }
authenticators = Authenticator.GetAuthenticatorsForHost(Option.None);
incomingLidgrenMessages.Clear();
netServer = new NetServer(netPeerConfiguration);
@@ -81,7 +82,7 @@ namespace Barotrauma.Networking
for (int i = connectedClients.Count - 1; i >= 0; i--)
{
Disconnect(connectedClients[i], PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
Disconnect(connectedClients[i].Connection, PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
}
netServer.Shutdown(PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown).ToLidgrenStringRepresentation());
@@ -91,8 +92,6 @@ namespace Barotrauma.Networking
netServer = null;
Steamworks.SteamServer.OnValidateAuthTicketResponse -= OnAuthChange;
callbacks.OnShutdown.Invoke();
}
@@ -166,9 +165,10 @@ namespace Barotrauma.Networking
ToolBox.ThrowIfNull(netPeerConfiguration);
netServer.UPnP.ForwardPort(netPeerConfiguration.Port, "barotrauma");
#if USE_STEAM
netServer.UPnP.ForwardPort(serverSettings.QueryPort, "barotrauma");
#endif
if (SteamManager.IsInitialized)
{
netServer.UPnP.ForwardPort(serverSettings.QueryPort, "barotrauma");
}
}
private bool DiscoveringUPnP()
@@ -209,7 +209,7 @@ namespace Barotrauma.Networking
return;
}
PendingClient? pendingClient = pendingClients.Find(c => c.Connection is LidgrenConnection l && l.NetConnection == inc.SenderConnection);
PendingClient? pendingClient = pendingClients.Find(c => c.Connection.NetConnection == inc.SenderConnection);
if (pendingClient is null)
{
@@ -224,7 +224,7 @@ namespace Barotrauma.Networking
{
if (netServer == null) { return; }
PendingClient? pendingClient = pendingClients.Find(c => c.Connection is LidgrenConnection l && l.NetConnection == lidgrenMsg.SenderConnection);
PendingClient? pendingClient = pendingClients.Find(c => c.Connection.NetConnection == lidgrenMsg.SenderConnection);
IReadMessage inc = lidgrenMsg.ToReadMessage();
@@ -236,7 +236,7 @@ namespace Barotrauma.Networking
}
else if (!packetHeader.IsConnectionInitializationStep())
{
if (connectedClients.Find(c => c is LidgrenConnection l && l.NetConnection == lidgrenMsg.SenderConnection) is not LidgrenConnection conn)
if (connectedClients.Find(c => c.Connection.NetConnection == lidgrenMsg.SenderConnection) is not { Connection: LidgrenConnection conn })
{
if (pendingClient != null)
{
@@ -273,7 +273,7 @@ namespace Barotrauma.Networking
switch (inc.SenderConnection.Status)
{
case NetConnectionStatus.Disconnected:
LidgrenConnection? conn = connectedClients.Cast<LidgrenConnection>().FirstOrDefault(c => c.NetConnection == inc.SenderConnection);
LidgrenConnection? conn = connectedClients.Select(c => c.Connection).FirstOrDefault(c => c.NetConnection == inc.SenderConnection);
if (conn != null)
{
if (conn == OwnerConnection)
@@ -300,12 +300,7 @@ namespace Barotrauma.Networking
}
}
public override void InitializeSteamServerCallbacks()
{
Steamworks.SteamServer.OnValidateAuthTicketResponse += OnAuthChange;
}
private void OnAuthChange(Steamworks.SteamId steamId, Steamworks.SteamId ownerId, Steamworks.AuthResponse status)
private void OnSteamAuthChange(Steamworks.SteamId steamId, Steamworks.SteamId ownerId, Steamworks.AuthResponse status)
{
if (netServer == null) { return; }
@@ -317,8 +312,8 @@ namespace Barotrauma.Networking
if (status == Steamworks.AuthResponse.OK) { return; }
if (connectedClients.Find(c
=> c.AccountInfo.AccountId.TryUnwrap<SteamId>(out var id) && id.Value == steamId)
is LidgrenConnection connection)
=> c.Connection.AccountInfo.AccountId.TryUnwrap<SteamId>(out var id) && id.Value == steamId)
is { Connection: LidgrenConnection connection })
{
Disconnect(connection, PeerDisconnectPacket.SteamAuthError(status));
}
@@ -351,9 +346,15 @@ namespace Barotrauma.Networking
{
if (netServer == null) { return; }
if (!connectedClients.Contains(conn))
if (conn is not LidgrenConnection lidgrenConnection)
{
DebugConsole.ThrowError($"Tried to send message to unauthenticated connection: {conn.Endpoint.StringRepresentation}");
DebugConsole.ThrowError($"Tried to send message to connection of incorrect type: expected {nameof(LidgrenConnection)}, got {conn.GetType().Name}");
return;
}
if (!connectedClients.Any(cc => cc.Connection == lidgrenConnection))
{
DebugConsole.ThrowError($"Tried to send message to unauthenticated connection: {lidgrenConnection.Endpoint.StringRepresentation}");
return;
}
@@ -377,7 +378,7 @@ namespace Barotrauma.Networking
{
Buffer = bufAux
};
SendMsgInternal(conn, headers, body);
SendMsgInternal(lidgrenConnection, headers, body);
}
public override void Disconnect(NetworkConnection conn, PeerDisconnectPacket peerDisconnectPacket)
@@ -386,18 +387,21 @@ namespace Barotrauma.Networking
if (conn is not LidgrenConnection lidgrenConn) { return; }
if (connectedClients.Contains(lidgrenConn))
if (connectedClients.FindIndex(cc => cc.Connection == lidgrenConn) is >= 0 and var ccIndex)
{
lidgrenConn.Status = NetworkConnectionStatus.Disconnected;
connectedClients.Remove(lidgrenConn);
connectedClients.RemoveAt(ccIndex);
callbacks.OnDisconnect.Invoke(conn, peerDisconnectPacket);
if (conn.AccountInfo.AccountId.TryUnwrap<SteamId>(out var steamId)) { SteamManager.StopAuthSession(steamId); }
if (conn.AccountInfo.AccountId.TryUnwrap(out var accountId))
{
authenticators?.Values.ForEach(authenticator => authenticator.EndAuthSession(accountId));
}
}
lidgrenConn.NetConnection.Disconnect(peerDisconnectPacket.ToLidgrenStringRepresentation());
}
protected override void SendMsgInternal(NetworkConnection conn, PeerPacketHeaders headers, INetSerializableStruct? body)
protected override void SendMsgInternal(LidgrenConnection conn, PeerPacketHeaders headers, INetSerializableStruct? body)
{
IWriteMessage msgToSend = new WriteOnlyMessage();
msgToSend.WriteNetSerializableStruct(headers);
@@ -412,75 +416,74 @@ namespace Barotrauma.Networking
protected override void CheckOwnership(PendingClient pendingClient)
{
if (OwnerConnection == null
&& pendingClient.Connection is LidgrenConnection l
&& IPAddress.IsLoopback(l.NetConnection.RemoteEndPoint.Address)
&& ownerKey.IsSome() && pendingClient.OwnerKey == ownerKey)
if (OwnerConnection != null
|| pendingClient.Connection is not LidgrenConnection l
|| !IPAddress.IsLoopback(l.NetConnection.RemoteEndPoint.Address)
|| !ownerKey.IsSome() || pendingClient.OwnerKey != ownerKey)
{
ownerKey = Option<int>.None();
OwnerConnection = pendingClient.Connection;
callbacks.OnOwnerDetermined.Invoke(OwnerConnection);
return;
}
ownerKey = Option.None;
OwnerConnection = pendingClient.Connection;
callbacks.OnOwnerDetermined.Invoke(OwnerConnection);
}
protected override void ProcessAuthTicket(ClientSteamTicketAndVersionPacket packet, PendingClient pendingClient)
private enum AuthResult
{
if (pendingClient.AccountInfo.AccountId.IsNone())
Success,
Failure
}
protected override void ProcessAuthTicket(ClientAuthTicketAndVersionPacket packet, PendingClient pendingClient)
{
if (pendingClient.AccountInfo.AccountId.IsSome())
{
if (pendingClient.AccountInfo.AccountId != packet.AccountId)
{
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.AuthenticationFailed));
}
return;
}
void acceptClient(AccountInfo accountInfo)
{
pendingClient.Connection.SetAccountInfo(accountInfo);
pendingClient.Name = packet.Name;
pendingClient.OwnerKey = packet.OwnerKey;
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
}
void rejectClient()
{
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.AuthenticationFailed));
}
if (authenticators is null
|| !packet.AuthTicket.TryUnwrap(out var authTicket)
|| !authenticators.TryGetValue(authTicket.Kind, out var authenticator))
{
bool requireSteamAuth = GameSettings.CurrentConfig.RequireSteamAuthentication;
#if DEBUG
requireSteamAuth = false;
DebugConsole.NewMessage($"Debug server accepts unauthenticated connections", Microsoft.Xna.Framework.Color.Yellow);
acceptClient(new AccountInfo(packet.AccountId));
#else
rejectClient();
#endif
bool hasSteamAuth = packet.SteamAuthTicket.TryUnwrap(out var ticket);
//steam auth cannot be done (SteamManager not initialized or no ticket given),
//but it's not required either -> let the client join without auth
if ((!SteamManager.IsInitialized || !hasSteamAuth) && !requireSteamAuth)
{
pendingClient.Name = packet.Name;
pendingClient.OwnerKey = packet.OwnerKey;
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
}
else
{
if (!packet.SteamId.TryUnwrap(out var id) || id is not SteamId steamId)
{
if (requireSteamAuth)
{
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.SteamAuthenticationFailed));
return;
}
}
else
{
Steamworks.BeginAuthResult authSessionStartState = SteamManager.StartAuthSession(ticket, steamId);
if (authSessionStartState != Steamworks.BeginAuthResult.OK)
{
if (requireSteamAuth)
{
RemovePendingClient(pendingClient, PeerDisconnectPacket.SteamAuthError(authSessionStartState));
}
else
{
packet.SteamId = Option<AccountId>.None();
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
}
}
}
pendingClient.Connection.SetAccountInfo(new AccountInfo(packet.SteamId.Select(uid => (AccountId)uid)));
pendingClient.Name = packet.Name;
pendingClient.OwnerKey = packet.OwnerKey;
pendingClient.AuthSessionStarted = true;
}
return;
}
else
pendingClient.AuthSessionStarted = true;
TaskPool.Add($"{nameof(LidgrenServerPeer)}.ProcessAuth", authenticator.VerifyTicket(authTicket), t =>
{
if (pendingClient.AccountInfo.AccountId != packet.SteamId.Select(uid => (AccountId)uid))
if (!t.TryGetResult(out AccountInfo accountInfo)
|| accountInfo.IsNone)
{
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.SteamAuthenticationFailed));
rejectClient();
return;
}
}
acceptClient(accountInfo);
});
}
private NetSendResult ForwardToLidgren(IWriteMessage msg, NetworkConnection connection, DeliveryMethod deliveryMethod)
@@ -1,30 +1,20 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma.Networking
{
internal sealed class SteamP2PServerPeer : ServerPeer
internal sealed class P2PServerPeer : ServerPeer<P2PConnection>
{
private bool started;
private readonly SteamId ownerSteamId;
private readonly P2PEndpoint ownerEndpoint;
private UInt64 ownerKey64 => unchecked((UInt64)ownerKey.Fallback(0));
private SteamId ReadSteamId(IReadMessage inc) => new SteamId(inc.ReadUInt64() ^ ownerKey64);
private void WriteSteamId(IWriteMessage msg, SteamId val) => msg.WriteUInt64(val.Value ^ ownerKey64);
public SteamP2PServerPeer(SteamId steamId, int ownerKey, ServerSettings settings, Callbacks callbacks) : base(callbacks)
public P2PServerPeer(P2PEndpoint ownerEp, int ownerKey, ServerSettings settings, Callbacks callbacks) : base(callbacks, settings)
{
serverSettings = settings;
this.ownerKey = Option.Some(ownerKey);
connectedClients = new List<NetworkConnection>();
pendingClients = new List<PendingClient>();
this.ownerKey = Option<int>.Some(ownerKey);
ownerSteamId = steamId;
ownerEndpoint = ownerEp;
started = false;
}
@@ -37,7 +27,7 @@ namespace Barotrauma.Networking
PacketHeader = PacketHeader.IsConnectionInitializationStep | PacketHeader.IsServerMessage,
Initialization = null
};
SendMsgInternal(ownerSteamId, headers, null);
SendMsgInternal(ownerEndpoint, headers, null);
started = true;
}
@@ -55,7 +45,7 @@ namespace Barotrauma.Networking
for (int i = connectedClients.Count - 1; i >= 0; i--)
{
Disconnect(connectedClients[i], PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
Disconnect(connectedClients[i].Connection, PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
}
pendingClients.Clear();
@@ -73,7 +63,7 @@ namespace Barotrauma.Networking
//backwards for loop so we can remove elements while iterating
for (int i = connectedClients.Count - 1; i >= 0; i--)
{
SteamP2PConnection conn = (SteamP2PConnection)connectedClients[i];
var conn = connectedClients[i].Connection;
conn.Decay(deltaTime);
if (conn.Timeout < 0.0)
{
@@ -83,7 +73,7 @@ namespace Barotrauma.Networking
try
{
while (ChildServerRelay.Read(out byte[] incBuf))
foreach (var incBuf in ChildServerRelay.Read())
{
IReadMessage inc = new ReadOnlyMessage(incBuf, false, 0, incBuf.Length, OwnerConnection);
@@ -114,51 +104,34 @@ namespace Barotrauma.Networking
{
if (!started) { return; }
SteamId senderSteamId = ReadSteamId(inc);
SteamId sentOwnerSteamId = ReadSteamId(inc);
var (deliveryMethod, packetHeader, initialization) = INetSerializableStruct.Read<PeerPacketHeaders>(inc);
if (packetHeader.IsServerMessage())
var senderInfo = INetSerializableStruct.Read<P2POwnerToServerHeader>(inc);
if (!senderInfo.Endpoint.TryUnwrap(out var senderEndpoint))
{
DebugConsole.ThrowError($"Got server message from {senderSteamId}");
return;
}
if (senderSteamId != ownerSteamId) //sender is remote, handle disconnects and heartbeats
{
bool connectionMatches(NetworkConnection conn) =>
conn is SteamP2PConnection { Endpoint: SteamP2PEndpoint { SteamId: var steamId } }
&& steamId == senderSteamId;
var (_, packetHeader, initialization) = INetSerializableStruct.Read<PeerPacketHeaders>(inc);
PendingClient? pendingClient = pendingClients.Find(c => connectionMatches(c.Connection));
SteamP2PConnection? connectedClient = connectedClients.Find(connectionMatches) as SteamP2PConnection;
if (packetHeader.IsServerMessage())
{
DebugConsole.ThrowError($"Got server message from {senderEndpoint}");
return;
}
if (senderEndpoint != ownerEndpoint) //sender is remote, handle disconnects and heartbeats
{
bool connectionMatches(P2PConnection conn) =>
conn.Endpoint == senderEndpoint;
var pendingClient = pendingClients.Find(c => connectionMatches(c.Connection));
var connectedClient = connectedClients.Find(c => connectionMatches(c.Connection));
pendingClient?.Connection.SetAccountInfo(senderInfo.AccountInfo);
pendingClient?.Heartbeat();
connectedClient?.Heartbeat();
connectedClient?.Connection.Heartbeat();
if (packetHeader.IsConnectionInitializationStep())
{
if (!initialization.HasValue) { return; }
ConnectionInitialization initializationStep = initialization.Value;
if (pendingClient != null)
{
pendingClient.Connection.SetAccountInfo(new AccountInfo(senderSteamId, sentOwnerSteamId));
ReadConnectionInitializationStep(
pendingClient,
new ReadWriteMessage(inc.Buffer, inc.BitPosition, inc.LengthBits, false),
initializationStep);
}
else if (initializationStep == ConnectionInitialization.ConnectionStarted)
{
pendingClient = new PendingClient(new SteamP2PConnection(senderSteamId));
pendingClient.Connection.SetAccountInfo(new AccountInfo(senderSteamId, sentOwnerSteamId));
pendingClients.Add(pendingClient);
}
}
else if (serverSettings.BanList.IsBanned(senderSteamId, out string banReason) ||
serverSettings.BanList.IsBanned(sentOwnerSteamId, out banReason))
if (serverSettings.BanList.IsBanned(senderEndpoint, out string banReason)
|| serverSettings.BanList.IsBanned(senderInfo.AccountInfo, out banReason))
{
if (pendingClient != null)
{
@@ -166,7 +139,7 @@ namespace Barotrauma.Networking
}
else if (connectedClient != null)
{
Disconnect(connectedClient, PeerDisconnectPacket.Banned(banReason));
Disconnect(connectedClient.Connection, PeerDisconnectPacket.Banned(banReason));
}
}
else if (packetHeader.IsDisconnectMessage())
@@ -177,7 +150,7 @@ namespace Barotrauma.Networking
}
else if (connectedClient != null)
{
Disconnect(connectedClient, PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
Disconnect(connectedClient.Connection, PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
}
}
else if (packetHeader.IsHeartbeatMessage())
@@ -185,16 +158,44 @@ namespace Barotrauma.Networking
//message exists solely as a heartbeat, ignore its contents
return;
}
else if (packetHeader.IsConnectionInitializationStep())
{
if (!initialization.HasValue) { return; }
ConnectionInitialization initializationStep = initialization.Value;
if (pendingClient != null)
{
ReadConnectionInitializationStep(
pendingClient,
new ReadWriteMessage(inc.Buffer, inc.BitPosition, inc.LengthBits, false),
initializationStep);
}
else if (initializationStep == ConnectionInitialization.ConnectionStarted)
{
pendingClients.Add(new PendingClient(senderEndpoint.MakeConnectionFromEndpoint()));
}
}
else if (connectedClient != null)
{
var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
IReadMessage msg = new ReadOnlyMessage(packet.Buffer, packetHeader.IsCompressed(), 0, packet.Length, connectedClient);
callbacks.OnMessageReceived.Invoke(connectedClient, msg);
if (packetHeader.IsDataFragment())
{
var completeMessageOption = connectedClient.Defragmenter.ProcessIncomingFragment(INetSerializableStruct.Read<MessageFragment>(inc));
if (!completeMessageOption.TryUnwrap(out var completeMessage)) { return; }
IReadMessage msg = new ReadOnlyMessage(completeMessage.ToArray(), false, 0, completeMessage.Length, connectedClient.Connection);
callbacks.OnMessageReceived.Invoke(connectedClient.Connection, msg);
}
else
{
var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
IReadMessage msg = new ReadOnlyMessage(packet.Buffer, packetHeader.IsCompressed(), 0, packet.Length, connectedClient.Connection);
callbacks.OnMessageReceived.Invoke(connectedClient.Connection, msg);
}
}
}
else //sender is owner
{
(OwnerConnection as SteamP2PConnection)?.Heartbeat();
OwnerConnection?.Heartbeat();
if (packetHeader.IsDisconnectMessage())
{
@@ -212,14 +213,12 @@ namespace Barotrauma.Networking
{
if (OwnerConnection is null)
{
var packet = INetSerializableStruct.Read<SteamP2PInitializationOwnerPacket>(inc);
OwnerConnection = new SteamP2PConnection(ownerSteamId)
{
Language = GameSettings.CurrentConfig.Language
};
OwnerConnection.SetAccountInfo(new AccountInfo(ownerSteamId, ownerSteamId));
var packet = INetSerializableStruct.Read<P2PInitializationOwnerPacket>(inc);
OwnerConnection = ownerEndpoint.MakeConnectionFromEndpoint();
OwnerConnection.Language = GameSettings.CurrentConfig.Language;
OwnerConnection.SetAccountInfo(senderInfo.AccountInfo);
callbacks.OnInitializationComplete.Invoke(OwnerConnection, packet.OwnerName);
callbacks.OnInitializationComplete.Invoke(OwnerConnection, packet.Name);
callbacks.OnOwnerDetermined.Invoke(OwnerConnection);
}
@@ -239,27 +238,38 @@ namespace Barotrauma.Networking
}
}
public override void InitializeSteamServerCallbacks()
{
throw new InvalidOperationException("Called InitializeSteamServerCallbacks on SteamP2PServerPeer!");
}
public override void Send(IWriteMessage msg, NetworkConnection conn, DeliveryMethod deliveryMethod, bool compressPastThreshold = true)
{
if (!started) { return; }
if (conn is not SteamP2PConnection steamP2PConn) { return; }
if (conn is not P2PConnection p2pConn) { return; }
if (!connectedClients.Contains(steamP2PConn) && conn != OwnerConnection)
int ccIndex = connectedClients.FindIndex(cc => cc.Connection == p2pConn);
if (ccIndex < 0 && conn != OwnerConnection)
{
DebugConsole.ThrowError($"Tried to send message to unauthenticated connection: {steamP2PConn.AccountInfo.AccountId}");
DebugConsole.ThrowError($"Tried to send message to unauthenticated connection: {p2pConn.AccountInfo.AccountId}");
return;
}
if (!conn.AccountInfo.AccountId.TryUnwrap(out var connAccountId) || connAccountId is not SteamId) { return; }
byte[] bufAux = msg.PrepareForSending(compressPastThreshold, out bool isCompressed, out _);
if (bufAux.Length > MessageFragment.MaxSize && conn != OwnerConnection)
{
var cc = connectedClients[ccIndex];
var fragments = cc.Fragmenter.FragmentMessage(msg.Buffer.AsSpan()[..msg.LengthBytes]);
foreach (var fragment in fragments)
{
var fragmentHeaders = new PeerPacketHeaders
{
DeliveryMethod = DeliveryMethod.Reliable,
PacketHeader = PacketHeader.IsDataFragment
| PacketHeader.IsServerMessage,
Initialization = null
};
SendMsgInternal(p2pConn, fragmentHeaders, fragment);
}
return;
}
var headers = new PeerPacketHeaders
{
DeliveryMethod = deliveryMethod,
@@ -271,10 +281,10 @@ namespace Barotrauma.Networking
{
Buffer = bufAux
};
SendMsgInternal(steamP2PConn, headers, body);
SendMsgInternal(p2pConn, headers, body);
}
private void SendDisconnectMessage(SteamId steamId, PeerDisconnectPacket peerDisconnectPacket)
private void SendDisconnectMessage(P2PEndpoint endpoint, PeerDisconnectPacket peerDisconnectPacket)
{
if (!started) { return; }
@@ -285,44 +295,41 @@ namespace Barotrauma.Networking
Initialization = null
};
SendMsgInternal(steamId, headers, peerDisconnectPacket);
SendMsgInternal(endpoint, headers, peerDisconnectPacket);
}
public override void Disconnect(NetworkConnection conn, PeerDisconnectPacket peerDisconnectPacket)
{
if (!started) { return; }
if (conn is not SteamP2PConnection steamp2pConn) { return; }
if (conn is not P2PConnection p2pConn) { return; }
if (!conn.AccountInfo.AccountId.TryUnwrap(out var connAccountId) || connAccountId is not SteamId connSteamId) { return; }
SendDisconnectMessage(p2pConn.Endpoint, peerDisconnectPacket);
SendDisconnectMessage(connSteamId, peerDisconnectPacket);
if (connectedClients.Contains(steamp2pConn))
if (connectedClients.FindIndex(cc => cc.Connection == p2pConn) is >= 0 and var ccIndex)
{
steamp2pConn.Status = NetworkConnectionStatus.Disconnected;
connectedClients.Remove(steamp2pConn);
p2pConn.Status = NetworkConnectionStatus.Disconnected;
connectedClients.RemoveAt(ccIndex);
callbacks.OnDisconnect.Invoke(conn, peerDisconnectPacket);
Steam.SteamManager.StopAuthSession(connSteamId);
}
else if (steamp2pConn == OwnerConnection)
else if (p2pConn == OwnerConnection)
{
throw new InvalidOperationException("Cannot disconnect owner peer");
}
}
protected override void SendMsgInternal(NetworkConnection conn, PeerPacketHeaders headers, INetSerializableStruct? body)
protected override void SendMsgInternal(P2PConnection conn, PeerPacketHeaders headers, INetSerializableStruct? body)
{
var connSteamId = conn is SteamP2PConnection { Endpoint: SteamP2PEndpoint { SteamId: var id } } ? id : null;
if (connSteamId is null) { return; }
SendMsgInternal(connSteamId, headers, body);
SendMsgInternal(conn.Endpoint, headers, body);
}
private void SendMsgInternal(SteamId connSteamId, PeerPacketHeaders headers, INetSerializableStruct? body)
private void SendMsgInternal(P2PEndpoint connEndpoint, PeerPacketHeaders headers, INetSerializableStruct? body)
{
IWriteMessage msgToSend = new WriteOnlyMessage();
WriteSteamId(msgToSend, connSteamId);
msgToSend.WriteNetSerializableStruct(new P2PServerToOwnerHeader
{
EndpointStr = connEndpoint.StringRepresentation
});
msgToSend.WriteNetSerializableStruct(headers);
body?.Write(msgToSend);
@@ -336,11 +343,13 @@ namespace Barotrauma.Networking
ChildServerRelay.Write(bufToSend);
}
protected override void ProcessAuthTicket(ClientSteamTicketAndVersionPacket packet, PendingClient pendingClient)
protected override void ProcessAuthTicket(ClientAuthTicketAndVersionPacket packet, PendingClient pendingClient)
{
// Do nothing with the auth ticket because that should be handled by the owner peer,
// just assume that authentication succeeded
pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder;
pendingClient.Name = packet.Name;
pendingClient.AuthSessionStarted = true;
}
}
}
}
@@ -7,28 +7,14 @@ using Barotrauma.Extensions;
namespace Barotrauma.Networking
{
internal abstract class ServerPeer
internal abstract class ServerPeer<TConnection> : ServerPeer where TConnection : NetworkConnection
{
public readonly record struct Callbacks(
Callbacks.MessageCallback OnMessageReceived,
Callbacks.DisconnectCallback OnDisconnect,
Callbacks.InitializationCompleteCallback OnInitializationComplete,
Callbacks.ShutdownCallback OnShutdown,
Callbacks.OwnerDeterminedCallback OnOwnerDetermined)
{
public delegate void MessageCallback(NetworkConnection connection, IReadMessage message);
public delegate void DisconnectCallback(NetworkConnection connection, PeerDisconnectPacket peerDisconnectPacket);
public delegate void InitializationCompleteCallback(NetworkConnection connection, string? clientName);
public delegate void ShutdownCallback();
public delegate void OwnerDeterminedCallback(NetworkConnection connection);
}
protected readonly Callbacks callbacks;
private readonly ImmutableArray<ContentPackage> contentPackages;
protected ServerPeer(Callbacks callbacks)
protected ServerPeer(Callbacks callbacks, ServerSettings serverSettings) : base(callbacks)
{
this.callbacks = callbacks;
this.serverSettings = serverSettings;
this.connectedClients = new List<ConnectedClient>();
this.pendingClients = new List<PendingClient>();
List<ContentPackage> contentPackageList = new List<ContentPackage>();
foreach (var cp in ContentPackageManager.EnabledPackages.All)
@@ -45,19 +31,13 @@ namespace Barotrauma.Networking
contentPackageList.Add(cp);
}
contentPackages = contentPackageList.ToImmutableArray();
}
public abstract void InitializeSteamServerCallbacks();
public abstract void Start();
public abstract void Close();
public abstract void Update(float deltaTime);
}
public sealed class PendingClient
{
public string? Name;
public Option<int> OwnerKey;
public readonly NetworkConnection Connection;
public readonly TConnection Connection;
public ConnectionInitialization InitializationStep;
public double UpdateTime;
public double TimeOut;
@@ -67,11 +47,11 @@ namespace Barotrauma.Networking
public AccountInfo AccountInfo => Connection.AccountInfo;
public PendingClient(NetworkConnection conn)
public PendingClient(TConnection conn)
{
OwnerKey = Option<int>.None();
OwnerKey = Option.None;
Connection = conn;
InitializationStep = ConnectionInitialization.SteamTicketAndVersion;
InitializationStep = ConnectionInitialization.AuthInfoAndVersion;
Retries = 0;
PasswordSalt = null;
UpdateTime = Timing.TotalTime + Timing.Step * 3.0;
@@ -85,11 +65,26 @@ namespace Barotrauma.Networking
}
}
protected List<NetworkConnection> connectedClients = null!;
protected List<PendingClient> pendingClients = null!;
protected ServerSettings serverSettings = null!;
protected sealed class ConnectedClient
{
public readonly TConnection Connection;
public readonly MessageFragmenter Fragmenter;
public readonly MessageDefragmenter Defragmenter;
public ConnectedClient(TConnection connection)
{
Connection = connection;
Fragmenter = new();
Defragmenter = new();
}
}
protected readonly List<ConnectedClient> connectedClients;
protected readonly List<PendingClient> pendingClients;
protected readonly ServerSettings serverSettings;
protected TConnection? OwnerConnection;
protected Option<int> ownerKey = Option.None;
protected NetworkConnection? OwnerConnection;
protected void ReadConnectionInitializationStep(PendingClient pendingClient, IReadMessage inc, ConnectionInitialization initializationStep)
{
@@ -101,8 +96,8 @@ namespace Barotrauma.Networking
switch (initializationStep)
{
case ConnectionInitialization.SteamTicketAndVersion:
var authPacket = INetSerializableStruct.Read<ClientSteamTicketAndVersionPacket>(inc);
case ConnectionInitialization.AuthInfoAndVersion:
var authPacket = INetSerializableStruct.Read<ClientAuthTicketAndVersionPacket>(inc);
if (!Client.IsValidName(authPacket.Name, serverSettings))
{
@@ -117,8 +112,8 @@ namespace Barotrauma.Networking
{
RemovePendingClient(pendingClient, PeerDisconnectPacket.InvalidVersion());
GameServer.Log($"{authPacket.Name} ({authPacket.SteamId}) couldn't join the server (incompatible game version)", ServerLog.MessageType.Error);
DebugConsole.NewMessage($"{authPacket.Name} ({authPacket.SteamId}) couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red);
GameServer.Log($"{authPacket.Name} ({authPacket.AccountId}) couldn't join the server (incompatible game version)", ServerLog.MessageType.Error);
DebugConsole.NewMessage($"{authPacket.Name} ({authPacket.AccountId}) couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red);
return;
}
@@ -128,7 +123,7 @@ namespace Barotrauma.Networking
if (nameTaken != null)
{
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.NameTaken));
GameServer.Log($"{authPacket.Name} ({authPacket.SteamId}) couldn't join the server (name too similar to the name of the client \"" + nameTaken.Name + "\").", ServerLog.MessageType.Error);
GameServer.Log($"{authPacket.Name} ({authPacket.AccountId}) couldn't join the server (name too similar to the name of the client \"" + nameTaken.Name + "\").", ServerLog.MessageType.Error);
return;
}
@@ -172,7 +167,7 @@ namespace Barotrauma.Networking
}
}
protected abstract void ProcessAuthTicket(ClientSteamTicketAndVersionPacket packet, PendingClient pendingClient);
protected abstract void ProcessAuthTicket(ClientAuthTicketAndVersionPacket packet, PendingClient pendingClient);
protected void BanPendingClient(PendingClient pendingClient, string banReason, TimeSpan? duration)
{
@@ -214,7 +209,7 @@ namespace Barotrauma.Networking
return isBanned;
}
protected abstract void SendMsgInternal(NetworkConnection conn, PeerPacketHeaders headers, INetSerializableStruct? body);
protected abstract void SendMsgInternal(TConnection conn, PeerPacketHeaders headers, INetSerializableStruct? body);
protected void UpdatePendingClient(PendingClient pendingClient)
{
@@ -236,8 +231,8 @@ namespace Barotrauma.Networking
if (pendingClient.InitializationStep == ConnectionInitialization.Success)
{
NetworkConnection newConnection = pendingClient.Connection;
connectedClients.Add(newConnection);
TConnection newConnection = pendingClient.Connection;
connectedClients.Add(new ConnectedClient(newConnection));
pendingClients.Remove(pendingClient);
callbacks.OnInitializationComplete.Invoke(newConnection, pendingClient.Name);
@@ -310,14 +305,38 @@ namespace Barotrauma.Networking
pendingClients.Remove(pendingClient);
if (pendingClient.AuthSessionStarted && pendingClient.AccountInfo.AccountId.TryUnwrap<SteamId>(out var steamId))
{
Steam.SteamManager.StopAuthSession(steamId);
pendingClient.Connection.SetAccountInfo(AccountInfo.None);
pendingClient.AuthSessionStarted = false;
}
pendingClient.Connection.SetAccountInfo(AccountInfo.None);
pendingClient.AuthSessionStarted = false;
}
}
}
internal abstract class ServerPeer
{
public readonly record struct Callbacks(
Callbacks.MessageCallback OnMessageReceived,
Callbacks.DisconnectCallback OnDisconnect,
Callbacks.InitializationCompleteCallback OnInitializationComplete,
Callbacks.ShutdownCallback OnShutdown,
Callbacks.OwnerDeterminedCallback OnOwnerDetermined)
{
public delegate void MessageCallback(NetworkConnection connection, IReadMessage message);
public delegate void DisconnectCallback(NetworkConnection connection, PeerDisconnectPacket peerDisconnectPacket);
public delegate void InitializationCompleteCallback(NetworkConnection connection, string? clientName);
public delegate void ShutdownCallback();
public delegate void OwnerDeterminedCallback(NetworkConnection connection);
}
protected readonly Callbacks callbacks;
protected ServerPeer(Callbacks callbacks)
{
this.callbacks = callbacks;
}
public abstract void Start();
public abstract void Close();
public abstract void Update(float deltaTime);
public abstract void Send(IWriteMessage msg, NetworkConnection conn, DeliveryMethod deliveryMethod, bool compressPastThreshold = true);
public abstract void Disconnect(NetworkConnection conn, PeerDisconnectPacket peerDisconnectPacket);
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Steam;
namespace Barotrauma.Networking
{
@@ -281,9 +282,11 @@ namespace Barotrauma.Networking
doc.Root.SetAttributeValue("name", ServerName);
doc.Root.SetAttributeValue("port", Port);
#if USE_STEAM
doc.Root.SetAttributeValue("queryport", QueryPort);
#endif
if (QueryPort != 0)
{
doc.Root.SetAttributeValue("queryport", QueryPort);
}
doc.Root.SetAttributeValue("password", password ?? "");
doc.Root.SetAttributeValue("enableupnp", EnableUPnP);