Build 0.21.6.0

This commit is contained in:
Markus Isberg
2023-01-31 18:01:29 +02:00
parent 697ec52120
commit 25fa5a9552
145 changed files with 2317 additions and 1145 deletions
@@ -14,6 +14,13 @@ namespace Barotrauma
/// </summary>
public bool Discarded;
public void ApplyDeathEffects()
{
RespawnManager.ReduceCharacterSkills(this);
RemoveSavedStatValuesOnDeath();
CauseOfDeath = null;
}
partial void OnSkillChanged(Identifier skillIdentifier, float prevLevel, float newLevel)
{
if (Character == null || Character.Removed) { return; }
@@ -0,0 +1,51 @@
#nullable enable
using System;
using System.Collections.Generic;
using Barotrauma.Networking;
namespace Barotrauma
{
internal static class HealingCooldown
{
private static readonly Dictionary<Client, DateTimeOffset> HealingCooldowns = new();
// Little bit less than client's 0.5 second cooldown to account for latency
private const float CooldownDuration = 0.4f;
public static bool IsOnCooldown(Client client)
{
RemoveExpiredCooldowns();
return HealingCooldowns.ContainsKey(client);
}
public static void SetCooldown(Client client)
{
RemoveExpiredCooldowns();
DateTimeOffset newCooldown = DateTimeOffset.UtcNow.AddSeconds(CooldownDuration);
HealingCooldowns[client] = newCooldown;
}
private static void RemoveExpiredCooldowns()
{
HashSet<Client>? expiredCooldowns = null;
DateTimeOffset now = DateTimeOffset.UtcNow;
foreach (var (client, cooldown) in HealingCooldowns)
{
if (now < cooldown) { continue; }
expiredCooldowns ??= new HashSet<Client>();
expiredCooldowns.Add(client);
}
if (expiredCooldowns is null) { return; }
foreach (Client expiredCooldown in expiredCooldowns)
{
HealingCooldowns.Remove(expiredCooldown);
}
}
}
}
@@ -109,15 +109,22 @@ namespace Barotrauma
return AccountId == other.AccountId && other.ClientAddress == ClientAddress;
}
public void Reset()
{
itemData = null;
healthData = null;
WalletData = null;
}
public void SpawnInventoryItems(Character character, Inventory inventory)
{
if (character == null)
{
throw new System.InvalidOperationException($"Failed to spawn inventory items. Character was null.");
throw new InvalidOperationException($"Failed to spawn inventory items. Character was null.");
}
if (itemData == null)
{
throw new System.InvalidOperationException($"Failed to spawn inventory items for the character \"{character.Name}\". No saved inventory data.");
throw new InvalidOperationException($"Failed to spawn inventory items for the character \"{character.Name}\". No saved inventory data.");
}
character.SpawnInventoryItems(inventory, itemData.FromPackage(null));
}
@@ -240,9 +240,7 @@ namespace Barotrauma
//reduce skills if the character has died
if (characterInfo.CauseOfDeath != null && characterInfo.CauseOfDeath.Type != CauseOfDeathType.Disconnected)
{
RespawnManager.ReduceCharacterSkills(characterInfo);
characterInfo.RemoveSavedStatValuesOnDeath();
characterInfo.CauseOfDeath = null;
characterInfo.ApplyDeathEffects();
}
c.CharacterInfo = characterInfo;
SetClientCharacterData(c);
@@ -254,13 +252,21 @@ namespace Barotrauma
{
if (data.HasSpawned && !GameMain.Server.ConnectedClients.Any(c => data.MatchesClient(c)))
{
var character = Character.CharacterList.Find(c => c.Info == data.CharacterInfo && !c.IsHusk);
if (character != null && (!character.IsDead || character.CauseOfDeath?.Type == CauseOfDeathType.Disconnected))
var character = Character.CharacterList.Find(c => c.Info == data.CharacterInfo && !c.IsHusk);
if (character != null &&
(!character.IsDead || character.CauseOfDeath?.Type == CauseOfDeathType.Disconnected))
{
//character still alive (or killed by Disconnect) -> save it as-is
characterData.RemoveAll(cd => cd.IsDuplicate(data));
data.Refresh(character);
characterData.Add(data);
}
else
{
//character dead or removed -> reduce skills, remove items, health data, etc
data.CharacterInfo.ApplyDeathEffects();
data.Reset();
}
}
}
@@ -1,4 +1,5 @@
using Barotrauma.Items.Components;
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
@@ -19,7 +20,7 @@ namespace Barotrauma
bool accessible = c.Character.CanAccessInventory(this);
if (this is CharacterInventory characterInventory && accessible)
{
if (Owner == null || !(Owner is Character ownerCharacter))
if (Owner == null || Owner is not Character ownerCharacter)
{
accessible = false;
}
@@ -39,7 +40,7 @@ namespace Barotrauma
{
foreach (ushort id in newItemIDs[i])
{
if (!(Entity.FindEntityByID(id) is Item item)) { continue; }
if (Entity.FindEntityByID(id) is not Item item) { continue; }
item.PositionUpdateInterval = 0.0f;
if (item.ParentInventory != null && item.ParentInventory != this)
{
@@ -94,7 +95,15 @@ namespace Barotrauma
{
foreach (ushort id in newItemIDs[i])
{
if (!(Entity.FindEntityByID(id) is Item item) || slots[i].Contains(item)) { continue; }
if (Entity.FindEntityByID(id) is not Item item || slots[i].Contains(item)) { continue; }
if (item.GetComponent<Pickable>() is not Pickable pickable ||
(pickable.IsAttached && !pickable.PickingDone) ||
item.AllowedSlots.None())
{
DebugConsole.AddWarning($"Client {c.Name} tried to pick up a non-pickable item \"{item}\" (parent inventory: {item.ParentInventory?.Owner.ToString() ?? "null"})");
continue;
}
if (GameMain.Server != null)
{
@@ -105,7 +114,7 @@ namespace Barotrauma
(c.Character == null || item.PreviousParentInventory == null || !c.Character.CanAccessInventory(item.PreviousParentInventory)))
{
#if DEBUG || UNSTABLE
DebugConsole.NewMessage($"Client {c.Name} failed to pick up item \"{item}\" (parent inventory: {(item.ParentInventory?.Owner.ToString() ?? "null")}). No access.", Color.Yellow);
DebugConsole.NewMessage($"Client {c.Name} failed to pick up item \"{item}\" (parent inventory: {item.ParentInventory?.Owner.ToString() ?? "null"}). No access.", Color.Yellow);
#endif
if (item.body != null && !c.PendingPositionUpdates.Contains(item))
{
@@ -153,25 +153,27 @@ namespace Barotrauma
(components[containerIndex] as ItemContainer).Inventory.ServerEventRead(msg, c);
break;
case EventType.Treatment:
if (c.Character == null || !c.Character.CanInteractWith(this)) return;
if (c.Character == null || !c.Character.CanInteractWith(this)) { return; }
UInt16 characterID = msg.ReadUInt16();
byte limbIndex = msg.ReadByte();
Character targetCharacter = FindEntityByID(characterID) as Character;
if (targetCharacter == null) break;
if (targetCharacter != c.Character && c.Character.SelectedCharacter != targetCharacter) break;
if (HealingCooldown.IsOnCooldown(c)) { return; }
if (FindEntityByID(characterID) is not Character targetCharacter) { break; }
if (targetCharacter != c.Character && c.Character.SelectedCharacter != targetCharacter) { break; }
HealingCooldown.SetCooldown(c);
Limb targetLimb = limbIndex < targetCharacter.AnimController.Limbs.Length ? targetCharacter.AnimController.Limbs[limbIndex] : null;
if (ContainedItems == null || ContainedItems.All(i => i == null))
if (ContainedItems == null || ContainedItems.All(static i => i == null))
{
GameServer.Log(GameServer.CharacterLogName(c.Character) + " used item " + Name, ServerLog.MessageType.ItemInteraction);
GameServer.Log($"{GameServer.CharacterLogName(c.Character)} used item {Name}", ServerLog.MessageType.ItemInteraction);
}
else
{
GameServer.Log(
GameServer.CharacterLogName(c.Character) + " used item " + Name + " (contained items: " + string.Join(", ", ContainedItems.Select(i => i.Name)) + ")",
$"{GameServer.CharacterLogName(c.Character)} used item {Name} (contained items: {string.Join(", ", ContainedItems.Select(i => i.Name))})",
ServerLog.MessageType.ItemInteraction);
}
@@ -11,10 +11,10 @@ namespace Barotrauma.Networking
{
private static UInt32 LastIdentifier = 0;
public bool Expired => ExpirationTime is { } expirationTime && DateTime.Now > expirationTime;
public bool Expired => ExpirationTime.TryUnwrap(out var expirationTime) && SerializableDateTime.LocalNow > expirationTime;
public BannedPlayer(
string name, Either<Address, AccountId> addressOrAccountId, string reason, DateTime? expirationTime)
string name, Either<Address, AccountId> addressOrAccountId, string reason, Option<SerializableDateTime> expirationTime)
{
this.Name = name;
this.AddressOrAccountId = addressOrAccountId;
@@ -39,6 +39,7 @@ namespace Barotrauma.Networking
{
LoadBanList();
}
RemoveExpired();
}
private void LoadLegacyBanList()
@@ -69,7 +70,7 @@ namespace Barotrauma.Networking
{
if (DateTime.TryParse(separatedLine[2], out DateTime parsedTime))
{
expirationTime = parsedTime;
expirationTime = DateTime.SpecifyKind(parsedTime, DateTimeKind.Local);
}
else
{
@@ -80,15 +81,18 @@ namespace Barotrauma.Networking
}
string reason = separatedLine.Length > 3 ? string.Join(",", separatedLine.Skip(3)) : "";
if (expirationTime.HasValue && DateTime.Now > expirationTime.Value) { continue; }
var serializableExpirationTime
= expirationTime.HasValue
? Option<SerializableDateTime>.Some(new SerializableDateTime(expirationTime.Value))
: Option<SerializableDateTime>.None();
if (AccountId.Parse(endpointStr).TryUnwrap(out var accountId))
{
bannedPlayers.Add(new BannedPlayer(name, accountId, reason, expirationTime));
bannedPlayers.Add(new BannedPlayer(name, accountId, reason, serializableExpirationTime));
}
else if (Address.Parse(endpointStr).TryUnwrap(out var address))
{
bannedPlayers.Add(new BannedPlayer(name, address, reason, expirationTime));
bannedPlayers.Add(new BannedPlayer(name, address, reason, serializableExpirationTime));
}
}
@@ -109,10 +113,22 @@ namespace Barotrauma.Networking
var name = element.GetAttributeString("name", "")!;
var reason = element.GetAttributeString("reason", "")!;
DateTime? expirationTime = DateTime.FromBinary(unchecked((long)element.GetAttributeUInt64("expirationtime", 0)));
if (expirationTime < DateTime.Now) { expirationTime = null; }
var expirationTime = Option<SerializableDateTime>.None();
var expirationTimeStr = element.GetAttributeString("expirationtime", "")!;
if (UInt64.TryParse(expirationTimeStr, out var binaryDateTime) && binaryDateTime > 0)
{
// Backwards compatibility: if expirationtime is stored as an int,
// convert to SerializableDateTime with local timezone because
// banlists used to assume local time
expirationTime = Option<SerializableDateTime>.Some(
new SerializableDateTime(
DateTime.FromBinary((long)binaryDateTime),
SerializableTimeZone.LocalTimeZone));
}
expirationTime = expirationTime.Fallback(SerializableDateTime.Parse(expirationTimeStr));
if (accountId.IsNone() && address.IsNone()) { return Option<BannedPlayer>.None(); }
Either<Address, AccountId> addressOrAccountId = accountId.TryUnwrap(out var accId)
@@ -171,14 +187,14 @@ namespace Barotrauma.Networking
string logMsg = "Banned " + name;
if (!string.IsNullOrEmpty(reason)) { logMsg += ", reason: " + reason; }
if (duration.HasValue) { logMsg += ", duration: " + duration.Value.ToString(); }
if (duration.HasValue) { logMsg += ", duration: " + duration.Value; }
DebugConsole.Log(logMsg);
DateTime? expirationTime = null;
Option<SerializableDateTime> expirationTime = Option<SerializableDateTime>.None();
if (duration.HasValue)
{
expirationTime = DateTime.Now + duration.Value;
expirationTime = Option<SerializableDateTime>.Some(new SerializableDateTime(DateTime.Now + duration.Value));
}
bannedPlayers.Add(new BannedPlayer(name, addressOrAccountId, reason, expirationTime));
@@ -232,9 +248,10 @@ namespace Barotrauma.Networking
{
retVal.SetAttributeValue("address", address.StringRepresentation);
}
if (bannedPlayer.ExpirationTime is { } expirationTime)
if (bannedPlayer.ExpirationTime.TryUnwrap(out var expirationTime))
{
retVal.SetAttributeValue("expirationtime", unchecked((ulong)expirationTime.ToBinary()));
#warning TODO: stop writing binary DateTime representation after this gets on main
retVal.SetAttributeValue("expirationtime", expirationTime.ToLocalValue().ToBinary());
}
return retVal;
@@ -269,11 +286,11 @@ namespace Barotrauma.Networking
outMsg.WriteString(bannedPlayer.Name);
outMsg.WriteUInt32(bannedPlayer.UniqueIdentifier);
outMsg.WriteBoolean(bannedPlayer.ExpirationTime != null);
outMsg.WriteBoolean(bannedPlayer.ExpirationTime.IsSome());
outMsg.WritePadBits();
if (bannedPlayer.ExpirationTime != null)
if (bannedPlayer.ExpirationTime.TryUnwrap(out var expirationTime))
{
double hoursFromNow = (bannedPlayer.ExpirationTime.Value - DateTime.Now).TotalHours;
double hoursFromNow = (expirationTime.ToUtcValue() - DateTime.UtcNow).TotalHours;
outMsg.WriteDouble(hoursFromNow);
}
@@ -140,6 +140,7 @@ namespace Barotrauma.Networking
ServerSettings = new ServerSettings(this, name, port, queryPort, maxPlayers, isPublic, attemptUPnP);
KarmaManager.SelectPreset(ServerSettings.KarmaPreset);
ServerSettings.SetPassword(password);
ServerSettings.SaveSettings();
Voting = new Voting();
@@ -3227,16 +3228,16 @@ namespace Barotrauma.Networking
}
//too far to hear the msg -> don't send
if (string.IsNullOrWhiteSpace(modifiedMessage)) continue;
if (string.IsNullOrWhiteSpace(modifiedMessage)) { continue; }
}
break;
case ChatMessageType.Dead:
//character still alive -> don't send
if (client != senderClient && client.Character != null && !client.Character.IsDead) continue;
if (client != senderClient && client.Character != null && !client.Character.IsDead) { continue; }
break;
case ChatMessageType.Private:
//private msg sent to someone else than this client -> don't send
if (client != targetClient && client != senderClient) continue;
if (client != targetClient && client != senderClient) { continue; }
break;
}
@@ -3272,11 +3273,17 @@ namespace Barotrauma.Networking
//too far to hear the msg -> don't send
if (!client.Character.CanHearCharacter(message.Sender)) { continue; }
}
SendDirectChatMessage(new OrderChatMessage(message.Order, message.TargetCharacter, message.Sender, isNewOrder: message.IsNewOrder), client);
SendDirectChatMessage(new OrderChatMessage(message.Order, message.Text, message.TargetCharacter, message.Sender, isNewOrder: message.IsNewOrder), client);
}
if (!string.IsNullOrWhiteSpace(message.Text))
{
AddChatMessage(new OrderChatMessage(message.Order, message.TargetCharacter, message.Sender, isNewOrder: message.IsNewOrder));
AddChatMessage(new OrderChatMessage(message.Order, message.Text, message.TargetCharacter, message.Sender, isNewOrder: message.IsNewOrder));
if (ChatMessage.CanUseRadio(message.Sender, out var senderRadio))
{
//send to chat-linked wifi components
Signal s = new Signal(message.Text, sender: message.Sender, source: senderRadio.Item);
senderRadio.TransmitSignal(s, sentFromChat: true);
}
}
}
@@ -10,6 +10,7 @@ namespace Barotrauma.Networking
segmentTable.StartNewSegment(ServerNetSegment.ChatMessage);
msg.WriteUInt16(NetStateID);
msg.WriteRangedInteger((int)ChatMessageType.Order, 0, Enum.GetValues(typeof(ChatMessageType)).Length - 1);
msg.WriteString(Text);
msg.WriteString(SenderName);
msg.WriteBoolean(SenderClient != null);
if (SenderClient != null)
@@ -246,7 +246,7 @@ namespace Barotrauma.Networking
{
case ConnectionInitialization.ContentPackageOrder:
DateTime timeNow = DateTime.UtcNow;
SerializableDateTime timeNow = SerializableDateTime.UtcNow;
structToSend = new ServerPeerContentPackageOrderPacket
{
ServerName = GameMain.Server.ServerName,