Build 0.21.6.0 (1.0 pre-patch)

This commit is contained in:
Regalis11
2023-01-31 18:08:26 +02:00
parent e1c04bc31d
commit cf9ecd35b3
231 changed files with 4479 additions and 2276 deletions
@@ -0,0 +1,26 @@
using Microsoft.Xna.Framework;
namespace Barotrauma.Utils;
public struct CoordinateSpace2D
{
public static readonly CoordinateSpace2D CanonicalSpace = new CoordinateSpace2D
{
Origin = Vector2.Zero,
I = Vector2.UnitX,
J = Vector2.UnitY
};
public Vector2 Origin;
public Vector2 I;
public Vector2 J;
public Matrix LocalToCanonical
=> new Matrix(
m11: I.X, m12: I.Y, m13: 0f, m14: 0f,
m21: J.X, m22: J.Y, m23: 0f, m24: 0f,
m31: 0f, m32: 0f, m33: 1f, m34: 0f,
m41: 0f, m42: 0f, m43: 0f, m44: 1f)
* Matrix.CreateTranslation(Origin.X, Origin.Y, 0f);
public Matrix CanonicalToLocal => Matrix.Invert(LocalToCanonical);
}
@@ -19,6 +19,9 @@ namespace Barotrauma
static class MathUtils
{
public static Vector2 DiscardZ(this Vector3 vector)
=> new Vector2(vector.X, vector.Y);
public static float Percentage(float portion, float total)
{
return portion / total * 100;
@@ -212,8 +212,13 @@ namespace Barotrauma
return false;
}
public override int GetHashCode()
{
return ShortRepresentation.GetHashCode(StringComparison.OrdinalIgnoreCase);
}
public static bool operator ==(Md5Hash? a, Md5Hash? b)
=> (a is null == b is null) && (a?.Equals(b) ?? true);
=> Equals(a, b);
public static bool operator !=(Md5Hash? a, Md5Hash? b) => !(a == b);
}
@@ -29,7 +29,7 @@ namespace Barotrauma
}
}
public bool Contains(in T v)
public readonly bool Contains(in T v)
=> start.CompareTo(v) <= 0 && end.CompareTo(v) >= 0;
private void VerifyStartLessThanEnd()
@@ -8,6 +8,7 @@ using System.Xml.Linq;
using System.Text.RegularExpressions;
using Barotrauma.IO;
using Microsoft.Xna.Framework;
using System.Collections.Immutable;
namespace Barotrauma
{
@@ -292,10 +293,11 @@ namespace Barotrauma
}
if (doc?.Root == null)
{
saveInfos.Add(new CampaignMode.SaveInfo()
{
FilePath = file
});
saveInfos.Add(new CampaignMode.SaveInfo(
FilePath: file,
SaveTime: Option.None,
SubmarineName: "",
EnabledContentPackageNames: ImmutableArray<string>.Empty));
}
else
{
@@ -326,13 +328,11 @@ namespace Barotrauma
enabledContentPackageNames.Add(packageName.Replace(@"\|", "|"));
}
saveInfos.Add(new CampaignMode.SaveInfo()
{
FilePath = file,
SubmarineName = doc?.Root?.GetAttributeStringUnrestricted("submarine", ""),
SaveTime = doc.Root.GetAttributeInt("savetime", 0),
EnabledContentPackageNames = enabledContentPackageNames.ToArray(),
});
saveInfos.Add(new CampaignMode.SaveInfo(
FilePath: file,
SaveTime: doc.Root.GetAttributeDateTime("savetime"),
SubmarineName: doc?.Root?.GetAttributeStringUnrestricted("submarine", ""),
EnabledContentPackageNames: enabledContentPackageNames.ToImmutableArray()));
}
}
@@ -77,7 +77,7 @@ namespace Barotrauma.Networking;
*/
[NetworkSerialize]
public readonly record struct Segment<T>(T Identifier, UInt16 Pointer) : INetSerializableStruct where T : struct;
public readonly record struct Segment<T>(T Identifier, int Pointer) : INetSerializableStruct where T : struct;
readonly ref struct SegmentTableWriter<T> where T : struct
{
@@ -94,7 +94,7 @@ readonly ref struct SegmentTableWriter<T> where T : struct
public static SegmentTableWriter<T> StartWriting(IWriteMessage msg)
{
var retVal = new SegmentTableWriter<T>(msg, msg.BitPosition);
msg.WriteUInt16(0); //reserve space for the table pointer
msg.WriteInt32(0); //reserve space for the table pointer
return retVal;
}
@@ -104,28 +104,22 @@ readonly ref struct SegmentTableWriter<T> where T : struct
{
throw new InvalidOperationException($"Too many segments in SegmentTable<{typeof(T).Name}>");
}
if (message.BitPosition - PointerLocation > UInt16.MaxValue)
{
throw new OverflowException(
$"Too much data is being stored in SegmentTable<{typeof(T).Name}> ({segments.Count} segments)");
}
}
public void StartNewSegment(T value)
{
ThrowOnInvalidState();
segments.Add(new Segment<T>(value, (UInt16)(message.BitPosition-PointerLocation)));
segments.Add(new Segment<T>(value, message.BitPosition - PointerLocation));
}
public void Dispose()
{
ThrowOnInvalidState();
int tablePosition = message.BitPosition;
//rewrite the table pointer now that we know where the table ends
message.BitPosition = PointerLocation;
message.WriteUInt16((UInt16)(tablePosition-PointerLocation));
message.WriteInt32(tablePosition - PointerLocation);
//write the table
message.BitPosition = tablePosition;
@@ -274,7 +268,7 @@ readonly ref struct SegmentTableReader<T> where T : struct
ExceptionHandler? exceptionHandler = null)
{
int pointerLocation = msg.BitPosition;
int tablePointer = msg.ReadUInt16();
int tablePointer = msg.ReadInt32();
int tableLocation = pointerLocation + tablePointer;
int returnPosition = msg.BitPosition;
@@ -0,0 +1,266 @@
#nullable enable
using System;
using System.Globalization;
using System.Linq;
namespace Barotrauma
{
public readonly struct SerializableTimeZone
{
/// <summary>
/// Diff from UTC
/// </summary>
public readonly TimeSpan Value;
private readonly int hours;
private readonly int minutes;
private readonly char sign;
public SerializableTimeZone(TimeSpan value)
{
Value = new TimeSpan(
hours: value.Hours,
minutes: value.Minutes,
seconds: 0);
hours = Math.Abs(value.Hours);
minutes = Math.Abs(value.Minutes);
sign = Value.Ticks < 0 ? '-' : '+';
}
public override string ToString()
=> (hours, minutes) switch
{
(0, 0) => "UTC",
(_, 0) => $"UTC{sign}{hours}",
(_, < 10) => $"UTC{sign}{hours}:0{minutes}",
_ => $"UTC{sign}{hours}:{minutes}"
};
public override int GetHashCode()
=> HashCode.Combine(Value.Ticks < 0, hours, minutes);
public static SerializableTimeZone FromDateTime(DateTime dateTime)
{
if (dateTime.Kind == DateTimeKind.Unspecified)
{
throw new InvalidOperationException(
$"Cannot determine timezone for {nameof(DateTime)} " +
$"of unspecified kind");
}
var utcDateTime = dateTime.ToUniversalTime();
return new SerializableTimeZone(dateTime - utcDateTime);
}
public static SerializableTimeZone LocalTimeZone
=> FromDateTime(DateTime.Now);
public static Option<SerializableTimeZone> Parse(string str)
{
if (!str.StartsWith("UTC", StringComparison.OrdinalIgnoreCase))
{
return Option<SerializableTimeZone>.None();
}
string timeZoneStr = str[3..];
bool negative = timeZoneStr.StartsWith("-");
bool valid = negative || timeZoneStr.StartsWith("+");
if (!valid) { return Option<SerializableTimeZone>.None(); }
timeZoneStr = str[4..];
TimeSpan makeTimeSpan(int hours, int minutes)
=> new TimeSpan(
ticks: (hours * TimeSpan.TicksPerHour + minutes * TimeSpan.TicksPerMinute)
* (negative ? -1L : 1L));
if (timeZoneStr.IndexOf(':') is var hrMinSeparator && hrMinSeparator > 0)
{
if (int.TryParse(timeZoneStr[..hrMinSeparator], out int timeZoneHours)
&& int.TryParse(timeZoneStr[(hrMinSeparator + 1)..], out int timeZoneMinutes))
{
return Option<SerializableTimeZone>.Some(
new SerializableTimeZone(makeTimeSpan(timeZoneHours, timeZoneMinutes)));
}
}
else if (int.TryParse(timeZoneStr, out int timeZoneHours))
{
return Option<SerializableTimeZone>.Some(
new SerializableTimeZone(makeTimeSpan(timeZoneHours, 0)));
}
return Option<SerializableTimeZone>.None();
}
}
/// <summary>
/// DateTime wrapper that tries to offer a reliable
/// string representation that's also human-friendly
/// </summary>
public readonly struct SerializableDateTime : IComparable<SerializableDateTime>
{
public bool Equals(SerializableDateTime other)
=> ToUtc().value.Equals(other.ToUtc().value);
public override bool Equals(object? obj)
=> obj is SerializableDateTime other && Equals(other);
private static DateTime UnixEpoch(DateTimeKind kind)
=> new DateTime(1970, 1, 1, 0, 0, 0, kind);
private readonly DateTime value;
public readonly SerializableTimeZone TimeZone;
public SerializableDateTime(DateTime value) : this(value, default)
{
if (value.Kind == DateTimeKind.Unspecified)
{
throw new Exception($"Timezone required when constructing {nameof(SerializableDateTime)} " +
$"from {nameof(DateTime)} of unspecified kind");
}
TimeZone = SerializableTimeZone.FromDateTime(value);
}
public SerializableDateTime(DateTime value, SerializableTimeZone timeZone)
{
this.value = new DateTime(
value.Year, value.Month, value.Day,
value.Hour, value.Minute, value.Second,
DateTimeKind.Unspecified);
TimeZone = timeZone;
}
public static SerializableDateTime LocalNow
=> new SerializableDateTime(DateTime.Now);
public static SerializableDateTime UtcNow
=> new SerializableDateTime(DateTime.UtcNow);
public SerializableDateTime ToUtc()
=> new SerializableDateTime(
DateTime.SpecifyKind(value - TimeZone.Value, DateTimeKind.Utc));
public SerializableDateTime ToLocal()
=> new SerializableDateTime(
DateTime.SpecifyKind(
value - TimeZone.Value + SerializableTimeZone.LocalTimeZone.Value,
DateTimeKind.Local));
public long Ticks => value.Ticks;
public DateTime ToUtcValue() => ToUtc().value;
public DateTime ToLocalValue() => ToLocal().value;
public static SerializableDateTime FromLocalUnixTime(long unixTime)
=> new SerializableDateTime(UnixEpoch(DateTimeKind.Local) + TimeSpan.FromSeconds(unixTime));
public static SerializableDateTime FromUtcUnixTime(long unixTime)
=> new SerializableDateTime(UnixEpoch(DateTimeKind.Utc) + TimeSpan.FromSeconds(unixTime));
public long ToUnixTime()
=> (value - UnixEpoch(value.Kind)).Ticks / TimeSpan.TicksPerSecond;
private static string MakeString(params (long Value, string Suffix)[] parts)
=> string.Join(' ',
parts.Select(p => $"{p.Value.ToString().PadLeft(2, '0')}{p.Suffix}"));
public override string ToString()
=> MakeString(
// Let's go out of our way to tag
// the year, month and day so nobody
// gets confused about the meaning of
// each number
(value.Year, "Y"),
(value.Month, "M"),
(value.Day, "D"),
(value.Hour, "HR"),
(value.Minute, "MIN"),
(value.Second, "SEC"))
+ $" {TimeZone}";
public string ToLocalUserString()
=> ToLocalValue().ToString(CultureInfo.InvariantCulture);
public override int GetHashCode()
=> HashCode.Combine(
value.Year, value.Month, value.Day,
value.Hour, value.Minute, value.Second,
TimeZone.GetHashCode());
public static Option<SerializableDateTime> Parse(string str)
{
if (long.TryParse(str, out long unixTime)
&& unixTime > 0
&& unixTime < (DateTime.MaxValue - UnixEpoch(DateTimeKind.Utc)).TotalSeconds)
{
return Option<SerializableDateTime>.Some(FromUtcUnixTime(unixTime));
}
string[] split = str.Split(' ');
int year = 0; int month = 0; int day = 0;
int hour = 0; int minute = 0; int second = 0;
SerializableTimeZone timeZone = default;
foreach (var part in split)
{
if (SerializableTimeZone.Parse(part).TryUnwrap(out var parsedTimeZone))
{
timeZone = parsedTimeZone;
continue;
}
Identifier suffix = string.Join("", part.Where(char.IsLetter)).ToIdentifier();
if (!part.EndsWith(suffix.Value)) { continue; }
if (!int.TryParse(
part[..^suffix.Value.Length],
NumberStyles.Integer,
CultureInfo.InvariantCulture,
out int value))
{
continue;
}
if (suffix == "Y") { year = value; }
else if (suffix == "M") { month = value; }
else if (suffix == "D") { day = value; }
else if (suffix == "HR") { hour = value; }
else if (suffix == "MIN") { minute = value; }
else if (suffix == "SEC") { second = value; }
}
if (year > 0 && month > 0 && day > 0)
{
return Option<SerializableDateTime>.Some(
new SerializableDateTime(
new DateTime(year, month, day, hour, minute, second),
timeZone));
}
return Option<SerializableDateTime>.None();
}
public int CompareTo(SerializableDateTime other)
=> ToUtc().value.CompareTo(other.ToUtc().value);
public static bool operator <(in SerializableDateTime a, in SerializableDateTime b)
=> a.CompareTo(b) < 0;
public static bool operator >(in SerializableDateTime a, in SerializableDateTime b)
=> a.CompareTo(b) > 0;
public static bool operator ==(in SerializableDateTime a, in SerializableDateTime b)
=> a.CompareTo(b) == 0;
public static bool operator !=(in SerializableDateTime a, in SerializableDateTime b)
=> !(a == b);
public static SerializableDateTime operator +(in SerializableDateTime dt, in TimeSpan ts)
=> new SerializableDateTime(dt.value + ts, dt.TimeZone);
public static SerializableDateTime operator -(in SerializableDateTime dt, in TimeSpan ts)
=> new SerializableDateTime(dt.value - ts, dt.TimeZone);
public static TimeSpan operator -(in SerializableDateTime a, in SerializableDateTime b)
=> a.ToUtc().value - b.ToUtc().value;
}
}
@@ -29,49 +29,6 @@ namespace Barotrauma
static partial class ToolBox
{
internal static class Epoch
{
private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
/// <summary>
/// Returns the current Unix Epoch (Coordinated Universal Time)
/// </summary>
public static int NowUTC
{
get
{
return (int)(DateTime.UtcNow.Subtract(epoch).TotalSeconds);
}
}
/// <summary>
/// Returns the current Unix Epoch (user's current time)
/// </summary>
public static int NowLocal
{
get
{
return (int)(DateTime.Now.Subtract(epoch).TotalSeconds);
}
}
/// <summary>
/// Convert an epoch to a datetime
/// </summary>
public static DateTime ToDateTime(decimal unixTime)
{
return epoch.AddSeconds((long)unixTime);
}
/// <summary>
/// Convert a DateTime to a unix time
/// </summary>
public static uint FromDateTime(DateTime dt)
{
return (uint)(dt.Subtract(epoch).TotalSeconds);
}
}
public static bool IsProperFilenameCase(string filename)
{
//File case only matters on Linux where the filesystem is case-sensitive, so we don't need these errors in release builds.