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.
This commit is contained in:
Kamron Batman 2021-11-13 13:38:01 -08:00 committed by GitHub
parent a4d9a3bdc2
commit c31bf20d0e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 192 additions and 231 deletions

View file

@ -23,10 +23,10 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
fetch-depth: 0 # avoid shallow clone so nbgv can do its work. 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 uses: actions/setup-dotnet@v1
with: with:
dotnet-version: 5.0.401 dotnet-version: 6.0.100
- name: Build - name: Build
run: ./publish.cmd run: ./publish.cmd
- name: Test - name: Test

View file

@ -4,7 +4,7 @@
<Authors>Kamron Batman</Authors> <Authors>Kamron Batman</Authors>
<Company>ModernUO</Company> <Company>ModernUO</Company>
<Copyright>2019-2020</Copyright> <Copyright>2019-2020</Copyright>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<LangVersion>preview</LangVersion> <LangVersion>preview</LangVersion>
@ -58,7 +58,7 @@
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" /> <PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0" /> <PackageReference Include="Serilog.Sinks.Console" Version="4.0.0" />
<PackageReference Include="Nerdbank.GitVersioning" Condition="!Exists('packages.config')"> <PackageReference Include="Nerdbank.GitVersioning" Condition="!Exists('packages.config')">
<Version>3.4.240</Version> <Version>3.4.244</Version>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<AdditionalFiles Include=".\Rules.ruleset" /> <AdditionalFiles Include=".\Rules.ruleset" />

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<LangVersion>9</LangVersion> <LangVersion>9</LangVersion>

View file

@ -6,7 +6,7 @@ using Server.Collections;
namespace Benchmarks namespace Benchmarks
{ {
[MemoryDiagnoser] [MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.NetCoreApp50)] [SimpleJob(RuntimeMoniker.Net60)]
public class BenchmarkOrderedHashSet public class BenchmarkOrderedHashSet
{ {
private readonly string[] _iterations = new string[16]; private readonly string[] _iterations = new string[16];

View file

@ -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<Type, FeatureFlag<Item>> m_Dictionary;
public ILookup<Type, FeatureFlag<Item>> 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<Type, FeatureFlag<Item>>();
List<FeatureFlag<Item>> m_Types = new List<FeatureFlag<Item>>();
m_TypesToLookUp = new Type[100];
foreach (var type in assembly.GetTypes())
{
if (typeof(Item).IsAssignableFrom(type))
{
m_Dictionary.Add(type, new FeatureFlag<Item>());
m_Types.Add(new FeatureFlag<Item>{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<byte> 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<Item> 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<Item> TestLookup()
{
FeatureFlag<Item> ff;
for (int i = 0; i < 100; i++)
{
ff = m_Lookup[typeof(ExplosionPotion)].GetEnumerator().Current;
if (i == 99)
{
return ff;
}
}
return null;
}
}
}

View file

@ -1,9 +0,0 @@
using System;
namespace Server
{
public class FeatureFlag<T> where T : Item
{
public Type Type { get; set; }
}
}

View file

@ -6,7 +6,7 @@ using Serilog.Core;
namespace Benchmarks namespace Benchmarks
{ {
[SimpleJob(RuntimeMoniker.NetCoreApp50)] [SimpleJob(RuntimeMoniker.Net60)]
public class BenchmarkConsoleLogging public class BenchmarkConsoleLogging
{ {
private const string text = "Sample message"; private const string text = "Sample message";

View file

@ -12,7 +12,7 @@ using Server.Tests.Network;
namespace Benchmarks namespace Benchmarks
{ {
[MemoryDiagnoser] [MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.NetCoreApp50)] [SimpleJob(RuntimeMoniker.Net60)]
public class OutgoingGumpPacketBenchmarks public class OutgoingGumpPacketBenchmarks
{ {
private static readonly byte[] _layoutBuffer = GC.AllocateUninitializedArray<byte>(0x20000); private static readonly byte[] _layoutBuffer = GC.AllocateUninitializedArray<byte>(0x20000);

View file

@ -7,7 +7,7 @@ using Server.Network;
namespace Benchmarks namespace Benchmarks
{ {
[SimpleJob(RuntimeMoniker.NetCoreApp50)] [SimpleJob(RuntimeMoniker.Net60)]
public class BenchmarkPacketBroadcast public class BenchmarkPacketBroadcast
{ {
public static int SendUnicodeMessage( public static int SendUnicodeMessage(

View file

@ -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));
}
}
}

View file

@ -5,7 +5,7 @@ using Server.Text;
namespace Benchmarks.BenchmarkText namespace Benchmarks.BenchmarkText
{ {
[MemoryDiagnoser] [MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.NetCoreApp50)] [SimpleJob(RuntimeMoniker.Net60)]
public class BenchmarkTextEncoding public class BenchmarkTextEncoding
{ {
private const string text = private const string text =

View file

@ -7,7 +7,7 @@ using Server.Buffers;
namespace Benchmarks.BenchmarkUtilities namespace Benchmarks.BenchmarkUtilities
{ {
[MemoryDiagnoser] [MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.NetCoreApp50)] [SimpleJob(RuntimeMoniker.Net60)]
public class BenchmarkStringHelpers public class BenchmarkStringHelpers
{ {
private readonly string[] names = private readonly string[] names =

View file

@ -1,4 +1,5 @@
using BenchmarkDotNet.Running; using BenchmarkDotNet.Running;
using Benchmarks.Benchmarks.Rng;
namespace Benchmarks namespace Benchmarks
{ {
@ -10,10 +11,11 @@ namespace Benchmarks
// var packetConstruction = BenchmarkRunner.Run<BenchmarkPacketConstruction>(); // var packetConstruction = BenchmarkRunner.Run<BenchmarkPacketConstruction>();
// var broadcast = BenchmarkRunner.Run<BenchmarkPacketBroadcast>(); // var broadcast = BenchmarkRunner.Run<BenchmarkPacketBroadcast>();
// var stringHelpers = BenchmarkRunner.Run<BenchmarkStringHelpers>(); // var stringHelpers = BenchmarkRunner.Run<BenchmarkStringHelpers>();
var indexList = BenchmarkRunner.Run<BenchmarkOrderedHashSet>(); // var indexList = BenchmarkRunner.Run<BenchmarkOrderedHashSet>();
// var textEncoding = BenchmarkRunner.Run<BenchmarkTextEncoding>(); // var textEncoding = BenchmarkRunner.Run<BenchmarkTextEncoding>();
// var logging = BenchmarkRunner.Run<BenchmarkConsoleLogging>(); // var logging = BenchmarkRunner.Run<BenchmarkConsoleLogging>();
// var gumpPacket = BenchmarkRunner.Run<OutgoingGumpPacketBenchmarks>(); // var gumpPacket = BenchmarkRunner.Run<OutgoingGumpPacketBenchmarks>();
var rngTest = BenchmarkRunner.Run<BenchmarkXoshiro>();
} }
} }
} }

View file

@ -18,6 +18,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
@ -30,7 +31,7 @@ namespace SerializableMigration
{ {
WriteIndented = true, WriteIndented = true,
AllowTrailingCommas = true, AllowTrailingCommas = true,
IgnoreNullValues = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
ReadCommentHandling = JsonCommentHandling.Skip ReadCommentHandling = JsonCommentHandling.Skip
}; };

View file

@ -17,6 +17,7 @@ using System;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks; using System.Threading.Tasks;
using SerializationGenerator; using SerializationGenerator;
@ -61,7 +62,7 @@ namespace SerializationSchemaGenerator
{ {
WriteIndented = true, WriteIndented = true,
AllowTrailingCommas = true, AllowTrailingCommas = true,
IgnoreNullValues = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
ReadCommentHandling = JsonCommentHandling.Skip ReadCommentHandling = JsonCommentHandling.Skip
}; };

View file

@ -3,7 +3,7 @@
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Moq" Version="4.16.1" /> <PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />

View file

@ -34,7 +34,7 @@ namespace Server.Json
{ {
WriteIndented = true, WriteIndented = true,
AllowTrailingCommas = true, AllowTrailingCommas = true,
IgnoreNullValues = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
ReadCommentHandling = JsonCommentHandling.Skip, ReadCommentHandling = JsonCommentHandling.Skip,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
}; };

View file

@ -25,13 +25,13 @@ namespace Server
{ {
private RandomNumberGenerator m_Random; private RandomNumberGenerator m_Random;
public RandomNumberGenerator Generator => m_Random ??= new RNGCryptoServiceProvider(); public RandomNumberGenerator Generator => m_Random ??= RandomNumberGenerator.Create();
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override ulong NextULong() public override ulong NextULong()
{ {
Span<byte> buffer = stackalloc byte[sizeof(ulong)]; Span<byte> buffer = stackalloc byte[sizeof(ulong)];
Generator.GetBytes(buffer); NextBytes(buffer);
return BinaryPrimitives.ReadUInt64BigEndian(buffer); return BinaryPrimitives.ReadUInt64BigEndian(buffer);
} }

View file

@ -63,5 +63,24 @@ namespace Server
Utility.RandomBytes(bytes); Utility.RandomBytes(bytes);
return EnsureDirectory(Path.Combine(basePath, bytes.ToHexString())); 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);
}
} }
} }

View file

@ -421,7 +421,7 @@ namespace Server
try try
{ {
EventSink.InvokeWorldSavePostSnapshot(SavePath, tempPath); EventSink.InvokeWorldSavePostSnapshot(SavePath, tempPath);
Directory.Move(tempPath, SavePath); PathUtility.MoveDirectory(tempPath, SavePath);
} }
catch (Exception ex) catch (Exception ex)
{ {

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Security.Cryptography;
using Server.Accounting; using Server.Accounting;
using Server.Accounting.Security; using Server.Accounting.Security;
using Xunit; using Xunit;
@ -9,12 +10,29 @@ namespace Server.Tests.Accounting.Security
{ {
private const string plainPassword = "hello-good-sir"; private const string plainPassword = "hello-good-sir";
[Theory, InlineData(typeof(Argon2PasswordProtection)), InlineData(typeof(PBKDF2PasswordProtection)), [Theory]
InlineData(typeof(SHA2PasswordProtection)), InlineData(typeof(SHA1PasswordProtection)), [InlineData(typeof(Argon2PasswordProtection), null)]
InlineData(typeof(MD5PasswordProtection))] [InlineData(typeof(PBKDF2PasswordProtection), null)]
public void TestValidates(Type protectionType) [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) if (passwordProtection == null)
{ {
Assert.False(true, $"{protectionType.Name} is not an IPasswordProtection."); Assert.False(true, $"{protectionType.Name} is not an IPasswordProtection.");
@ -25,12 +43,29 @@ namespace Server.Tests.Accounting.Security
Assert.True(passwordProtection.ValidatePassword(encryptedPassword, plainPassword)); Assert.True(passwordProtection.ValidatePassword(encryptedPassword, plainPassword));
} }
[Theory, InlineData(typeof(Argon2PasswordProtection)), InlineData(typeof(PBKDF2PasswordProtection)), [Theory]
InlineData(typeof(SHA2PasswordProtection)), InlineData(typeof(SHA1PasswordProtection)), [InlineData(typeof(Argon2PasswordProtection), null)]
InlineData(typeof(MD5PasswordProtection))] [InlineData(typeof(PBKDF2PasswordProtection), null)]
public void TestPasswordDoesNotValidate(Type protectionType) [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) if (passwordProtection == null)
{ {
Assert.False(true, $"{protectionType.Name} is not an IPasswordProtection."); Assert.False(true, $"{protectionType.Name} is not an IPasswordProtection.");

View file

@ -3,7 +3,7 @@
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Moq" Version="4.16.1" /> <PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />

View file

@ -55,9 +55,9 @@ namespace Server.Accounting.Security
{ {
var passwordProtection = algorithm switch var passwordProtection = algorithm switch
{ {
PasswordProtectionAlgorithm.MD5 => MD5PasswordProtection.Instance, PasswordProtectionAlgorithm.MD5 => HashAlgorithmPasswordProtection.MD5Instance,
PasswordProtectionAlgorithm.SHA1 => SHA1PasswordProtection.Instance, PasswordProtectionAlgorithm.SHA1 => HashAlgorithmPasswordProtection.SHA1Instance,
PasswordProtectionAlgorithm.SHA2 => SHA2PasswordProtection.Instance, PasswordProtectionAlgorithm.SHA2 => HashAlgorithmPasswordProtection.SHA2Instance,
PasswordProtectionAlgorithm.PBKDF2 => PBKDF2PasswordProtection.Instance, PasswordProtectionAlgorithm.PBKDF2 => PBKDF2PasswordProtection.Instance,
PasswordProtectionAlgorithm.Argon2 => Argon2PasswordProtection.Instance, PasswordProtectionAlgorithm.Argon2 => Argon2PasswordProtection.Instance,
PasswordProtectionAlgorithm.None => throw new Exception("Do not use PasswordProtectionAlgorithm.None"), PasswordProtectionAlgorithm.None => throw new Exception("Do not use PasswordProtectionAlgorithm.None"),

View file

@ -1,8 +1,8 @@
/************************************************************************* /*************************************************************************
* ModernUO * * ModernUO *
* Copyright 2019-2020 - ModernUO Development Team * * Copyright 2019-2021 - ModernUO Development Team *
* Email: hi@modernuo.com * * Email: hi@modernuo.com *
* File: SHA2PasswordProtection.cs * * File: HashAlgorithmPasswordProtection.cs *
* * * *
* This program is free software: you can redistribute it and/or modify * * 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 * * it under the terms of the GNU General Public License as published by *
@ -19,15 +19,19 @@ using Server.Text;
namespace Server.Accounting.Security namespace Server.Accounting.Security
{ {
public class SHA2PasswordProtection : IPasswordProtection public class HashAlgorithmPasswordProtection : IPasswordProtection
{ {
public static IPasswordProtection Instance = new SHA2PasswordProtection(); public static IPasswordProtection MD5Instance = new HashAlgorithmPasswordProtection(MD5.Create());
private readonly SHA512CryptoServiceProvider m_SHA2HashProvider = new(); 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) public string EncryptPassword(string plainPassword)
{ {
byte[] bytes = plainPassword.AsSpan(0, Math.Min(256, plainPassword.Length)).GetBytesAscii(); 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) => public bool ValidatePassword(string encryptedPassword, string plainPassword) =>

View file

@ -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 <http://www.gnu.org/licenses/>. *
*************************************************************************/
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;
}
}

View file

@ -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 <http://www.gnu.org/licenses/>. *
*************************************************************************/
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;
}
}

View file

@ -5,6 +5,10 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Net; using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Server.Buffers; using Server.Buffers;
namespace Server.Compression namespace Server.Compression
@ -53,13 +57,18 @@ namespace Server.Compression
var tempDir = PathUtility.EnsureRandomPath(Path.GetTempPath()); var tempDir = PathUtility.EnsureRandomPath(Path.GetTempPath());
var libarchiveFile = Path.Combine(tempDir, "libarchive.zip"); var libarchiveFile = Path.Combine(tempDir, "libarchive.zip");
using WebClient wc = new WebClient(); // This isn't called often so we don't need to optimize
wc.DownloadFile (new Uri(_libArchiveWindowsUrl), libarchiveFile); 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); ZipFile.ExtractToDirectory(libarchiveFile, tempDir);
var libArchivePath = Path.Combine(tempDir, "libarchive"); var libArchivePath = Path.Combine(tempDir, "libarchive");
Directory.Move(Path.Combine(libArchivePath, "bin"), "bsdtar"); PathUtility.MoveDirectory(Path.Combine(libArchivePath, "bin"), Path.Combine(Core.BaseDirectory, "bsdtar"));
Directory.Delete(libArchivePath, true);
File.Delete(libarchiveFile); File.Delete(libarchiveFile);
return Path.Combine(Core.BaseDirectory, "bsdtar/bsdtar.exe"); return Path.Combine(Core.BaseDirectory, "bsdtar/bsdtar.exe");

View file

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;

View file

@ -43,7 +43,7 @@ namespace Server
} }
}; };
var file = Core.FindDataFile("prof.txt"); var file = Core.FindDataFile("prof.txt", false);
if (!File.Exists(file)) if (!File.Exists(file))
{ {
var parent = Path.Combine(Core.BaseDirectory, "Data/Professions"); var parent = Path.Combine(Core.BaseDirectory, "Data/Professions");

View file

@ -1,5 +1,7 @@
using System; using System;
using System.IO;
using System.Net; using System.Net;
using System.Net.Http;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Net.Sockets; using System.Net.Sockets;
using Server.Logging; using Server.Logging;
@ -165,12 +167,16 @@ namespace Server.Misc
Utility.IPMatch("169.254.*", ip) || Utility.IPMatch("169.254.*", ip) ||
Utility.IPMatch("100.64-127.*", ip)); Utility.IPMatch("100.64-127.*", ip));
private const string _ipifyUrl = "https://api.ipify.org";
private static IPAddress FindPublicAddress() private static IPAddress FindPublicAddress()
{ {
try try
{ {
using WebClient wc = new WebClient(); // This isn't called often so we don't need to optimize
return IPAddress.Parse(wc.DownloadString("https://api.ipify.org")); using HttpClient hc = new HttpClient();
var ipAddress = hc.GetStringAsync(_ipifyUrl).Result;
return IPAddress.Parse(ipAddress);
} }
catch catch
{ {

View file

@ -25,7 +25,6 @@ namespace Server.Mobiles
{ {
return Utility.Random(6) switch return Utility.Random(6) switch
{ {
0 => 0,
1 => Utility.RandomBlueHue(), 1 => Utility.RandomBlueHue(),
2 => Utility.RandomGreenHue(), 2 => Utility.RandomGreenHue(),
3 => Utility.RandomRedHue(), 3 => Utility.RandomRedHue(),

View file

@ -39,7 +39,7 @@
<IncludeInPackage>false</IncludeInPackage> <IncludeInPackage>false</IncludeInPackage>
</ProjectReference> </ProjectReference>
<PackageReference Include="MailKit" Version="2.15.0" /> <PackageReference Include="MailKit" Version="2.15.0" />
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="6.0.0" />
<PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.1.0" /> <PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.1.0" />
<PackageReference Include="Zlib.Bindings" Version="1.5.0" /> <PackageReference Include="Zlib.Bindings" Version="1.5.0" />
<PackageReference Include="Argon2.Bindings" Version="1.9.1" /> <PackageReference Include="Argon2.Bindings" Version="1.9.1" />

View file

@ -97,7 +97,7 @@ namespace Server.Saves
Directory.CreateDirectory(AutomaticBackupPath); Directory.CreateDirectory(AutomaticBackupPath);
var backupPath = Path.Combine(AutomaticBackupPath, Utility.GetTimeStamp()); var backupPath = Path.Combine(AutomaticBackupPath, Utility.GetTimeStamp());
Directory.Move(args.OldSavePath, backupPath); PathUtility.MoveDirectory(args.OldSavePath, backupPath);
logger.Information($"Created backup at {backupPath}"); logger.Information($"Created backup at {backupPath}");
@ -150,7 +150,7 @@ namespace Server.Saves
Directory.Delete(savePath, true); Directory.Delete(savePath, true);
var dirInfo = new DirectoryInfo(folder); var dirInfo = new DirectoryInfo(folder);
logger.Information($"Restoring backup {dirInfo.Name}"); logger.Information($"Restoring backup {dirInfo.Name}");
Directory.Move(folder, savePath); PathUtility.MoveDirectory(folder, savePath);
break; break;
} }

View file

@ -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) [![RedHat 7/8](https://img.shields.io/badge/-8-BE0000?logo=red%20hat&logoColor=white)](https://access.redhat.com/downloads)
#### Running the server #### 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 #### Development
[![git](https://img.shields.io/badge/-git-F05032?logo=git&logoColor=white)](https://git-scm.com/downloads) [![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 #### Supported IDEs
[<img width="64" alt="Jetbrains Rider 2021.2" src="https://user-images.githubusercontent.com/3953314/133473479-734e425c-fbb6-433a-af2d-2cc8444398e8.png">](https://www.jetbrains.com/rider/download) &nbsp;
[<img width="64" alt="Visual Studio 2019" src="https://user-images.githubusercontent.com/3953314/133473556-35fd48b4-6460-49b1-b7c5-b4a8c529cc04.png">](https://visualstudio.microsoft.com/downloads) [<img width="64" alt="Jetbrains Rider 2021.3" src="https://user-images.githubusercontent.com/3953314/133473479-734e425c-fbb6-433a-af2d-2cc8444398e8.png">](https://www.jetbrains.com/rider/download)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
[<img width="64" alt="Visual Studio 2022" src="https://user-images.githubusercontent.com/3953314/133473556-35fd48b4-6460-49b1-b7c5-b4a8c529cc04.png">](https://visualstudio.microsoft.com/downloads)
<br /> <br />
Rider 2021.3+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Visual Studio 2022+
###### Note: VS Code is not currently supported. ###### Note: VS Code is not currently supported.
## Getting Started ## Getting Started

View file

@ -18,7 +18,12 @@ jobs:
displayName: 'Install .NET 5' displayName: 'Install .NET 5'
inputs: inputs:
packageType: sdk 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 - task: NuGetAuthenticate@0
- script: ./publish.cmd Release win - script: ./publish.cmd Release win
displayName: 'Build' displayName: 'Build'
@ -62,7 +67,12 @@ jobs:
displayName: 'Install .NET 5' displayName: 'Install .NET 5'
inputs: inputs:
packageType: sdk 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 - task: NuGetAuthenticate@0
- script: ./publish.cmd Release $(os) - script: ./publish.cmd Release $(os)
displayName: 'Build' displayName: 'Build'