Unstable v0.19.5.0

This commit is contained in:
Juan Pablo Arce
2022-09-14 12:47:17 -03:00
parent 3f2c843247
commit 1fd2a51bbb
158 changed files with 5702 additions and 4813 deletions
@@ -61,8 +61,9 @@ namespace Barotrauma
msg.WriteColorR8G8B8(Head.SkinColor);
msg.WriteColorR8G8B8(Head.HairColor);
msg.WriteColorR8G8B8(Head.FacialHairColor);
msg.WriteString(ragdollFileName);
msg.WriteString(ragdollFileName);
msg.WriteIdentifier(HumanPrefabIds.NpcIdentifier);
if (Job != null)
{
msg.WriteUInt32(Job.Prefab.UintIdentifier);
@@ -243,6 +243,7 @@ namespace Barotrauma
maxPlayers,
ownerKey,
steamId);
Server.StartServer();
for (int i = 0; i < CommandLineArgs.Length; i++)
{
@@ -1,4 +1,5 @@
using Barotrauma.Networking;
using System;
using System.Xml.Linq;
namespace Barotrauma
@@ -20,19 +21,22 @@ namespace Barotrauma
CharacterInfo = client.CharacterInfo;
healthData = new XElement("health");
client.Character?.CharacterHealth?.Save(healthData);
if (client.Character?.Inventory != null)
//the character may not be controlled by the client atm, but still exist
Character character = client.Character ?? CharacterInfo?.Character;
character?.CharacterHealth?.Save(healthData);
if (character?.Inventory != null)
{
itemData = new XElement("inventory");
Character.SaveInventory(client.Character.Inventory, itemData);
Character.SaveInventory(character.Inventory, itemData);
}
OrderData = new XElement("orders");
if (client.CharacterInfo != null)
if (CharacterInfo != null)
{
CharacterInfo.SaveOrderData(client.CharacterInfo, OrderData);
CharacterInfo.SaveOrderData(CharacterInfo, OrderData);
}
if (client.Character?.Wallet.Save() is { } walletSave)
if (character?.Wallet.Save() is { } walletSave)
{
WalletData = walletSave;
}
@@ -118,9 +122,9 @@ namespace Barotrauma
character.SpawnInventoryItems(inventory, itemData.FromPackage(null));
}
public void ApplyHealthData(Character character)
public void ApplyHealthData(Character character, Func<AfflictionPrefab, bool> afflictionPredicate = null)
{
CharacterInfo.ApplyHealthData(character, healthData);
CharacterInfo.ApplyHealthData(character, healthData, afflictionPredicate);
}
public void ApplyOrderData(Character character)
@@ -917,15 +917,14 @@ namespace Barotrauma
foreach (var kvp in purchasedItems)
{
foreach (var purchasedItemList in purchasedItems.Values)
var storeId = kvp.Key;
var purchasedItemList = kvp.Value;
foreach (var purchasedItem in purchasedItemList)
{
foreach (var purchasedItem in purchasedItemList)
{
int availableQuantity = map.CurrentLocation.Stores[kvp.Key].Stock.Find(s => s.ItemPrefab == purchasedItem.ItemPrefab)?.Quantity ?? 0;
purchasedItem.Quantity = Math.Min(purchasedItem.Quantity, availableQuantity);
}
}
CargoManager.PurchaseItems(kvp.Key, kvp.Value, false, sender);
int availableQuantity = map.CurrentLocation.Stores[storeId].Stock.Find(s => s.ItemPrefab == purchasedItem.ItemPrefab)?.Quantity ?? 0;
purchasedItem.Quantity = Math.Min(purchasedItem.Quantity, availableQuantity);
}
CargoManager.PurchaseItems(storeId, purchasedItemList, false, sender);
}
foreach (var (storeIdentifier, items) in CargoManager.PurchasedItems)
@@ -71,6 +71,12 @@ namespace Barotrauma.Networking
{
expirationTime = parsedTime;
}
else
{
string error = $"Failed to parse the ban duration of \"{name}\" ({separatedLine[2]}) from the legacy ban list file (text file which has now been changed to XML). Considering the ban permanent.";
DebugConsole.ThrowError(error);
GameServer.AddPendingMessageToOwner(error, ChatMessageType.Error);
}
}
string reason = separatedLine.Length > 3 ? string.Join(",", separatedLine.Skip(3)) : "";
@@ -156,6 +162,8 @@ namespace Barotrauma.Networking
public void BanPlayer(string name, Either<Address, AccountId> addressOrAccountId, string reason, TimeSpan? duration)
{
if (addressOrAccountId.TryGet(out Address address) && address.IsLocalHost) { return; }
var existingBan = bannedPlayers.Find(bp => bp.AddressOrAccountId == addressOrAccountId);
if (existingBan != null) { bannedPlayers.Remove(existingBan); }
@@ -224,27 +224,55 @@ namespace Barotrauma.Networking
}
}
/// <summary>
/// Reset what this client has voted for and the kick votes given to this client
/// </summary>
public void ResetVotes(bool resetKickVotes)
{
for (int i = 0; i < votes.Length; i++)
{
votes[i] = null;
}
if (resetKickVotes)
{
kickVoters.Clear();
}
}
public void SetPermissions(ClientPermissions permissions, IEnumerable<DebugConsole.Command> permittedConsoleCommands)
{
this.Permissions = permissions;
this.PermittedConsoleCommands.Clear();
this.PermittedConsoleCommands.UnionWith(permittedConsoleCommands);
Permissions = permissions;
PermittedConsoleCommands.Clear();
PermittedConsoleCommands.UnionWith(permittedConsoleCommands);
if (Permissions.HasFlag(ClientPermissions.ManageSettings))
{
//ensure the client has the up-to-date server settings
GameMain.Server?.ServerSettings?.ForcePropertyUpdate();
}
}
public void GivePermission(ClientPermissions permission)
{
if (!this.Permissions.HasFlag(permission)) this.Permissions |= permission;
if (!Permissions.HasFlag(permission))
{
Permissions |= permission;
if (permission.HasFlag(ClientPermissions.ManageSettings))
{
//ensure the client has the up-to-date server settings
GameMain.Server?.ServerSettings?.ForcePropertyUpdate();
}
}
}
public void RemovePermission(ClientPermissions permission)
{
this.Permissions &= ~permission;
Permissions &= ~permission;
}
public bool HasPermission(ClientPermissions permission)
{
return this.Permissions.HasFlag(permission);
return Permissions.HasFlag(permission);
}
}
}
@@ -39,7 +39,7 @@ namespace Barotrauma.Networking
string resultFileName
= dir.StartsWith(ContentPackage.LocalModsDir)
? $"Local_{mod.Name}"
: $"Workshop_{mod.Name}_{mod.SteamWorkshopId}";
: $"Workshop_{mod.Name}_{(mod.UgcId.TryUnwrap(out var ugcId) ? ugcId.ToString() : "NULL")}";
resultFileName = ToolBox.RemoveInvalidFileNameChars(resultFileName.Replace('\\', '_').Replace('/', '_'));
resultFileName = $"{resultFileName}{Extension}";
return Path.Combine(UploadFolder, resultFileName);
File diff suppressed because it is too large Load Diff
@@ -246,7 +246,7 @@ namespace Barotrauma.Networking
" (created " + (Timing.TotalTime - firstEventToResend.CreateTime).ToString("0.##") + " s ago, " +
(lastSentToAnyoneTime - firstEventToResend.CreateTime).ToString("0.##") + " s older than last event sent to anyone)" +
" Events queued: " + events.Count + ", last sent to all: " + lastSentToAll, ServerLog.MessageType.Error);
server.DisconnectClient(c, "", DisconnectReason.ExcessiveDesyncOldEvent + "/ServerMessage.ExcessiveDesyncOldEvent");
server.DisconnectClient(c, PeerDisconnectPacket.WithReason(DisconnectReason.ExcessiveDesyncOldEvent));
}
);
}
@@ -260,7 +260,7 @@ namespace Barotrauma.Networking
{
DebugConsole.NewMessage(c.Name + " was kicked because they were expecting a removed network event (" + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", Color.Red);
GameServer.Log(GameServer.ClientLogName(c) + " was kicked because they were expecting a removed network event (" + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", ServerLog.MessageType.Error);
server.DisconnectClient(c, "", DisconnectReason.ExcessiveDesyncRemovedEvent + "/ServerMessage.ExcessiveDesyncRemovedEvent");
server.DisconnectClient(c, PeerDisconnectPacket.WithReason(DisconnectReason.ExcessiveDesyncRemovedEvent));
});
}
}
@@ -269,7 +269,7 @@ namespace Barotrauma.Networking
foreach (Client timedOutClient in timedOutClients)
{
GameServer.Log("Disconnecting client " + GameServer.ClientLogName(timedOutClient) + ". Syncing the client with the server took too long.", ServerLog.MessageType.Error);
GameMain.Server.DisconnectClient(timedOutClient, "", DisconnectReason.SyncTimeout + "/ServerMessage.SyncTimeout");
GameMain.Server.DisconnectClient(timedOutClient, PeerDisconnectPacket.WithReason(DisconnectReason.SyncTimeout));
}
bufferedEvents.RemoveAll(b => b.IsProcessed);
@@ -20,7 +20,7 @@ namespace Barotrauma.Networking
if (Sender != null && c.InGame)
{
msg.WriteUInt16(Sender.ID);
}
}
msg.WriteBoolean(false); //text color (no custom text colors for order messages)
msg.WritePadBits();
WriteOrder(msg);
@@ -15,7 +15,7 @@ namespace Barotrauma.Networking
private readonly List<NetIncomingMessage> incomingLidgrenMessages;
public LidgrenServerPeer(Option<int> ownKey, ServerSettings settings)
public LidgrenServerPeer(Option<int> ownKey, ServerSettings settings, Callbacks callbacks) : base(callbacks)
{
serverSettings = settings;
@@ -68,21 +68,21 @@ namespace Barotrauma.Networking
}
}
public override void Close(string? msg = null)
public override void Close()
{
if (netServer == null) { return; }
for (int i = pendingClients.Count - 1; i >= 0; i--)
{
RemovePendingClient(pendingClients[i], DisconnectReason.ServerShutdown, msg);
RemovePendingClient(pendingClients[i], PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
}
for (int i = connectedClients.Count - 1; i >= 0; i--)
{
Disconnect(connectedClients[i], msg ?? DisconnectReason.ServerShutdown.ToString());
Disconnect(connectedClients[i], PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
}
netServer.Shutdown(msg ?? DisconnectReason.ServerShutdown.ToString());
netServer.Shutdown(PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown).ToLidgrenStringRepresentation());
pendingClients.Clear();
connectedClients.Clear();
@@ -91,7 +91,7 @@ namespace Barotrauma.Networking
Steamworks.SteamServer.OnValidateAuthTicketResponse -= OnAuthChange;
OnShutdown?.Invoke();
callbacks.OnShutdown.Invoke();
}
public override void Update(float deltaTime)
@@ -100,12 +100,6 @@ namespace Barotrauma.Networking
ToolBox.ThrowIfNull(incomingLidgrenMessages);
if (OnOwnerDetermined != null && OwnerConnection != null)
{
OnOwnerDetermined?.Invoke(OwnerConnection);
OnOwnerDetermined = null;
}
netServer.ReadMessages(incomingLidgrenMessages);
//process incoming connections first
@@ -193,14 +187,14 @@ namespace Barotrauma.Networking
if (connectedClients.Count >= serverSettings.MaxPlayers)
{
inc.SenderConnection.Deny(DisconnectReason.ServerFull.ToString());
inc.SenderConnection.Deny(PeerDisconnectPacket.WithReason(DisconnectReason.ServerFull).ToLidgrenStringRepresentation());
return;
}
if (serverSettings.BanList.IsBanned(new LidgrenEndpoint(inc.SenderConnection.RemoteEndPoint), out string banReason))
{
//IP banned: deny immediately
inc.SenderConnection.Deny($"{DisconnectReason.Banned}/ {banReason}");
inc.SenderConnection.Deny(PeerDisconnectPacket.Banned(banReason).ToLidgrenStringRepresentation());
return;
}
@@ -235,12 +229,12 @@ namespace Barotrauma.Networking
{
if (pendingClient != null)
{
RemovePendingClient(pendingClient, DisconnectReason.AuthenticationRequired, "Received data message from unauthenticated client");
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.AuthenticationRequired));
}
else if (lidgrenMsg.SenderConnection.Status != NetConnectionStatus.Disconnected &&
lidgrenMsg.SenderConnection.Status != NetConnectionStatus.Disconnecting)
{
lidgrenMsg.SenderConnection.Disconnect($"{DisconnectReason.AuthenticationRequired}/ Received data message from unauthenticated client");
lidgrenMsg.SenderConnection.Disconnect(PeerDisconnectPacket.WithReason(DisconnectReason.AuthenticationRequired).ToLidgrenStringRepresentation());
}
return;
@@ -252,12 +246,12 @@ namespace Barotrauma.Networking
|| (conn.AccountInfo.AccountId.TryUnwrap(out var accountId) && serverSettings.BanList.IsBanned(accountId, out banReason))
|| conn.AccountInfo.OtherMatchingIds.Any(id => serverSettings.BanList.IsBanned(id, out banReason)))
{
Disconnect(conn, $"{DisconnectReason.Banned}/ {banReason}");
Disconnect(conn, PeerDisconnectPacket.Banned(banReason));
return;
}
var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
OnMessageReceived?.Invoke(conn, packet.GetReadMessage(packetHeader.IsCompressed(), conn));
callbacks.OnMessageReceived.Invoke(conn, packet.GetReadMessage(packetHeader.IsCompressed(), conn));
}
}
@@ -275,12 +269,11 @@ namespace Barotrauma.Networking
{
DebugConsole.NewMessage("Owner disconnected: closing the server...");
GameServer.Log("Owner disconnected: closing the server...", ServerLog.MessageType.ServerMessage);
Close($"{DisconnectReason.ServerShutdown}/ Owner disconnected");
Close();
}
else
{
#warning TODO: kill off disconnect in layer 1
Disconnect(conn, $"ServerMessage.HasDisconnected~[client]={GameMain.Server.ConnectedClients.First(c => c.Connection == conn).Name}");
Disconnect(conn, PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
}
}
else
@@ -288,7 +281,7 @@ namespace Barotrauma.Networking
PendingClient? pendingClient = pendingClients.Find(c => c.Connection is LidgrenConnection l && l.NetConnection == inc.SenderConnection);
if (pendingClient != null)
{
RemovePendingClient(pendingClient, DisconnectReason.Unknown, $"ServerMessage.HasDisconnected~[client]={pendingClient.Name}");
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
}
}
@@ -312,9 +305,11 @@ namespace Barotrauma.Networking
{
if (status == Steamworks.AuthResponse.OK) { return; }
if (connectedClients.Find(c => c.AccountInfo.AccountId is Some<AccountId> { Value: SteamId id } && id.Value == steamId) is LidgrenConnection connection)
if (connectedClients.Find(c
=> c.AccountInfo.AccountId is Some<AccountId> { Value: SteamId id } && id.Value == steamId)
is LidgrenConnection connection)
{
Disconnect(connection, $"{DisconnectReason.SteamAuthenticationFailed}/ Steam authentication status changed: {status}");
Disconnect(connection, PeerDisconnectPacket.SteamAuthError(status));
}
return;
@@ -325,7 +320,7 @@ namespace Barotrauma.Networking
|| serverSettings.BanList.IsBanned(new SteamId(steamId), out banReason)
|| serverSettings.BanList.IsBanned(new SteamId(ownerId), out banReason))
{
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
RemovePendingClient(pendingClient, PeerDisconnectPacket.Banned(banReason));
return;
}
@@ -337,7 +332,7 @@ namespace Barotrauma.Networking
}
else
{
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, $"Steam authentication failed: {status}");
RemovePendingClient(pendingClient, PeerDisconnectPacket.SteamAuthError(status));
}
}
@@ -374,7 +369,7 @@ namespace Barotrauma.Networking
SendMsgInternal(conn, headers, body);
}
public override void Disconnect(NetworkConnection conn, string? msg = null)
public override void Disconnect(NetworkConnection conn, PeerDisconnectPacket peerDisconnectPacket)
{
if (netServer == null) { return; }
@@ -384,11 +379,11 @@ namespace Barotrauma.Networking
{
lidgrenConn.Status = NetworkConnectionStatus.Disconnected;
connectedClients.Remove(lidgrenConn);
OnDisconnect?.Invoke(conn, msg);
callbacks.OnDisconnect.Invoke(conn, peerDisconnectPacket);
if (conn.AccountInfo.AccountId is Some<AccountId> { Value: SteamId steamId }) { SteamManager.StopAuthSession(steamId); }
}
lidgrenConn.NetConnection.Disconnect(msg ?? "Disconnected");
lidgrenConn.NetConnection.Disconnect(peerDisconnectPacket.ToLidgrenStringRepresentation());
}
protected override void SendMsgInternal(NetworkConnection conn, PeerPacketHeaders headers, INetSerializableStruct? body)
@@ -413,6 +408,7 @@ namespace Barotrauma.Networking
{
ownerKey = Option<int>.None();
OwnerConnection = pendingClient.Connection;
callbacks.OnOwnerDetermined.Invoke(OwnerConnection);
}
}
@@ -440,7 +436,7 @@ namespace Barotrauma.Networking
{
if (requireSteamAuth)
{
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "Steam auth session failed to start: Steam ID not provided");
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.SteamAuthenticationFailed));
return;
}
}
@@ -451,7 +447,7 @@ namespace Barotrauma.Networking
{
if (requireSteamAuth)
{
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, $"Steam auth session failed to start: {authSessionStartState}");
RemovePendingClient(pendingClient, PeerDisconnectPacket.SteamAuthError(authSessionStartState));
}
else
{
@@ -471,7 +467,7 @@ namespace Barotrauma.Networking
{
if (pendingClient.AccountInfo.AccountId != packet.SteamId.Select(uid => (AccountId)uid))
{
RemovePendingClient(pendingClient, DisconnectReason.SteamAuthenticationFailed, "SteamID mismatch");
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.SteamAuthenticationFailed));
}
}
}
@@ -9,26 +9,31 @@ namespace Barotrauma.Networking
{
internal abstract class ServerPeer
{
public delegate void MessageCallback(NetworkConnection connection, IReadMessage message);
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);
}
public delegate void DisconnectCallback(NetworkConnection connection, string? reason);
public delegate void InitializationCompleteCallback(NetworkConnection connection, string? clientName);
public delegate void ShutdownCallback();
public delegate void OwnerDeterminedCallback(NetworkConnection connection);
public MessageCallback? OnMessageReceived;
public DisconnectCallback? OnDisconnect;
public InitializationCompleteCallback? OnInitializationComplete;
public ShutdownCallback? OnShutdown;
public OwnerDeterminedCallback? OnOwnerDetermined;
protected readonly Callbacks callbacks;
protected ServerPeer(Callbacks callbacks)
{
this.callbacks = callbacks;
}
public abstract void InitializeSteamServerCallbacks();
public abstract void Start();
public abstract void Close(string? msg = null);
public abstract void Close();
public abstract void Update(float deltaTime);
protected sealed class PendingClient
@@ -84,15 +89,16 @@ namespace Barotrauma.Networking
if (!Client.IsValidName(authPacket.Name, serverSettings))
{
RemovePendingClient(pendingClient, DisconnectReason.InvalidName, "");
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.InvalidName));
return;
}
bool isCompatibleVersion = NetworkMember.IsCompatible(authPacket.GameVersion, GameMain.Version.ToString()) ?? false;
bool isCompatibleVersion =
Version.TryParse(authPacket.GameVersion, out var remoteVersion)
&& NetworkMember.IsCompatible(remoteVersion, GameMain.Version);
if (!isCompatibleVersion)
{
RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion,
$"DisconnectMessage.InvalidVersion~[version]={GameMain.Version}~[clientversion]={authPacket.GameVersion}");
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);
@@ -104,7 +110,7 @@ namespace Barotrauma.Networking
Client nameTaken = GameMain.Server.ConnectedClients.Find(c => Homoglyphs.Compare(c.Name.ToLower(), authPacket.Name.ToLower()));
if (nameTaken != null)
{
RemovePendingClient(pendingClient, DisconnectReason.NameTaken, "");
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);
return;
}
@@ -135,7 +141,7 @@ namespace Barotrauma.Networking
{
const string banMsg = "Failed to enter correct password too many times";
BanPendingClient(pendingClient, banMsg, null);
RemovePendingClient(pendingClient, DisconnectReason.Banned, banMsg);
RemovePendingClient(pendingClient, PeerDisconnectPacket.Banned(banMsg));
return;
}
}
@@ -190,13 +196,13 @@ namespace Barotrauma.Networking
{
if (IsPendingClientBanned(pendingClient, out string? banReason))
{
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
RemovePendingClient(pendingClient, PeerDisconnectPacket.Banned(banReason));
return;
}
if (connectedClients.Count >= serverSettings.MaxPlayers)
{
RemovePendingClient(pendingClient, DisconnectReason.ServerFull, "");
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.ServerFull));
}
if (pendingClient.InitializationStep == ConnectionInitialization.Success)
@@ -205,15 +211,15 @@ namespace Barotrauma.Networking
connectedClients.Add(newConnection);
pendingClients.Remove(pendingClient);
CheckOwnership(pendingClient);
callbacks.OnInitializationComplete.Invoke(newConnection, pendingClient.Name);
OnInitializationComplete?.Invoke(newConnection, pendingClient.Name);
CheckOwnership(pendingClient);
}
pendingClient.TimeOut -= Timing.Step;
if (pendingClient.TimeOut < 0.0)
{
RemovePendingClient(pendingClient, DisconnectReason.Unknown, Lidgren.Network.NetConnection.NoResponseMessage);
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.Timeout));
}
if (Timing.TotalTime < pendingClient.UpdateTime) { return; }
@@ -237,7 +243,7 @@ namespace Barotrauma.Networking
structToSend = new ServerPeerContentPackageOrderPacket
{
ServerName = GameMain.Server.ServerName,
ContentPackages = ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent)
ContentPackages = ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent || cp.Files.All(f => f is SubmarineFile))
.Select(contentPackage => new ServerContentPackage(contentPackage, timeNow))
.ToImmutableArray()
};
@@ -267,11 +273,11 @@ namespace Barotrauma.Networking
protected virtual void CheckOwnership(PendingClient pendingClient) { }
protected void RemovePendingClient(PendingClient pendingClient, DisconnectReason reason, string? msg)
protected void RemovePendingClient(PendingClient pendingClient, PeerDisconnectPacket peerDisconnectPacket)
{
if (pendingClients.Contains(pendingClient))
{
Disconnect(pendingClient.Connection, $"{reason}/{msg}");
Disconnect(pendingClient.Connection, peerDisconnectPacket);
pendingClients.Remove(pendingClient);
@@ -285,6 +291,6 @@ namespace Barotrauma.Networking
}
public abstract void Send(IWriteMessage msg, NetworkConnection conn, DeliveryMethod deliveryMethod, bool compressPastThreshold = true);
public abstract void Disconnect(NetworkConnection conn, string? msg = null);
public abstract void Disconnect(NetworkConnection conn, PeerDisconnectPacket peerDisconnectPacket);
}
}
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
namespace Barotrauma.Networking
{
@@ -16,7 +17,7 @@ namespace Barotrauma.Networking
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)
public SteamP2PServerPeer(SteamId steamId, int ownerKey, ServerSettings settings, Callbacks callbacks) : base(callbacks)
{
serverSettings = settings;
@@ -43,7 +44,7 @@ namespace Barotrauma.Networking
started = true;
}
public override void Close(string? msg = null)
public override void Close()
{
if (!started) { return; }
@@ -51,12 +52,12 @@ namespace Barotrauma.Networking
for (int i = pendingClients.Count - 1; i >= 0; i--)
{
RemovePendingClient(pendingClients[i], DisconnectReason.ServerShutdown, msg);
RemovePendingClient(pendingClients[i], PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
}
for (int i = connectedClients.Count - 1; i >= 0; i--)
{
Disconnect(connectedClients[i], msg ?? DisconnectReason.ServerShutdown.ToString());
Disconnect(connectedClients[i], PeerDisconnectPacket.WithReason(DisconnectReason.ServerShutdown));
}
pendingClients.Clear();
@@ -64,19 +65,13 @@ namespace Barotrauma.Networking
ChildServerRelay.ShutDown();
OnShutdown?.Invoke();
callbacks.OnShutdown.Invoke();
}
public override void Update(float deltaTime)
{
if (!started) { return; }
if (OnOwnerDetermined != null && OwnerConnection != null)
{
OnOwnerDetermined?.Invoke(OwnerConnection);
OnOwnerDetermined = null;
}
//backwards for loop so we can remove elements while iterating
for (int i = connectedClients.Count - 1; i >= 0; i--)
{
@@ -84,7 +79,7 @@ namespace Barotrauma.Networking
conn.Decay(deltaTime);
if (conn.Timeout < 0.0)
{
Disconnect(conn, "Timed out");
Disconnect(conn, PeerDisconnectPacket.WithReason(DisconnectReason.Timeout));
}
}
@@ -149,24 +144,22 @@ namespace Barotrauma.Networking
{
if (pendingClient != null)
{
RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
RemovePendingClient(pendingClient, PeerDisconnectPacket.Banned(banReason));
}
else if (connectedClient != null)
{
Disconnect(connectedClient, $"{DisconnectReason.Banned}/ {banReason}");
Disconnect(connectedClient, PeerDisconnectPacket.Banned(banReason));
}
}
else if (packetHeader.IsDisconnectMessage())
{
if (pendingClient != null)
{
string disconnectMsg = $"ServerMessage.HasDisconnected~[client]={pendingClient.Name}";
RemovePendingClient(pendingClient, DisconnectReason.Unknown, disconnectMsg);
RemovePendingClient(pendingClient, PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
}
else if (connectedClient != null)
{
string disconnectMsg = $"ServerMessage.HasDisconnected~[client]={GameMain.Server.ConnectedClients.First(c => c.Connection == connectedClient).Name}";
Disconnect(connectedClient, disconnectMsg, false);
Disconnect(connectedClient, PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
}
}
else if (packetHeader.IsHeartbeatMessage())
@@ -176,28 +169,27 @@ namespace Barotrauma.Networking
}
else 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),
initialization ?? throw new Exception("Initialization step missing"));
initializationStep);
}
else if (initialization.HasValue)
else if (initializationStep == ConnectionInitialization.ConnectionStarted)
{
ConnectionInitialization initializationStep = initialization.Value;
if (initializationStep == ConnectionInitialization.ConnectionStarted)
{
pendingClients.Add(new PendingClient(new SteamP2PConnection(senderSteamId)));
}
pendingClients.Add(new PendingClient(new SteamP2PConnection(senderSteamId)));
}
}
else if (connectedClient != null)
{
var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
IReadMessage msg = new ReadOnlyMessage(packet.Buffer, packetHeader.IsCompressed(), 0, packet.Length, connectedClient);
OnMessageReceived?.Invoke(connectedClient, msg);
callbacks.OnMessageReceived.Invoke(connectedClient, msg);
}
}
else //sender is owner
@@ -227,7 +219,8 @@ namespace Barotrauma.Networking
};
OwnerConnection.SetAccountInfo(new AccountInfo(ownerSteamId, ownerSteamId));
OnInitializationComplete?.Invoke(OwnerConnection, packet.OwnerName);
callbacks.OnInitializationComplete.Invoke(OwnerConnection, packet.OwnerName);
callbacks.OnOwnerDetermined.Invoke(OwnerConnection);
}
return;
@@ -241,7 +234,7 @@ namespace Barotrauma.Networking
{
var packet = INetSerializableStruct.Read<PeerPacketMessage>(inc);
IReadMessage msg = new ReadOnlyMessage(packet.Buffer, packetHeader.IsCompressed(), 0, packet.Length, OwnerConnection);
OnMessageReceived?.Invoke(OwnerConnection!, msg);
callbacks.OnMessageReceived.Invoke(OwnerConnection!, msg);
}
}
}
@@ -281,27 +274,21 @@ namespace Barotrauma.Networking
SendMsgInternal(steamP2PConn, headers, body);
}
private void SendDisconnectMessage(SteamId steamId, string? msg)
private void SendDisconnectMessage(SteamId steamId, PeerDisconnectPacket peerDisconnectPacket)
{
if (!started) { return; }
if (string.IsNullOrWhiteSpace(msg)) { return; }
var headers = new PeerPacketHeaders
{
DeliveryMethod = DeliveryMethod.Reliable,
PacketHeader = PacketHeader.IsDisconnectMessage | PacketHeader.IsServerMessage,
Initialization = null
};
var packet = new PeerDisconnectPacket
{
Message = msg
};
SendMsgInternal(steamId, headers, packet);
SendMsgInternal(steamId, headers, peerDisconnectPacket);
}
private void Disconnect(NetworkConnection conn, string? msg, bool sendDisconnectMessage)
public override void Disconnect(NetworkConnection conn, PeerDisconnectPacket peerDisconnectPacket)
{
if (!started) { return; }
@@ -309,26 +296,21 @@ namespace Barotrauma.Networking
if (!conn.AccountInfo.AccountId.TryUnwrap(out var connAccountId) || !(connAccountId is SteamId connSteamId)) { return; }
if (sendDisconnectMessage) { SendDisconnectMessage(connSteamId, msg); }
SendDisconnectMessage(connSteamId, peerDisconnectPacket);
if (connectedClients.Contains(steamp2pConn))
{
steamp2pConn.Status = NetworkConnectionStatus.Disconnected;
connectedClients.Remove(steamp2pConn);
OnDisconnect?.Invoke(conn, msg);
callbacks.OnDisconnect.Invoke(conn, peerDisconnectPacket);
Steam.SteamManager.StopAuthSession(connSteamId);
}
else if (steamp2pConn == OwnerConnection)
{
//TODO: fix?
throw new InvalidOperationException("Cannot disconnect owner peer");
}
}
public override void Disconnect(NetworkConnection conn, string? msg = null)
{
Disconnect(conn, msg, true);
}
protected override void SendMsgInternal(NetworkConnection conn, PeerPacketHeaders headers, INetSerializableStruct? body)
{
var connSteamId = conn is SteamP2PConnection { Endpoint: SteamP2PEndpoint { SteamId: var id } } ? id : null;
@@ -505,9 +505,10 @@ namespace Barotrauma.Networking
var characterData = campaign?.GetClientCharacterData(clients[i]);
if (characterData != null && Level.Loaded?.Type != LevelData.LevelType.Outpost && characterData.HasSpawned)
{
//we need to reapply the previous respawn penalty affliction or successive deaths won't make it stack
characterData.ApplyHealthData(character, (AfflictionPrefab ap) => ap == GetRespawnPenaltyAfflictionPrefab());
GiveRespawnPenaltyAffliction(character);
}
if (characterData == null || characterData.HasSpawned)
{
//give the character the items they would've gotten if they had spawned in the main sub
@@ -555,7 +556,7 @@ namespace Barotrauma.Networking
foreach (Skill skill in characterInfo.Job.GetSkills())
{
var skillPrefab = characterInfo.Job.Prefab.Skills.Find(s => skill.Identifier == s.Identifier);
if (skillPrefab == null) { continue; }
if (skillPrefab == null || skill.Level < skillPrefab.LevelRange.End) { continue; }
skill.Level = MathHelper.Lerp(skill.Level, skillPrefab.LevelRange.End, SkillReductionOnDeath);
}
}
@@ -18,10 +18,14 @@ namespace Barotrauma.Networking
{
if (!PropEquals(lastSyncedValue, Value))
{
LastUpdateID = (UInt16)(GameMain.NetLobbyScreen.LastUpdateID);
LastUpdateID = GameMain.NetLobbyScreen.LastUpdateID;
lastSyncedValue = Value;
}
}
public void ForceUpdate()
{
LastUpdateID = GameMain.NetLobbyScreen.LastUpdateID++;
}
}
public static readonly string ClientPermissionsFile = "Data" + Path.DirectorySeparatorChar + "clientpermissions.xml";
@@ -54,6 +58,15 @@ namespace Barotrauma.Networking
LoadClientPermissions();
}
public void ForcePropertyUpdate()
{
UpdateFlag(NetFlags.Properties);
foreach (NetPropertyData property in netProperties.Values)
{
property.ForceUpdate();
}
}
private void WriteNetProperties(IWriteMessage outMsg, Client c)
{
foreach (UInt32 key in netProperties.Keys)
@@ -197,27 +210,32 @@ namespace Barotrauma.Networking
{
int orBits = incMsg.ReadRangedInteger(0, (int)Barotrauma.MissionType.All) & (int)Barotrauma.MissionType.All;
int andBits = incMsg.ReadRangedInteger(0, (int)Barotrauma.MissionType.All) & (int)Barotrauma.MissionType.All;
GameMain.NetLobbyScreen.MissionType = (Barotrauma.MissionType)(((int)GameMain.NetLobbyScreen.MissionType | orBits) & andBits);
GameMain.NetLobbyScreen.MissionType = (MissionType)(((int)GameMain.NetLobbyScreen.MissionType | orBits) & andBits);
int traitorSetting = (int)TraitorsEnabled + incMsg.ReadByte() - 1;
if (traitorSetting < 0) traitorSetting = 2;
if (traitorSetting > 2) traitorSetting = 0;
if (traitorSetting < 0) { traitorSetting = 2; }
if (traitorSetting > 2) { traitorSetting = 0; }
TraitorsEnabled = (YesNoMaybe)traitorSetting;
int botCount = BotCount + incMsg.ReadByte() - 1;
if (botCount < 0) botCount = MaxBotCount;
if (botCount > MaxBotCount) botCount = 0;
if (botCount < 0) { botCount = MaxBotCount; }
if (botCount > MaxBotCount) { botCount = 0; }
BotCount = botCount;
int botSpawnMode = (int)BotSpawnMode + incMsg.ReadByte() - 1;
if (botSpawnMode < 0) botSpawnMode = 1;
if (botSpawnMode > 1) botSpawnMode = 0;
if (botSpawnMode < 0) { botSpawnMode = 1; }
if (botSpawnMode > 1) { botSpawnMode = 0; }
BotSpawnMode = (BotSpawnMode)botSpawnMode;
float levelDifficulty = incMsg.ReadSingle();
if (levelDifficulty >= 0.0f) SelectedLevelDifficulty = levelDifficulty;
if (levelDifficulty >= 0.0f) { SelectedLevelDifficulty = levelDifficulty; }
UseRespawnShuttle = incMsg.ReadBoolean();
bool changedUseRespawnShuttle = incMsg.ReadBoolean();
bool useRespawnShuttle = incMsg.ReadBoolean();
if (changedUseRespawnShuttle)
{
UseRespawnShuttle = useRespawnShuttle;
}
bool changedAutoRestart = incMsg.ReadBoolean();
bool autoRestart = incMsg.ReadBoolean();
@@ -216,6 +216,14 @@ namespace Barotrauma
}
}
public void ResetVotes(IEnumerable<Client> connectedClients, bool resetKickVotes)
{
foreach (Client client in connectedClients)
{
client.ResetVotes(resetKickVotes);
}
}
public void ServerRead(IReadMessage inc, Client sender)
{
if (GameMain.Server == null || sender == null) { return; }
@@ -192,7 +192,7 @@ namespace Barotrauma
public override void Select()
{
base.Select();
GameMain.Server.Voting.ResetVotes(GameMain.Server.ConnectedClients);
GameMain.Server.Voting.ResetVotes(GameMain.Server.ConnectedClients, resetKickVotes: false);
if (SelectedMode != GameModePreset.MultiPlayerCampaign && GameMain.GameSession?.GameMode is CampaignMode && Selected == this)
{
GameMain.GameSession = null;
@@ -53,12 +53,13 @@ namespace Barotrauma.Steam
Steamworks.SteamServer.Passworded = server.ServerSettings.HasPassword;
Steamworks.SteamServer.MapName = GameMain.NetLobbyScreen?.SelectedSub?.DisplayName?.Value ?? "";
Steamworks.SteamServer.SetKey("haspassword", server.ServerSettings.HasPassword.ToString());
Steamworks.SteamServer.SetKey("message", GameMain.Server.ServerSettings.ServerMessageText);
Steamworks.SteamServer.SetKey("message", server.ServerSettings.ServerMessageText);
Steamworks.SteamServer.SetKey("version", GameMain.Version.ToString());
Steamworks.SteamServer.SetKey("playercount", GameMain.Server.ConnectedClients.Count.ToString());
Steamworks.SteamServer.SetKey("playercount", server.ConnectedClients.Count.ToString());
Steamworks.SteamServer.SetKey("contentpackage", string.Join(",", contentPackages.Select(cp => cp.Name)));
Steamworks.SteamServer.SetKey("contentpackagehash", string.Join(",", contentPackages.Select(cp => cp.Hash.StringRepresentation)));
Steamworks.SteamServer.SetKey("contentpackageid", string.Join(",", contentPackages.Select(cp => cp.SteamWorkshopId)));
Steamworks.SteamServer.SetKey("contentpackageid", string.Join(",", contentPackages.Select(cp
=> cp.UgcId.TryUnwrap(out var ugcId) ? ugcId.StringRepresentation : "")));
Steamworks.SteamServer.SetKey("modeselectionmode", server.ServerSettings.ModeSelectionMode.ToString());
Steamworks.SteamServer.SetKey("subselectionmode", server.ServerSettings.SubSelectionMode.ToString());
Steamworks.SteamServer.SetKey("voicechatenabled", server.ServerSettings.VoiceChatEnabled.ToString());