diff --git a/Projects/Server/Items/Item.cs b/Projects/Server/Items/Item.cs index d6d5d0d07..6515aeca4 100644 --- a/Projects/Server/Items/Item.cs +++ b/Projects/Server/Items/Item.cs @@ -687,9 +687,9 @@ namespace Server if (!Stackable && m_Amount > 1) { - Console.WriteLine( - "Warning: 0x{0:X}: Amount changed for non-stackable item '{2}'. ({1})", - Serial.Value, + logger.Warning( + "{Serial}: Amount changed for non-stackable item '{Name}'. ({Amount})", + Serial, m_Amount, GetType().Name ); @@ -3164,27 +3164,29 @@ namespace Server if (item == this) { - Console.WriteLine( - "Warning: Adding item to itself: [0x{0} {1}].AddItem( [0x{2} {3}] )", + var customException = new InvalidOperationException("Adding item to itself"); + logger.Warning( + customException, + "Adding item to itself: ({Serial1} {Item1}).AddItem({Serial2} {Item2})", Serial, GetType().Name, item.Serial, item.GetType().Name ); - Console.WriteLine(new StackTrace()); return; } if (IsChildOf(item)) { - Console.WriteLine( - "Warning: Adding parent item to child: [0x{0} {1}].AddItem( [0x{2} {3}] )", + var customException = new InvalidOperationException("Adding parent item to child"); + logger.Warning( + customException, + "Adding parent item to child: [{Serial1} {Item1}].AddItem( [{Serial2} {Item2}] )", Serial, GetType().Name, item.Serial, item.GetType().Name ); - Console.WriteLine(new StackTrace()); return; } @@ -3270,9 +3272,10 @@ namespace Server if (m_DeltaQueue.Count > 0) { - Utility.PushColor(ConsoleColor.DarkYellow); - Console.WriteLine("Warning: {0} items left in delta queue after processing.", m_DeltaQueue.Count); - Utility.PopColor(); + logger.Warning( + "{Count} items left in delta queue after processing.", + m_DeltaQueue.Count + ); } } diff --git a/Projects/Server/Items/ItemBounds.cs b/Projects/Server/Items/ItemBounds.cs index 4fea6ae67..d2333395b 100644 --- a/Projects/Server/Items/ItemBounds.cs +++ b/Projects/Server/Items/ItemBounds.cs @@ -1,44 +1,60 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ItemBounds.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + using System; using System.IO; +using Server.Logging; -namespace Server +namespace Server; + +public static class ItemBounds { - public static class ItemBounds + private static readonly ILogger logger = LogFactory.GetLogger(typeof(ItemBounds)); + + static ItemBounds() { - static ItemBounds() + Table = new Rectangle2D[TileData.ItemTable.Length]; + + if (!File.Exists("Data/Binary/Bounds.bin")) { - Table = new Rectangle2D[TileData.ItemTable.Length]; - - if (File.Exists("Data/Binary/Bounds.bin")) - { - using var fs = new FileStream( - "Data/Binary/Bounds.bin", - FileMode.Open, - FileAccess.Read, - FileShare.Read - ); - var bin = new BinaryReader(fs); - - var count = Math.Min(Table.Length, (int)(fs.Length / 8)); - - for (var i = 0; i < count; ++i) - { - int xMin = bin.ReadInt16(); - int yMin = bin.ReadInt16(); - int xMax = bin.ReadInt16(); - int yMax = bin.ReadInt16(); - - Table[i].Set(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1); - } - - bin.Close(); - } - else - { - Console.WriteLine("Warning: Data/Binary/Bounds.bin does not exist"); - } + logger.Error("Data/Binary/Bounds.bin does not exist"); + return; } - public static Rectangle2D[] Table { get; } + using var fs = new FileStream( + "Data/Binary/Bounds.bin", + FileMode.Open, + FileAccess.Read, + FileShare.Read + ); + var bin = new BinaryReader(fs); + + var count = Math.Min(Table.Length, (int)(fs.Length / 8)); + + for (var i = 0; i < count; ++i) + { + int xMin = bin.ReadInt16(); + int yMin = bin.ReadInt16(); + int xMax = bin.ReadInt16(); + int yMax = bin.ReadInt16(); + + Table[i].Set(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1); + } + + bin.Close(); } + + public static Rectangle2D[] Table { get; } } diff --git a/Projects/Server/Json/Converters/ClientVersionConverter.cs b/Projects/Server/Json/Converters/ClientVersionConverter.cs index f5f4b41ee..571881ce3 100644 --- a/Projects/Server/Json/Converters/ClientVersionConverter.cs +++ b/Projects/Server/Json/Converters/ClientVersionConverter.cs @@ -17,21 +17,20 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class ClientVersionConverter : JsonConverter - { - public override ClientVersion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.String) - { - return new ClientVersion(reader.GetString()); - } +namespace Server.Json; - throw new JsonException("Value must be a string"); +public class ClientVersionConverter : JsonConverter +{ + public override ClientVersion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + return new ClientVersion(reader.GetString()); } - public override void Write(Utf8JsonWriter writer, ClientVersion value, JsonSerializerOptions options) => - writer.WriteStringValue(value.ToString()); + throw new JsonException("Value must be a string"); } -} + + public override void Write(Utf8JsonWriter writer, ClientVersion value, JsonSerializerOptions options) => + writer.WriteStringValue(value.ToString()); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs b/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs index c5a5b6e00..86e8f503e 100644 --- a/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs +++ b/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs @@ -17,13 +17,12 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class ClientVersionConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(ClientVersion); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new ClientVersionConverter(); - } -} +public class ClientVersionConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(ClientVersion); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new ClientVersionConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/FlagsConverter.cs b/Projects/Server/Json/Converters/FlagsConverter.cs index b174e6681..0eea803b9 100644 --- a/Projects/Server/Json/Converters/FlagsConverter.cs +++ b/Projects/Server/Json/Converters/FlagsConverter.cs @@ -18,133 +18,132 @@ using System.Runtime.CompilerServices; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json +namespace Server.Json; + +public class FlagsConverter : JsonConverter where T : struct, Enum { - public class FlagsConverter : JsonConverter where T : struct, Enum + public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + var flags = 0ul; + var underlyingType = Enum.GetUnderlyingType(typeof(T)); + + while (true) { - var flags = 0ul; - var underlyingType = Enum.GetUnderlyingType(typeof(T)); - - while (true) + reader.Read(); + if (reader.TokenType == JsonTokenType.EndObject) { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndObject) - { - break; - } - - if (reader.TokenType != JsonTokenType.PropertyName) - { - throw new JsonException("Invalid Json structure for Flag object"); - } - - var key = reader.GetString(); - - reader.Read(); - - if (!reader.GetBoolean() || !Enum.TryParse(key, out var val)) - { - continue; - } - - flags |= ConvertToUInt64(underlyingType, val); + break; } - switch (Type.GetTypeCode(underlyingType)) + if (reader.TokenType != JsonTokenType.PropertyName) { - case TypeCode.SByte: - { - var num = (sbyte)flags; - return Unsafe.As(ref num); - } - case TypeCode.Byte: - { - var num = (byte)flags; - return Unsafe.As(ref num); - } - case TypeCode.Int16: - { - var num = (short)flags; - return Unsafe.As(ref num); - } - case TypeCode.UInt16: - { - var num = (ushort)flags; - return Unsafe.As(ref num); - } - case TypeCode.UInt32: - { - var num = (uint)flags; - return Unsafe.As(ref num); - } - case TypeCode.Int64: - { - var num = (long)flags; - return Unsafe.As(ref num); - } - case TypeCode.UInt64: - { - return Unsafe.As(ref flags); - } - default: - { - var num = (int)flags; - return Unsafe.As(ref num); - } + throw new JsonException("Invalid Json structure for Flag object"); } + + var key = reader.GetString(); + + reader.Read(); + + if (!reader.GetBoolean() || !Enum.TryParse(key, out var val)) + { + continue; + } + + flags |= ConvertToUInt64(underlyingType, val); } - public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + switch (Type.GetTypeCode(underlyingType)) { - writer.WriteStartObject(); - var underlyingType = Enum.GetUnderlyingType(typeof(T)); - var intValue = ConvertToUInt64(underlyingType, value); - - foreach (var flagName in Enum.GetNames(typeof(T))) - { - var flagValue = Enum.Parse(flagName, false); - var flag = ConvertToUInt64(underlyingType, flagValue); - - // Do not write out multi-bit values. This is a custom behavior - if (flag > 0 && (flag & (flag - 1)) == 0) + case TypeCode.SByte: { - writer.WriteBoolean(flagName, (intValue & flag) == flag); + var num = (sbyte)flags; + return Unsafe.As(ref num); + } + case TypeCode.Byte: + { + var num = (byte)flags; + return Unsafe.As(ref num); + } + case TypeCode.Int16: + { + var num = (short)flags; + return Unsafe.As(ref num); + } + case TypeCode.UInt16: + { + var num = (ushort)flags; + return Unsafe.As(ref num); + } + case TypeCode.UInt32: + { + var num = (uint)flags; + return Unsafe.As(ref num); + } + case TypeCode.Int64: + { + var num = (long)flags; + return Unsafe.As(ref num); + } + case TypeCode.UInt64: + { + return Unsafe.As(ref flags); + } + default: + { + var num = (int)flags; + return Unsafe.As(ref num); } - } - - writer.WriteEndObject(); } - - private static ulong ConvertToUInt64(Type underlyingType, object value) => - Type.GetTypeCode(underlyingType) switch - { - TypeCode.SByte => (ulong)(sbyte)value, - TypeCode.Byte => (byte)value, - TypeCode.Int16 => (ulong)(short)value, - TypeCode.UInt16 => (ushort)value, - TypeCode.Int32 => (ulong)(int)value, - TypeCode.UInt32 => (uint)value, - TypeCode.Int64 => (ulong)(long)value, - TypeCode.UInt64 => (ulong)value, - _ => throw new InvalidOperationException() - }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong GetUnderlyingTypeLength(TypeCode typeCode) => - typeCode switch - { - TypeCode.Byte => 8, - TypeCode.SByte => 8, - TypeCode.Int16 => 16, - TypeCode.UInt16 => 16, - TypeCode.Char => 16, - TypeCode.Int32 => 32, - TypeCode.UInt32 => 32, - TypeCode.Int64 => 64, - TypeCode.UInt64 => 64, - _ => 64 - }; } -} + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + var underlyingType = Enum.GetUnderlyingType(typeof(T)); + var intValue = ConvertToUInt64(underlyingType, value); + + foreach (var flagName in Enum.GetNames(typeof(T))) + { + var flagValue = Enum.Parse(flagName, false); + var flag = ConvertToUInt64(underlyingType, flagValue); + + // Do not write out multi-bit values. This is a custom behavior + if (flag > 0 && (flag & (flag - 1)) == 0) + { + writer.WriteBoolean(flagName, (intValue & flag) == flag); + } + } + + writer.WriteEndObject(); + } + + private static ulong ConvertToUInt64(Type underlyingType, object value) => + Type.GetTypeCode(underlyingType) switch + { + TypeCode.SByte => (ulong)(sbyte)value, + TypeCode.Byte => (byte)value, + TypeCode.Int16 => (ulong)(short)value, + TypeCode.UInt16 => (ushort)value, + TypeCode.Int32 => (ulong)(int)value, + TypeCode.UInt32 => (uint)value, + TypeCode.Int64 => (ulong)(long)value, + TypeCode.UInt64 => (ulong)value, + _ => throw new InvalidOperationException() + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong GetUnderlyingTypeLength(TypeCode typeCode) => + typeCode switch + { + TypeCode.Byte => 8, + TypeCode.SByte => 8, + TypeCode.Int16 => 16, + TypeCode.UInt16 => 16, + TypeCode.Char => 16, + TypeCode.Int32 => 32, + TypeCode.UInt32 => 32, + TypeCode.Int64 => 64, + TypeCode.UInt64 => 64, + _ => 64 + }; +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/GuidConverter.cs b/Projects/Server/Json/Converters/GuidConverter.cs index b6c373094..53034d2d4 100644 --- a/Projects/Server/Json/Converters/GuidConverter.cs +++ b/Projects/Server/Json/Converters/GuidConverter.cs @@ -17,21 +17,20 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class GuidConverter : JsonConverter - { - public override Guid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (Guid.TryParse(reader.GetString()!, out var guid)) - { - return guid; - } +namespace Server.Json; - throw new JsonException("Guid must be in the correct format"); +public class GuidConverter : JsonConverter +{ + public override Guid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (Guid.TryParse(reader.GetString()!, out var guid)) + { + return guid; } - public override void Write(Utf8JsonWriter writer, Guid value, JsonSerializerOptions options) - => writer.WriteStringValue(value.ToString()); + throw new JsonException("Guid must be in the correct format"); } -} + + public override void Write(Utf8JsonWriter writer, Guid value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToString()); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/GuidConverterFactory.cs b/Projects/Server/Json/Converters/GuidConverterFactory.cs index e0ba7c4ce..8ec9a5444 100644 --- a/Projects/Server/Json/Converters/GuidConverterFactory.cs +++ b/Projects/Server/Json/Converters/GuidConverterFactory.cs @@ -17,13 +17,12 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class GuidConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Guid); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new GuidConverter(); - } -} +public class GuidConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Guid); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new GuidConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/IPEndPointConverter.cs b/Projects/Server/Json/Converters/IPEndPointConverter.cs index d6c74d0da..41b4bab66 100644 --- a/Projects/Server/Json/Converters/IPEndPointConverter.cs +++ b/Projects/Server/Json/Converters/IPEndPointConverter.cs @@ -18,21 +18,20 @@ using System.Net; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class IPEndPointConverter : JsonConverter - { - public override IPEndPoint Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (IPEndPoint.TryParse(reader.GetString()!, out var ipep)) - { - return ipep; - } +namespace Server.Json; - throw new JsonException("IPEndPoint must be in the correct format"); +public class IPEndPointConverter : JsonConverter +{ + public override IPEndPoint Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (IPEndPoint.TryParse(reader.GetString()!, out var ipep)) + { + return ipep; } - public override void Write(Utf8JsonWriter writer, IPEndPoint value, JsonSerializerOptions options) - => writer.WriteStringValue(value.ToString()); + throw new JsonException("IPEndPoint must be in the correct format"); } -} + + public override void Write(Utf8JsonWriter writer, IPEndPoint value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToString()); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/IPEndPointConverterFactory.cs b/Projects/Server/Json/Converters/IPEndPointConverterFactory.cs index 3a05cb364..3906cbb4d 100644 --- a/Projects/Server/Json/Converters/IPEndPointConverterFactory.cs +++ b/Projects/Server/Json/Converters/IPEndPointConverterFactory.cs @@ -18,13 +18,12 @@ using System.Net; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class IPEndPointConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(IPEndPoint); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new IPEndPointConverter(); - } -} +public class IPEndPointConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(IPEndPoint); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new IPEndPointConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/MapConverter.cs b/Projects/Server/Json/Converters/MapConverter.cs index 832c366ab..a220e5ba0 100644 --- a/Projects/Server/Json/Converters/MapConverter.cs +++ b/Projects/Server/Json/Converters/MapConverter.cs @@ -17,19 +17,18 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class MapConverter : JsonConverter - { - public override Map Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => - reader.TokenType switch - { - JsonTokenType.String => Map.Parse(reader.GetString()), - JsonTokenType.Number => Map.Maps[reader.GetInt32()], - _ => throw new JsonException("Value must be a number or string") - }; +namespace Server.Json; - public override void Write(Utf8JsonWriter writer, Map value, JsonSerializerOptions options) => - writer.WriteStringValue(value.Name); - } -} +public class MapConverter : JsonConverter +{ + public override Map Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + reader.TokenType switch + { + JsonTokenType.String => Map.Parse(reader.GetString()), + JsonTokenType.Number => Map.Maps[reader.GetInt32()], + _ => throw new JsonException("Value must be a number or string") + }; + + public override void Write(Utf8JsonWriter writer, Map value, JsonSerializerOptions options) => + writer.WriteStringValue(value.Name); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/MapConverterFactory.cs b/Projects/Server/Json/Converters/MapConverterFactory.cs index 9e00e463e..a6d312c52 100644 --- a/Projects/Server/Json/Converters/MapConverterFactory.cs +++ b/Projects/Server/Json/Converters/MapConverterFactory.cs @@ -17,13 +17,12 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class MapConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Map); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new MapConverter(); - } -} +public class MapConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Map); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new MapConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/Point2DConverter.cs b/Projects/Server/Json/Converters/Point2DConverter.cs index 633aeb075..33bc8719b 100644 --- a/Projects/Server/Json/Converters/Point2DConverter.cs +++ b/Projects/Server/Json/Converters/Point2DConverter.cs @@ -17,95 +17,94 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json +namespace Server.Json; + +public class Point2DConverter : JsonConverter { - public class Point2DConverter : JsonConverter + private Point2D DeserializeArray(ref Utf8JsonReader reader) { - private Point2D DeserializeArray(ref Utf8JsonReader reader) + Span data = stackalloc int[2]; + var count = 0; + + while (true) { - Span data = stackalloc int[2]; - var count = 0; - - while (true) + reader.Read(); + if (reader.TokenType == JsonTokenType.EndArray) { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndArray) - { - break; - } - - if (reader.TokenType == JsonTokenType.Number) - { - if (count < 2) - { - data[count] = reader.GetInt32(); - } - - count++; - } + break; } - if (count > 2) + if (reader.TokenType == JsonTokenType.Number) { - throw new JsonException("Point2D must be an array of x, y"); - } + if (count < 2) + { + data[count] = reader.GetInt32(); + } - return new Point2D(data[0], data[1]); + count++; + } } - private Point2D DeserializeObj(ref Utf8JsonReader reader) + if (count > 2) { - Span data = stackalloc int[2]; - - while (true) - { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndObject) - { - break; - } - - if (reader.TokenType != JsonTokenType.PropertyName) - { - throw new JsonException("Invalid json structure for Point2D object"); - } - - var key = reader.GetString(); - - var i = key switch - { - "x" => 0, - "y" => 1, - _ => throw new JsonException($"Invalid property {key} for Point2D") - }; - - reader.Read(); - - if (reader.TokenType != JsonTokenType.Number) - { - throw new JsonException($"Value for {key} must be a number"); - } - - data[i] = reader.GetInt32(); - } - - return new Point2D(data[0], data[1]); + throw new JsonException("Point2D must be an array of x, y"); } - public override Point2D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => - reader.TokenType switch + return new Point2D(data[0], data[1]); + } + + private Point2D DeserializeObj(ref Utf8JsonReader reader) + { + Span data = stackalloc int[2]; + + while (true) + { + reader.Read(); + if (reader.TokenType == JsonTokenType.EndObject) { - JsonTokenType.StartArray => DeserializeArray(ref reader), - JsonTokenType.StartObject => DeserializeObj(ref reader), - _ => throw new JsonException("Invalid Json for Point3D") + break; + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("Invalid json structure for Point2D object"); + } + + var key = reader.GetString(); + + var i = key switch + { + "x" => 0, + "y" => 1, + _ => throw new JsonException($"Invalid property {key} for Point2D") }; - public override void Write(Utf8JsonWriter writer, Point2D value, JsonSerializerOptions options) - { - writer.WriteStartArray(); - writer.WriteNumberValue(value.X); - writer.WriteNumberValue(value.Y); - writer.WriteEndArray(); + reader.Read(); + + if (reader.TokenType != JsonTokenType.Number) + { + throw new JsonException($"Value for {key} must be a number"); + } + + data[i] = reader.GetInt32(); } + + return new Point2D(data[0], data[1]); } -} + + public override Point2D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + reader.TokenType switch + { + JsonTokenType.StartArray => DeserializeArray(ref reader), + JsonTokenType.StartObject => DeserializeObj(ref reader), + _ => throw new JsonException("Invalid Json for Point3D") + }; + + public override void Write(Utf8JsonWriter writer, Point2D value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + writer.WriteNumberValue(value.X); + writer.WriteNumberValue(value.Y); + writer.WriteEndArray(); + } +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/Point2DConverterFactory.cs b/Projects/Server/Json/Converters/Point2DConverterFactory.cs index 7243f3854..3e064bd9d 100644 --- a/Projects/Server/Json/Converters/Point2DConverterFactory.cs +++ b/Projects/Server/Json/Converters/Point2DConverterFactory.cs @@ -17,14 +17,13 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class Point2DConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => - typeToConvert == typeof(Point2D) || typeToConvert == typeof(IPoint2D); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new Point2DConverter(); - } -} +public class Point2DConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => + typeToConvert == typeof(Point2D) || typeToConvert == typeof(IPoint2D); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new Point2DConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/Point3DConverter.cs b/Projects/Server/Json/Converters/Point3DConverter.cs index 3c9281065..d508dcd66 100644 --- a/Projects/Server/Json/Converters/Point3DConverter.cs +++ b/Projects/Server/Json/Converters/Point3DConverter.cs @@ -17,97 +17,96 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json +namespace Server.Json; + +public class Point3DConverter : JsonConverter { - public class Point3DConverter : JsonConverter + private Point3D DeserializeArray(ref Utf8JsonReader reader) { - private Point3D DeserializeArray(ref Utf8JsonReader reader) + Span data = stackalloc int[3]; + var count = 0; + + while (true) { - Span data = stackalloc int[3]; - var count = 0; - - while (true) + reader.Read(); + if (reader.TokenType == JsonTokenType.EndArray) { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndArray) - { - break; - } - - if (reader.TokenType == JsonTokenType.Number) - { - if (count < 3) - { - data[count] = reader.GetInt32(); - } - - count++; - } + break; } - if (count > 3) + if (reader.TokenType == JsonTokenType.Number) { - throw new JsonException("Point3D must be an array of x, y, z"); - } + if (count < 3) + { + data[count] = reader.GetInt32(); + } - return new Point3D(data[0], data[1], data[2]); + count++; + } } - private Point3D DeserializeObj(ref Utf8JsonReader reader) + if (count > 3) { - Span data = stackalloc int[3]; - - while (true) - { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndObject) - { - break; - } - - if (reader.TokenType != JsonTokenType.PropertyName) - { - throw new JsonException("Invalid json structure for Point3D object"); - } - - var key = reader.GetString(); - - var i = key switch - { - "x" => 0, - "y" => 1, - "z" => 2, - _ => throw new JsonException($"Invalid property {key} for Point3D") - }; - - reader.Read(); - - if (reader.TokenType != JsonTokenType.Number) - { - throw new JsonException($"Value for {key} must be a number"); - } - - data[i] = reader.GetInt32(); - } - - return new Point3D(data[0], data[1], data[2]); + throw new JsonException("Point3D must be an array of x, y, z"); } - public override Point3D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => - reader.TokenType switch + return new Point3D(data[0], data[1], data[2]); + } + + private Point3D DeserializeObj(ref Utf8JsonReader reader) + { + Span data = stackalloc int[3]; + + while (true) + { + reader.Read(); + if (reader.TokenType == JsonTokenType.EndObject) { - JsonTokenType.StartArray => DeserializeArray(ref reader), - JsonTokenType.StartObject => DeserializeObj(ref reader), - _ => throw new JsonException("Invalid Json for Point3D") + break; + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("Invalid json structure for Point3D object"); + } + + var key = reader.GetString(); + + var i = key switch + { + "x" => 0, + "y" => 1, + "z" => 2, + _ => throw new JsonException($"Invalid property {key} for Point3D") }; - public override void Write(Utf8JsonWriter writer, Point3D value, JsonSerializerOptions options) - { - writer.WriteStartArray(); - writer.WriteNumberValue(value.X); - writer.WriteNumberValue(value.Y); - writer.WriteNumberValue(value.Z); - writer.WriteEndArray(); + reader.Read(); + + if (reader.TokenType != JsonTokenType.Number) + { + throw new JsonException($"Value for {key} must be a number"); + } + + data[i] = reader.GetInt32(); } + + return new Point3D(data[0], data[1], data[2]); } -} + + public override Point3D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + reader.TokenType switch + { + JsonTokenType.StartArray => DeserializeArray(ref reader), + JsonTokenType.StartObject => DeserializeObj(ref reader), + _ => throw new JsonException("Invalid Json for Point3D") + }; + + public override void Write(Utf8JsonWriter writer, Point3D value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + writer.WriteNumberValue(value.X); + writer.WriteNumberValue(value.Y); + writer.WriteNumberValue(value.Z); + writer.WriteEndArray(); + } +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/Point3DConverterFactory.cs b/Projects/Server/Json/Converters/Point3DConverterFactory.cs index 1b8097cfa..f7dd0835f 100644 --- a/Projects/Server/Json/Converters/Point3DConverterFactory.cs +++ b/Projects/Server/Json/Converters/Point3DConverterFactory.cs @@ -17,14 +17,13 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class Point3DConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => - typeToConvert == typeof(Point3D) || typeToConvert == typeof(IPoint3D); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new Point3DConverter(); - } -} +public class Point3DConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => + typeToConvert == typeof(Point3D) || typeToConvert == typeof(IPoint3D); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new Point3DConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/Rectangle3DConverter.cs b/Projects/Server/Json/Converters/Rectangle3DConverter.cs index f957337e5..b614c8196 100644 --- a/Projects/Server/Json/Converters/Rectangle3DConverter.cs +++ b/Projects/Server/Json/Converters/Rectangle3DConverter.cs @@ -17,168 +17,167 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json +namespace Server.Json; + +public class Rectangle3DConverter : JsonConverter { - public class Rectangle3DConverter : JsonConverter + private Rectangle3D DeserializeArray(ref Utf8JsonReader reader) { - private Rectangle3D DeserializeArray(ref Utf8JsonReader reader) + Span data = stackalloc int[6]; + var count = 0; + + while (true) { - Span data = stackalloc int[6]; - var count = 0; - - while (true) + reader.Read(); + if (reader.TokenType == JsonTokenType.EndArray) { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndArray) - { - break; - } - - if (reader.TokenType == JsonTokenType.Number) - { - if (count < 6) - { - data[count] = reader.GetInt32(); - } - - count++; - } + break; } - if (count > 6) + if (reader.TokenType == JsonTokenType.Number) { - throw new JsonException("Rectangle3D must be an array of x, y, z, h, w, d"); - } + if (count < 6) + { + data[count] = reader.GetInt32(); + } - return new Rectangle3D(data[0], data[1], data[2], data[3], data[4], data[5]); + count++; + } } - private Rectangle3D DeserializeObj(ref Utf8JsonReader reader, JsonSerializerOptions options) + if (count > 6) { - Span data = stackalloc int[6]; + throw new JsonException("Rectangle3D must be an array of x, y, z, h, w, d"); + } - // 0 - xyzwhd, 1 - x1y1z1x2y2z2, 2 - start/end - var objType = -1; - var hasZ = false; + return new Rectangle3D(data[0], data[1], data[2], data[3], data[4], data[5]); + } - while (true) + private Rectangle3D DeserializeObj(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + Span data = stackalloc int[6]; + + // 0 - xyzwhd, 1 - x1y1z1x2y2z2, 2 - start/end + var objType = -1; + var hasZ = false; + + while (true) + { + reader.Read(); + if (reader.TokenType == JsonTokenType.EndObject) { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndObject) - { - break; - } + break; + } - if (reader.TokenType != JsonTokenType.PropertyName) - { - throw new JsonException("Invalid json structure for Rectangle3D object"); - } + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("Invalid json structure for Rectangle3D object"); + } - var key = reader.GetString(); + var key = reader.GetString(); - reader.Read(); + reader.Read(); - if (key is "start" or "end") - { - if (objType > -1 && objType != 2) - { - throw new JsonException("Rectangle3D must have a start/end, or x/y/z/w/h/d, but not both."); - } - - objType = 2; - - var point3D = reader.ToObject(options); - var offset = key == "end" ? 3 : 0; - data[0 + offset] = point3D.X; - data[1 + offset] = point3D.Y; - // We can't do implicit z-level. Abandon using Point3D deserialization? - data[2 + offset] = point3D.Z; - continue; - } - - var i = key switch - { - "x" => 0, - "y" => 1, - "z" => 2, - "w" => 3, - "width" => 3, - "h" => 4, - "height" => 4, - "d" => 5, - "depth" => 5, - "x1" => 10, - "y1" => 11, - "z1" => 12, - "x2" => 13, - "y2" => 14, - "z2" => 15, - _ => throw new JsonException($"Invalid property {key} for Rectangle3D") - }; - - if (i < 10) - { - if (objType > -1 && objType != 0) - { - throw new JsonException("Rectangle3D must have a start/end, or x/y/z/w/h/d, but not both."); - } - - objType = 0; - data[i] = reader.GetInt32(); - if (i == 2) - { - hasZ = true; - } - - continue; - } - - if (objType > -1 && objType != 1) + if (key is "start" or "end") + { + if (objType > -1 && objType != 2) { throw new JsonException("Rectangle3D must have a start/end, or x/y/z/w/h/d, but not both."); } - objType = 1; - data[i - 10] = reader.GetInt32(); - if (i is 12 or 15) + objType = 2; + + var point3D = reader.ToObject(options); + var offset = key == "end" ? 3 : 0; + data[0 + offset] = point3D.X; + data[1 + offset] = point3D.Y; + // We can't do implicit z-level. Abandon using Point3D deserialization? + data[2 + offset] = point3D.Z; + continue; + } + + var i = key switch + { + "x" => 0, + "y" => 1, + "z" => 2, + "w" => 3, + "width" => 3, + "h" => 4, + "height" => 4, + "d" => 5, + "depth" => 5, + "x1" => 10, + "y1" => 11, + "z1" => 12, + "x2" => 13, + "y2" => 14, + "z2" => 15, + _ => throw new JsonException($"Invalid property {key} for Rectangle3D") + }; + + if (i < 10) + { + if (objType > -1 && objType != 0) + { + throw new JsonException("Rectangle3D must have a start/end, or x/y/z/w/h/d, but not both."); + } + + objType = 0; + data[i] = reader.GetInt32(); + if (i == 2) { hasZ = true; } + + continue; } - if (!hasZ) + if (objType > -1 && objType != 1) { - // Bottom to top? - data[2] = -128; - data[5] = 127; + throw new JsonException("Rectangle3D must have a start/end, or x/y/z/w/h/d, but not both."); } - return objType == 0 - ? new Rectangle3D(data[0], data[1], data[2], data[3], data[4], data[5]) - : new Rectangle3D( - new Point3D(data[0], data[1], data[2]), - new Point3D(data[3], data[4], data[5]) - ); + objType = 1; + data[i - 10] = reader.GetInt32(); + if (i is 12 or 15) + { + hasZ = true; + } } - public override Rectangle3D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => - reader.TokenType switch - { - JsonTokenType.StartArray => DeserializeArray(ref reader), - JsonTokenType.StartObject => DeserializeObj(ref reader, options), - _ => throw new JsonException("Invalid Json for Point3D") - }; - - public override void Write(Utf8JsonWriter writer, Rectangle3D value, JsonSerializerOptions options) + if (!hasZ) { - writer.WriteStartArray(); - writer.WriteNumberValue(value.Start.X); - writer.WriteNumberValue(value.Start.Y); - writer.WriteNumberValue(value.Start.Z); - writer.WriteNumberValue(value.Width); - writer.WriteNumberValue(value.Height); - writer.WriteNumberValue(value.Depth); - writer.WriteEndArray(); + // Bottom to top? + data[2] = -128; + data[5] = 127; } + + return objType == 0 + ? new Rectangle3D(data[0], data[1], data[2], data[3], data[4], data[5]) + : new Rectangle3D( + new Point3D(data[0], data[1], data[2]), + new Point3D(data[3], data[4], data[5]) + ); } -} + + public override Rectangle3D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + reader.TokenType switch + { + JsonTokenType.StartArray => DeserializeArray(ref reader), + JsonTokenType.StartObject => DeserializeObj(ref reader, options), + _ => throw new JsonException("Invalid Json for Point3D") + }; + + public override void Write(Utf8JsonWriter writer, Rectangle3D value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + writer.WriteNumberValue(value.Start.X); + writer.WriteNumberValue(value.Start.Y); + writer.WriteNumberValue(value.Start.Z); + writer.WriteNumberValue(value.Width); + writer.WriteNumberValue(value.Height); + writer.WriteNumberValue(value.Depth); + writer.WriteEndArray(); + } +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/Rectangle3DConverterFactory.cs b/Projects/Server/Json/Converters/Rectangle3DConverterFactory.cs index 9a2f6093f..ca5c44fd8 100644 --- a/Projects/Server/Json/Converters/Rectangle3DConverterFactory.cs +++ b/Projects/Server/Json/Converters/Rectangle3DConverterFactory.cs @@ -17,13 +17,12 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class Rectangle3DConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Rectangle3D); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new Rectangle3DConverter(); - } -} +public class Rectangle3DConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Rectangle3D); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new Rectangle3DConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/TimeSpanConverter.cs b/Projects/Server/Json/Converters/TimeSpanConverter.cs index 3115e2440..17ccb2583 100644 --- a/Projects/Server/Json/Converters/TimeSpanConverter.cs +++ b/Projects/Server/Json/Converters/TimeSpanConverter.cs @@ -17,14 +17,13 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class TimeSpanConverter : JsonConverter - { - public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - => TimeSpan.Parse(reader.GetString()!); +namespace Server.Json; - public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) - => writer.WriteStringValue(value.ToString()); - } -} +public class TimeSpanConverter : JsonConverter +{ + public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => TimeSpan.Parse(reader.GetString()!); + + public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToString()); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/TimeSpanConverterFactory.cs b/Projects/Server/Json/Converters/TimeSpanConverterFactory.cs index f329b5708..9b6164665 100644 --- a/Projects/Server/Json/Converters/TimeSpanConverterFactory.cs +++ b/Projects/Server/Json/Converters/TimeSpanConverterFactory.cs @@ -17,13 +17,12 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class TimeSpanConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(TimeSpan); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new TimeSpanConverter(); - } -} +public class TimeSpanConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(TimeSpan); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new TimeSpanConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/TypeConverter.cs b/Projects/Server/Json/Converters/TypeConverter.cs index d21531317..3b59c6024 100644 --- a/Projects/Server/Json/Converters/TypeConverter.cs +++ b/Projects/Server/Json/Converters/TypeConverter.cs @@ -16,29 +16,31 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; +using Server.Logging; -namespace Server.Json +namespace Server.Json; + +public class TypeConverter : JsonConverter { - public class TypeConverter : JsonConverter + private static readonly ILogger logger = LogFactory.GetLogger(typeof(TypeConverter)); + + public override Type Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - public override Type Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + if (reader.TokenType != JsonTokenType.String) { - if (reader.TokenType != JsonTokenType.String) - { - throw new JsonException("The JSON value could not be converted to System.Type"); - } - - var typeName = reader.GetString(); - var type = AssemblyHandler.FindTypeByName(typeName); - if (type == null) - { - Console.WriteLine("Invalid type {0} deserialized", typeName); - } - - return type; + throw new JsonException("The JSON value could not be converted to System.Type"); } - public override void Write(Utf8JsonWriter writer, Type value, JsonSerializerOptions options) => - writer.WriteStringValue(value.FullName); + var typeName = reader.GetString(); + var type = AssemblyHandler.FindTypeByName(typeName); + if (type == null) + { + logger.Warning("Attempted to deserialize type {Type} which does not exist.", typeName); + } + + return type; } + + public override void Write(Utf8JsonWriter writer, Type value, JsonSerializerOptions options) => + writer.WriteStringValue(value.FullName); } diff --git a/Projects/Server/Json/Converters/TypeConverterFactory.cs b/Projects/Server/Json/Converters/TypeConverterFactory.cs index ea4b41504..4392bba1e 100644 --- a/Projects/Server/Json/Converters/TypeConverterFactory.cs +++ b/Projects/Server/Json/Converters/TypeConverterFactory.cs @@ -17,13 +17,12 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class TypeConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Type); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new TypeConverter(); - } +public class TypeConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Type); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new TypeConverter(); } diff --git a/Projects/Server/Json/Converters/WorldLocationConverter.cs b/Projects/Server/Json/Converters/WorldLocationConverter.cs index 5cdfc909a..9e957f993 100644 --- a/Projects/Server/Json/Converters/WorldLocationConverter.cs +++ b/Projects/Server/Json/Converters/WorldLocationConverter.cs @@ -17,173 +17,172 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json +namespace Server.Json; + +public class WorldLocationConverter : JsonConverter { - public class WorldLocationConverter : JsonConverter + private static Point3DConverter _point3DConverter; + private static MapConverter _mapConverter; + + private WorldLocation DeserializeArray(ref Utf8JsonReader reader) { - private static Point3DConverter _point3DConverter; - private static MapConverter _mapConverter; + Span data = stackalloc int[3]; + var count = 0; + var hasMap = false; + Map map = null; - private WorldLocation DeserializeArray(ref Utf8JsonReader reader) + while (true) { - Span data = stackalloc int[3]; - var count = 0; - var hasMap = false; - Map map = null; - - while (true) + reader.Read(); + if (reader.TokenType == JsonTokenType.EndArray) { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndArray) + break; + } + + if (reader.TokenType == JsonTokenType.Number) + { + if (count < 3) { - break; + data[count] = reader.GetInt32(); } - - if (reader.TokenType == JsonTokenType.Number) + else if (count == 3) { - if (count < 3) - { - data[count] = reader.GetInt32(); - } - else if (count == 3) - { - map = Map.Maps[reader.GetInt32()]; - hasMap = true; - } - - count++; - } - - if (reader.TokenType == JsonTokenType.String) - { - var key = reader.GetString(); - - if (count != 3 || hasMap) - { - throw new JsonException($"Value {key} is not valid for this element."); - } - - map = Map.Parse(key); + map = Map.Maps[reader.GetInt32()]; hasMap = true; - break; } + + count++; } - if (!hasMap || count != 3) + if (reader.TokenType == JsonTokenType.String) { - throw new JsonException("WorldLocation must be an array of x, y, z, and map"); - } - - return new WorldLocation(data[0], data[1], data[2], map); - } - - private WorldLocation DeserializeObj(ref Utf8JsonReader reader, JsonSerializerOptions options) - { - Span data = stackalloc int[3]; - var count = 0; - var hasLoc = false; - var hasXYZ = false; - var hasMap = false; - Map map = null; - - while (true) - { - reader.Read(); - if (reader.TokenType == JsonTokenType.EndObject) - { - break; - } - - if (reader.TokenType != JsonTokenType.PropertyName) - { - throw new JsonException("Invalid Json structure for WorldLocation object"); - } - var key = reader.GetString(); - var i = key switch + if (count != 3 || hasMap) { - "x" => 0, - "y" => 1, - "z" => 2, - "loc" => 3, - "map" => 4, - _ => 5 - }; - - if (i == 5) - { - continue; + throw new JsonException($"Value {key} is not valid for this element."); } - reader.Read(); - - if (i < 3) - { - if (hasLoc) - { - throw new JsonException("WorldLocation must have loc or x, y, z, but not both"); - } - - if (reader.TokenType != JsonTokenType.Number) - { - throw new JsonException($"Value for {key} must be a number"); - } - - hasXYZ = true; - data[i] = reader.GetInt32(); - continue; - } - - if (i == 3) - { - if (hasXYZ) - { - throw new JsonException("WorldLocation must have loc or x, y, z, but not both"); - } - - hasLoc = true; - - _point3DConverter ??= new Point3DConverter(); - - var loc = _point3DConverter.Read(ref reader, typeof(Point3D), options); - data[0] = loc.X; - data[1] = loc.Y; - data[2] = loc.Z; - count = 3; - continue; - } - - _mapConverter ??= new MapConverter(); - map = _mapConverter.Read(ref reader, typeof(Map), options); - + map = Map.Parse(key); hasMap = true; + break; } - - if (!hasMap || count != 3) - { - throw new JsonException("WorldLocation must have an x, y, z, and map properties"); - } - - return new WorldLocation(data[0], data[1], data[2], map); } - public override WorldLocation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => - reader.TokenType switch + if (!hasMap || count != 3) + { + throw new JsonException("WorldLocation must be an array of x, y, z, and map"); + } + + return new WorldLocation(data[0], data[1], data[2], map); + } + + private WorldLocation DeserializeObj(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + Span data = stackalloc int[3]; + var count = 0; + var hasLoc = false; + var hasXYZ = false; + var hasMap = false; + Map map = null; + + while (true) + { + reader.Read(); + if (reader.TokenType == JsonTokenType.EndObject) { - JsonTokenType.StartArray => DeserializeArray(ref reader), - JsonTokenType.StartObject => DeserializeObj(ref reader, options), - _ => throw new JsonException("Invalid Json for Point3D") + break; + } + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("Invalid Json structure for WorldLocation object"); + } + + var key = reader.GetString(); + + var i = key switch + { + "x" => 0, + "y" => 1, + "z" => 2, + "loc" => 3, + "map" => 4, + _ => 5 }; - public override void Write(Utf8JsonWriter writer, WorldLocation value, JsonSerializerOptions options) - { - writer.WriteStartArray(); - writer.WriteNumberValue(value.X); - writer.WriteNumberValue(value.Y); - writer.WriteNumberValue(value.Z); - writer.WriteStringValue(value.Map.ToString()); - writer.WriteEndArray(); + if (i == 5) + { + continue; + } + + reader.Read(); + + if (i < 3) + { + if (hasLoc) + { + throw new JsonException("WorldLocation must have loc or x, y, z, but not both"); + } + + if (reader.TokenType != JsonTokenType.Number) + { + throw new JsonException($"Value for {key} must be a number"); + } + + hasXYZ = true; + data[i] = reader.GetInt32(); + continue; + } + + if (i == 3) + { + if (hasXYZ) + { + throw new JsonException("WorldLocation must have loc or x, y, z, but not both"); + } + + hasLoc = true; + + _point3DConverter ??= new Point3DConverter(); + + var loc = _point3DConverter.Read(ref reader, typeof(Point3D), options); + data[0] = loc.X; + data[1] = loc.Y; + data[2] = loc.Z; + count = 3; + continue; + } + + _mapConverter ??= new MapConverter(); + map = _mapConverter.Read(ref reader, typeof(Map), options); + + hasMap = true; } + + if (!hasMap || count != 3) + { + throw new JsonException("WorldLocation must have an x, y, z, and map properties"); + } + + return new WorldLocation(data[0], data[1], data[2], map); } -} + + public override WorldLocation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + reader.TokenType switch + { + JsonTokenType.StartArray => DeserializeArray(ref reader), + JsonTokenType.StartObject => DeserializeObj(ref reader, options), + _ => throw new JsonException("Invalid Json for Point3D") + }; + + public override void Write(Utf8JsonWriter writer, WorldLocation value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + writer.WriteNumberValue(value.X); + writer.WriteNumberValue(value.Y); + writer.WriteNumberValue(value.Z); + writer.WriteStringValue(value.Map.ToString()); + writer.WriteEndArray(); + } +} \ No newline at end of file diff --git a/Projects/Server/Json/Converters/WorldLocationConverterFactory.cs b/Projects/Server/Json/Converters/WorldLocationConverterFactory.cs index a370e9b9b..961684e90 100644 --- a/Projects/Server/Json/Converters/WorldLocationConverterFactory.cs +++ b/Projects/Server/Json/Converters/WorldLocationConverterFactory.cs @@ -17,13 +17,12 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Server.Json -{ - public class WorldLocationConverterFactory : JsonConverterFactory - { - public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(WorldLocation); +namespace Server.Json; - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => - new WorldLocationConverter(); - } -} +public class WorldLocationConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(WorldLocation); + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => + new WorldLocationConverter(); +} \ No newline at end of file diff --git a/Projects/Server/Mobiles/Body.cs b/Projects/Server/Mobiles/Body.cs index 1b89f9e2a..41e1861ec 100644 --- a/Projects/Server/Mobiles/Body.cs +++ b/Projects/Server/Mobiles/Body.cs @@ -1,137 +1,152 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2022 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Body.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + using System; using System.IO; using System.Runtime.CompilerServices; +using Server.Logging; -namespace Server +namespace Server; + +public enum BodyType : byte { - public enum BodyType : byte - { - Empty, - Monster, - Sea, - Animal, - Human, - Equipment - } + Empty, + Monster, + Sea, + Animal, + Human, + Equipment +} - [Parsable] - public readonly struct Body : IEquatable, IEquatable, IEquatable, IComparable, IComparable - { - private static readonly BodyType[] m_Types = Array.Empty(); +[Parsable] +public readonly struct Body : IEquatable, IEquatable, IEquatable, IComparable, IComparable +{ + private static readonly ILogger logger = LogFactory.GetLogger(typeof(Body)); - static Body() + private static readonly BodyType[] m_Types = Array.Empty(); + + static Body() + { + if (!File.Exists("Data/bodyTable.cfg")) { - if (File.Exists("Data/bodyTable.cfg")) + logger.Error("Data/bodyTable.cfg does not exist."); + return; + } + + using var ip = new StreamReader("Data/bodyTable.cfg"); + m_Types = new BodyType[0x1000]; + + string line; + + while ((line = ip.ReadLine()) != null) + { + if (line.Length == 0 || line.StartsWithOrdinal("#")) { - using var ip = new StreamReader("Data/bodyTable.cfg"); - m_Types = new BodyType[0x1000]; + continue; + } - string line; + var split = line.Split('\t'); - while ((line = ip.ReadLine()) != null) - { - if (line.Length == 0 || line.StartsWithOrdinal("#")) - { - continue; - } - - var split = line.Split('\t'); - - if (int.TryParse(split[0], out var bodyID) && Enum.TryParse(split[1], true, out BodyType type) && - bodyID >= 0 && - bodyID < m_Types.Length) - { - m_Types[bodyID] = type; - } - else - { - Console.WriteLine("Warning: Invalid bodyTable entry:"); - Console.WriteLine(line); - } - } + if (int.TryParse(split[0], out var bodyID) && Enum.TryParse(split[1], true, out BodyType type) && + bodyID >= 0 && + bodyID < m_Types.Length) + { + m_Types[bodyID] = type; } else { - Console.WriteLine("Warning: Data/bodyTable.cfg does not exist"); + logger.Warning("Invalid bodyTable entry: {Entry}", line); } } - - public Body(int bodyID) => BodyID = bodyID; - - public BodyType Type => BodyID >= 0 && BodyID < m_Types.Length ? m_Types[BodyID] : BodyType.Empty; - - public bool IsHuman => BodyID >= 0 - && BodyID < m_Types.Length - && m_Types[BodyID] == BodyType.Human - && BodyID != 402 - && BodyID != 403 - && BodyID != 607 - && BodyID != 608 - && BodyID != 694 - && BodyID != 695 - && BodyID != 970; - - public bool IsGargoyle => BodyID is 666 or 667 or 694 or 695; - - public bool IsMale => BodyID is 183 or 185 or 400 or 402 or 605 or 607 or 666 or 694 or 750; - - public bool IsFemale => BodyID is 184 or 186 or 401 or 403 or 606 or 608 or 667 or 695 or 751; - - public bool IsGhost => BodyID is 402 or 403 or 607 or 608 or 694 or 695 or 970; - - public bool IsMonster => BodyID >= 0 - && BodyID < m_Types.Length - && m_Types[BodyID] == BodyType.Monster; - - public bool IsAnimal => BodyID >= 0 - && BodyID < m_Types.Length - && m_Types[BodyID] == BodyType.Animal; - - public bool IsEmpty => BodyID >= 0 - && BodyID < m_Types.Length - && m_Types[BodyID] == BodyType.Empty; - - public bool IsSea => BodyID >= 0 - && BodyID < m_Types.Length - && m_Types[BodyID] == BodyType.Sea; - - public bool IsEquipment => BodyID >= 0 - && BodyID < m_Types.Length - && m_Types[BodyID] == BodyType.Equipment; - - public int BodyID { get; } - - public static implicit operator int(Body a) => a.BodyID; - - public static implicit operator Body(int a) => new(a); - - public override string ToString() => $"0x{BodyID:X}"; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => BodyID.GetHashCode(); - - public override bool Equals(object o) => o is Body b && b.BodyID == BodyID; - - public bool Equals(Body b) => b.BodyID == BodyID; - - public bool Equals(int number) => number == BodyID; - - public static bool operator ==(Body l, Body r) => l.BodyID == r.BodyID; - - public static bool operator !=(Body l, Body r) => l.BodyID != r.BodyID; - - public static bool operator >(Body l, Body r) => l.BodyID > r.BodyID; - - public static bool operator >=(Body l, Body r) => l.BodyID >= r.BodyID; - - public static bool operator <(Body l, Body r) => l.BodyID < r.BodyID; - - public static bool operator <=(Body l, Body r) => l.BodyID <= r.BodyID; - - public int CompareTo(Body other) => BodyID.CompareTo(other.BodyID); - - public int CompareTo(int other) => BodyID.CompareTo(other); - - public static Body Parse(string value) => Utility.ToInt32(value); } + + public Body(int bodyID) => BodyID = bodyID; + + public BodyType Type => BodyID >= 0 && BodyID < m_Types.Length ? m_Types[BodyID] : BodyType.Empty; + + public bool IsHuman => BodyID >= 0 + && BodyID < m_Types.Length + && m_Types[BodyID] == BodyType.Human + && BodyID != 402 + && BodyID != 403 + && BodyID != 607 + && BodyID != 608 + && BodyID != 694 + && BodyID != 695 + && BodyID != 970; + + public bool IsGargoyle => BodyID is 666 or 667 or 694 or 695; + + public bool IsMale => BodyID is 183 or 185 or 400 or 402 or 605 or 607 or 666 or 694 or 750; + + public bool IsFemale => BodyID is 184 or 186 or 401 or 403 or 606 or 608 or 667 or 695 or 751; + + public bool IsGhost => BodyID is 402 or 403 or 607 or 608 or 694 or 695 or 970; + + public bool IsMonster => BodyID >= 0 + && BodyID < m_Types.Length + && m_Types[BodyID] == BodyType.Monster; + + public bool IsAnimal => BodyID >= 0 + && BodyID < m_Types.Length + && m_Types[BodyID] == BodyType.Animal; + + public bool IsEmpty => BodyID >= 0 + && BodyID < m_Types.Length + && m_Types[BodyID] == BodyType.Empty; + + public bool IsSea => BodyID >= 0 + && BodyID < m_Types.Length + && m_Types[BodyID] == BodyType.Sea; + + public bool IsEquipment => BodyID >= 0 + && BodyID < m_Types.Length + && m_Types[BodyID] == BodyType.Equipment; + + public int BodyID { get; } + + public static implicit operator int(Body a) => a.BodyID; + + public static implicit operator Body(int a) => new(a); + + public override string ToString() => $"0x{BodyID:X}"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => BodyID.GetHashCode(); + + public override bool Equals(object o) => o is Body b && b.BodyID == BodyID; + + public bool Equals(Body b) => b.BodyID == BodyID; + + public bool Equals(int number) => number == BodyID; + + public static bool operator ==(Body l, Body r) => l.BodyID == r.BodyID; + + public static bool operator !=(Body l, Body r) => l.BodyID != r.BodyID; + + public static bool operator >(Body l, Body r) => l.BodyID > r.BodyID; + + public static bool operator >=(Body l, Body r) => l.BodyID >= r.BodyID; + + public static bool operator <(Body l, Body r) => l.BodyID < r.BodyID; + + public static bool operator <=(Body l, Body r) => l.BodyID <= r.BodyID; + + public int CompareTo(Body other) => BodyID.CompareTo(other.BodyID); + + public int CompareTo(int other) => BodyID.CompareTo(other); + + public static Body Parse(string value) => Utility.ToInt32(value); } diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index ece4d3fc9..84a041743 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -861,7 +861,7 @@ namespace Server { if (m_Spell != null && value != null) { - Console.WriteLine("Warning: Spell has been overwritten"); + logger.Warning("Spell has been overwritten."); } m_Spell = value; @@ -5087,13 +5087,15 @@ namespace Server { item = oldItem.GetType().CreateInstance(); } - catch + catch (Exception e) { - Console.WriteLine( - "Warning: {0}: Item must have a zero parameter constructor to be separated from a stack. '{1}'.", + logger.Warning( + e, + "[{Serial} {Name}]: Item must have a zero parameter constructor to be separated from a stack.", oldItem.Serial, oldItem.GetType().Name ); + return null; } @@ -7685,9 +7687,7 @@ namespace Server if (m_DeltaQueue.Count > 0) { - Utility.PushColor(ConsoleColor.DarkYellow); - Console.WriteLine("Warning: {0} mobiles left in delta queue after processing.", m_DeltaQueue.Count); - Utility.PopColor(); + logger.Warning("{Count} mobiles left in delta queue after processing.", m_DeltaQueue.Count); } } diff --git a/Projects/Server/Network/NetState/NetState.cs b/Projects/Server/Network/NetState/NetState.cs index 2d7246feb..0282c0504 100755 --- a/Projects/Server/Network/NetState/NetState.cs +++ b/Projects/Server/Network/NetState/NetState.cs @@ -726,7 +726,7 @@ public partial class NetState : IComparable catch (Exception ex) { #if DEBUG - Console.WriteLine(ex); + Console.WriteLine(ex); #endif TraceException(ex); Disconnect("Exception during HandleReceive"); diff --git a/Projects/Server/Network/Packets/OutgoingContainerPackets.cs b/Projects/Server/Network/Packets/OutgoingContainerPackets.cs index d664e40f6..f3071c135 100644 --- a/Projects/Server/Network/Packets/OutgoingContainerPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingContainerPackets.cs @@ -16,11 +16,14 @@ using System; using System.Buffers; using System.IO; +using Server.Logging; namespace Server.Network; public static class OutgoingContainerPackets { + private static readonly ILogger logger = LogFactory.GetLogger(typeof(OutgoingContainerPackets)); + public static void SendDisplaySpellbook(this NetState ns, Serial book) => ns.SendDisplayContainer(book, -1); public static void SendSpellbookContent(this NetState ns, Serial book, int graphic, int offset, ulong content) @@ -136,7 +139,12 @@ public static class OutgoingContainerPackets } else { - Console.WriteLine("Warning: ContainerContentUpdate on item with !(parent is Item)"); + logger.Warning( + "ContainerContentUpdate on Item {Type} ({Serial}) where parent is not an Item", + item.GetType().Name, + item.Serial + ); + parentSerial = Serial.Zero; } diff --git a/Projects/Server/Regions/Region.cs b/Projects/Server/Regions/Region.cs index 3e9162979..6f1b903e5 100644 --- a/Projects/Server/Regions/Region.cs +++ b/Projects/Server/Regions/Region.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json; using Server.Json; +using Server.Logging; using Server.Network; using Server.Targeting; @@ -82,10 +83,11 @@ namespace Server public class Region : IComparable { - public static readonly int DefaultPriority = 50; + private static readonly ILogger logger = LogFactory.GetLogger(typeof(Region)); - public static readonly int MinZ = sbyte.MinValue; - public static readonly int MaxZ = sbyte.MaxValue + 1; + public const int DefaultPriority = 50; + public const int MinZ = sbyte.MinValue; + public const int MaxZ = sbyte.MaxValue + 1; public Region(string name, Map map, int priority, params Rectangle2D[] area) : this( name, @@ -156,7 +158,7 @@ namespace Server if (Area.Length == 0) { - Console.WriteLine("Empty area for region '{0}'", this); + logger.Debug("Empty area for region '{Region}'", this); } if (json.GetProperty("go", options, out Point3D go)) @@ -368,7 +370,7 @@ namespace Server if (Children.Count > 0) { - Console.WriteLine("Warning: Unregistering region '{0}' with children", this); + logger.Warning("Unregistering region '{Region}' with children", this); } if (Parent != null) diff --git a/Projects/Server/Skills.cs b/Projects/Server/Skills.cs index 26532d06f..6ad91ebbf 100644 --- a/Projects/Server/Skills.cs +++ b/Projects/Server/Skills.cs @@ -136,7 +136,6 @@ namespace Server if (Lock is < SkillLock.Up or > SkillLock.Locked) { - Console.WriteLine("Bad skill lock -> {0}.{1}", owner.Owner, Lock); Lock = SkillLock.Up; } } diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs index 347568c98..96a790360 100644 --- a/Projects/Server/Utilities/Utility.cs +++ b/Projects/Server/Utilities/Utility.cs @@ -11,1506 +11,1508 @@ using System.Xml; using Microsoft.Toolkit.HighPerformance; using Server.Buffers; using Server.Collections; +using Server.Logging; using Server.Random; using Server.Text; -namespace Server +namespace Server; + +public static class Utility { - public static class Utility + private static readonly ILogger logger = LogFactory.GetLogger(typeof(Utility)); + + private static Dictionary _ipAddressTable; + + private static readonly SkillName[] m_AllSkills = { - private static Dictionary _ipAddressTable; + SkillName.Alchemy, + SkillName.Anatomy, + SkillName.AnimalLore, + SkillName.ItemID, + SkillName.ArmsLore, + SkillName.Parry, + SkillName.Begging, + SkillName.Blacksmith, + SkillName.Fletching, + SkillName.Peacemaking, + SkillName.Camping, + SkillName.Carpentry, + SkillName.Cartography, + SkillName.Cooking, + SkillName.DetectHidden, + SkillName.Discordance, + SkillName.EvalInt, + SkillName.Healing, + SkillName.Fishing, + SkillName.Forensics, + SkillName.Herding, + SkillName.Hiding, + SkillName.Provocation, + SkillName.Inscribe, + SkillName.Lockpicking, + SkillName.Magery, + SkillName.MagicResist, + SkillName.Tactics, + SkillName.Snooping, + SkillName.Musicianship, + SkillName.Poisoning, + SkillName.Archery, + SkillName.SpiritSpeak, + SkillName.Stealing, + SkillName.Tailoring, + SkillName.AnimalTaming, + SkillName.TasteID, + SkillName.Tinkering, + SkillName.Tracking, + SkillName.Veterinary, + SkillName.Swords, + SkillName.Macing, + SkillName.Fencing, + SkillName.Wrestling, + SkillName.Lumberjacking, + SkillName.Mining, + SkillName.Meditation, + SkillName.Stealth, + SkillName.RemoveTrap, + SkillName.Necromancy, + SkillName.Focus, + SkillName.Chivalry, + SkillName.Bushido, + SkillName.Ninjitsu, + SkillName.Spellweaving + }; - private static readonly SkillName[] m_AllSkills = + private static readonly SkillName[] m_CombatSkills = + { + SkillName.Archery, + SkillName.Swords, + SkillName.Macing, + SkillName.Fencing, + SkillName.Wrestling + }; + + private static readonly SkillName[] m_CraftSkills = + { + SkillName.Alchemy, + SkillName.Blacksmith, + SkillName.Fletching, + SkillName.Carpentry, + SkillName.Cartography, + SkillName.Cooking, + SkillName.Inscribe, + SkillName.Tailoring, + SkillName.Tinkering + }; + + private static readonly Stack m_ConsoleColors = new(); + + public static void Separate(StringBuilder sb, string value, string separator) + { + if (sb.Length > 0) { - SkillName.Alchemy, - SkillName.Anatomy, - SkillName.AnimalLore, - SkillName.ItemID, - SkillName.ArmsLore, - SkillName.Parry, - SkillName.Begging, - SkillName.Blacksmith, - SkillName.Fletching, - SkillName.Peacemaking, - SkillName.Camping, - SkillName.Carpentry, - SkillName.Cartography, - SkillName.Cooking, - SkillName.DetectHidden, - SkillName.Discordance, - SkillName.EvalInt, - SkillName.Healing, - SkillName.Fishing, - SkillName.Forensics, - SkillName.Herding, - SkillName.Hiding, - SkillName.Provocation, - SkillName.Inscribe, - SkillName.Lockpicking, - SkillName.Magery, - SkillName.MagicResist, - SkillName.Tactics, - SkillName.Snooping, - SkillName.Musicianship, - SkillName.Poisoning, - SkillName.Archery, - SkillName.SpiritSpeak, - SkillName.Stealing, - SkillName.Tailoring, - SkillName.AnimalTaming, - SkillName.TasteID, - SkillName.Tinkering, - SkillName.Tracking, - SkillName.Veterinary, - SkillName.Swords, - SkillName.Macing, - SkillName.Fencing, - SkillName.Wrestling, - SkillName.Lumberjacking, - SkillName.Mining, - SkillName.Meditation, - SkillName.Stealth, - SkillName.RemoveTrap, - SkillName.Necromancy, - SkillName.Focus, - SkillName.Chivalry, - SkillName.Bushido, - SkillName.Ninjitsu, - SkillName.Spellweaving - }; - - private static readonly SkillName[] m_CombatSkills = - { - SkillName.Archery, - SkillName.Swords, - SkillName.Macing, - SkillName.Fencing, - SkillName.Wrestling - }; - - private static readonly SkillName[] m_CraftSkills = - { - SkillName.Alchemy, - SkillName.Blacksmith, - SkillName.Fletching, - SkillName.Carpentry, - SkillName.Cartography, - SkillName.Cooking, - SkillName.Inscribe, - SkillName.Tailoring, - SkillName.Tinkering - }; - - private static readonly Stack m_ConsoleColors = new(); - - public static void Separate(StringBuilder sb, string value, string separator) - { - if (sb.Length > 0) - { - sb.Append(separator); - } - - sb.Append(value); + sb.Append(separator); } - public static string Intern(string str) => str?.Length > 0 ? string.Intern(str) : str; + sb.Append(value); + } - public static void Intern(ref string str) + public static string Intern(string str) => str?.Length > 0 ? string.Intern(str) : str; + + public static void Intern(ref string str) + { + str = Intern(str); + } + + public static IPAddress Intern(IPAddress ipAddress) + { + if (ipAddress == null) { - str = Intern(str); + return null; } - public static IPAddress Intern(IPAddress ipAddress) + if (ipAddress.IsIPv4MappedToIPv6) { - if (ipAddress == null) - { - return null; - } - - if (ipAddress.IsIPv4MappedToIPv6) - { - ipAddress = ipAddress.MapToIPv4(); - } - - _ipAddressTable ??= new Dictionary(); - - if (!_ipAddressTable.TryGetValue(ipAddress, out var interned)) - { - interned = ipAddress; - _ipAddressTable[ipAddress] = interned; - } - - return interned; + ipAddress = ipAddress.MapToIPv4(); } - public static void Intern(ref IPAddress ipAddress) + _ipAddressTable ??= new Dictionary(); + + if (!_ipAddressTable.TryGetValue(ipAddress, out var interned)) { - ipAddress = Intern(ipAddress); + interned = ipAddress; + _ipAddressTable[ipAddress] = interned; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint IPv4ToAddress(IPAddress ipAddress) + return interned; + } + + public static void Intern(ref IPAddress ipAddress) + { + ipAddress = Intern(ipAddress); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint IPv4ToAddress(IPAddress ipAddress) + { + if (ipAddress.IsIPv4MappedToIPv6) { - if (ipAddress.IsIPv4MappedToIPv6) + ipAddress = ipAddress.MapToIPv4(); + } + else if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) + { + return 0; + } + + Span integer = stackalloc byte[4]; + ipAddress.TryWriteBytes(integer, out var bytesWritten); + return bytesWritten != 4 ? 0 : BinaryPrimitives.ReadUInt32BigEndian(integer); + } + + public static bool IPMatchClassC(IPAddress ip1, IPAddress ip2) + { + var a = IPv4ToAddress(ip1); + var b = IPv4ToAddress(ip2); + + return a == 0 || b == 0 ? ip1.Equals(ip2) : (a & 0xFFFFFF) == (b & 0xFFFFFF); + } + + public static bool IPMatchCIDR(IPAddress cidrAddress, IPAddress address, int cidrLength) + { + if (cidrAddress.AddressFamily == AddressFamily.InterNetwork) + { + if (address.AddressFamily == AddressFamily.InterNetworkV6) { - ipAddress = ipAddress.MapToIPv4(); - } - else if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) - { - return 0; + return false; } - Span integer = stackalloc byte[4]; - ipAddress.TryWriteBytes(integer, out var bytesWritten); - return bytesWritten != 4 ? 0 : BinaryPrimitives.ReadUInt32BigEndian(integer); + cidrLength += 96; } - public static bool IPMatchClassC(IPAddress ip1, IPAddress ip2) - { - var a = IPv4ToAddress(ip1); - var b = IPv4ToAddress(ip2); + cidrAddress = cidrAddress.MapToIPv6(); + address = address.MapToIPv6(); - return a == 0 || b == 0 ? ip1.Equals(ip2) : (a & 0xFFFFFF) == (b & 0xFFFFFF); + cidrLength = Math.Clamp(cidrLength, 0, 128); + + Span cidrBytes = stackalloc byte[16]; + cidrAddress.TryWriteBytes(cidrBytes, out var _); + + Span addrBytes = stackalloc byte[16]; + address.TryWriteBytes(addrBytes, out var _); + + var i = 0; + int offset; + + if (cidrLength < 32) + { + offset = cidrLength; } - - public static bool IPMatchCIDR(IPAddress cidrAddress, IPAddress address, int cidrLength) + else { - if (cidrAddress.AddressFamily == AddressFamily.InterNetwork) + var index = Math.DivRem(cidrLength, 32, out offset); + while (index > 0) { - if (address.AddressFamily == AddressFamily.InterNetworkV6) + if ( + BinaryPrimitives.ReadInt32BigEndian(cidrBytes.Slice(i, 4)) != + BinaryPrimitives.ReadInt32BigEndian(addrBytes.Slice(i, 4)) + ) { return false; } - cidrLength += 96; + i += 4; + --index; } - - cidrAddress = cidrAddress.MapToIPv6(); - address = address.MapToIPv6(); - - cidrLength = Math.Clamp(cidrLength, 0, 128); - - Span cidrBytes = stackalloc byte[16]; - cidrAddress.TryWriteBytes(cidrBytes, out var _); - - Span addrBytes = stackalloc byte[16]; - address.TryWriteBytes(addrBytes, out var _); - - var i = 0; - int offset; - - if (cidrLength < 32) - { - offset = cidrLength; - } - else - { - var index = Math.DivRem(cidrLength, 32, out offset); - while (index > 0) - { - if ( - BinaryPrimitives.ReadInt32BigEndian(cidrBytes.Slice(i, 4)) != - BinaryPrimitives.ReadInt32BigEndian(addrBytes.Slice(i, 4)) - ) - { - return false; - } - - i += 4; - --index; - } - } - - if (offset == 0) - { - return true; - } - - var c = BinaryPrimitives.ReadInt32BigEndian(cidrBytes.Slice(i, 4)); - var a = BinaryPrimitives.ReadInt32BigEndian(addrBytes.Slice(i, 4)); - - var mask = (1 << (32 - offset)) - 1; - var min = ~mask & c; - var max = c | mask; - - return a >= min && a <= max; } - public static bool IsValidIP(string val) => IPMatch(val, IPAddress.Any, out var valid) || valid; - - public static bool IPMatch(string val, IPAddress ip) => IPMatch(val, ip, out _); - - public static bool IPMatch(string val, IPAddress ip, out bool valid) + if (offset == 0) { - var family = ip.AddressFamily; - var useIPv6 = family == AddressFamily.InterNetworkV6 || val.ContainsOrdinal(':'); - - ip = useIPv6 ? ip.MapToIPv6() : ip.MapToIPv4(); - - Span ipBytes = stackalloc byte[useIPv6 ? 16 : 4]; - ip.TryWriteBytes(ipBytes, out _); - - return useIPv6 ? IPv6Match(val, ipBytes, out valid) : IPv4Match(val, ipBytes, out valid); + return true; } - public static bool IPv4Match(ReadOnlySpan val, ReadOnlySpan ip, out bool valid) + var c = BinaryPrimitives.ReadInt32BigEndian(cidrBytes.Slice(i, 4)); + var a = BinaryPrimitives.ReadInt32BigEndian(addrBytes.Slice(i, 4)); + + var mask = (1 << (32 - offset)) - 1; + var min = ~mask & c; + var max = c | mask; + + return a >= min && a <= max; + } + + public static bool IsValidIP(string val) => IPMatch(val, IPAddress.Any, out var valid) || valid; + + public static bool IPMatch(string val, IPAddress ip) => IPMatch(val, ip, out _); + + public static bool IPMatch(string val, IPAddress ip, out bool valid) + { + var family = ip.AddressFamily; + var useIPv6 = family == AddressFamily.InterNetworkV6 || val.ContainsOrdinal(':'); + + ip = useIPv6 ? ip.MapToIPv6() : ip.MapToIPv4(); + + Span ipBytes = stackalloc byte[useIPv6 ? 16 : 4]; + ip.TryWriteBytes(ipBytes, out _); + + return useIPv6 ? IPv6Match(val, ipBytes, out valid) : IPv4Match(val, ipBytes, out valid); + } + + public static bool IPv4Match(ReadOnlySpan val, ReadOnlySpan ip, out bool valid) + { + var match = true; + valid = true; + var end = val.Length; + var byteIndex = 0; + var section = 0; + var number = 0; + var isRange = false; + var intBase = 10; + var endOfSection = false; + var sectionStart = 0; + + var num = ip[byteIndex++]; + + for (var i = 0; i < end; i++) { - var match = true; - valid = true; - var end = val.Length; - var byteIndex = 0; - var section = 0; - var number = 0; - var isRange = false; - var intBase = 10; - var endOfSection = false; - var sectionStart = 0; - - var num = ip[byteIndex++]; - - for (var i = 0; i < end; i++) - { - var chr = val[i]; - if (section >= 4) - { - valid = false; - return false; - } - - switch (chr) - { - default: - { - if (!Uri.IsHexDigit(chr)) - { - valid = false; - return false; - } - - number = number * intBase + Uri.FromHex(chr); - break; - } - case 'x': - case 'X': - { - if (i == sectionStart) - { - intBase = 16; - break; - } - - valid = false; - return false; - } - case '-': - { - if (i == sectionStart || i + 1 == end || val[i + 1] == '.') - { - valid = false; - return false; - } - - // Only allows a single range in a section - if (isRange) - { - valid = false; - return false; - } - - isRange = true; - match = match && num >= number; - number = 0; - break; - } - case '*': - { - if (i != sectionStart || i + 1 < end && val[i + 1] != '.') - { - valid = false; - return false; - } - - isRange = true; - number = 255; - break; - } - case '.': - { - endOfSection = true; - break; - } - } - - if (endOfSection || i + 1 == end) - { - if (number is < 0 or > 255) - { - valid = false; - return false; - } - - match = match && (isRange ? num <= number : number == num); - - if (++section < 4) - { - num = ip[byteIndex++]; - } - - intBase = 10; - number = 0; - endOfSection = false; - sectionStart = i + 1; - isRange = false; - } - } - - return match; - } - - public static bool IPv6Match(ReadOnlySpan val, ReadOnlySpan ip, out bool valid) - { - valid = true; - - // Start must be two `::` or a number - if (val[0] == ':' && val[1] != ':') + var chr = val[i]; + if (section >= 4) { valid = false; return false; } - var match = true; - var end = val.Length; - var byteIndex = 2; - var section = 0; - var number = 0; - var isRange = false; - var endOfSection = false; - var sectionStart = 0; - var hasCompressor = false; - - var num = BinaryPrimitives.ReadUInt16BigEndian(ip[..2]); - - for (int i = 0; i < end; i++) + switch (chr) { - if (section > 7) + default: + { + if (!Uri.IsHexDigit(chr)) + { + valid = false; + return false; + } + + number = number * intBase + Uri.FromHex(chr); + break; + } + case 'x': + case 'X': + { + if (i == sectionStart) + { + intBase = 16; + break; + } + + valid = false; + return false; + } + case '-': + { + if (i == sectionStart || i + 1 == end || val[i + 1] == '.') + { + valid = false; + return false; + } + + // Only allows a single range in a section + if (isRange) + { + valid = false; + return false; + } + + isRange = true; + match = match && num >= number; + number = 0; + break; + } + case '*': + { + if (i != sectionStart || i + 1 < end && val[i + 1] != '.') + { + valid = false; + return false; + } + + isRange = true; + number = 255; + break; + } + case '.': + { + endOfSection = true; + break; + } + } + + if (endOfSection || i + 1 == end) + { + if (number is < 0 or > 255) { valid = false; return false; } - var chr = val[i]; - // We are starting a new sequence, check the previous one then continue - switch (chr) + match = match && (isRange ? num <= number : number == num); + + if (++section < 4) { - default: - { - if (!Uri.IsHexDigit(chr)) - { - valid = false; - return false; - } - - number = number * 16 + Uri.FromHex(chr); - break; - } - case '?': - { - Console.WriteLine("IP Match '?' character is not supported."); - valid = false; - return false; - } - // Range - case '-': - { - if (i == sectionStart || i + 1 == end || val[i + 1] == ':') - { - valid = false; - return false; - } - - // Only allows a single range in a section - if (isRange) - { - valid = false; - return false; - } - - isRange = true; - - // Check low part of the range - match = match && num >= number; - number = 0; - break; - } - // Wild section - case '*': - { - if (i != sectionStart || i + 1 < end && val[i + 1] != ':') - { - valid = false; - return false; - } - - isRange = true; - number = 65535; - break; - } - case ':': - { - endOfSection = true; - break; - } - } - - if (!endOfSection && i + 1 != end) - { - continue; - } - - if (++i == end || val[i] != ':' || section > 0) - { - match = match && (isRange ? num <= number : number == num); - - // IPv4 matching at the end - if (section == 6 && num == 0xFFFF) - { - var ipv4 = val[(i + 1)..]; - if (ipv4.Contains('.')) - { - return IPv4Match(ipv4, ip[^4..], out valid); - } - } - - if (i == end) - { - break; - } - - num = BinaryPrimitives.ReadUInt16BigEndian(ip.Slice(byteIndex, 2)); - byteIndex += 2; - - ++section; - } - - if (i < end && val[i] == ':') - { - if (hasCompressor) - { - valid = false; - return false; - } - - int newSection; - - if (i + 1 < end) - { - var remainingColons = val[(i + 1)..].Count(':'); - // double colon must be at least 2 sections - // we need at least 1 section remaining out of 8 - // This means 8 - 2 would be 6 sections (5 colons) - newSection = section + 2 + (5 - remainingColons); - if (newSection > 7) - { - valid = false; - return false; - } - } - else - { - newSection = 7; - } - - var zeroEnd = (newSection + 1) * 2; - do - { - if (match) - { - if (num != 0) - { - match = false; - } - - num = BinaryPrimitives.ReadUInt16BigEndian(ip.Slice(byteIndex, 2)); - } - - byteIndex += 2; - } while (byteIndex < zeroEnd); - - section = newSection; - hasCompressor = true; - } - else - { - i--; + num = ip[byteIndex++]; } + intBase = 10; number = 0; endOfSection = false; sectionStart = i + 1; isRange = false; } - - return match; } - public static string FixHtml(string str) + return match; + } + + public static bool IPv6Match(ReadOnlySpan val, ReadOnlySpan ip, out bool valid) + { + valid = true; + + // Start must be two `::` or a number + if (val[0] == ':' && val[1] != ':') { - if (string.IsNullOrEmpty(str)) - { - return str; - } - - using var sb = new ValueStringBuilder(str, stackalloc char[Math.Min(128, str.Length)]); - ReadOnlySpan invalid = stackalloc []{ '<', '>', '#' }; - ReadOnlySpan replacement = stackalloc []{ '(', ')', '-' }; - sb.ReplaceAny(invalid, replacement, 0, sb.Length); - - return sb.ToString(); + valid = false; + return false; } - public static void FixHtml(Span chars) + var match = true; + var end = val.Length; + var byteIndex = 2; + var section = 0; + var number = 0; + var isRange = false; + var endOfSection = false; + var sectionStart = 0; + var hasCompressor = false; + + var num = BinaryPrimitives.ReadUInt16BigEndian(ip[..2]); + + for (int i = 0; i < end; i++) { - if (chars.Length == 0) + if (section > 7) { - return; + valid = false; + return false; } - ReadOnlySpan invalid = stackalloc []{ '<', '>', '#' }; - ReadOnlySpan replacement = stackalloc []{ '(', ')', '-' }; - - chars.ReplaceAny(invalid, replacement); - } - - public static int InsensitiveCompare(string first, string second) => first.InsensitiveCompare(second); - - public static bool InsensitiveStartsWith(string first, string second) => first.InsensitiveStartsWith(second); - - public static Direction GetDirection(Point3D from, Point3D to) => GetDirection(from.X, from.Y, to.X, to.Y); - - public static Direction GetDirection(Point2D from, Point2D to) => GetDirection(from.X, from.Y, to.X, to.Y); - - public static Direction GetDirection(int fromX, int fromY, int toX, int toY) - { - var dx = toX - fromX; - var dy = toY - fromY; - - var adx = Abs(dx); - var ady = Abs(dy); - - if (adx >= ady * 3) + var chr = val[i]; + // We are starting a new sequence, check the previous one then continue + switch (chr) { - return dx > 0 ? Direction.East : Direction.West; - } - - if (ady >= adx * 3) - { - return dy > 0 ? Direction.South : Direction.North; - } - - if (dx > 0) - { - return dy > 0 ? Direction.Down : Direction.Right; - } - - return dy > 0 ? Direction.Left : Direction.Up; - } - - public static object GetArrayCap(Array array, int index, object emptyValue = null) => - array.Length > 0 ? array.GetValue(Math.Clamp(index, 0, array.Length - 1)) : emptyValue; - - public static SkillName RandomSkill() => - m_AllSkills[Random( - m_AllSkills.Length - (Core.ML ? 0 : - Core.SE ? 1 : - Core.AOS ? 3 : 6) - )]; - - public static SkillName RandomCombatSkill() => m_CombatSkills.RandomElement(); - - public static SkillName RandomCraftSkill() => m_CraftSkills.RandomElement(); - - public static void FixPoints(ref Point3D top, ref Point3D bottom) - { - if (bottom.m_X < top.m_X) - { - (top.m_X, bottom.m_X) = (bottom.m_X, top.m_X); - } - - if (bottom.m_Y < top.m_Y) - { - (top.m_Y, bottom.m_Y) = (bottom.m_Y, top.m_Y); - } - - if (bottom.m_Z < top.m_Z) - { - (top.m_Z, bottom.m_Z) = (bottom.m_Z, top.m_Z); - } - } - - public static void FormatBuffer(this TextWriter op, ReadOnlySpan first, ReadOnlySpan second, int totalLength) - { - op.WriteLine(" 0 1 2 3 4 5 6 7 8 9 A B C D E F"); - op.WriteLine(" -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); - - if (totalLength <= 0) - { - op.WriteLine("0000 "); - return; - } - - Span lineBytes = stackalloc byte[16]; - Span lineChars = stackalloc char[47]; - for (var i = 0; i < totalLength; i += 16) - { - var length = Math.Min(totalLength - i, 16); - if (i < first.Length) - { - var firstLength = Math.Min(length, first.Length - i); - first.Slice(i, firstLength).CopyTo(lineBytes); - - if (firstLength < length) + default: { - second[..(length - first.Length - i)].CopyTo(lineBytes[(length - firstLength)..]); + if (!Uri.IsHexDigit(chr)) + { + valid = false; + return false; + } + + number = number * 16 + Uri.FromHex(chr); + break; + } + case '?': + { + logger.Debug("IP Match '?' character is not supported."); + valid = false; + return false; + } + // Range + case '-': + { + if (i == sectionStart || i + 1 == end || val[i + 1] == ':') + { + valid = false; + return false; + } + + // Only allows a single range in a section + if (isRange) + { + valid = false; + return false; + } + + isRange = true; + + // Check low part of the range + match = match && num >= number; + number = 0; + break; + } + // Wild section + case '*': + { + if (i != sectionStart || i + 1 < end && val[i + 1] != ':') + { + valid = false; + return false; + } + + isRange = true; + number = 65535; + break; + } + case ':': + { + endOfSection = true; + break; + } + } + + if (!endOfSection && i + 1 != end) + { + continue; + } + + if (++i == end || val[i] != ':' || section > 0) + { + match = match && (isRange ? num <= number : number == num); + + // IPv4 matching at the end + if (section == 6 && num == 0xFFFF) + { + var ipv4 = val[(i + 1)..]; + if (ipv4.Contains('.')) + { + return IPv4Match(ipv4, ip[^4..], out valid); + } + } + + if (i == end) + { + break; + } + + num = BinaryPrimitives.ReadUInt16BigEndian(ip.Slice(byteIndex, 2)); + byteIndex += 2; + + ++section; + } + + if (i < end && val[i] == ':') + { + if (hasCompressor) + { + valid = false; + return false; + } + + int newSection; + + if (i + 1 < end) + { + var remainingColons = val[(i + 1)..].Count(':'); + // double colon must be at least 2 sections + // we need at least 1 section remaining out of 8 + // This means 8 - 2 would be 6 sections (5 colons) + newSection = section + 2 + (5 - remainingColons); + if (newSection > 7) + { + valid = false; + return false; } } else { - second.Slice(i - first.Length, length).CopyTo(lineBytes); + newSection = 7; } - var charsWritten = ((ReadOnlySpan)lineBytes[..length]).ToSpacedHexString(lineChars); - - op.Write("{0:X4} ", i); - op.Write(lineChars[..charsWritten]); - op.WriteLine(); - } - } - - public static void PushColor(ConsoleColor color) - { - try - { - m_ConsoleColors.Push(Console.ForegroundColor); - Console.ForegroundColor = color; - } - catch - { - // ignored - } - } - - public static void PopColor() - { - try - { - Console.ForegroundColor = m_ConsoleColors.Pop(); - } - catch - { - // ignored - } - } - - public static bool NumberBetween(double num, int bound1, int bound2, double allowance) - { - if (bound1 > bound2) - { - (bound1, bound2) = (bound2, bound1); - } - - return num < bound2 + allowance && num > bound1 - allowance; - } - - public static void AssignRandomHair(Mobile m, int hue) - { - m.HairItemID = m.Race.RandomHair(m); - m.HairHue = hue; - } - - public static void AssignRandomHair(Mobile m, bool randomHue = true) - { - m.HairItemID = m.Race.RandomHair(m); - - if (randomHue) - { - m.HairHue = m.Race.RandomHairHue(); - } - } - - public static void AssignRandomFacialHair(Mobile m, int hue) - { - m.FacialHairItemID = m.Race.RandomFacialHair(m); - m.FacialHairHue = hue; - } - - public static void AssignRandomFacialHair(Mobile m, bool randomHue = true) - { - m.FacialHairItemID = m.Race.RandomFacialHair(m); - - if (randomHue) - { - m.FacialHairHue = m.Race.RandomHairHue(); - } - } - - // Using this instead of Linq Cast<> means we can ditch the yield and enforce contravariance - public static HashSet SafeConvertSet(this IEnumerable coll) - where TOutput : TInput => coll.SafeConvert, TInput, TOutput>(); - - public static List SafeConvertList(this IEnumerable coll) - where TOutput : TInput => coll.SafeConvert, TInput, TOutput>(); - - public static TColl SafeConvert(this IEnumerable coll) - where TOutput : TInput where TColl : ICollection, new() - { - var outputList = new TColl(); - - foreach (var entry in coll) - { - if (entry is TOutput outEntry) + var zeroEnd = (newSection + 1) * 2; + do { - outputList.Add(outEntry); + if (match) + { + if (num != 0) + { + match = false; + } + + num = BinaryPrimitives.ReadUInt16BigEndian(ip.Slice(byteIndex, 2)); + } + + byteIndex += 2; + } while (byteIndex < zeroEnd); + + section = newSection; + hasCompressor = true; + } + else + { + i--; + } + + number = 0; + endOfSection = false; + sectionStart = i + 1; + isRange = false; + } + + return match; + } + + public static string FixHtml(string str) + { + if (string.IsNullOrEmpty(str)) + { + return str; + } + + using var sb = new ValueStringBuilder(str, stackalloc char[Math.Min(128, str.Length)]); + ReadOnlySpan invalid = stackalloc []{ '<', '>', '#' }; + ReadOnlySpan replacement = stackalloc []{ '(', ')', '-' }; + sb.ReplaceAny(invalid, replacement, 0, sb.Length); + + return sb.ToString(); + } + + public static void FixHtml(Span chars) + { + if (chars.Length == 0) + { + return; + } + + ReadOnlySpan invalid = stackalloc []{ '<', '>', '#' }; + ReadOnlySpan replacement = stackalloc []{ '(', ')', '-' }; + + chars.ReplaceAny(invalid, replacement); + } + + public static int InsensitiveCompare(string first, string second) => first.InsensitiveCompare(second); + + public static bool InsensitiveStartsWith(string first, string second) => first.InsensitiveStartsWith(second); + + public static Direction GetDirection(Point3D from, Point3D to) => GetDirection(from.X, from.Y, to.X, to.Y); + + public static Direction GetDirection(Point2D from, Point2D to) => GetDirection(from.X, from.Y, to.X, to.Y); + + public static Direction GetDirection(int fromX, int fromY, int toX, int toY) + { + var dx = toX - fromX; + var dy = toY - fromY; + + var adx = Abs(dx); + var ady = Abs(dy); + + if (adx >= ady * 3) + { + return dx > 0 ? Direction.East : Direction.West; + } + + if (ady >= adx * 3) + { + return dy > 0 ? Direction.South : Direction.North; + } + + if (dx > 0) + { + return dy > 0 ? Direction.Down : Direction.Right; + } + + return dy > 0 ? Direction.Left : Direction.Up; + } + + public static object GetArrayCap(Array array, int index, object emptyValue = null) => + array.Length > 0 ? array.GetValue(Math.Clamp(index, 0, array.Length - 1)) : emptyValue; + + public static SkillName RandomSkill() => + m_AllSkills[Random( + m_AllSkills.Length - (Core.ML ? 0 : + Core.SE ? 1 : + Core.AOS ? 3 : 6) + )]; + + public static SkillName RandomCombatSkill() => m_CombatSkills.RandomElement(); + + public static SkillName RandomCraftSkill() => m_CraftSkills.RandomElement(); + + public static void FixPoints(ref Point3D top, ref Point3D bottom) + { + if (bottom.m_X < top.m_X) + { + (top.m_X, bottom.m_X) = (bottom.m_X, top.m_X); + } + + if (bottom.m_Y < top.m_Y) + { + (top.m_Y, bottom.m_Y) = (bottom.m_Y, top.m_Y); + } + + if (bottom.m_Z < top.m_Z) + { + (top.m_Z, bottom.m_Z) = (bottom.m_Z, top.m_Z); + } + } + + public static void FormatBuffer(this TextWriter op, ReadOnlySpan first, ReadOnlySpan second, int totalLength) + { + op.WriteLine(" 0 1 2 3 4 5 6 7 8 9 A B C D E F"); + op.WriteLine(" -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); + + if (totalLength <= 0) + { + op.WriteLine("0000 "); + return; + } + + Span lineBytes = stackalloc byte[16]; + Span lineChars = stackalloc char[47]; + for (var i = 0; i < totalLength; i += 16) + { + var length = Math.Min(totalLength - i, 16); + if (i < first.Length) + { + var firstLength = Math.Min(length, first.Length - i); + first.Slice(i, firstLength).CopyTo(lineBytes); + + if (firstLength < length) + { + second[..(length - first.Length - i)].CopyTo(lineBytes[(length - firstLength)..]); } } - - return outputList; - } - - public static bool ToBoolean(string value) => - bool.TryParse(value, out var b) && b || - value.InsensitiveEquals("enabled") || - value.InsensitiveEquals("on") || - !value.InsensitiveEquals("disabled") && !value.InsensitiveEquals("off"); - - public static double ToDouble(string value) - { -#pragma warning disable CA1806 // Do not ignore method results - double.TryParse(value, out var d); -#pragma warning restore CA1806 // Do not ignore method results - - return d; - } - - public static TimeSpan ToTimeSpan(string value) - { -#pragma warning disable CA1806 // Do not ignore method results - TimeSpan.TryParse(value, out var t); -#pragma warning restore CA1806 // Do not ignore method results - - return t; - } - - public static int ToInt32(ReadOnlySpan value) - { - int i; - -#pragma warning disable CA1806 // Do not ignore method results - if (value.StartsWithOrdinal("0x")) - { - int.TryParse(value[2..], NumberStyles.HexNumber, null, out i); - } else { - int.TryParse(value, out i); + second.Slice(i - first.Length, length).CopyTo(lineBytes); } -#pragma warning restore CA1806 // Do not ignore method results - return i; + var charsWritten = ((ReadOnlySpan)lineBytes[..length]).ToSpacedHexString(lineChars); + + op.Write("{0:X4} ", i); + op.Write(lineChars[..charsWritten]); + op.WriteLine(); + } + } + + public static void PushColor(ConsoleColor color) + { + try + { + m_ConsoleColors.Push(Console.ForegroundColor); + Console.ForegroundColor = color; + } + catch + { + // ignored + } + } + + public static void PopColor() + { + try + { + Console.ForegroundColor = m_ConsoleColors.Pop(); + } + catch + { + // ignored + } + } + + public static bool NumberBetween(double num, int bound1, int bound2, double allowance) + { + if (bound1 > bound2) + { + (bound1, bound2) = (bound2, bound1); } - public static uint ToUInt32(ReadOnlySpan value) + return num < bound2 + allowance && num > bound1 - allowance; + } + + public static void AssignRandomHair(Mobile m, int hue) + { + m.HairItemID = m.Race.RandomHair(m); + m.HairHue = hue; + } + + public static void AssignRandomHair(Mobile m, bool randomHue = true) + { + m.HairItemID = m.Race.RandomHair(m); + + if (randomHue) { - uint i; + m.HairHue = m.Race.RandomHairHue(); + } + } + + public static void AssignRandomFacialHair(Mobile m, int hue) + { + m.FacialHairItemID = m.Race.RandomFacialHair(m); + m.FacialHairHue = hue; + } + + public static void AssignRandomFacialHair(Mobile m, bool randomHue = true) + { + m.FacialHairItemID = m.Race.RandomFacialHair(m); + + if (randomHue) + { + m.FacialHairHue = m.Race.RandomHairHue(); + } + } + + // Using this instead of Linq Cast<> means we can ditch the yield and enforce contravariance + public static HashSet SafeConvertSet(this IEnumerable coll) + where TOutput : TInput => coll.SafeConvert, TInput, TOutput>(); + + public static List SafeConvertList(this IEnumerable coll) + where TOutput : TInput => coll.SafeConvert, TInput, TOutput>(); + + public static TColl SafeConvert(this IEnumerable coll) + where TOutput : TInput where TColl : ICollection, new() + { + var outputList = new TColl(); + + foreach (var entry in coll) + { + if (entry is TOutput outEntry) + { + outputList.Add(outEntry); + } + } + + return outputList; + } + + public static bool ToBoolean(string value) => + bool.TryParse(value, out var b) && b || + value.InsensitiveEquals("enabled") || + value.InsensitiveEquals("on") || + !value.InsensitiveEquals("disabled") && !value.InsensitiveEquals("off"); + + public static double ToDouble(string value) + { +#pragma warning disable CA1806 // Do not ignore method results + double.TryParse(value, out var d); +#pragma warning restore CA1806 // Do not ignore method results + + return d; + } + + public static TimeSpan ToTimeSpan(string value) + { +#pragma warning disable CA1806 // Do not ignore method results + TimeSpan.TryParse(value, out var t); +#pragma warning restore CA1806 // Do not ignore method results + + return t; + } + + public static int ToInt32(ReadOnlySpan value) + { + int i; #pragma warning disable CA1806 // Do not ignore method results - if (value.InsensitiveStartsWith("0x")) - { - uint.TryParse(value[2..], NumberStyles.HexNumber, null, out i); - } - else - { - uint.TryParse(value, out i); - } + if (value.StartsWithOrdinal("0x")) + { + int.TryParse(value[2..], NumberStyles.HexNumber, null, out i); + } + else + { + int.TryParse(value, out i); + } #pragma warning restore CA1806 // Do not ignore method results - return i; - } + return i; + } - public static bool ToInt32(ReadOnlySpan value, out int i) => - value.InsensitiveStartsWith("0x") - ? int.TryParse(value[2..], NumberStyles.HexNumber, null, out i) - : int.TryParse(value, out i); + public static uint ToUInt32(ReadOnlySpan value) + { + uint i; - public static bool ToUInt32(ReadOnlySpan value, out uint i) => - value.InsensitiveStartsWith("0x") - ? uint.TryParse(value[2..], NumberStyles.HexNumber, null, out i) - : uint.TryParse(value, out i); - - public static int GetXMLInt32(string intString, int defaultValue) +#pragma warning disable CA1806 // Do not ignore method results + if (value.InsensitiveStartsWith("0x")) { - try - { - return XmlConvert.ToInt32(intString); - } - catch - { - return int.TryParse(intString, out var val) ? val : defaultValue; - } + uint.TryParse(value[2..], NumberStyles.HexNumber, null, out i); } - - public static uint GetXMLUInt32(string uintString, uint defaultValue) + else { - try - { - return XmlConvert.ToUInt32(uintString); - } - catch - { - return uint.TryParse(uintString, out var val) ? val : defaultValue; - } + uint.TryParse(value, out i); } +#pragma warning restore CA1806 // Do not ignore method results - public static DateTime GetXMLDateTime(string dateTimeString, DateTime defaultValue) + return i; + } + + public static bool ToInt32(ReadOnlySpan value, out int i) => + value.InsensitiveStartsWith("0x") + ? int.TryParse(value[2..], NumberStyles.HexNumber, null, out i) + : int.TryParse(value, out i); + + public static bool ToUInt32(ReadOnlySpan value, out uint i) => + value.InsensitiveStartsWith("0x") + ? uint.TryParse(value[2..], NumberStyles.HexNumber, null, out i) + : uint.TryParse(value, out i); + + public static int GetXMLInt32(string intString, int defaultValue) + { + try { - try - { - return XmlConvert.ToDateTime(dateTimeString, XmlDateTimeSerializationMode.Utc); - } - catch - { - return DateTime.TryParse(dateTimeString, out var d) ? d : defaultValue; - } + return XmlConvert.ToInt32(intString); } - - public static TimeSpan GetXMLTimeSpan(string timeSpanString, TimeSpan defaultValue) + catch { - try - { - return XmlConvert.ToTimeSpan(timeSpanString); - } - catch - { - return defaultValue; - } + return int.TryParse(intString, out var val) ? val : defaultValue; } + } - public static string GetAttribute(XmlElement node, string attributeName, string defaultValue = null) => - node?.Attributes[attributeName]?.Value ?? defaultValue; - - public static string GetText(XmlElement node, string defaultValue) => node?.InnerText ?? defaultValue; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool InRange(int p1X, int p1Y, int p2X, int p2Y, int range) => - p1X >= p2X - range - && p1X <= p2X + range - && p1Y >= p2Y - range - && p1Y <= p2Y + range; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool InRange(Point2D p1, Point2D p2, int range) => - InRange(p1.m_X, p1.m_Y, p2.m_X, p2.m_Y, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool InUpdateRange(Point2D p1, Point2D p2) => InRange(p1, p2, 18); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool InRange(Point3D p1, Point3D p2, int range) => - InRange(p1.m_X, p1.m_Y, p2.m_X, p2.m_Y, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool InUpdateRange(Point3D p1, Point3D p2) => InRange(p1, p2, 18); - - // 4d6+8 would be: Utility.Dice( 4, 6, 8 ) - public static int Dice(uint amount, uint sides, int bonus) + public static uint GetXMLUInt32(string uintString, uint defaultValue) + { + try { - var total = 0; - - for (var i = 0; i < amount; ++i) - { - total += (int)RandomSources.Source.Next(1, sides); - } - - return total + bonus; + return XmlConvert.ToUInt32(uintString); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Shuffle(this IList list) + catch { - var count = list.Count; - for (var i = 0; i < count; i++) - { - var r = RandomMinMax(i, count - 1); - (list[r], list[i]) = (list[i], list[r]); - } + return uint.TryParse(uintString, out var val) ? val : defaultValue; } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Shuffle(this Span list) + public static DateTime GetXMLDateTime(string dateTimeString, DateTime defaultValue) + { + try { - var count = list.Length; - for (var i = 0; i < count; i++) - { - var r = RandomMinMax(i, count - 1); - (list[r], list[i]) = (list[i], list[r]); - } + return XmlConvert.ToDateTime(dateTimeString, XmlDateTimeSerializationMode.Utc); + } + catch + { + return DateTime.TryParse(dateTimeString, out var d) ? d : defaultValue; + } + } + + public static TimeSpan GetXMLTimeSpan(string timeSpanString, TimeSpan defaultValue) + { + try + { + return XmlConvert.ToTimeSpan(timeSpanString); + } + catch + { + return defaultValue; + } + } + + public static string GetAttribute(XmlElement node, string attributeName, string defaultValue = null) => + node?.Attributes[attributeName]?.Value ?? defaultValue; + + public static string GetText(XmlElement node, string defaultValue) => node?.InnerText ?? defaultValue; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool InRange(int p1X, int p1Y, int p2X, int p2Y, int range) => + p1X >= p2X - range + && p1X <= p2X + range + && p1Y >= p2Y - range + && p1Y <= p2Y + range; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool InRange(Point2D p1, Point2D p2, int range) => + InRange(p1.m_X, p1.m_Y, p2.m_X, p2.m_Y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool InUpdateRange(Point2D p1, Point2D p2) => InRange(p1, p2, 18); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool InRange(Point3D p1, Point3D p2, int range) => + InRange(p1.m_X, p1.m_Y, p2.m_X, p2.m_Y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool InUpdateRange(Point3D p1, Point3D p2) => InRange(p1, p2, 18); + + // 4d6+8 would be: Utility.Dice( 4, 6, 8 ) + public static int Dice(uint amount, uint sides, int bonus) + { + var total = 0; + + for (var i = 0; i < amount; ++i) + { + total += (int)RandomSources.Source.Next(1, sides); } - /** + return total + bonus; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Shuffle(this IList list) + { + var count = list.Count; + for (var i = 0; i < count; i++) + { + var r = RandomMinMax(i, count - 1); + (list[r], list[i]) = (list[i], list[r]); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Shuffle(this Span list) + { + var count = list.Length; + for (var i = 0; i < count; i++) + { + var r = RandomMinMax(i, count - 1); + (list[r], list[i]) = (list[i], list[r]); + } + } + + /** * Gets a random sample from the source list. * Not meant for unbounded lists. Does not shuffle or modify source. */ - public static T[] RandomSample(this T[] source, int count) + public static T[] RandomSample(this T[] source, int count) + { + if (count <= 0) { - if (count <= 0) - { - return Array.Empty(); - } - - var length = source.Length; - Span list = stackalloc bool[length]; - var sampleList = new T[count]; - - var i = 0; - do - { - var rand = Random(length); - if (!(list[rand] && (list[rand] = true))) - { - sampleList[i++] = source[rand]; - } - } while (i < count); - - return sampleList; + return Array.Empty(); } - public static List RandomSample(this List source, int count) + var length = source.Length; + Span list = stackalloc bool[length]; + var sampleList = new T[count]; + + var i = 0; + do { - if (count <= 0) + var rand = Random(length); + if (!(list[rand] && (list[rand] = true))) { - return new List(); + sampleList[i++] = source[rand]; } + } while (i < count); - var length = source.Count; - Span list = stackalloc bool[length]; - var sampleList = new List(count); + return sampleList; + } - var i = 0; - do - { - var rand = Random(length); - if (!(list[rand] && (list[rand] = true))) - { - sampleList[i++] = source[rand]; - } - } while (i < count); - - return sampleList; + public static List RandomSample(this List source, int count) + { + if (count <= 0) + { + return new List(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T RandomList(params T[] list) => list.RandomElement(); + var length = source.Count; + Span list = stackalloc bool[length]; + var sampleList = new List(count); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T RandomElement(this IList list) => list.RandomElement(default); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T TakeRandomElement(this IList list) + var i = 0; + do { + var rand = Random(length); + if (!(list[rand] && (list[rand] = true))) + { + sampleList[i++] = source[rand]; + } + } while (i < count); + + return sampleList; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T RandomList(params T[] list) => list.RandomElement(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T RandomElement(this IList list) => list.RandomElement(default); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T TakeRandomElement(this IList list) + { + if (list.Count == 0) + { + return default; + } + + var index = Random(list.Count); + var value = list[index]; + list.RemoveAt(index); + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T RandomElement(this IList list, T valueIfZero) => + list.Count == 0 ? valueIfZero : list[Random(list.Count)]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool RandomBool() => RandomSources.Source.NextBool(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double RandomMinMax(double min, double max) + { + if (min > max) + { + (min, max) = (max, min); + } + else if (min == max) + { + return min; + } + + return min + RandomSources.Source.NextDouble() * (max - min); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint RandomMinMax(uint min, uint max) + { + if (min > max) + { + (min, max) = (max, min); + } + else if (min == max) + { + return min; + } + + return min + RandomSources.Source.Next(max - min + 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int RandomMinMax(int min, int max) + { + if (min > max) + { + (min, max) = (max, min); + } + else if (min == max) + { + return min; + } + + return min + (int)RandomSources.Source.Next((uint)(max - min + 1)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long RandomMinMax(long min, long max) + { + if (min > max) + { + (min, max) = (max, min); + } + else if (min == max) + { + return min; + } + + return min + RandomSources.Source.Next(max - min + 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Random(int from, int count) => RandomSources.Source.Next(from, count); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Random(int count) => RandomSources.Source.Next(count); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Random(uint count) => RandomSources.Source.Next(count); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void RandomBytes(Span buffer) => RandomSources.Source.NextBytes(buffer); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double RandomDouble() => RandomSources.Source.NextDouble(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point3D RandomPointIn(Rectangle2D rect, Map map) + { + var x = Random(rect.X, rect.Width); + var y = Random(rect.Y, rect.Height); + + return new Point3D(x, y, map.GetAverageZ(x, y)); + } + + /// + /// Random pink, blue, green, orange, red or yellow hue + /// + public static int RandomNondyedHue() + { + return Random(6) switch + { + 0 => RandomPinkHue(), + 1 => RandomBlueHue(), + 2 => RandomGreenHue(), + 3 => RandomOrangeHue(), + 4 => RandomRedHue(), + 5 => RandomYellowHue(), + _ => 0 + }; + } + + /// + /// Random hue in the range 1201-1254 + /// + public static int RandomPinkHue() => Random(1201, 54); + + /// + /// Random hue in the range 1301-1354 + /// + public static int RandomBlueHue() => Random(1301, 54); + + /// + /// Random hue in the range 1401-1454 + /// + public static int RandomGreenHue() => Random(1401, 54); + + /// + /// Random hue in the range 1501-1554 + /// + public static int RandomOrangeHue() => Random(1501, 54); + + /// + /// Random hue in the range 1601-1654 + /// + public static int RandomRedHue() => Random(1601, 54); + + /// + /// Random hue in the range 1701-1754 + /// + public static int RandomYellowHue() => Random(1701, 54); + + /// + /// Random hue in the range 1801-1908 + /// + public static int RandomNeutralHue() => Random(1801, 108); + + /// + /// Random hue in the range 2001-2018 + /// + public static int RandomSnakeHue() => Random(2001, 18); + + /// + /// Random hue in the range 2101-2130 + /// + public static int RandomBirdHue() => Random(2101, 30); + + /// + /// Random hue in the range 2201-2224 + /// + public static int RandomSlimeHue() => Random(2201, 24); + + /// + /// Random hue in the range 2301-2318 + /// + public static int RandomAnimalHue() => Random(2301, 18); + + /// + /// Random hue in the range 2401-2430 + /// + public static int RandomMetalHue() => Random(2401, 30); + + public static int ClipDyedHue(int hue) => hue < 2 ? 2 : + hue > 1001 ? 1001 : hue; + + /// + /// Random hue in the range 2-1001 + /// + public static int RandomDyedHue() => Random(2, 1000); + + /// + /// Random hue from 0x62, 0x71, 0x03, 0x0D, 0x13, 0x1C, 0x21, 0x30, 0x37, 0x3A, 0x44, 0x59 + /// + public static int RandomBrightHue() => + RandomDouble() < 0.1 + ? RandomList(0x62, 0x71) + : RandomList(0x03, 0x0D, 0x13, 0x1C, 0x21, 0x30, 0x37, 0x3A, 0x44, 0x59); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Clamp(this T val, T min, T max) where T : IComparable => + val.CompareTo(min) < 0 ? min : + val.CompareTo(max) > 0 ? max : val; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Min(T val, T min) where T : IComparable => val.CompareTo(min) < 0 ? val : min; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Max(T val, T max) where T : IComparable => val.CompareTo(max) > 0 ? val : max; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Tidy(this List list) where T : ISerializable + { + for (int i = list.Count - 1; i >= 0; i--) + { + var entry = list[i]; + if (entry?.Deleted != false) + { + list.RemoveAt(i); + } + } + + list.TrimExcess(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Tidy(this HashSet set) where T : ISerializable + { + set.RemoveWhere(entry => entry?.Deleted != false); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Tidy(this Dictionary dictionary) + { + var serializable = typeof(ISerializable); + var serializableKey = typeof(K).IsAssignableTo(serializable); + var serializableValue = typeof(V).IsAssignableTo(serializable); + + if (!serializableKey && !serializableValue) + { + return; + } + + using var queue = PooledRefQueue.Create(); + foreach (var (key, value) in dictionary) + { + if (serializableKey) + { + if (key == null || ((ISerializable)key).Deleted) + { + queue.Enqueue(key); + } + } + else + { + if (value == null || ((ISerializable)value).Deleted) + { + queue.Enqueue(key); + } + } + } + + while (queue.Count > 0) + { + dictionary.Remove(queue.Dequeue()); + } + + dictionary.TrimExcess(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int NumberOfSetBits(this ulong i) + { + i -= (i >> 1) & 0x5555555555555555UL; + i = (i & 0x3333333333333333UL) + ((i >> 2) & 0x3333333333333333UL); + return (int)(unchecked(((i + (i >> 4)) & 0xF0F0F0F0F0F0F0FUL) * 0x101010101010101UL) >> 56); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Abs(this int value) + { + int mask = value >> 31; + return (value + mask) ^ mask; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetTimeStamp() => Core.Now.ToTimeStamp(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToTimeStamp(this DateTime dt) => dt.ToString("yyyy-MM-dd-HH-mm-ss"); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(ref List list, T value) + { + list ??= new List(); + list.Add(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(ref HashSet set, T value) + { + set ??= new HashSet(); + set.Add(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(ref Dictionary dict, K key, V value) + { + dict ??= new Dictionary(); + dict.Add(key, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Remove(ref List list, T value) + { + if (list != null) + { + var removed = list.Remove(value); + if (list.Count == 0) { - return default; + list = null; } - var index = Random(list.Count); - var value = list[index]; - list.RemoveAt(index); - return value; + return removed; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T RandomElement(this IList list, T valueIfZero) => - list.Count == 0 ? valueIfZero : list[Random(list.Count)]; + return false; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool RandomBool() => RandomSources.Source.NextBool(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double RandomMinMax(double min, double max) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Remove(ref HashSet set, T value) + { + if (set != null) { - if (min > max) + var removed = set.Remove(value); + + if (set.Count == 0) { - (min, max) = (max, min); - } - else if (min == max) - { - return min; + set = null; } - return min + RandomSources.Source.NextDouble() * (max - min); + return removed; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint RandomMinMax(uint min, uint max) + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Remove(ref Dictionary dict, K key) + { + if (dict != null) { - if (min > max) + var removed = dict.Remove(key); + + if (dict.Count == 0) { - (min, max) = (max, min); - } - else if (min == max) - { - return min; + dict = null; } - return min + RandomSources.Source.Next(max - min + 1); + return removed; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int RandomMinMax(int min, int max) + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Remove(ref Dictionary dict, K key, out V value) + { + if (dict != null) { - if (min > max) + var removed = dict.Remove(key, out value); + + if (dict.Count == 0) { - (min, max) = (max, min); - } - else if (min == max) - { - return min; + dict = null; } - return min + (int)RandomSources.Source.Next((uint)(max - min + 1)); + return removed; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long RandomMinMax(long min, long max) + value = default; + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Replace(ref List list, T oldValue, T newValue) + { + if (oldValue != null && newValue != null) { - if (min > max) + var index = list?.IndexOf(oldValue) ?? -1; + + if (index >= 0) { - (min, max) = (max, min); + list![index] = newValue; } - else if (min == max) - { - return min; - } - - return min + RandomSources.Source.Next(max - min + 1); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Random(int from, int count) => RandomSources.Source.Next(from, count); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Random(int count) => RandomSources.Source.Next(count); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint Random(uint count) => RandomSources.Source.Next(count); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void RandomBytes(Span buffer) => RandomSources.Source.NextBytes(buffer); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double RandomDouble() => RandomSources.Source.NextDouble(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point3D RandomPointIn(Rectangle2D rect, Map map) - { - var x = Random(rect.X, rect.Width); - var y = Random(rect.Y, rect.Height); - - return new Point3D(x, y, map.GetAverageZ(x, y)); - } - - /// - /// Random pink, blue, green, orange, red or yellow hue - /// - public static int RandomNondyedHue() - { - return Random(6) switch - { - 0 => RandomPinkHue(), - 1 => RandomBlueHue(), - 2 => RandomGreenHue(), - 3 => RandomOrangeHue(), - 4 => RandomRedHue(), - 5 => RandomYellowHue(), - _ => 0 - }; - } - - /// - /// Random hue in the range 1201-1254 - /// - public static int RandomPinkHue() => Random(1201, 54); - - /// - /// Random hue in the range 1301-1354 - /// - public static int RandomBlueHue() => Random(1301, 54); - - /// - /// Random hue in the range 1401-1454 - /// - public static int RandomGreenHue() => Random(1401, 54); - - /// - /// Random hue in the range 1501-1554 - /// - public static int RandomOrangeHue() => Random(1501, 54); - - /// - /// Random hue in the range 1601-1654 - /// - public static int RandomRedHue() => Random(1601, 54); - - /// - /// Random hue in the range 1701-1754 - /// - public static int RandomYellowHue() => Random(1701, 54); - - /// - /// Random hue in the range 1801-1908 - /// - public static int RandomNeutralHue() => Random(1801, 108); - - /// - /// Random hue in the range 2001-2018 - /// - public static int RandomSnakeHue() => Random(2001, 18); - - /// - /// Random hue in the range 2101-2130 - /// - public static int RandomBirdHue() => Random(2101, 30); - - /// - /// Random hue in the range 2201-2224 - /// - public static int RandomSlimeHue() => Random(2201, 24); - - /// - /// Random hue in the range 2301-2318 - /// - public static int RandomAnimalHue() => Random(2301, 18); - - /// - /// Random hue in the range 2401-2430 - /// - public static int RandomMetalHue() => Random(2401, 30); - - public static int ClipDyedHue(int hue) => hue < 2 ? 2 : - hue > 1001 ? 1001 : hue; - - /// - /// Random hue in the range 2-1001 - /// - public static int RandomDyedHue() => Random(2, 1000); - - /// - /// Random hue from 0x62, 0x71, 0x03, 0x0D, 0x13, 0x1C, 0x21, 0x30, 0x37, 0x3A, 0x44, 0x59 - /// - public static int RandomBrightHue() => - RandomDouble() < 0.1 - ? RandomList(0x62, 0x71) - : RandomList(0x03, 0x0D, 0x13, 0x1C, 0x21, 0x30, 0x37, 0x3A, 0x44, 0x59); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T Clamp(this T val, T min, T max) where T : IComparable => - val.CompareTo(min) < 0 ? min : - val.CompareTo(max) > 0 ? max : val; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T Min(T val, T min) where T : IComparable => val.CompareTo(min) < 0 ? val : min; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T Max(T val, T max) where T : IComparable => val.CompareTo(max) > 0 ? val : max; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Tidy(this List list) where T : ISerializable - { - for (int i = list.Count - 1; i >= 0; i--) - { - var entry = list[i]; - if (entry?.Deleted != false) - { - list.RemoveAt(i); - } - } - - list.TrimExcess(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Tidy(this HashSet set) where T : ISerializable - { - set.RemoveWhere(entry => entry?.Deleted != false); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Tidy(this Dictionary dictionary) - { - var serializable = typeof(ISerializable); - var serializableKey = typeof(K).IsAssignableTo(serializable); - var serializableValue = typeof(V).IsAssignableTo(serializable); - - if (!serializableKey && !serializableValue) - { - return; - } - - using var queue = PooledRefQueue.Create(); - foreach (var (key, value) in dictionary) - { - if (serializableKey) - { - if (key == null || ((ISerializable)key).Deleted) - { - queue.Enqueue(key); - } - } - else - { - if (value == null || ((ISerializable)value).Deleted) - { - queue.Enqueue(key); - } - } - } - - while (queue.Count > 0) - { - dictionary.Remove(queue.Dequeue()); - } - - dictionary.TrimExcess(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int NumberOfSetBits(this ulong i) - { - i -= (i >> 1) & 0x5555555555555555UL; - i = (i & 0x3333333333333333UL) + ((i >> 2) & 0x3333333333333333UL); - return (int)(unchecked(((i + (i >> 4)) & 0xF0F0F0F0F0F0F0FUL) * 0x101010101010101UL) >> 56); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Abs(this int value) - { - int mask = value >> 31; - return (value + mask) ^ mask; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string GetTimeStamp() => Core.Now.ToTimeStamp(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string ToTimeStamp(this DateTime dt) => dt.ToString("yyyy-MM-dd-HH-mm-ss"); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Add(ref List list, T value) - { - list ??= new List(); - list.Add(value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Add(ref HashSet set, T value) - { - set ??= new HashSet(); - set.Add(value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Add(ref Dictionary dict, K key, V value) - { - dict ??= new Dictionary(); - dict.Add(key, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Remove(ref List list, T value) - { - if (list != null) - { - var removed = list.Remove(value); - - if (list.Count == 0) - { - list = null; - } - - return removed; - } - - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Remove(ref HashSet set, T value) - { - if (set != null) - { - var removed = set.Remove(value); - - if (set.Count == 0) - { - set = null; - } - - return removed; - } - - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Remove(ref Dictionary dict, K key) - { - if (dict != null) - { - var removed = dict.Remove(key); - - if (dict.Count == 0) - { - dict = null; - } - - return removed; - } - - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Remove(ref Dictionary dict, K key, out V value) - { - if (dict != null) - { - var removed = dict.Remove(key, out value); - - if (dict.Count == 0) - { - dict = null; - } - - return removed; - } - - value = default; - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Replace(ref List list, T oldValue, T newValue) - { - if (oldValue != null && newValue != null) - { - var index = list?.IndexOf(oldValue) ?? -1; - - if (index >= 0) - { - list![index] = newValue; - } - else - { - Add(ref list, newValue); - } - } - else if (oldValue != null) - { - Remove(ref list, oldValue); - } - else if (newValue != null) + else { Add(ref list, newValue); } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Replace(ref Dictionary dict, K key, V oldValue, V newValue) + else if (oldValue != null) { - if (newValue != null) - { - Add(ref dict, key, newValue); - } - else if (oldValue != null) - { - Remove(ref dict, key); - } + Remove(ref list, oldValue); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Clear(ref List list) + else if (newValue != null) { - list.Clear(); - list = null; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Clear(ref HashSet set) - { - set.Clear(); - set = null; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Clear(ref Dictionary dict) - { - dict.Clear(); - dict = null; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void DateToComponents( - DateTime date, - out int year, - out int month, - out int day, - out DayOfWeek dayOfWeek, - out int hour, - out int min, - out int sec - ) - { - year = date.Year; - month = date.Month; - day = date.Day; - dayOfWeek = date.DayOfWeek; - hour = date.Hour; - min = date.Minute; - sec = date.Second; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T[] Combine(this IList source, params IList[] arrays) => - source.Combine(false, arrays); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T[] CombinePooled(this IList source, params IList[] arrays) => - source.Combine(true, arrays); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T[] Combine(this IList source, bool pooled, params IList[] arrays) - { - var totalLength = source.Count; - foreach (var arr in arrays) - { - totalLength += arr.Count; - } - - if (totalLength == 0) - { - return Array.Empty(); - } - - var combined = pooled ? STArrayPool.Shared.Rent(totalLength) : new T[totalLength]; - - source.CopyTo(combined, 0); - var position = source.Count; - foreach (var arr in arrays) - { - arr.CopyTo(combined, position); - position += arr.Count; - } - - return combined; + Add(ref list, newValue); } } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Replace(ref Dictionary dict, K key, V oldValue, V newValue) + { + if (newValue != null) + { + Add(ref dict, key, newValue); + } + else if (oldValue != null) + { + Remove(ref dict, key); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear(ref List list) + { + list.Clear(); + list = null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear(ref HashSet set) + { + set.Clear(); + set = null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear(ref Dictionary dict) + { + dict.Clear(); + dict = null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void DateToComponents( + DateTime date, + out int year, + out int month, + out int day, + out DayOfWeek dayOfWeek, + out int hour, + out int min, + out int sec + ) + { + year = date.Year; + month = date.Month; + day = date.Day; + dayOfWeek = date.DayOfWeek; + hour = date.Hour; + min = date.Minute; + sec = date.Second; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] Combine(this IList source, params IList[] arrays) => + source.Combine(false, arrays); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] CombinePooled(this IList source, params IList[] arrays) => + source.Combine(true, arrays); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] Combine(this IList source, bool pooled, params IList[] arrays) + { + var totalLength = source.Count; + foreach (var arr in arrays) + { + totalLength += arr.Count; + } + + if (totalLength == 0) + { + return Array.Empty(); + } + + var combined = pooled ? STArrayPool.Shared.Rent(totalLength) : new T[totalLength]; + + source.CopyTo(combined, 0); + var position = source.Count; + foreach (var arr in arrays) + { + arr.CopyTo(combined, position); + position += arr.Count; + } + + return combined; + } }