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:
parent
a4d9a3bdc2
commit
c31bf20d0e
35 changed files with 192 additions and 231 deletions
4
.github/workflows/build-test.yml
vendored
4
.github/workflows/build-test.yml
vendored
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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" />
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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];
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Server
|
|
||||||
{
|
|
||||||
public class FeatureFlag<T> where T : Item
|
|
||||||
{
|
|
||||||
public Type Type { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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";
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
46
Projects/Benchmarks/Benchmarks/Rng/BenchmarkXoshiro.cs
Normal file
46
Projects/Benchmarks/Benchmarks/Rng/BenchmarkXoshiro.cs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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" />
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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.");
|
||||||
|
|
|
||||||
|
|
@ -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" />
|
||||||
|
|
|
||||||
|
|
@ -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"),
|
||||||
|
|
|
||||||
|
|
@ -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) =>
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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" />
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
11
README.md
11
README.md
|
|
@ -24,16 +24,19 @@ ModernUO [](https://access.redhat.com/downloads)
|
[](https://access.redhat.com/downloads)
|
||||||
|
|
||||||
#### Running the server
|
#### Running the server
|
||||||
[](https://dotnet.microsoft.com/download/dotnet/5.0)
|
[](https://dotnet.microsoft.com/download/dotnet/6.0)
|
||||||
|
|
||||||
#### Development
|
#### Development
|
||||||
[](https://git-scm.com/downloads)
|
[](https://git-scm.com/downloads)
|
||||||
[](https://dotnet.microsoft.com/download/dotnet/5.0)
|
[](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)
|
|
||||||
[<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)
|
||||||
|
|
||||||
|
[<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+ Visual Studio 2022+
|
||||||
###### Note: VS Code is not currently supported.
|
###### Note: VS Code is not currently supported.
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
|
||||||
|
|
@ -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'
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue