From c31bf20d0e96dcc93c4c0f8737eaeb9cae40aa90 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sat, 13 Nov 2021 13:38:01 -0800 Subject: [PATCH] feat: Updates to .NET 6 (#843) * Fixes an issue with moving directories across volumes * Removes usages of WebClient * Removes usages of Cryptographic Providers Note: Even though .NET 6 introduces Xoshiro RNG, there is no way to control the seed. I'll do some reconciliation of Xoshiro so it functions closer to the built in one. For the most part, it has parity though. Benchmarks show there is nothing odd about the implementations, they are within 1ns of each other. --- .github/workflows/build-test.yml | 4 +- Directory.Build.props | 4 +- Projects/Benchmarks/Benchmarks.csproj | 2 +- .../Collections/BenchmarkOrderedHashSet.cs | 2 +- .../FeatureFlags/BenchmarkFeatureFlags.cs | 90 ------------------- .../Benchmarks/FeatureFlags/FeatureFlag.cs | 9 -- .../Logging/BenchmarkConsoleLogging.cs | 2 +- .../Packets/BenchmarkOutgoingGumpPacket.cs | 2 +- .../Packets/BenchmarkPacketBroadcast.cs | 2 +- .../Benchmarks/Rng/BenchmarkXoshiro.cs | 46 ++++++++++ .../Benchmarks/Text/BenchmarkTextEncoding.cs | 2 +- .../Utilities/BenchmarkStringHelpers.cs | 2 +- Projects/Benchmarks/Program.cs | 4 +- .../SerializableMigrationSchema.cs | 3 +- .../Application.cs | 3 +- Projects/Server.Tests/Server.Tests.csproj | 2 +- Projects/Server/Json/JsonConfig.cs | 2 +- Projects/Server/Random/SecureRandom.cs | 4 +- Projects/Server/Utilities/PathUtility.cs | 19 ++++ Projects/Server/World/World.cs | 2 +- .../Security/PasswordProtectionTest.cs | 55 +++++++++--- .../UOContent.Tests/UOContent.Tests.csproj | 2 +- .../Accounting/Security/AccountSecurity.cs | 6 +- ....cs => HashAlgorithmPasswordProtection.cs} | 16 ++-- .../Security/MD5PasswordProtection.cs | 38 -------- .../Security/SHA1PasswordProtection.cs | 38 -------- Projects/UOContent/Compression/TarArchive.cs | 17 +++- Projects/UOContent/Compression/ZstdArchive.cs | 1 + Projects/UOContent/Misc/ProfessionInfo.cs | 2 +- Projects/UOContent/Misc/ServerList.cs | 10 ++- Projects/UOContent/Mobiles/Townfolk/Noble.cs | 1 - Projects/UOContent/UOContent.csproj | 2 +- Projects/UOContent/World Saves/AutoArchive.cs | 4 +- README.md | 11 ++- azure-pipelines.yml | 14 ++- 35 files changed, 192 insertions(+), 231 deletions(-) delete mode 100644 Projects/Benchmarks/Benchmarks/FeatureFlags/BenchmarkFeatureFlags.cs delete mode 100644 Projects/Benchmarks/Benchmarks/FeatureFlags/FeatureFlag.cs create mode 100644 Projects/Benchmarks/Benchmarks/Rng/BenchmarkXoshiro.cs rename Projects/UOContent/Accounting/Security/{SHA2PasswordProtection.cs => HashAlgorithmPasswordProtection.cs} (64%) delete mode 100644 Projects/UOContent/Accounting/Security/MD5PasswordProtection.cs delete mode 100644 Projects/UOContent/Accounting/Security/SHA1PasswordProtection.cs diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index cf746c82e..3a2b28871 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -23,10 +23,10 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 # avoid shallow clone so nbgv can do its work. - - name: Setup .NET 5 + - name: Setup .NET 6 uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.401 + dotnet-version: 6.0.100 - name: Build run: ./publish.cmd - name: Test diff --git a/Directory.Build.props b/Directory.Build.props index 0ac231599..7ebed0b53 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,7 +4,7 @@ Kamron Batman ModernUO 2019-2020 - net5.0 + net6.0 x64 x64 preview @@ -58,7 +58,7 @@ - 3.4.240 + 3.4.244 all diff --git a/Projects/Benchmarks/Benchmarks.csproj b/Projects/Benchmarks/Benchmarks.csproj index 2ed8e89fd..eff9ed660 100644 --- a/Projects/Benchmarks/Benchmarks.csproj +++ b/Projects/Benchmarks/Benchmarks.csproj @@ -1,7 +1,7 @@ Exe - net5.0 + net6.0 x64 x64 9 diff --git a/Projects/Benchmarks/Benchmarks/Collections/BenchmarkOrderedHashSet.cs b/Projects/Benchmarks/Benchmarks/Collections/BenchmarkOrderedHashSet.cs index 1414c571e..b9ec3d676 100644 --- a/Projects/Benchmarks/Benchmarks/Collections/BenchmarkOrderedHashSet.cs +++ b/Projects/Benchmarks/Benchmarks/Collections/BenchmarkOrderedHashSet.cs @@ -6,7 +6,7 @@ using Server.Collections; namespace Benchmarks { [MemoryDiagnoser] - [SimpleJob(RuntimeMoniker.NetCoreApp50)] + [SimpleJob(RuntimeMoniker.Net60)] public class BenchmarkOrderedHashSet { private readonly string[] _iterations = new string[16]; diff --git a/Projects/Benchmarks/Benchmarks/FeatureFlags/BenchmarkFeatureFlags.cs b/Projects/Benchmarks/Benchmarks/FeatureFlags/BenchmarkFeatureFlags.cs deleted file mode 100644 index fcfffebbe..000000000 --- a/Projects/Benchmarks/Benchmarks/FeatureFlags/BenchmarkFeatureFlags.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Buffers.Binary; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Runtime.Loader; -using System.Security.Cryptography; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Jobs; -using Server; -using Server.Items; - -namespace Benchmarks -{ - [SimpleJob(RuntimeMoniker.NetCoreApp50)] - public class BenchmarkFeatureFlags - { - public Dictionary> m_Dictionary; - public ILookup> m_Lookup; - - public Type[] m_TypesToLookUp; - - [GlobalSetup] - public void Setup() - { - RNGCryptoServiceProvider csp = new RNGCryptoServiceProvider(); - - string file = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "UOContent.dll"); - Assembly assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file); - - m_Dictionary = new Dictionary>(); - List> m_Types = new List>(); - m_TypesToLookUp = new Type[100]; - - foreach (var type in assembly.GetTypes()) - { - if (typeof(Item).IsAssignableFrom(type)) - { - m_Dictionary.Add(type, new FeatureFlag()); - m_Types.Add(new FeatureFlag{Type = type}); - } - } - - Console.WriteLine("Dictionary Size: {0}", m_Dictionary.Count); - Console.WriteLine("Lookup Size: {0}", m_Types.Count); - - m_Dictionary.TrimExcess(); - m_Lookup = m_Types.ToLookup(f => f.Type); - Span bytes = stackalloc byte[4]; - - for (int i = 0; i < 100; i++) - { - csp.GetBytes(bytes); - m_TypesToLookUp[i] = m_Types[(int)(BinaryPrimitives.ReadUInt32BigEndian(bytes) % m_Types.Count)].Type; - } - } - - [Benchmark] - public FeatureFlag TestDictionary() - { - for (int i = 0; i < 100; i++) - { - m_Dictionary.TryGetValue(typeof(ExplosionPotion), out var ff); - if (i == 99) - { - return ff; - } - } - - return null; - } - - [Benchmark] - public FeatureFlag TestLookup() - { - FeatureFlag ff; - for (int i = 0; i < 100; i++) - { - ff = m_Lookup[typeof(ExplosionPotion)].GetEnumerator().Current; - if (i == 99) - { - return ff; - } - } - - return null; - } - } -} diff --git a/Projects/Benchmarks/Benchmarks/FeatureFlags/FeatureFlag.cs b/Projects/Benchmarks/Benchmarks/FeatureFlags/FeatureFlag.cs deleted file mode 100644 index f8b04716c..000000000 --- a/Projects/Benchmarks/Benchmarks/FeatureFlags/FeatureFlag.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Server -{ - public class FeatureFlag where T : Item - { - public Type Type { get; set; } - } -} diff --git a/Projects/Benchmarks/Benchmarks/Logging/BenchmarkConsoleLogging.cs b/Projects/Benchmarks/Benchmarks/Logging/BenchmarkConsoleLogging.cs index 62bc428fa..f794d64b4 100644 --- a/Projects/Benchmarks/Benchmarks/Logging/BenchmarkConsoleLogging.cs +++ b/Projects/Benchmarks/Benchmarks/Logging/BenchmarkConsoleLogging.cs @@ -6,7 +6,7 @@ using Serilog.Core; namespace Benchmarks { - [SimpleJob(RuntimeMoniker.NetCoreApp50)] + [SimpleJob(RuntimeMoniker.Net60)] public class BenchmarkConsoleLogging { private const string text = "Sample message"; diff --git a/Projects/Benchmarks/Benchmarks/Packets/BenchmarkOutgoingGumpPacket.cs b/Projects/Benchmarks/Benchmarks/Packets/BenchmarkOutgoingGumpPacket.cs index 0225af652..114cac192 100644 --- a/Projects/Benchmarks/Benchmarks/Packets/BenchmarkOutgoingGumpPacket.cs +++ b/Projects/Benchmarks/Benchmarks/Packets/BenchmarkOutgoingGumpPacket.cs @@ -12,7 +12,7 @@ using Server.Tests.Network; namespace Benchmarks { [MemoryDiagnoser] - [SimpleJob(RuntimeMoniker.NetCoreApp50)] + [SimpleJob(RuntimeMoniker.Net60)] public class OutgoingGumpPacketBenchmarks { private static readonly byte[] _layoutBuffer = GC.AllocateUninitializedArray(0x20000); diff --git a/Projects/Benchmarks/Benchmarks/Packets/BenchmarkPacketBroadcast.cs b/Projects/Benchmarks/Benchmarks/Packets/BenchmarkPacketBroadcast.cs index 2535dca0f..4db59d432 100644 --- a/Projects/Benchmarks/Benchmarks/Packets/BenchmarkPacketBroadcast.cs +++ b/Projects/Benchmarks/Benchmarks/Packets/BenchmarkPacketBroadcast.cs @@ -7,7 +7,7 @@ using Server.Network; namespace Benchmarks { - [SimpleJob(RuntimeMoniker.NetCoreApp50)] + [SimpleJob(RuntimeMoniker.Net60)] public class BenchmarkPacketBroadcast { public static int SendUnicodeMessage( diff --git a/Projects/Benchmarks/Benchmarks/Rng/BenchmarkXoshiro.cs b/Projects/Benchmarks/Benchmarks/Rng/BenchmarkXoshiro.cs new file mode 100644 index 000000000..caced76b3 --- /dev/null +++ b/Projects/Benchmarks/Benchmarks/Rng/BenchmarkXoshiro.cs @@ -0,0 +1,46 @@ +using System; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using Server.Random; + +namespace Benchmarks.Benchmarks.Rng +{ + [MemoryDiagnoser] + [SimpleJob(RuntimeMoniker.Net60)] + public class BenchmarkXoshiro + { + private Random _random; + private Xoshiro256PlusPlus _xoshiro256PlusPlus; + + [GlobalSetup] + public void Setup() + { + _xoshiro256PlusPlus = new Xoshiro256PlusPlus(); + _random = new Random(); + } + + [Benchmark] + public int SystemRandomULong() => _random.Next(10000); + + [Benchmark] + public int XoshiroRandomULong() => _xoshiro256PlusPlus.Next(10000); + + [Benchmark] + public double SystemRandomDouble() => _random.NextDouble(); + + [Benchmark] + public double XoshiroRandomDouble() => _xoshiro256PlusPlus.NextDouble(); + + [Benchmark] + public int SystemRandomMinMax() => _random.Next(5000, 85000); + + [Benchmark] + public int XoshiroRandomMinMax() + { + const int min = 5000; + const int max = 85000; + + return min + (int)_xoshiro256PlusPlus.Next((uint)(max - min + 1)); + } + } +} diff --git a/Projects/Benchmarks/Benchmarks/Text/BenchmarkTextEncoding.cs b/Projects/Benchmarks/Benchmarks/Text/BenchmarkTextEncoding.cs index 5594bf937..b2f716861 100644 --- a/Projects/Benchmarks/Benchmarks/Text/BenchmarkTextEncoding.cs +++ b/Projects/Benchmarks/Benchmarks/Text/BenchmarkTextEncoding.cs @@ -5,7 +5,7 @@ using Server.Text; namespace Benchmarks.BenchmarkText { [MemoryDiagnoser] - [SimpleJob(RuntimeMoniker.NetCoreApp50)] + [SimpleJob(RuntimeMoniker.Net60)] public class BenchmarkTextEncoding { private const string text = diff --git a/Projects/Benchmarks/Benchmarks/Utilities/BenchmarkStringHelpers.cs b/Projects/Benchmarks/Benchmarks/Utilities/BenchmarkStringHelpers.cs index 01cf5bb3c..55e4fd289 100644 --- a/Projects/Benchmarks/Benchmarks/Utilities/BenchmarkStringHelpers.cs +++ b/Projects/Benchmarks/Benchmarks/Utilities/BenchmarkStringHelpers.cs @@ -7,7 +7,7 @@ using Server.Buffers; namespace Benchmarks.BenchmarkUtilities { [MemoryDiagnoser] - [SimpleJob(RuntimeMoniker.NetCoreApp50)] + [SimpleJob(RuntimeMoniker.Net60)] public class BenchmarkStringHelpers { private readonly string[] names = diff --git a/Projects/Benchmarks/Program.cs b/Projects/Benchmarks/Program.cs index f28cc3e6f..845354d56 100644 --- a/Projects/Benchmarks/Program.cs +++ b/Projects/Benchmarks/Program.cs @@ -1,4 +1,5 @@ using BenchmarkDotNet.Running; +using Benchmarks.Benchmarks.Rng; namespace Benchmarks { @@ -10,10 +11,11 @@ namespace Benchmarks // var packetConstruction = BenchmarkRunner.Run(); // var broadcast = BenchmarkRunner.Run(); // var stringHelpers = BenchmarkRunner.Run(); - var indexList = BenchmarkRunner.Run(); + // var indexList = BenchmarkRunner.Run(); // var textEncoding = BenchmarkRunner.Run(); // var logging = BenchmarkRunner.Run(); // var gumpPacket = BenchmarkRunner.Run(); + var rngTest = BenchmarkRunner.Run(); } } } diff --git a/Projects/SerializationGenerator/SerializableMigration/SerializableMigrationSchema.cs b/Projects/SerializationGenerator/SerializableMigration/SerializableMigrationSchema.cs index 52b94d505..2f8613456 100644 --- a/Projects/SerializationGenerator/SerializableMigration/SerializableMigrationSchema.cs +++ b/Projects/SerializationGenerator/SerializableMigration/SerializableMigrationSchema.cs @@ -18,6 +18,7 @@ using System.IO; using System.Linq; using System.Text; using System.Text.Json; +using System.Text.Json.Serialization; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis; @@ -30,7 +31,7 @@ namespace SerializableMigration { WriteIndented = true, AllowTrailingCommas = true, - IgnoreNullValues = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, ReadCommentHandling = JsonCommentHandling.Skip }; diff --git a/Projects/SerializationSchemaGenerator/Application.cs b/Projects/SerializationSchemaGenerator/Application.cs index e8634aab7..eb6946787 100644 --- a/Projects/SerializationSchemaGenerator/Application.cs +++ b/Projects/SerializationSchemaGenerator/Application.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Immutable; using System.IO; using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading.Tasks; using SerializationGenerator; @@ -61,7 +62,7 @@ namespace SerializationSchemaGenerator { WriteIndented = true, AllowTrailingCommas = true, - IgnoreNullValues = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, ReadCommentHandling = JsonCommentHandling.Skip }; diff --git a/Projects/Server.Tests/Server.Tests.csproj b/Projects/Server.Tests/Server.Tests.csproj index b6339a9a0..46a5a0981 100644 --- a/Projects/Server.Tests/Server.Tests.csproj +++ b/Projects/Server.Tests/Server.Tests.csproj @@ -3,7 +3,7 @@ false - + diff --git a/Projects/Server/Json/JsonConfig.cs b/Projects/Server/Json/JsonConfig.cs index 163776d84..7bddd24c8 100644 --- a/Projects/Server/Json/JsonConfig.cs +++ b/Projects/Server/Json/JsonConfig.cs @@ -34,7 +34,7 @@ namespace Server.Json { WriteIndented = true, AllowTrailingCommas = true, - IgnoreNullValues = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, ReadCommentHandling = JsonCommentHandling.Skip, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; diff --git a/Projects/Server/Random/SecureRandom.cs b/Projects/Server/Random/SecureRandom.cs index 284502d1b..30006b871 100644 --- a/Projects/Server/Random/SecureRandom.cs +++ b/Projects/Server/Random/SecureRandom.cs @@ -25,13 +25,13 @@ namespace Server { private RandomNumberGenerator m_Random; - public RandomNumberGenerator Generator => m_Random ??= new RNGCryptoServiceProvider(); + public RandomNumberGenerator Generator => m_Random ??= RandomNumberGenerator.Create(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public override ulong NextULong() { Span buffer = stackalloc byte[sizeof(ulong)]; - Generator.GetBytes(buffer); + NextBytes(buffer); return BinaryPrimitives.ReadUInt64BigEndian(buffer); } diff --git a/Projects/Server/Utilities/PathUtility.cs b/Projects/Server/Utilities/PathUtility.cs index d2206143d..6f22c46db 100644 --- a/Projects/Server/Utilities/PathUtility.cs +++ b/Projects/Server/Utilities/PathUtility.cs @@ -63,5 +63,24 @@ namespace Server Utility.RandomBytes(bytes); return EnsureDirectory(Path.Combine(basePath, bytes.ToHexString())); } + + public static void CopyDirectory(string sourcePath, string destinationPath, bool recursive = true) + { + var searchOptions = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; + foreach (var file in Directory.EnumerateFiles(sourcePath, "*", searchOptions)) + { + var fi = new FileInfo(file); + var relativePath = Path.GetRelativePath(sourcePath, fi.DirectoryName!); + var destFolder = Path.Combine(destinationPath, relativePath); + EnsureDirectory(destFolder); + fi.CopyTo(Path.Combine(destFolder, fi.Name)); + } + } + + public static void MoveDirectory(string sourcePath, string destinationPath) + { + CopyDirectory(sourcePath, destinationPath); + Directory.Delete(sourcePath, true); + } } } diff --git a/Projects/Server/World/World.cs b/Projects/Server/World/World.cs index 02b3013d5..72d21ac2b 100644 --- a/Projects/Server/World/World.cs +++ b/Projects/Server/World/World.cs @@ -421,7 +421,7 @@ namespace Server try { EventSink.InvokeWorldSavePostSnapshot(SavePath, tempPath); - Directory.Move(tempPath, SavePath); + PathUtility.MoveDirectory(tempPath, SavePath); } catch (Exception ex) { diff --git a/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs b/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs index e69e8db6c..380ab8954 100644 --- a/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs +++ b/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs @@ -1,4 +1,5 @@ using System; +using System.Security.Cryptography; using Server.Accounting; using Server.Accounting.Security; using Xunit; @@ -9,12 +10,29 @@ namespace Server.Tests.Accounting.Security { private const string plainPassword = "hello-good-sir"; - [Theory, InlineData(typeof(Argon2PasswordProtection)), InlineData(typeof(PBKDF2PasswordProtection)), - InlineData(typeof(SHA2PasswordProtection)), InlineData(typeof(SHA1PasswordProtection)), - InlineData(typeof(MD5PasswordProtection))] - public void TestValidates(Type protectionType) + [Theory] + [InlineData(typeof(Argon2PasswordProtection), null)] + [InlineData(typeof(PBKDF2PasswordProtection), null)] + [InlineData(typeof(HashAlgorithmPasswordProtection), "MD5")] + [InlineData(typeof(HashAlgorithmPasswordProtection), "SHA1")] + [InlineData(typeof(HashAlgorithmPasswordProtection), "SHA2")] + public void TestValidates(Type protectionType, string algorithmType) { - var passwordProtection = Activator.CreateInstance(protectionType) as IPasswordProtection; + IPasswordProtection passwordProtection; + if (protectionType == typeof(HashAlgorithmPasswordProtection)) + { + passwordProtection = algorithmType switch + { + "SHA1" => HashAlgorithmPasswordProtection.SHA1Instance, + "SHA2" => HashAlgorithmPasswordProtection.SHA2Instance, + _ => HashAlgorithmPasswordProtection.MD5Instance, + }; + } + else + { + passwordProtection = Activator.CreateInstance(protectionType) as IPasswordProtection; + } + if (passwordProtection == null) { Assert.False(true, $"{protectionType.Name} is not an IPasswordProtection."); @@ -25,12 +43,29 @@ namespace Server.Tests.Accounting.Security Assert.True(passwordProtection.ValidatePassword(encryptedPassword, plainPassword)); } - [Theory, InlineData(typeof(Argon2PasswordProtection)), InlineData(typeof(PBKDF2PasswordProtection)), - InlineData(typeof(SHA2PasswordProtection)), InlineData(typeof(SHA1PasswordProtection)), - InlineData(typeof(MD5PasswordProtection))] - public void TestPasswordDoesNotValidate(Type protectionType) + [Theory] + [InlineData(typeof(Argon2PasswordProtection), null)] + [InlineData(typeof(PBKDF2PasswordProtection), null)] + [InlineData(typeof(HashAlgorithmPasswordProtection), "MD5")] + [InlineData(typeof(HashAlgorithmPasswordProtection), "SHA1")] + [InlineData(typeof(HashAlgorithmPasswordProtection), "SHA2")] + public void TestPasswordDoesNotValidate(Type protectionType, string algorithmType) { - var passwordProtection = Activator.CreateInstance(protectionType) as IPasswordProtection; + IPasswordProtection passwordProtection; + if (protectionType == typeof(HashAlgorithmPasswordProtection)) + { + passwordProtection = algorithmType switch + { + "SHA1" => HashAlgorithmPasswordProtection.SHA1Instance, + "SHA2" => HashAlgorithmPasswordProtection.SHA2Instance, + _ => HashAlgorithmPasswordProtection.MD5Instance, + }; + } + else + { + passwordProtection = Activator.CreateInstance(protectionType) as IPasswordProtection; + } + if (passwordProtection == null) { Assert.False(true, $"{protectionType.Name} is not an IPasswordProtection."); diff --git a/Projects/UOContent.Tests/UOContent.Tests.csproj b/Projects/UOContent.Tests/UOContent.Tests.csproj index baa2a972e..fc960bae1 100644 --- a/Projects/UOContent.Tests/UOContent.Tests.csproj +++ b/Projects/UOContent.Tests/UOContent.Tests.csproj @@ -3,7 +3,7 @@ false - + diff --git a/Projects/UOContent/Accounting/Security/AccountSecurity.cs b/Projects/UOContent/Accounting/Security/AccountSecurity.cs index 0725b3af8..b6d697f6a 100644 --- a/Projects/UOContent/Accounting/Security/AccountSecurity.cs +++ b/Projects/UOContent/Accounting/Security/AccountSecurity.cs @@ -55,9 +55,9 @@ namespace Server.Accounting.Security { var passwordProtection = algorithm switch { - PasswordProtectionAlgorithm.MD5 => MD5PasswordProtection.Instance, - PasswordProtectionAlgorithm.SHA1 => SHA1PasswordProtection.Instance, - PasswordProtectionAlgorithm.SHA2 => SHA2PasswordProtection.Instance, + PasswordProtectionAlgorithm.MD5 => HashAlgorithmPasswordProtection.MD5Instance, + PasswordProtectionAlgorithm.SHA1 => HashAlgorithmPasswordProtection.SHA1Instance, + PasswordProtectionAlgorithm.SHA2 => HashAlgorithmPasswordProtection.SHA2Instance, PasswordProtectionAlgorithm.PBKDF2 => PBKDF2PasswordProtection.Instance, PasswordProtectionAlgorithm.Argon2 => Argon2PasswordProtection.Instance, PasswordProtectionAlgorithm.None => throw new Exception("Do not use PasswordProtectionAlgorithm.None"), diff --git a/Projects/UOContent/Accounting/Security/SHA2PasswordProtection.cs b/Projects/UOContent/Accounting/Security/HashAlgorithmPasswordProtection.cs similarity index 64% rename from Projects/UOContent/Accounting/Security/SHA2PasswordProtection.cs rename to Projects/UOContent/Accounting/Security/HashAlgorithmPasswordProtection.cs index 02d333aa2..5a689b3ce 100644 --- a/Projects/UOContent/Accounting/Security/SHA2PasswordProtection.cs +++ b/Projects/UOContent/Accounting/Security/HashAlgorithmPasswordProtection.cs @@ -1,8 +1,8 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2020 - ModernUO Development Team * + * Copyright 2019-2021 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: SHA2PasswordProtection.cs * + * File: HashAlgorithmPasswordProtection.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 * @@ -19,15 +19,19 @@ using Server.Text; namespace Server.Accounting.Security { - public class SHA2PasswordProtection : IPasswordProtection + public class HashAlgorithmPasswordProtection : IPasswordProtection { - public static IPasswordProtection Instance = new SHA2PasswordProtection(); - private readonly SHA512CryptoServiceProvider m_SHA2HashProvider = new(); + public static IPasswordProtection MD5Instance = new HashAlgorithmPasswordProtection(MD5.Create()); + public static IPasswordProtection SHA1Instance = new HashAlgorithmPasswordProtection(SHA1.Create()); + public static IPasswordProtection SHA2Instance = new HashAlgorithmPasswordProtection(SHA512.Create()); + private readonly HashAlgorithm _hashAlgorithm; + + public HashAlgorithmPasswordProtection(HashAlgorithm hashAlgorithm) => _hashAlgorithm = hashAlgorithm; public string EncryptPassword(string plainPassword) { byte[] bytes = plainPassword.AsSpan(0, Math.Min(256, plainPassword.Length)).GetBytesAscii(); - return m_SHA2HashProvider.ComputeHash(bytes).ToHexString(); + return _hashAlgorithm.ComputeHash(bytes).ToHexString(); } public bool ValidatePassword(string encryptedPassword, string plainPassword) => diff --git a/Projects/UOContent/Accounting/Security/MD5PasswordProtection.cs b/Projects/UOContent/Accounting/Security/MD5PasswordProtection.cs deleted file mode 100644 index ba69522d3..000000000 --- a/Projects/UOContent/Accounting/Security/MD5PasswordProtection.cs +++ /dev/null @@ -1,38 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2020 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: MD5PasswordProtection.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.Security.Cryptography; -using Server.Text; - -namespace Server.Accounting.Security -{ - public class MD5PasswordProtection : IPasswordProtection - { - public static IPasswordProtection Instance = new MD5PasswordProtection(); -#pragma warning disable CA5351 - private readonly MD5CryptoServiceProvider m_MD5HashProvider = new(); -#pragma warning restore CA5351 - - public string EncryptPassword(string plainPassword) - { - byte[] bytes = plainPassword.AsSpan(0, Math.Min(256, plainPassword.Length)).GetBytesAscii(); - return m_MD5HashProvider.ComputeHash(bytes).ToHexString(); - } - - public bool ValidatePassword(string encryptedPassword, string plainPassword) => - EncryptPassword(plainPassword) == encryptedPassword; - } -} diff --git a/Projects/UOContent/Accounting/Security/SHA1PasswordProtection.cs b/Projects/UOContent/Accounting/Security/SHA1PasswordProtection.cs deleted file mode 100644 index fb2fa0ea5..000000000 --- a/Projects/UOContent/Accounting/Security/SHA1PasswordProtection.cs +++ /dev/null @@ -1,38 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2020 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SHA1PasswordProtection.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.Security.Cryptography; -using Server.Text; - -namespace Server.Accounting.Security -{ - public class SHA1PasswordProtection : IPasswordProtection - { - public static IPasswordProtection Instance = new SHA1PasswordProtection(); -#pragma warning disable CA5350 - private readonly SHA1CryptoServiceProvider m_SHA1HashProvider = new(); -#pragma warning restore CA5350 - - public string EncryptPassword(string plainPassword) - { - byte[] bytes = plainPassword.AsSpan(0, Math.Min(256, plainPassword.Length)).GetBytesAscii(); - return m_SHA1HashProvider.ComputeHash(bytes).ToHexString(); - } - - public bool ValidatePassword(string encryptedPassword, string plainPassword) => - EncryptPassword(plainPassword) == encryptedPassword; - } -} diff --git a/Projects/UOContent/Compression/TarArchive.cs b/Projects/UOContent/Compression/TarArchive.cs index 0e5889e4a..69083e509 100755 --- a/Projects/UOContent/Compression/TarArchive.cs +++ b/Projects/UOContent/Compression/TarArchive.cs @@ -5,6 +5,10 @@ using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Net; +using System.Net.Http; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; using Server.Buffers; namespace Server.Compression @@ -53,13 +57,18 @@ namespace Server.Compression var tempDir = PathUtility.EnsureRandomPath(Path.GetTempPath()); var libarchiveFile = Path.Combine(tempDir, "libarchive.zip"); - using WebClient wc = new WebClient(); - wc.DownloadFile (new Uri(_libArchiveWindowsUrl), libarchiveFile); + // This isn't called often so we don't need to optimize + using (HttpClient hc = new HttpClient()) + { + var result = hc.Send(new HttpRequestMessage(HttpMethod.Get, new Uri(_libArchiveWindowsUrl))); + using var stream = result.Content.ReadAsStream(); + using FileStream fs = new FileStream(libarchiveFile, FileMode.Create, FileAccess.Write, FileShare.None); + stream.CopyTo(fs); + } ZipFile.ExtractToDirectory(libarchiveFile, tempDir); var libArchivePath = Path.Combine(tempDir, "libarchive"); - Directory.Move(Path.Combine(libArchivePath, "bin"), "bsdtar"); - Directory.Delete(libArchivePath, true); + PathUtility.MoveDirectory(Path.Combine(libArchivePath, "bin"), Path.Combine(Core.BaseDirectory, "bsdtar")); File.Delete(libarchiveFile); return Path.Combine(Core.BaseDirectory, "bsdtar/bsdtar.exe"); diff --git a/Projects/UOContent/Compression/ZstdArchive.cs b/Projects/UOContent/Compression/ZstdArchive.cs index 35fd533d5..a1f1fee2c 100755 --- a/Projects/UOContent/Compression/ZstdArchive.cs +++ b/Projects/UOContent/Compression/ZstdArchive.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; diff --git a/Projects/UOContent/Misc/ProfessionInfo.cs b/Projects/UOContent/Misc/ProfessionInfo.cs index 9eda06bd9..6a2902d20 100644 --- a/Projects/UOContent/Misc/ProfessionInfo.cs +++ b/Projects/UOContent/Misc/ProfessionInfo.cs @@ -43,7 +43,7 @@ namespace Server } }; - var file = Core.FindDataFile("prof.txt"); + var file = Core.FindDataFile("prof.txt", false); if (!File.Exists(file)) { var parent = Path.Combine(Core.BaseDirectory, "Data/Professions"); diff --git a/Projects/UOContent/Misc/ServerList.cs b/Projects/UOContent/Misc/ServerList.cs index 031f44bc8..7d3a95194 100644 --- a/Projects/UOContent/Misc/ServerList.cs +++ b/Projects/UOContent/Misc/ServerList.cs @@ -1,5 +1,7 @@ using System; +using System.IO; using System.Net; +using System.Net.Http; using System.Net.NetworkInformation; using System.Net.Sockets; using Server.Logging; @@ -165,12 +167,16 @@ namespace Server.Misc Utility.IPMatch("169.254.*", ip) || Utility.IPMatch("100.64-127.*", ip)); + private const string _ipifyUrl = "https://api.ipify.org"; + private static IPAddress FindPublicAddress() { try { - using WebClient wc = new WebClient(); - return IPAddress.Parse(wc.DownloadString("https://api.ipify.org")); + // This isn't called often so we don't need to optimize + using HttpClient hc = new HttpClient(); + var ipAddress = hc.GetStringAsync(_ipifyUrl).Result; + return IPAddress.Parse(ipAddress); } catch { diff --git a/Projects/UOContent/Mobiles/Townfolk/Noble.cs b/Projects/UOContent/Mobiles/Townfolk/Noble.cs index 85af8457d..46cbe227b 100644 --- a/Projects/UOContent/Mobiles/Townfolk/Noble.cs +++ b/Projects/UOContent/Mobiles/Townfolk/Noble.cs @@ -25,7 +25,6 @@ namespace Server.Mobiles { return Utility.Random(6) switch { - 0 => 0, 1 => Utility.RandomBlueHue(), 2 => Utility.RandomGreenHue(), 3 => Utility.RandomRedHue(), diff --git a/Projects/UOContent/UOContent.csproj b/Projects/UOContent/UOContent.csproj index aab3a5561..5f322c829 100755 --- a/Projects/UOContent/UOContent.csproj +++ b/Projects/UOContent/UOContent.csproj @@ -39,7 +39,7 @@ false - + diff --git a/Projects/UOContent/World Saves/AutoArchive.cs b/Projects/UOContent/World Saves/AutoArchive.cs index 7d46d6168..b7cf2d50b 100755 --- a/Projects/UOContent/World Saves/AutoArchive.cs +++ b/Projects/UOContent/World Saves/AutoArchive.cs @@ -97,7 +97,7 @@ namespace Server.Saves Directory.CreateDirectory(AutomaticBackupPath); var backupPath = Path.Combine(AutomaticBackupPath, Utility.GetTimeStamp()); - Directory.Move(args.OldSavePath, backupPath); + PathUtility.MoveDirectory(args.OldSavePath, backupPath); logger.Information($"Created backup at {backupPath}"); @@ -150,7 +150,7 @@ namespace Server.Saves Directory.Delete(savePath, true); var dirInfo = new DirectoryInfo(folder); logger.Information($"Restoring backup {dirInfo.Name}"); - Directory.Move(folder, savePath); + PathUtility.MoveDirectory(folder, savePath); break; } diff --git a/README.md b/README.md index 9cd03b05e..b37e7ef2e 100644 --- a/README.md +++ b/README.md @@ -24,16 +24,19 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc [![RedHat 7/8](https://img.shields.io/badge/-8-BE0000?logo=red%20hat&logoColor=white)](https://access.redhat.com/downloads) #### Running the server -[![.NET](https://img.shields.io/badge/.NET-%205.0-5C2D91)](https://dotnet.microsoft.com/download/dotnet/5.0) +[![.NET](https://img.shields.io/badge/.NET-%206.0-5C2D91)](https://dotnet.microsoft.com/download/dotnet/6.0) #### Development [![git](https://img.shields.io/badge/-git-F05032?logo=git&logoColor=white)](https://git-scm.com/downloads) -[![.NET](https://img.shields.io/badge/.NET-%205.0.10%20SDK-5C2D91)](https://dotnet.microsoft.com/download/dotnet/5.0) +[![.NET](https://img.shields.io/badge/.NET-%206.0%20SDK-5C2D91)](https://dotnet.microsoft.com/download/dotnet/6.0) #### Supported IDEs -[Jetbrains Rider 2021.2](https://www.jetbrains.com/rider/download) -[Visual Studio 2019](https://visualstudio.microsoft.com/downloads) +  +[Jetbrains Rider 2021.3](https://www.jetbrains.com/rider/download) +                     +[Visual Studio 2022](https://visualstudio.microsoft.com/downloads)
+Rider 2021.3+             Visual Studio 2022+ ###### Note: VS Code is not currently supported. ## Getting Started diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4f721fc49..db3ef8f9a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -18,7 +18,12 @@ jobs: displayName: 'Install .NET 5' inputs: packageType: sdk - version: 5.0.401 + version: 5.0.403 + - task: UseDotNet@2 + displayName: 'Install .NET 6' + inputs: + packageType: sdk + version: 6.0.100 - task: NuGetAuthenticate@0 - script: ./publish.cmd Release win displayName: 'Build' @@ -62,7 +67,12 @@ jobs: displayName: 'Install .NET 5' inputs: packageType: sdk - version: 5.0.401 + version: 5.0.403 + - task: UseDotNet@2 + displayName: 'Install .NET 6' + inputs: + packageType: sdk + version: 6.0.100 - task: NuGetAuthenticate@0 - script: ./publish.cmd Release $(os) displayName: 'Build'