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
@@ -205,15 +205,7 @@ namespace Barotrauma.Networking
{
votes[(int)voteType] = value;
}
public void ResetVotes()
{
for (int i = 0; i < votes.Length; i++)
{
votes[i] = null;
}
}
public bool SessionOrAccountIdMatches(string userId)
=> (AccountId.IsSome() && Networking.AccountId.Parse(userId) == AccountId)
|| (byte.TryParse(userId, out byte sessionId) && SessionId == sessionId);
@@ -131,28 +131,29 @@ namespace Barotrauma.Networking
enum DisconnectReason
{
//do not attempt reconnecting with these reasons
Unknown,
Disconnected,
Banned,
Kicked,
ServerShutdown,
ServerCrashed,
ServerFull,
AuthenticationRequired,
SteamAuthenticationRequired,
SteamAuthenticationFailed,
SessionTaken,
TooManyFailedLogins,
NoName,
InvalidName,
NameTaken,
InvalidVersion,
MissingContentPackage,
IncompatibleContentPackage,
SteamP2PError,
//attempt reconnecting with these reasons
Timeout,
ExcessiveDesyncOldEvent,
ExcessiveDesyncRemovedEvent,
SyncTimeout,
SteamP2PError,
SteamP2PTimeOut,
SteamP2PTimeOut
}
abstract partial class NetworkMember
@@ -163,71 +164,38 @@ namespace Barotrauma.Networking
set;
}
public virtual bool IsServer
{
get { return false; }
}
public abstract bool IsServer { get; }
public virtual bool IsClient
{
get { return false; }
}
public abstract bool IsClient { get; }
public abstract void CreateEntityEvent(INetSerializable entity, NetEntityEvent.IData extraData = null);
#if DEBUG
public Dictionary<string, long> messageCount = new Dictionary<string, long>();
#endif
protected ServerSettings serverSettings;
public abstract Voting Voting { get; }
public Voting Voting { get; protected set; }
protected TimeSpan updateInterval;
protected DateTime updateTimer;
protected bool gameStarted;
protected RespawnManager respawnManager;
public bool ShowNetStats;
public float SimulatedRandomLatency, SimulatedMinimumLatency;
public float SimulatedLoss;
public float SimulatedDuplicatesChance;
public int TickRate
{
get { return serverSettings.TickRate; }
set
{
serverSettings.TickRate = MathHelper.Clamp(value, 1, 60);
updateInterval = new TimeSpan(0, 0, 0, 0, MathHelper.Clamp(1000 / serverSettings.TickRate, 1, 500));
}
}
public KarmaManager KarmaManager
{
get;
private set;
} = new KarmaManager();
public bool GameStarted
{
get { return gameStarted; }
}
public bool GameStarted { get; protected set; }
public abstract IReadOnlyList<Client> ConnectedClients { get; }
public RespawnManager RespawnManager
{
get { return respawnManager; }
}
public RespawnManager RespawnManager { get; protected set; }
public ServerSettings ServerSettings { get; protected set; }
public TimeSpan UpdateInterval => new TimeSpan(0, 0, 0, 0, MathHelper.Clamp(1000 / ServerSettings.TickRate, 1, 500));
public ServerSettings ServerSettings
{
get { return serverSettings; }
}
public bool CanUseRadio(Character sender)
{
@@ -277,24 +245,6 @@ namespace Barotrauma.Networking
public abstract void UnbanPlayer(Endpoint endpoint);
public virtual void Update(float deltaTime) { }
public virtual void Quit() { }
/// <summary>
/// Check if the two version are compatible (= if they can play together in multiplayer).
/// Returns null if compatibility could not be determined (invalid/unknown version number).
/// </summary>
public static bool? IsCompatible(string myVersion, string remoteVersion)
{
if (string.IsNullOrEmpty(myVersion) || string.IsNullOrEmpty(remoteVersion)) { return null; }
if (!Version.TryParse(myVersion, out Version myVersionNumber)) { return null; }
if (!Version.TryParse(remoteVersion, out Version remoteVersionNumber)) { return null; }
return IsCompatible(myVersionNumber, remoteVersionNumber);
}
/// <summary>
/// Check if the two version are compatible (= if they can play together in multiplayer).
/// </summary>
@@ -7,7 +7,7 @@ namespace Barotrauma.Networking
public abstract string StringRepresentation { get; }
public static Option<AccountId> Parse(string str)
=> ReflectionUtils.ParseDerived<AccountId>(str);
=> ReflectionUtils.ParseDerived<AccountId, string>(str);
public abstract override bool Equals(object? obj);
@@ -7,7 +7,7 @@ namespace Barotrauma.Networking
public abstract string StringRepresentation { get; }
public static Option<Address> Parse(string str)
=> ReflectionUtils.ParseDerived<Address>(str);
=> ReflectionUtils.ParseDerived<Address, string>(str);
public abstract bool IsLocalHost { get; }
@@ -17,31 +17,27 @@ namespace Barotrauma.Networking
public LidgrenAddress(IPAddress netAddress)
{
NetAddress = netAddress;
if (IPAddress.IsLoopback(netAddress))
{
NetAddress = IPAddress.Loopback;
}
else
{
NetAddress = netAddress;
}
}
public new static Option<LidgrenAddress> Parse(string endpointStr)
{
if (IPAddress.TryParse(endpointStr, out IPAddress? netEndpoint))
if (endpointStr.Equals("localhost", StringComparison.OrdinalIgnoreCase))
{
return Option<LidgrenAddress>.Some(new LidgrenAddress(IPAddress.Loopback));
}
else if (IPAddress.TryParse(endpointStr, out IPAddress? netEndpoint))
{
return Option<LidgrenAddress>.Some(new LidgrenAddress(netEndpoint!));
}
try
{
var resolvedAddresses = Dns.GetHostAddresses(endpointStr);
return resolvedAddresses.Any()
? Option<LidgrenAddress>.Some(new LidgrenAddress(resolvedAddresses.First()))
: Option<LidgrenAddress>.None();
}
catch (SocketException)
{
return Option<LidgrenAddress>.None();
}
catch (ArgumentOutOfRangeException)
{
return Option<LidgrenAddress>.None();
}
return Option<LidgrenAddress>.None();
}
public override bool Equals(object? obj)
@@ -22,12 +22,21 @@ namespace Barotrauma.Networking
public override string ToString() => StringRepresentation;
public static Option<Endpoint> Parse(string str)
=> ReflectionUtils.ParseDerived<Endpoint>(str);
=> ReflectionUtils.ParseDerived<Endpoint, string>(str);
public static bool operator ==(Endpoint a, Endpoint b)
=> a.Equals(b);
public static bool operator ==(Endpoint? a, Endpoint? b)
{
if (a is null)
{
return b is null;
}
else
{
return a.Equals(b);
}
}
public static bool operator !=(Endpoint a, Endpoint b)
public static bool operator !=(Endpoint? a, Endpoint? b)
=> !(a == b);
}
}
@@ -24,24 +24,23 @@ namespace Barotrauma.Networking
public new static Option<LidgrenEndpoint> Parse(string endpointStr)
{
if (IPEndPoint.TryParse(endpointStr, out IPEndPoint? netEndpoint))
{
return Option<LidgrenEndpoint>.Some(new LidgrenEndpoint(netEndpoint!));
}
string hostName = endpointStr;
int port = NetConfig.DefaultPort;
if (endpointStr.Count(c => c == ':') == 1)
{
string[] split = endpointStr.Split(':');
string hostName = split[0];
if (LidgrenAddress.Parse(hostName).TryUnwrap(out var adr)
&& int.TryParse(split[1], out var port))
{
return Option<LidgrenEndpoint>.Some(new LidgrenEndpoint(adr.NetAddress, port));
}
hostName = split[0];
port = int.TryParse(split[1], out var tmpPort) ? tmpPort : port;
}
return LidgrenAddress.Parse(endpointStr)
.Select(adr => new LidgrenEndpoint(adr.NetAddress, NetConfig.DefaultPort));
if (LidgrenAddress.Parse(hostName).TryUnwrap(out var adr))
{
return Option<LidgrenEndpoint>.Some(new LidgrenEndpoint(adr.NetAddress, port));
}
return IPEndPoint.TryParse(endpointStr, out IPEndPoint? netEndpoint)
? Option<LidgrenEndpoint>.Some(new LidgrenEndpoint(netEndpoint))
: Option<LidgrenEndpoint>.None();
}
public override bool Equals(object? obj)
@@ -1,8 +1,8 @@
#nullable enable
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
namespace Barotrauma.Networking
{
@@ -68,7 +68,7 @@ namespace Barotrauma.Networking
public byte[] Buffer;
public readonly int Length => Buffer.Length;
public readonly IReadMessage GetReadMessage() => new ReadWriteMessage(Buffer, 0, Length, copyBuf: false);
public readonly IReadMessage GetReadMessageUncompressed() => new ReadWriteMessage(Buffer, 0, Length, copyBuf: false);
public readonly IReadMessage GetReadMessage(bool isCompressed, NetworkConnection conn) => new ReadOnlyMessage(Buffer, isCompressed, 0, Length, conn);
}
@@ -86,9 +86,164 @@ namespace Barotrauma.Networking
}
[NetworkSerialize]
internal struct PeerDisconnectPacket : INetSerializableStruct
internal readonly struct PeerDisconnectPacket : INetSerializableStruct
{
public string Message;
public readonly DisconnectReason DisconnectReason;
public readonly string AdditionalInformation;
private PeerDisconnectPacket(
DisconnectReason disconnectReason,
string additionalInformation = "")
{
DisconnectReason = disconnectReason;
AdditionalInformation = additionalInformation;
}
public LocalizedString ChatMessage(Client c)
=> DisconnectReason switch
{
DisconnectReason.Disconnected => TextManager.GetWithVariable("ServerMessage.ClientLeftServer",
"[client]", c.Name),
_ => TextManager.GetWithVariables("ChatMsg.DisconnectedWithReason",
("[client]", c.Name),
("[reason]", TextManager.Get($"ChatMsg.DisconnectReason.{DisconnectReason}")))
};
private LocalizedString msgWithReason
=> TextManager.Get($"DisconnectReason.{DisconnectReason}")
+ "\n\n"
+ TextManager.Get("banreason") + " " + AdditionalInformation;
private LocalizedString serverMessage
=> TextManager.Get($"ServerMessage.{DisconnectReason}");
public LocalizedString PopupMessage
=> DisconnectReason switch
{
DisconnectReason.Banned => msgWithReason,
DisconnectReason.Kicked => msgWithReason,
DisconnectReason.InvalidVersion => TextManager.GetWithVariables("DisconnectMessage.InvalidVersion",
("[version]", AdditionalInformation),
("[clientversion]", GameMain.Version.ToString())),
DisconnectReason.ExcessiveDesyncOldEvent => serverMessage,
DisconnectReason.ExcessiveDesyncRemovedEvent => serverMessage,
DisconnectReason.SyncTimeout => serverMessage,
_ => TextManager.Get($"DisconnectReason.{DisconnectReason}").Fallback(TextManager.Get("ConnectionLost"))
};
public LocalizedString ReconnectMessage
=> PopupMessage + "\n\n" + TextManager.Get("ConnectionLostReconnecting");
public PlayerConnectionChangeType ConnectionChangeType
=> DisconnectReason switch
{
DisconnectReason.Banned => PlayerConnectionChangeType.Banned,
DisconnectReason.Kicked => PlayerConnectionChangeType.Kicked,
_ => PlayerConnectionChangeType.Disconnected
};
public bool ShouldAttemptReconnect
=> DisconnectReason
is DisconnectReason.ExcessiveDesyncOldEvent
or DisconnectReason.ExcessiveDesyncRemovedEvent
or DisconnectReason.Timeout
or DisconnectReason.SyncTimeout
or DisconnectReason.SteamP2PTimeOut;
public bool IsEventSyncError
=> DisconnectReason
is DisconnectReason.ExcessiveDesyncOldEvent
or DisconnectReason.ExcessiveDesyncRemovedEvent
or DisconnectReason.SyncTimeout;
public bool ShouldCreateAnalyticsEvent
=> DisconnectReason is not (
DisconnectReason.Disconnected
or DisconnectReason.Banned
or DisconnectReason.Kicked
or DisconnectReason.TooManyFailedLogins
or DisconnectReason.InvalidVersion);
public bool ShouldShowMessage
=> DisconnectReason is not DisconnectReason.Disconnected;
private const string lidgrenSeparator = ":hankey:";
/// <summary>
/// This exists because Lidgren is a piece of shit and
/// doesn't readily support sending anything other than
/// a string through a disconnect packet, so this thing
/// needs a sufficiently nasty string representation that
/// can be decoded with some certainty that it won't get
/// mangled by user input.
/// </summary>
public string ToLidgrenStringRepresentation()
{
static string strToBase64(string str)
=> Convert.ToBase64String(Encoding.UTF8.GetBytes(str));
return DisconnectReason
+ lidgrenSeparator
+ strToBase64(AdditionalInformation);
}
public static Option<PeerDisconnectPacket> FromLidgrenStringRepresentation(string str)
{
// Lidgren has some hardcoded disconnect strings that it uses
// when it detects that a connection has failed. We can handle
// timeouts, so let's look for strings related to that and return
// an appropriate PeerDisconnectPacket.
switch (str)
{
case Lidgren.Network.NetConnection.NoResponseMessage:
case "Connection timed out":
case "Reconnecting":
return Option<PeerDisconnectPacket>.Some(WithReason(DisconnectReason.Timeout));
}
static string base64ToStr(string base64)
=> Encoding.UTF8.GetString(Convert.FromBase64String(base64));
string[] split = str.Split(lidgrenSeparator);
if (split.Length != 2) { return Option<PeerDisconnectPacket>.None(); }
if (!Enum.TryParse(split[0], out DisconnectReason disconnectReason)) { return Option<PeerDisconnectPacket>.None(); }
return Option<PeerDisconnectPacket>.Some(new PeerDisconnectPacket(disconnectReason, base64ToStr(split[1])));
}
public static PeerDisconnectPacket Custom(string customMessage)
=> new PeerDisconnectPacket(
DisconnectReason.Unknown,
customMessage);
public static PeerDisconnectPacket WithReason(DisconnectReason disconnectReason)
=> new PeerDisconnectPacket(disconnectReason);
public static PeerDisconnectPacket Kicked(string? msg)
=> new PeerDisconnectPacket(DisconnectReason.Kicked, msg ?? "");
public static PeerDisconnectPacket Banned(string? msg)
=> new PeerDisconnectPacket(DisconnectReason.Banned, msg ?? "");
public static PeerDisconnectPacket InvalidVersion()
=> new PeerDisconnectPacket(
DisconnectReason.InvalidVersion,
GameMain.Version.ToString());
public static PeerDisconnectPacket SteamP2PError(Steamworks.P2PSessionError error)
=> new PeerDisconnectPacket(
DisconnectReason.SteamP2PError,
error.ToString());
public static PeerDisconnectPacket SteamAuthError(Steamworks.BeginAuthResult error)
=> new PeerDisconnectPacket(
DisconnectReason.SteamAuthenticationFailed,
$"{nameof(Steamworks.BeginAuthResult)}.{error}");
public static PeerDisconnectPacket SteamAuthError(Steamworks.AuthResponse error)
=> new PeerDisconnectPacket(
DisconnectReason.SteamAuthenticationFailed,
$"{nameof(Steamworks.AuthResponse)}.{error}");
}
// ReSharper disable MemberCanBePrivate.Global, FieldCanBeMadeReadOnly.Global, UnassignedField.Global
@@ -101,11 +256,14 @@ namespace Barotrauma.Networking
public byte[] HashBytes = Array.Empty<byte>();
[NetworkSerialize]
public ulong WorkshopId;
public string UgcId = "";
[NetworkSerialize]
public uint InstallTimeDiffInSeconds;
[NetworkSerialize]
public bool IsMandatory;
private Md5Hash? cachedHash;
private DateTime? cachedDateTime;
@@ -130,9 +288,12 @@ namespace Barotrauma.Networking
{
Name = contentPackage.Name;
Hash = contentPackage.Hash;
WorkshopId = contentPackage.SteamWorkshopId;
UgcId = contentPackage.UgcId.TryUnwrap(out var ugcId)
? ugcId.StringRepresentation
: "";
IsMandatory = !contentPackage.Files.All(f => f is SubmarineFile);
InstallTimeDiffInSeconds =
contentPackage.InstallTime is { } installTime
contentPackage.InstallTime.TryUnwrap(out var installTime)
? (uint)(installTime - referenceTime).TotalSeconds
: 0;
}
@@ -333,10 +333,14 @@ namespace Barotrauma.Networking
RespawnCharactersProjSpecific(shuttlePos);
}
public static AfflictionPrefab GetRespawnPenaltyAfflictionPrefab()
{
return AfflictionPrefab.Prefabs.First(a => a.AfflictionType == "respawnpenalty");
}
public static Affliction GetRespawnPenaltyAffliction()
{
var respawnPenaltyAffliction = AfflictionPrefab.Prefabs.First(a => a.AfflictionType == "respawnpenalty");
return respawnPenaltyAffliction?.Instantiate(10.0f);
return GetRespawnPenaltyAfflictionPrefab()?.Instantiate(10.0f);
}
public static void GiveRespawnPenaltyAffliction(Character character)
@@ -388,11 +388,12 @@ namespace Barotrauma.Networking
public List<SavedClientPermission> ClientPermissions { get; private set; } = new List<SavedClientPermission>();
private int tickRate = 20;
[Serialize(20, IsPropertySaveable.Yes)]
public int TickRate
{
get;
set;
get { return tickRate; }
set { tickRate = MathHelper.Clamp(value, 1, 60); }
}
[Serialize(true, IsPropertySaveable.Yes)]
@@ -56,23 +56,5 @@ namespace Barotrauma
return selected;
}
public void ResetVotes(IEnumerable<Client> connectedClients)
{
foreach (Client client in connectedClients)
{
client.ResetVotes();
}
#if CLIENT
foreach (VoteType voteType in Enum.GetValues(typeof(VoteType)))
{
SetVoteCountYes(voteType, 0);
SetVoteCountNo(voteType, 0);
SetVoteCountMax(voteType, 0);
}
UpdateVoteTexts(connectedClients, VoteType.Mode);
UpdateVoteTexts(connectedClients, VoteType.Sub);
#endif
}
}
}