Merge remote-tracking branch 'upstream/dev' into develop

This commit is contained in:
EvilFactory
2022-12-09 17:33:44 -03:00
416 changed files with 12674 additions and 5862 deletions
@@ -159,7 +159,11 @@ namespace Barotrauma.Networking
return command;
}
public static float GetGarbleAmount(Entity listener, Entity sender, float range, float obstructionmult = 2.0f)
/// <summary>
/// How much messages sent by <paramref name="sender"/> should get garbled. Takes the distance between the entities and optionally the obstructions between them into account (see <paramref name="obstructionMultiplier"/>).
/// </summary>
/// <param name="obstructionMultiplier">Values greater than or equal to 1 cause the message to get garbled more heavily when there's some obstruction between the characters. Values smaller than 1 mean the garbling only depends on distance.</param>
public static float GetGarbleAmount(Entity listener, Entity sender, float range, float obstructionMultiplier = 2.0f)
{
if (listener == null || sender == null)
{
@@ -172,12 +176,12 @@ namespace Barotrauma.Networking
Hull listenerHull = listener == null ? null : Hull.FindHull(listener.WorldPosition);
Hull sourceHull = sender == null ? null : Hull.FindHull(sender.WorldPosition);
if (sourceHull != listenerHull)
if (sourceHull != listenerHull && obstructionMultiplier >= 1.0f)
{
if ((sourceHull == null || !sourceHull.GetConnectedHulls(includingThis: false, searchDepth: 2, ignoreClosedGaps: true).Contains(listenerHull)) &&
Submarine.CheckVisibility(listener.SimPosition, sender.SimPosition) != null)
{
dist = (dist + 100f) * obstructionmult;
dist = (dist + 100f) * obstructionMultiplier;
}
}
if (dist > range) { return 1.0f; }
@@ -192,9 +196,9 @@ namespace Barotrauma.Networking
return ApplyDistanceEffect(listener, Sender, Text, SpeakRange);
}
public static string ApplyDistanceEffect(Entity listener, Entity sender, string text, float range, float obstructionmult = 2.0f)
public static string ApplyDistanceEffect(Entity listener, Entity sender, string text, float range, float obstructionMultiplier = 2.0f)
{
return ApplyDistanceEffect(text, GetGarbleAmount(listener, sender, range, obstructionmult));
return ApplyDistanceEffect(text, GetGarbleAmount(listener, sender, range, obstructionMultiplier));
}
public static string ApplyDistanceEffect(string text, float garbleAmount)
@@ -247,7 +251,7 @@ namespace Barotrauma.Networking
var senderRadio = senderItem.GetComponent<WifiComponent>();
if (!receiverRadio.CanReceive(senderRadio)) { continue; }
string msg = ApplyDistanceEffect(receiverItem, senderItem, message, senderRadio.Range);
string msg = ApplyDistanceEffect(receiverItem, senderItem, message, senderRadio.Range, obstructionMultiplier: 0);
if (sender.SpeechImpediment > 0.0f)
{
//speech impediment doesn't reduce the range when using a radio, but adds extra garbling
@@ -23,13 +23,22 @@ namespace Barotrauma.Networking
{
Success = 0x00,
Heartbeat = 0x01,
RequestShutdown = 0xCC,
Crash = 0xFF
}
private static ManualResetEvent writeManualResetEvent;
private static volatile bool shutDown;
public static bool HasShutDown => shutDown;
private enum StatusEnum
{
NeverStarted,
Active,
RequestedShutDown,
ShutDown
}
private static volatile StatusEnum status = StatusEnum.NeverStarted;
public static bool HasShutDown => status is StatusEnum.ShutDown;
private const int ReadBufferSize = MsgConstants.MTU * 2;
private static byte[] readTempBytes;
@@ -38,7 +47,7 @@ namespace Barotrauma.Networking
private static ConcurrentQueue<byte[]> msgsToWrite;
private static ConcurrentQueue<string> errorsToWrite;
private static ConcurrentQueue<byte[]> msgsToRead;
private static Thread readThread;
@@ -48,6 +57,8 @@ namespace Barotrauma.Networking
private static void PrivateStart()
{
status = StatusEnum.Active;
readIncOffset = 0;
readIncTotal = 0;
@@ -58,8 +69,6 @@ namespace Barotrauma.Networking
msgsToRead = new ConcurrentQueue<byte[]>();
shutDown = false;
readCancellationToken = new CancellationTokenSource();
writeManualResetEvent = new ManualResetEvent(false);
@@ -80,7 +89,13 @@ namespace Barotrauma.Networking
private static void PrivateShutDown()
{
shutDown = true;
if (Thread.CurrentThread != GameMain.MainThread)
{
throw new InvalidOperationException(
$"Cannot call {nameof(ChildServerRelay)}.{nameof(PrivateShutDown)} from a thread other than the main one");
}
if (status is StatusEnum.NeverStarted) { return; }
status = StatusEnum.ShutDown;
writeManualResetEvent?.Set();
readCancellationToken?.Cancel();
readThread?.Join(); readThread = null;
@@ -93,18 +108,18 @@ namespace Barotrauma.Networking
}
private static int ReadIncomingMsgs()
private static Option<int> ReadIncomingMsgs()
{
Task<int> readTask = readStream?.ReadAsync(readTempBytes, 0, readTempBytes.Length, readCancellationToken.Token);
if (readTask is null) { return -1; }
if (readTask is null) { return Option<int>.None(); }
int timeOutMilliseconds = 100;
for (int i = 0; i < 150; i++)
{
if (shutDown)
if (status is StatusEnum.ShutDown)
{
readCancellationToken?.Cancel();
return -1;
return Option<int>.None();
}
try
{
@@ -115,36 +130,36 @@ namespace Barotrauma.Networking
}
catch (AggregateException aggregateException)
{
if (aggregateException.InnerException is OperationCanceledException) { return -1; }
if (aggregateException.InnerException is OperationCanceledException) { return Option<int>.None(); }
throw;
}
catch (OperationCanceledException)
{
return -1;
return Option<int>.None();
}
}
if (readTask.Status != TaskStatus.RanToCompletion)
if (readTask.Status == TaskStatus.RanToCompletion)
{
bool swallowException = shutDown
&& ((readTask.Exception?.InnerException is ObjectDisposedException)
|| (readTask.Exception?.InnerException is System.IO.IOException));
if (swallowException)
{
readCancellationToken?.Cancel();
return -1;
}
throw new Exception(
$"ChildServerRelay readTask did not run to completion: status was {readTask.Status}.",
readTask.Exception);
return Option<int>.Some(readTask.Result);
}
return readTask.Result;
bool swallowException =
status is not StatusEnum.Active
&& readTask.Exception?.InnerException is ObjectDisposedException or System.IO.IOException;
if (swallowException)
{
readCancellationToken?.Cancel();
return Option<int>.None();
}
throw new Exception(
$"ChildServerRelay readTask did not run to completion: status was {readTask.Status}.",
readTask.Exception);
}
private static void CheckPipeConnected(string name, PipeType pipe)
{
if (!(pipe is { IsConnected: true }))
if (status is StatusEnum.Active && pipe is not { IsConnected: true })
{
throw new Exception($"{name} was disconnected unexpectedly");
}
@@ -155,7 +170,7 @@ namespace Barotrauma.Networking
private static void UpdateRead()
{
Span<byte> msgLengthSpan = stackalloc byte[4 + 1];
while (!shutDown)
while (!HasShutDown)
{
CheckPipeConnected(nameof(readStream), readStream);
@@ -165,10 +180,9 @@ namespace Barotrauma.Networking
{
if (readIncOffset >= readIncTotal)
{
readIncTotal = ReadIncomingMsgs();
if (!ReadIncomingMsgs().TryUnwrap(out readIncTotal)) { return false; }
readIncOffset = 0;
if (readIncTotal == 0) { Thread.Yield(); continue; }
if (readIncTotal < 0) { return false; }
}
readTo[i] = readTempBytes[readIncOffset];
readIncOffset++;
@@ -176,7 +190,7 @@ namespace Barotrauma.Networking
return true;
}
if (!readBytes(msgLengthSpan)) { shutDown = true; break; }
if (!readBytes(msgLengthSpan)) { status = StatusEnum.ShutDown; break; }
int msgLength = msgLengthSpan[0]
| (msgLengthSpan[1] << 8)
@@ -184,24 +198,24 @@ namespace Barotrauma.Networking
| (msgLengthSpan[3] << 24);
WriteStatus writeStatus = (WriteStatus)msgLengthSpan[4];
if (msgLength > 0)
{
byte[] msg = new byte[msgLength];
if (!readBytes(msg.AsSpan())) { shutDown = true; break; }
byte[] msg = msgLength > 0 ? new byte[msgLength] : Array.Empty<byte>();
if (msg.Length > 0 && !readBytes(msg.AsSpan())) { status = StatusEnum.ShutDown; break; }
switch (writeStatus)
{
case WriteStatus.Success:
msgsToRead.Enqueue(msg);
break;
case WriteStatus.Heartbeat:
//do nothing
break;
case WriteStatus.Crash:
HandleCrashString(Encoding.UTF8.GetString(msg));
shutDown = true;
break;
}
switch (writeStatus)
{
case WriteStatus.Success:
msgsToRead.Enqueue(msg);
break;
case WriteStatus.Heartbeat:
//do nothing
break;
case WriteStatus.RequestShutdown:
status = StatusEnum.ShutDown;
break;
case WriteStatus.Crash:
HandleCrashString(Encoding.UTF8.GetString(msg));
status = StatusEnum.ShutDown;
break;
}
Thread.Yield();
@@ -210,13 +224,11 @@ namespace Barotrauma.Networking
private static void UpdateWrite()
{
while (!shutDown)
while (!HasShutDown)
{
CheckPipeConnected(nameof(writeStream), writeStream);
byte[] msg;
void writeMsg(WriteStatus writeStatus)
void writeMsg(WriteStatus writeStatus, byte[] msg)
{
// It's SUPER IMPORTANT that this stack allocation
// remains in this local function and is never inlined,
@@ -224,21 +236,19 @@ namespace Barotrauma.Networking
// when the function returns; placing it in the loop
// this method is based around would lead to a stack
// overflow real quick!
Span<byte> bytesToWrite = stackalloc byte[4 + 1 + msg.Length];
Span<byte> headerBytes = stackalloc byte[4 + 1];
bytesToWrite[0] = (byte)(msg.Length & 0xFF);
bytesToWrite[1] = (byte)((msg.Length >> 8) & 0xFF);
bytesToWrite[2] = (byte)((msg.Length >> 16) & 0xFF);
bytesToWrite[3] = (byte)((msg.Length >> 24) & 0xFF);
headerBytes[0] = (byte)(msg.Length & 0xFF);
headerBytes[1] = (byte)((msg.Length >> 8) & 0xFF);
headerBytes[2] = (byte)((msg.Length >> 16) & 0xFF);
headerBytes[3] = (byte)((msg.Length >> 24) & 0xFF);
bytesToWrite[4] = (byte)writeStatus;
Span<byte> msgSlice = bytesToWrite.Slice(4 + 1, msg.Length);
msg.AsSpan().CopyTo(msgSlice);
headerBytes[4] = (byte)writeStatus;
try
{
writeStream?.Write(bytesToWrite);
writeStream?.Write(headerBytes);
writeStream?.Write(msg);
}
catch (Exception exception)
{
@@ -246,7 +256,7 @@ namespace Barotrauma.Networking
{
case ObjectDisposedException _:
case System.IO.IOException _:
if (!shutDown) { throw; }
if (!HasShutDown) { throw; }
break;
default:
throw;
@@ -254,29 +264,34 @@ namespace Barotrauma.Networking
}
}
if (status is StatusEnum.RequestedShutDown)
{
writeMsg(WriteStatus.RequestShutdown, Array.Empty<byte>());
status = StatusEnum.ShutDown;
}
while (errorsToWrite.TryDequeue(out var error))
{
msg = Encoding.UTF8.GetBytes(error);
writeMsg(WriteStatus.Crash);
shutDown = true;
writeMsg(WriteStatus.Crash, Encoding.UTF8.GetBytes(error));
status = StatusEnum.ShutDown;
}
while (msgsToWrite.TryDequeue(out msg))
while (msgsToWrite.TryDequeue(out var msg))
{
writeMsg(WriteStatus.Success);
writeMsg(WriteStatus.Success, msg);
if (shutDown) { break; }
if (HasShutDown) { break; }
}
if (!shutDown)
if (!HasShutDown)
{
writeManualResetEvent.Reset();
if (!writeManualResetEvent.WaitOne(1000))
{
if (shutDown) { return; }
if (HasShutDown) { return; }
//heartbeat to keep the other end alive
msg = Array.Empty<byte>(); writeMsg(WriteStatus.Heartbeat);
writeMsg(WriteStatus.Heartbeat, Array.Empty<byte>());
}
}
}
@@ -284,7 +299,7 @@ namespace Barotrauma.Networking
public static void Write(byte[] msg)
{
if (shutDown) { return; }
if (HasShutDown) { return; }
if (msg.Length > 0x1fff_ffff)
{
@@ -298,7 +313,7 @@ namespace Barotrauma.Networking
public static bool Read(out byte[] msg)
{
if (shutDown) { msg = null; return false; }
if (HasShutDown) { msg = null; return false; }
return msgsToRead.TryDequeue(out msg);
}
@@ -27,7 +27,8 @@ namespace Barotrauma.Networking
SellSubItems = 0x4000,
ManageMap = 0x8000,
ManageHires = 0x10000,
All = 0x1FFFF
ManageBotTalents = 0x20000,
All = 0x3FFFF
}
class PermissionPreset
@@ -40,15 +40,15 @@ namespace Barotrauma.Networking
LUA_NET_MESSAGE
}
enum ClientNetObject
enum ClientNetSegment
{
END_OF_MESSAGE, //self-explanatory
SYNC_IDS, //ids of the last changes the client knows about
CHAT_MESSAGE, //also self-explanatory
VOTE, //you get the idea
CHARACTER_INPUT,
ENTITY_STATE,
SPECTATING_POS
SyncIds, //ids of the last changes the client knows about
ChatMessage, //also self-explanatory
Vote, //you get the idea
CharacterInput,
EntityState,
SpectatingPos
}
enum ClientNetError
@@ -92,16 +92,15 @@ namespace Barotrauma.Networking
LUA_NET_MESSAGE
}
enum ServerNetObject
enum ServerNetSegment
{
END_OF_MESSAGE,
SYNC_IDS,
CHAT_MESSAGE,
VOTE,
CLIENT_LIST,
ENTITY_POSITION,
ENTITY_EVENT,
ENTITY_EVENT_INITIAL
SyncIds,
ChatMessage,
Vote,
ClientList,
EntityPosition,
EntityEvent,
EntityEventInitial
}
enum TraitorMessageType
@@ -17,14 +17,9 @@ namespace Barotrauma.Networking
public LidgrenAddress(IPAddress netAddress)
{
if (IPAddress.IsLoopback(netAddress))
{
NetAddress = IPAddress.Loopback;
}
else
{
NetAddress = netAddress;
}
if (IPAddress.IsLoopback(netAddress)) { netAddress = IPAddress.Loopback; }
if (netAddress.IsIPv4MappedToIPv6) { netAddress = netAddress.MapToIPv4(); }
NetAddress = netAddress;
}
public new static Option<LidgrenAddress> Parse(string endpointStr)
@@ -19,7 +19,7 @@ namespace Barotrauma.Networking
public LidgrenEndpoint(IPEndPoint netEndpoint) : base(new LidgrenAddress(netEndpoint.Address))
{
NetEndpoint = netEndpoint;
NetEndpoint = new IPEndPoint((Address as LidgrenAddress)!.NetAddress, netEndpoint.Port);
}
public new static Option<LidgrenEndpoint> Parse(string endpointStr)
@@ -272,6 +272,9 @@ namespace Barotrauma.Networking
[NetworkSerialize]
public bool IsMandatory;
[NetworkSerialize]
public bool IsVanilla;
private Md5Hash? cachedHash;
private DateTime? cachedDateTime;
@@ -305,6 +308,7 @@ namespace Barotrauma.Networking
? ugcId.StringRepresentation
: "";
IsMandatory = !contentPackage.Files.All(f => f is SubmarineFile);
IsVanilla = contentPackage == ContentPackageManager.VanillaCorePackage;
InstallTimeDiffInSeconds =
contentPackage.InstallTime.TryUnwrap(out var installTime)
? (uint)(installTime - referenceTime).TotalSeconds
@@ -65,7 +65,7 @@ namespace Barotrauma.Networking
public State CurrentState { get; private set; }
public bool UseRespawnPrompt
public static bool UseRespawnPrompt
{
get
{
@@ -199,6 +199,7 @@ namespace Barotrauma.Networking
public void ForceRespawn()
{
ResetShuttle();
RespawnCountdownStarted = true;
RespawnTime = DateTime.Now;
CurrentState = State.Waiting;
}
@@ -274,7 +275,7 @@ namespace Barotrauma.Networking
var powerContainer = item.GetComponent<PowerContainer>();
if (powerContainer != null)
{
powerContainer.Charge = powerContainer.Capacity;
powerContainer.Charge = powerContainer.GetCapacity();
}
var door = item.GetComponent<Door>();
@@ -386,12 +386,17 @@ namespace Barotrauma.Networking
private bool autoRestart;
public bool IsPublic;
private int maxPlayers;
public List<SavedClientPermission> ClientPermissions { get; private set; } = new List<SavedClientPermission>();
[Serialize(true, IsPropertySaveable.Yes)]
public bool IsPublic
{
get;
set;
}
private int tickRate = 20;
[Serialize(20, IsPropertySaveable.Yes)]
public int TickRate
@@ -522,13 +527,20 @@ namespace Barotrauma.Networking
}
}
[Serialize(Barotrauma.LosMode.Opaque, IsPropertySaveable.Yes)]
[Serialize(LosMode.Opaque, IsPropertySaveable.Yes)]
public LosMode LosMode
{
get;
set;
}
[Serialize(EnemyHealthBarMode.ShowAll, IsPropertySaveable.Yes)]
public EnemyHealthBarMode ShowEnemyHealthBars
{
get;
set;
}
[Serialize(800, IsPropertySaveable.Yes)]
public int LinesPerLogFile
{