mirror of
https://github.com/ppy/osu.git
synced 2025-02-20 05:47:50 +08:00
Merge branch 'master' into hitobject-pooling-base
This commit is contained in:
commit
2840fd0f8f
@ -52,6 +52,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.1030.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.1110.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.1111.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
@ -54,12 +55,77 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHoldNoteMissAfterNextObjectStartTime()
|
||||
{
|
||||
var objects = new List<ManiaHitObject>
|
||||
{
|
||||
new HoldNote
|
||||
{
|
||||
StartTime = 1000,
|
||||
EndTime = 1010,
|
||||
},
|
||||
new HoldNote
|
||||
{
|
||||
StartTime = 1020,
|
||||
EndTime = 1030
|
||||
}
|
||||
};
|
||||
|
||||
performTest(objects, new List<ReplayFrame>());
|
||||
|
||||
addJudgementAssert(objects[0], HitResult.IgnoreHit);
|
||||
addJudgementAssert(objects[1], HitResult.IgnoreHit);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHoldNoteReleasedHitAfterNextObjectStartTime()
|
||||
{
|
||||
var objects = new List<ManiaHitObject>
|
||||
{
|
||||
new HoldNote
|
||||
{
|
||||
StartTime = 1000,
|
||||
EndTime = 1010,
|
||||
},
|
||||
new HoldNote
|
||||
{
|
||||
StartTime = 1020,
|
||||
EndTime = 1030
|
||||
}
|
||||
};
|
||||
|
||||
var frames = new List<ReplayFrame>
|
||||
{
|
||||
new ManiaReplayFrame(1000, ManiaAction.Key1),
|
||||
new ManiaReplayFrame(1030),
|
||||
new ManiaReplayFrame(1040, ManiaAction.Key1),
|
||||
new ManiaReplayFrame(1050)
|
||||
};
|
||||
|
||||
performTest(objects, frames);
|
||||
|
||||
addJudgementAssert(objects[0], HitResult.IgnoreHit);
|
||||
addJudgementAssert("first head", () => ((HoldNote)objects[0]).Head, HitResult.Perfect);
|
||||
addJudgementAssert("first tail", () => ((HoldNote)objects[0]).Tail, HitResult.Perfect);
|
||||
|
||||
addJudgementAssert(objects[1], HitResult.IgnoreHit);
|
||||
addJudgementAssert("second head", () => ((HoldNote)objects[1]).Head, HitResult.Great);
|
||||
addJudgementAssert("second tail", () => ((HoldNote)objects[1]).Tail, HitResult.Perfect);
|
||||
}
|
||||
|
||||
private void addJudgementAssert(ManiaHitObject hitObject, HitResult result)
|
||||
{
|
||||
AddAssert($"({hitObject.GetType().ReadableName()} @ {hitObject.StartTime}) judgement is {result}",
|
||||
() => judgementResults.Single(r => r.HitObject == hitObject).Type == result);
|
||||
}
|
||||
|
||||
private void addJudgementAssert(string name, Func<ManiaHitObject> hitObject, HitResult result)
|
||||
{
|
||||
AddAssert($"{name} judgement is {result}",
|
||||
() => judgementResults.Single(r => r.HitObject == hitObject()).Type == result);
|
||||
}
|
||||
|
||||
private void addJudgementOffsetAssert(ManiaHitObject hitObject, double offset)
|
||||
{
|
||||
AddAssert($"({hitObject.GetType().ReadableName()} @ {hitObject.StartTime}) judged at {offset}",
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
@ -44,9 +43,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
/// <param name="hitObject">The <see cref="HitObject"/> that was hit.</param>
|
||||
public void HandleHit(DrawableHitObject hitObject)
|
||||
{
|
||||
if (!IsHittable(hitObject, hitObject.HitObject.StartTime + hitObject.Result.TimeOffset))
|
||||
throw new InvalidOperationException($"A {hitObject} was hit before it became hittable!");
|
||||
|
||||
foreach (var obj in enumerateHitObjectsUpTo(hitObject.HitObject.StartTime))
|
||||
{
|
||||
if (obj.Judged)
|
||||
|
@ -125,6 +125,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
if (!enabled) return null;
|
||||
|
||||
if (component is OsuSkinComponent osuComponent && osuComponent.Component == OsuSkinComponents.SliderBody)
|
||||
return null;
|
||||
|
||||
return new OsuSpriteText
|
||||
{
|
||||
Text = identifier,
|
||||
|
@ -27,17 +27,23 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
||||
{
|
||||
foreach (var d in drawables)
|
||||
d.ApplyCustomUpdateState += applyFadeInAdjustment;
|
||||
{
|
||||
d.HitObjectApplied += applyFadeInAdjustment;
|
||||
applyFadeInAdjustment(d);
|
||||
}
|
||||
|
||||
base.ApplyToDrawableHitObjects(drawables);
|
||||
}
|
||||
|
||||
private void applyFadeInAdjustment(DrawableHitObject hitObject, ArmedState state)
|
||||
private void applyFadeInAdjustment(DrawableHitObject hitObject)
|
||||
{
|
||||
if (!(hitObject is DrawableOsuHitObject d))
|
||||
return;
|
||||
|
||||
d.HitObject.TimeFadeIn = d.HitObject.TimePreempt * fade_in_duration_multiplier;
|
||||
|
||||
foreach (var nested in d.NestedHitObjects)
|
||||
applyFadeInAdjustment(nested);
|
||||
}
|
||||
|
||||
private double lastSliderHeadFadeOutStartTime;
|
||||
|
@ -0,0 +1,37 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneKiaiHitExplosion : TaikoSkinnableTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestKiaiHits()
|
||||
{
|
||||
AddStep("rim hit", () => SetContents(() => getContentFor(createHit(HitType.Rim))));
|
||||
AddStep("centre hit", () => SetContents(() => getContentFor(createHit(HitType.Centre))));
|
||||
}
|
||||
|
||||
private Drawable getContentFor(DrawableTestHit hit)
|
||||
{
|
||||
return new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new KiaiHitExplosion(hit, hit.HitObject.Type)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private DrawableTestHit createHit(HitType type) => new DrawableTestHit(new Hit { StartTime = Time.Current, Type = type });
|
||||
}
|
||||
}
|
@ -114,6 +114,14 @@ namespace osu.Game.Rulesets.Taiko.Skinning
|
||||
|
||||
return null;
|
||||
|
||||
case TaikoSkinComponents.TaikoExplosionKiai:
|
||||
// suppress the default kiai explosion if the skin brings its own sprites.
|
||||
// the drawable needs to expire as soon as possible to avoid accumulating empty drawables on the playfield.
|
||||
if (hasExplosion.Value)
|
||||
return Drawable.Empty().With(d => d.LifetimeEnd = double.MinValue);
|
||||
|
||||
return null;
|
||||
|
||||
case TaikoSkinComponents.Scroller:
|
||||
if (GetTexture("taiko-slider") != null)
|
||||
return new LegacyTaikoScroller();
|
||||
|
@ -18,6 +18,7 @@ namespace osu.Game.Rulesets.Taiko
|
||||
TaikoExplosionMiss,
|
||||
TaikoExplosionOk,
|
||||
TaikoExplosionGreat,
|
||||
TaikoExplosionKiai,
|
||||
Scroller,
|
||||
Mascot,
|
||||
}
|
||||
|
64
osu.Game.Rulesets.Taiko/UI/DefaultKiaiHitExplosion.cs
Normal file
64
osu.Game.Rulesets.Taiko/UI/DefaultKiaiHitExplosion.cs
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osuTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
public class DefaultKiaiHitExplosion : CircularContainer
|
||||
{
|
||||
public override bool RemoveWhenNotAlive => true;
|
||||
|
||||
private readonly HitType type;
|
||||
|
||||
public DefaultKiaiHitExplosion(HitType type)
|
||||
{
|
||||
this.type = type;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
Blending = BlendingParameters.Additive;
|
||||
|
||||
Masking = true;
|
||||
Alpha = 0.25f;
|
||||
|
||||
Children = new[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = type == HitType.Rim ? colours.BlueDarker : colours.PinkDarker,
|
||||
Radius = 60,
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
this.ScaleTo(new Vector2(1, 3f), 500, Easing.OutQuint);
|
||||
this.FadeOut(250);
|
||||
|
||||
Expire(true);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,71 +1,47 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osuTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
public class KiaiHitExplosion : CircularContainer
|
||||
public class KiaiHitExplosion : Container
|
||||
{
|
||||
public override bool RemoveWhenNotAlive => true;
|
||||
|
||||
[Cached(typeof(DrawableHitObject))]
|
||||
public readonly DrawableHitObject JudgedObject;
|
||||
private readonly HitType type;
|
||||
|
||||
public KiaiHitExplosion(DrawableHitObject judgedObject, HitType type)
|
||||
private readonly HitType hitType;
|
||||
|
||||
private SkinnableDrawable skinnable;
|
||||
|
||||
public override double LifetimeStart => skinnable.Drawable.LifetimeStart;
|
||||
|
||||
public override double LifetimeEnd => skinnable.Drawable.LifetimeEnd;
|
||||
|
||||
public KiaiHitExplosion(DrawableHitObject judgedObject, HitType hitType)
|
||||
{
|
||||
JudgedObject = judgedObject;
|
||||
this.type = type;
|
||||
this.hitType = hitType;
|
||||
|
||||
Anchor = Anchor.CentreLeft;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Size = new Vector2(TaikoHitObject.DEFAULT_SIZE, 1);
|
||||
|
||||
Blending = BlendingParameters.Additive;
|
||||
|
||||
Masking = true;
|
||||
Alpha = 0.25f;
|
||||
|
||||
Children = new[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
private void load()
|
||||
{
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = type == HitType.Rim ? colours.BlueDarker : colours.PinkDarker,
|
||||
Radius = 60,
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
this.ScaleTo(new Vector2(1, 3f), 500, Easing.OutQuint);
|
||||
this.FadeOut(250);
|
||||
|
||||
Expire(true);
|
||||
Child = skinnable = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoExplosionKiai), _ => new DefaultKiaiHitExplosion(hitType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,6 @@ namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
public class LegacyBeatmapDecoder : LegacyDecoder<Beatmap>
|
||||
{
|
||||
public const int LATEST_VERSION = 14;
|
||||
|
||||
private Beatmap beatmap;
|
||||
|
||||
private ConvertHitObjectParser parser;
|
||||
|
@ -16,6 +16,8 @@ namespace osu.Game.Beatmaps.Formats
|
||||
public abstract class LegacyDecoder<T> : Decoder<T>
|
||||
where T : new()
|
||||
{
|
||||
public const int LATEST_VERSION = 14;
|
||||
|
||||
protected readonly int FormatVersion;
|
||||
|
||||
protected LegacyDecoder(int version)
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
@ -23,15 +24,15 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
private readonly Dictionary<string, string> variables = new Dictionary<string, string>();
|
||||
|
||||
public LegacyStoryboardDecoder()
|
||||
: base(0)
|
||||
public LegacyStoryboardDecoder(int version = LATEST_VERSION)
|
||||
: base(version)
|
||||
{
|
||||
}
|
||||
|
||||
public static void Register()
|
||||
{
|
||||
// note that this isn't completely correct
|
||||
AddDecoder<Storyboard>(@"osu file format v", m => new LegacyStoryboardDecoder());
|
||||
AddDecoder<Storyboard>(@"osu file format v", m => new LegacyStoryboardDecoder(Parsing.ParseInt(m.Split('v').Last())));
|
||||
AddDecoder<Storyboard>(@"[Events]", m => new LegacyStoryboardDecoder());
|
||||
SetFallbackDecoder<Storyboard>(() => new LegacyStoryboardDecoder());
|
||||
}
|
||||
@ -133,6 +134,11 @@ namespace osu.Game.Beatmaps.Formats
|
||||
var y = Parsing.ParseFloat(split[5], Parsing.MAX_COORDINATE_VALUE);
|
||||
var frameCount = Parsing.ParseInt(split[6]);
|
||||
var frameDelay = Parsing.ParseDouble(split[7]);
|
||||
|
||||
if (FormatVersion < 6)
|
||||
// this is random as hell but taken straight from osu-stable.
|
||||
frameDelay = Math.Round(0.015 * frameDelay) * 1.186 * (1000 / 60f);
|
||||
|
||||
var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever;
|
||||
storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType);
|
||||
storyboard.GetLayer(layer).Add(storyboardSprite);
|
||||
|
@ -681,7 +681,6 @@ namespace osu.Game
|
||||
|
||||
loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true);
|
||||
loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true);
|
||||
loadComponentSingleFile(externalLinkOpener = new ExternalLinkOpener(), topMostOverlayContent.Add);
|
||||
|
||||
chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible;
|
||||
|
||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
|
||||
public class PaginatedMostPlayedBeatmapContainer : PaginatedContainer<APIUserMostPlayedBeatmap>
|
||||
{
|
||||
public PaginatedMostPlayedBeatmapContainer(Bindable<User> user)
|
||||
: base(user, "Most Played Beatmaps", "No records. :(")
|
||||
: base(user, "Most Played Beatmaps", "No records. :(", CounterVisibilityState.AlwaysVisible)
|
||||
{
|
||||
ItemsPerPage = 5;
|
||||
}
|
||||
@ -27,6 +27,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
|
||||
ItemsContainer.Direction = FillDirection.Vertical;
|
||||
}
|
||||
|
||||
protected override int GetCount(User user) => user.BeatmapPlaycountsCount;
|
||||
|
||||
protected override APIRequest<List<APIUserMostPlayedBeatmap>> CreateRequest() =>
|
||||
new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, ItemsPerPage);
|
||||
|
||||
|
@ -27,8 +27,16 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
[Cached(typeof(DrawableHitObject))]
|
||||
public abstract class DrawableHitObject : SkinReloadableDrawable
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked after this <see cref="DrawableHitObject"/>'s applied <see cref="HitObject"/> has had its defaults applied.
|
||||
/// </summary>
|
||||
public event Action<DrawableHitObject> DefaultsApplied;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked after a <see cref="HitObject"/> has been applied to this <see cref="DrawableHitObject"/>.
|
||||
/// </summary>
|
||||
public event Action<DrawableHitObject> HitObjectApplied;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="HitObject"/> currently represented by this <see cref="DrawableHitObject"/>.
|
||||
/// </summary>
|
||||
@ -221,6 +229,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
HitObject.DefaultsApplied += onDefaultsApplied;
|
||||
|
||||
OnApply(hitObject);
|
||||
HitObjectApplied?.Invoke(this);
|
||||
|
||||
// If not loaded, the state update happens in LoadComplete(). Otherwise, the update is scheduled to allow for lifetime updates.
|
||||
if (IsLoaded)
|
||||
|
@ -144,6 +144,9 @@ namespace osu.Game.Users
|
||||
[JsonProperty(@"scores_first_count")]
|
||||
public int ScoresFirstCount;
|
||||
|
||||
[JsonProperty(@"beatmap_playcounts_count")]
|
||||
public int BeatmapPlaycountsCount;
|
||||
|
||||
[JsonProperty]
|
||||
private string[] playstyle
|
||||
{
|
||||
|
@ -26,7 +26,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.1110.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.1111.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.1030.0" />
|
||||
<PackageReference Include="Sentry" Version="2.1.6" />
|
||||
<PackageReference Include="SharpCompress" Version="0.26.0" />
|
||||
|
@ -70,7 +70,7 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.1110.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.1111.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.1030.0" />
|
||||
</ItemGroup>
|
||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||
@ -88,7 +88,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.1110.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.1111.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.26.0" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user