Release 1.10.5.0 - Autumn Update 2025

This commit is contained in:
Regalis11
2025-09-17 13:44:21 +03:00
parent d13836ce87
commit caa0326cf8
120 changed files with 2584 additions and 635 deletions
@@ -1850,6 +1850,10 @@ namespace Barotrauma
HandleCommandForCrewOrSingleCharacter(args, ToggleGodMode, client);
void ToggleGodMode(Character targetCharacter)
{
if (args.Length > 1 && bool.TryParse(args[1], out bool removeafflictions))
{
if (removeafflictions) { targetCharacter.CharacterHealth.RemoveAllAfflictions(); }
}
targetCharacter.GodMode = godmodeStateOnFirstCharacter ?? !targetCharacter.GodMode;
godmodeStateOnFirstCharacter = targetCharacter.GodMode;
GameMain.NetworkMember.CreateEntityEvent(targetCharacter, new Character.CharacterStatusEventData());
@@ -1530,9 +1530,12 @@ namespace Barotrauma
modeElement.Add(GameMain.GameSession?.EventManager.Save());
}
foreach (Identifier unlockedRecipe in GameMain.GameSession.UnlockedRecipes)
foreach ((CharacterTeamType team, Identifier unlockedRecipe) in GameMain.GameSession.UnlockedRecipes)
{
modeElement.Add(new XElement("unlockedrecipe", new XAttribute("identifier", unlockedRecipe)));
modeElement.Add(
new XElement("unlockedrecipe",
new XAttribute("identifier", unlockedRecipe),
new XAttribute("team", team)));
}
CampaignMetadata?.Save(modeElement);
@@ -7,7 +7,7 @@ namespace Barotrauma.Items.Components
const float NetworkUpdateInterval = 5.0f;
private float networkUpdateTimer;
partial void UpdateProjSpecific(float deltaTime)
partial void UpdateNetworking(float deltaTime)
{
networkUpdateTimer -= deltaTime;
if (networkUpdateTimer <= 0.0f)
@@ -51,6 +51,7 @@ namespace Barotrauma.Items.Components
msg.WriteRangedInteger((int)(flowPercentage / 10.0f), -10, 10);
msg.WriteBoolean(IsActive);
msg.WriteBoolean(Hijacked);
msg.WriteBoolean(Disabled);
if (TargetLevel != null)
{
msg.WriteBoolean(true);
@@ -4,6 +4,8 @@ namespace Barotrauma.Items.Components
{
partial class WifiComponent
{
private readonly int[] networkReceivedChannelMemory = new int[ChannelMemorySize];
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
{
SharedEventWrite(msg);
@@ -11,8 +13,21 @@ namespace Barotrauma.Items.Components
public void ServerEventRead(IReadMessage msg, Client c)
{
SharedEventRead(msg);
int newChannel = msg.ReadRangedInteger(MinChannel, MaxChannel);
for (int i = 0; i < ChannelMemorySize; i++)
{
networkReceivedChannelMemory[i] = msg.ReadRangedInteger(MinChannel, MaxChannel);
}
if (item.CanClientAccess(c))
{
Channel = newChannel;
for (int i = 0; i < ChannelMemorySize; i++)
{
channelMemory[i] = networkReceivedChannelMemory[i];
}
}
// Create an event to notify other clients about the changes
item.CreateServerEvent(this);
}
@@ -59,7 +59,7 @@ namespace Barotrauma
ServerLogRemovedItems();
#region local functions
bool IsInventoryAccessible() => sender.Character.CanAccessInventory(this, IsDragAndDropGiveAllowed ? CharacterInventory.AccessLevel.Allowed : CharacterInventory.AccessLevel.Limited);
bool IsInventoryAccessible() => sender.Character.CanAccessInventory(this, IsDragAndDropGiveAllowed ? CharacterInventory.AccessLevel.AllowFriendly : CharacterInventory.AccessLevel.AllowBotsAndPets);
void CreateCorrectiveNetworkEvent()
{
@@ -177,7 +177,7 @@ namespace Barotrauma
if (item.GetComponent<Pickable>() is not Pickable pickable ||
(pickable.IsAttached && !pickable.PickingDone) || item.AllowedSlots.None() || !item.IsInteractable(sender.Character))
{
DebugConsole.AddWarning($"Client {sender.Name} tried to pick up a non-pickable item \"{item}\" (parent inventory: {item.ParentInventory?.Owner.ToString() ?? "null"})",
DebugConsole.AddWarning($"Client {sender.Name} failed to put \"{item}\" in the inventory of {Owner} (parent inventory: {item.ParentInventory?.Owner.ToString() ?? "null"})",
item.Prefab.ContentPackage);
continue;
}
@@ -187,13 +187,20 @@ namespace Barotrauma
var holdable = item.GetComponent<Holdable>();
if (holdable != null && !holdable.CanBeDeattached()) { continue; }
bool itemAccessDenied = !prevItems.Contains(item) && !itemAccessibility[item] &&
(sender.Character == null || item.PreviousParentInventory == null || !sender.Character.CanAccessInventory(item.PreviousParentInventory));
//more restricted "adding" of handcuffs: we can't allow putting handcuffs on a player just because dragging and dropping is allowed
if (item.HasTag(Tags.HandLockerItem) && !itemAccessDenied)
{
itemAccessDenied =
!sender.Character.CanAccessInventory(this, CharacterInventory.AccessLevel.AllowBotsAndPets);
}
if (itemAccessDenied)
{
#if DEBUG || UNSTABLE
DebugConsole.NewMessage($"Client {sender.Name} failed to pick up item \"{item}\" (parent inventory: {item.ParentInventory?.Owner.ToString() ?? "null"}). No access.", Color.Yellow);
DebugConsole.NewMessage($"Client {sender.Name} failed to put \"{item}\" in the inventory of {Owner} (parent inventory: {item.ParentInventory?.Owner.ToString() ?? "null"}). No access.", Color.Yellow);
#endif
if (item.body != null && !sender.PendingPositionUpdates.Contains(item))
{
@@ -3716,7 +3716,7 @@ namespace Barotrauma.Networking
UpdateVoteStatus();
SendChatMessage(peerDisconnectPacket.ChatMessage(client).Value, ChatMessageType.Server, changeType: peerDisconnectPacket.ConnectionChangeType);
SendChatMessage(peerDisconnectPacket.ChatMessage(client.Name).Value, ChatMessageType.Server, changeType: peerDisconnectPacket.ConnectionChangeType);
UpdateCrewFrame();
@@ -4234,13 +4234,14 @@ namespace Barotrauma.Networking
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
}
public void UnlockRecipe(Identifier identifier)
public void UnlockRecipe(CharacterTeamType team, Identifier identifier)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.WriteByte((byte)ServerPacketHeader.UNLOCKRECIPE);
msg.WriteByte((byte)team);
msg.WriteIdentifier(identifier);
foreach (var client in connectedClients)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.WriteByte((byte)ServerPacketHeader.UNLOCKRECIPE);
msg.WriteIdentifier(identifier);
serverPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
}
}
@@ -137,6 +137,10 @@ namespace Barotrauma.Networking
{
Disconnect(connectedClient.Connection, PeerDisconnectPacket.Banned(banReason));
}
else
{
SendDisconnectMessage(senderEndpoint, PeerDisconnectPacket.Banned(banReason));
}
}
else if (packetHeader.IsDisconnectMessage())
{
@@ -149,9 +153,10 @@ namespace Barotrauma.Networking
Disconnect(connectedClient.Connection, PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
}
}
else if (packetHeader.IsHeartbeatMessage())
else if (packetHeader.IsHeartbeatMessage() || packetHeader.IsDoSProtectionMessage())
{
//message exists solely as a heartbeat, ignore its contents
// ignore these messages, heartbeat messages just need to be acknowledged,
// and only the owner should be sending DoS protection messages
return;
}
else if (packetHeader.IsConnectionInitializationStep())
@@ -206,6 +211,49 @@ namespace Barotrauma.Networking
return;
}
if (packetHeader.IsDoSProtectionMessage())
{
var packet = INetSerializableStruct.Read<DoSProtectionPacket>(inc);
var disconnectPacket = INetSerializableStruct.Read<PeerDisconnectPacket>(inc);
if (packet.Endpoint.TryUnwrap(out var endpoint))
{
PendingClient? pendingClient = pendingClients.Find(c => c.Connection.Endpoint == endpoint);
ClientConnectionData? connectedClientData = connectedClients.Find(c => c.Connection.Endpoint == endpoint);
string? clientName;
if (pendingClient != null)
{
clientName = pendingClient.Name;
if (packet.ShouldBan)
{
BanPendingClient(pendingClient, disconnectPacket.AdditionalInformation, duration: null);
}
RemovePendingClient(pendingClient, disconnectPacket);
}
else if (connectedClientData != null)
{
clientName = connectedClientData.TryGetClientName();
if (packet.ShouldBan)
{
connectedClientData.BanClient(serverSettings, disconnectPacket.AdditionalInformation, duration: null);
}
Disconnect(connectedClientData.Connection, disconnectPacket);
}
else
{
string errorMsg = $"Unable to remove client {endpoint} for triggering DoS protection, client not found in pending or connected clients";
DebugConsole.ThrowError(errorMsg);
GameServer.Log(errorMsg, ServerLog.MessageType.Error);
return;
}
GameServer.Log($"Client {clientName ?? endpoint.ToString()} {(packet.ShouldBan ? "banned" : "disconnected")} due to DoS protection (Sending too many packets).", ServerLog.MessageType.DoSProtection);
GameMain.Server?.SendChatMessage(disconnectPacket.ChatMessage(clientName).Value, ChatMessageType.Server, changeType: disconnectPacket.ConnectionChangeType);
}
return;
}
if (packetHeader.IsConnectionInitializationStep())
{
if (OwnerConnection is null)
@@ -13,7 +13,7 @@ namespace Barotrauma.Networking
protected ServerPeer(Callbacks callbacks, ServerSettings serverSettings) : base(callbacks)
{
this.serverSettings = serverSettings;
this.connectedClients = new List<ConnectedClient>();
this.connectedClients = new List<ClientConnectionData>();
this.pendingClients = new List<PendingClient>();
List<ContentPackage> contentPackageList = new List<ContentPackage>();
@@ -65,21 +65,57 @@ namespace Barotrauma.Networking
}
}
protected sealed class ConnectedClient
protected sealed class ClientConnectionData(TConnection connection)
{
public readonly TConnection Connection;
public readonly MessageFragmenter Fragmenter;
public readonly MessageDefragmenter Defragmenter;
public readonly TConnection Connection = connection;
public readonly MessageFragmenter Fragmenter = new();
public readonly MessageDefragmenter Defragmenter = new();
public ConnectedClient(TConnection connection)
/// <summary>
/// Attempts to retrieve the name of the client associated with this connection
/// from a higher layer.
/// </summary>
/// <returns>Name of the client if found, null otherwise.</returns>
public string? TryGetClientName()
{
Connection = connection;
Fragmenter = new();
Defragmenter = new();
if (GameMain.Server?.ConnectedClients is { } connClients)
{
foreach (Client? client in connClients)
{
if (client?.Connection is not { } clientConnection) { continue; }
if (clientConnection.EndpointMatches(Connection.Endpoint) )
{
return client.Name;
}
}
}
return null;
}
public void BanClient(ServerSettings settings, string banReason, TimeSpan? duration)
{
string clientName = TryGetClientName() ?? "Player";
Connection.AccountInfo.OtherMatchingIds.ForEach(BanAccountId);
if (Connection.AccountInfo.AccountId.TryUnwrap(out var accountId))
{
BanAccountId(accountId);
}
else
{
settings.BanList.BanPlayer(clientName, Connection.Endpoint, banReason, duration);
}
return;
void BanAccountId(AccountId id)
=> settings.BanList.BanPlayer(clientName, id, banReason, duration);
}
}
protected readonly List<ConnectedClient> connectedClients;
protected readonly List<ClientConnectionData> connectedClients;
protected readonly List<PendingClient> pendingClients;
protected readonly ServerSettings serverSettings;
@@ -235,7 +271,7 @@ namespace Barotrauma.Networking
if (pendingClient.InitializationStep == ConnectionInitialization.Success)
{
TConnection newConnection = pendingClient.Connection;
connectedClients.Add(new ConnectedClient(newConnection));
connectedClients.Add(new ClientConnectionData(newConnection));
pendingClients.Remove(pendingClient);
callbacks.OnInitializationComplete.Invoke(newConnection, pendingClient.Name);