1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-12 11:42:54 +08:00

Merge branch 'master' into legacy-skin-default-fallback

This commit is contained in:
Dan Balasescu 2021-06-02 19:08:52 +09:00 committed by GitHub
commit c065f761c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 389 additions and 342 deletions

View File

@ -10,14 +10,6 @@ trim_trailing_whitespace = true
#Roslyn naming styles
#PascalCase for public and protected members
dotnet_naming_style.pascalcase.capitalization = pascal_case
dotnet_naming_symbols.public_members.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
dotnet_naming_symbols.public_members.applicable_kinds = property,method,field,event
dotnet_naming_rule.public_members_pascalcase.severity = error
dotnet_naming_rule.public_members_pascalcase.symbols = public_members
dotnet_naming_rule.public_members_pascalcase.style = pascalcase
#camelCase for private members
dotnet_naming_style.camelcase.capitalization = camel_case
@ -197,4 +189,4 @@ dotnet_diagnostic.IDE0069.severity = none
dotnet_diagnostic.CA2225.severity = none
# Banned APIs
dotnet_diagnostic.RS0030.severity = error
dotnet_diagnostic.RS0030.severity = error

View File

@ -97,7 +97,7 @@ Before committing your code, please run a code formatter. This can be achieved b
We have adopted some cross-platform, compiler integrated analyzers. They can provide warnings when you are editing, building inside IDE or from command line, as-if they are provided by the compiler itself.
JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it from PowerShell with `.\InspectCode.ps1`, which is [only supported on Windows](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternatively, you can install ReSharper or use Rider to get inline support in your IDE of choice.
JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it from PowerShell with `.\InspectCode.ps1`. Alternatively, you can install ReSharper or use Rider to get inline support in your IDE of choice.
## Contributing

View File

@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -52,6 +52,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.525.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.528.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.601.0" />
</ItemGroup>
</Project>

View File

@ -57,7 +57,7 @@ namespace osu.Desktop
private string getStableInstallPath()
{
static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs"));
static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")) || File.Exists(Path.Combine(p, "osu!.cfg"));
string stableInstallPath;

View File

@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="BenchmarkDotNet" Version="0.13.0" />
<PackageReference Include="nunit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
</ItemGroup>

View File

@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Catch.Tests
CircleSize = circleSize
};
SetContents(() =>
SetContents(_ =>
{
var droppedObjectContainer = new Container<CaughtObject>
{

View File

@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Tests
{
scoreProcessor = new ScoreProcessor();
SetContents(() => new CatchComboDisplay
SetContents(_ => new CatchComboDisplay
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -19,22 +19,22 @@ namespace osu.Game.Rulesets.Catch.Tests
{
base.LoadComplete();
AddStep("show pear", () => SetContents(() => createDrawableFruit(0)));
AddStep("show grape", () => SetContents(() => createDrawableFruit(1)));
AddStep("show pineapple / apple", () => SetContents(() => createDrawableFruit(2)));
AddStep("show raspberry / orange", () => SetContents(() => createDrawableFruit(3)));
AddStep("show pear", () => SetContents(_ => createDrawableFruit(0)));
AddStep("show grape", () => SetContents(_ => createDrawableFruit(1)));
AddStep("show pineapple / apple", () => SetContents(_ => createDrawableFruit(2)));
AddStep("show raspberry / orange", () => SetContents(_ => createDrawableFruit(3)));
AddStep("show banana", () => SetContents(createDrawableBanana));
AddStep("show banana", () => SetContents(_ => createDrawableBanana()));
AddStep("show droplet", () => SetContents(() => createDrawableDroplet()));
AddStep("show tiny droplet", () => SetContents(createDrawableTinyDroplet));
AddStep("show droplet", () => SetContents(_ => createDrawableDroplet()));
AddStep("show tiny droplet", () => SetContents(_ => createDrawableTinyDroplet()));
AddStep("show hyperdash pear", () => SetContents(() => createDrawableFruit(0, true)));
AddStep("show hyperdash grape", () => SetContents(() => createDrawableFruit(1, true)));
AddStep("show hyperdash pineapple / apple", () => SetContents(() => createDrawableFruit(2, true)));
AddStep("show hyperdash raspberry / orange", () => SetContents(() => createDrawableFruit(3, true)));
AddStep("show hyperdash pear", () => SetContents(_ => createDrawableFruit(0, true)));
AddStep("show hyperdash grape", () => SetContents(_ => createDrawableFruit(1, true)));
AddStep("show hyperdash pineapple / apple", () => SetContents(_ => createDrawableFruit(2, true)));
AddStep("show hyperdash raspberry / orange", () => SetContents(_ => createDrawableFruit(3, true)));
AddStep("show hyperdash droplet", () => SetContents(() => createDrawableDroplet(true)));
AddStep("show hyperdash droplet", () => SetContents(_ => createDrawableDroplet(true)));
}
private Drawable createDrawableFruit(int indexInBeatmap, bool hyperdash = false) =>

View File

@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Catch.Tests
protected override void LoadComplete()
{
AddStep("fruit changes visual and hyper", () => SetContents(() => new TestDrawableCatchHitObjectSpecimen(new DrawableFruit(new Fruit
AddStep("fruit changes visual and hyper", () => SetContents(_ => new TestDrawableCatchHitObjectSpecimen(new DrawableFruit(new Fruit
{
IndexInBeatmapBindable = { BindTarget = indexInBeatmap },
HyperDashBindable = { BindTarget = hyperDash },
}))));
AddStep("droplet changes hyper", () => SetContents(() => new TestDrawableCatchHitObjectSpecimen(new DrawableDroplet(new Droplet
AddStep("droplet changes hyper", () => SetContents(_ => new TestDrawableCatchHitObjectSpecimen(new DrawableDroplet(new Droplet
{
HyperDashBindable = { BindTarget = hyperDash },
}))));

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
[SetUp]
public void SetUp() => Schedule(() =>
{
SetContents(() => new FillFlowContainer
SetContents(_ => new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
[BackgroundDependencyLoader]
private void load()
{
SetContents(() => new FillFlowContainer
SetContents(_ => new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
[BackgroundDependencyLoader]
private void load()
{
SetContents(() => new FillFlowContainer
SetContents(_ => new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
{
if (hitWindows.IsHitResultAllowed(result))
{
AddStep("Show " + result.GetDescription(), () => SetContents(() =>
AddStep("Show " + result.GetDescription(), () => SetContents(_ =>
new DrawableManiaJudgement(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement())
{
Type = result

View File

@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
[BackgroundDependencyLoader]
private void load()
{
SetContents(() =>
SetContents(_ =>
{
var pool = new DrawablePool<PoolableHitExplosion>(5);
hitExplosionPools.Add(pool);

View File

@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
[BackgroundDependencyLoader]
private void load()
{
SetContents(() => new FillFlowContainer
SetContents(_ => new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
new StageDefinition { Columns = 2 }
};
SetContents(() => new ManiaPlayfield(stageDefinitions));
SetContents(_ => new ManiaPlayfield(stageDefinitions));
});
}
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
new StageDefinition { Columns = 2 }
};
SetContents(() => new ManiaPlayfield(stageDefinitions));
SetContents(_ => new ManiaPlayfield(stageDefinitions));
});
}

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
[BackgroundDependencyLoader]
private void load()
{
SetContents(() =>
SetContents(_ =>
{
ManiaAction normalAction = ManiaAction.Key1;
ManiaAction specialAction = ManiaAction.Special1;

View File

@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
[BackgroundDependencyLoader]
private void load()
{
SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: new StageDefinition { Columns = 4 }),
SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: new StageDefinition { Columns = 4 }),
_ => new DefaultStageBackground())
{
Anchor = Anchor.Centre,

View File

@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
[BackgroundDependencyLoader]
private void load()
{
SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: new StageDefinition { Columns = 4 }), _ => null)
SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: new StageDefinition { Columns = 4 }), _ => null)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
int poolIndex = 0;
SetContents(() =>
SetContents(_ =>
{
DrawablePool<TestDrawableOsuJudgement> pool;

View File

@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Osu.Tests
private void loadContent(bool automated = true, Func<SkinProvidingContainer> skinProvider = null)
{
SetContents(() =>
SetContents(_ =>
{
var inputManager = automated ? (InputManager)new MovingCursorInputManager() : new OsuInputManager(new OsuRuleset().RulesetInfo);
var skinContainer = skinProvider?.Invoke() ?? new SkinProvidingContainer(null);

View File

@ -23,18 +23,18 @@ namespace osu.Game.Rulesets.Osu.Tests
[Test]
public void TestVariousHitCircles()
{
AddStep("Miss Big Single", () => SetContents(() => testSingle(2)));
AddStep("Miss Medium Single", () => SetContents(() => testSingle(5)));
AddStep("Miss Small Single", () => SetContents(() => testSingle(7)));
AddStep("Hit Big Single", () => SetContents(() => testSingle(2, true)));
AddStep("Hit Medium Single", () => SetContents(() => testSingle(5, true)));
AddStep("Hit Small Single", () => SetContents(() => testSingle(7, true)));
AddStep("Miss Big Stream", () => SetContents(() => testStream(2)));
AddStep("Miss Medium Stream", () => SetContents(() => testStream(5)));
AddStep("Miss Small Stream", () => SetContents(() => testStream(7)));
AddStep("Hit Big Stream", () => SetContents(() => testStream(2, true)));
AddStep("Hit Medium Stream", () => SetContents(() => testStream(5, true)));
AddStep("Hit Small Stream", () => SetContents(() => testStream(7, true)));
AddStep("Miss Big Single", () => SetContents(_ => testSingle(2)));
AddStep("Miss Medium Single", () => SetContents(_ => testSingle(5)));
AddStep("Miss Small Single", () => SetContents(_ => testSingle(7)));
AddStep("Hit Big Single", () => SetContents(_ => testSingle(2, true)));
AddStep("Hit Medium Single", () => SetContents(_ => testSingle(5, true)));
AddStep("Hit Small Single", () => SetContents(_ => testSingle(7, true)));
AddStep("Miss Big Stream", () => SetContents(_ => testStream(2)));
AddStep("Miss Medium Stream", () => SetContents(_ => testStream(5)));
AddStep("Miss Small Stream", () => SetContents(_ => testStream(7)));
AddStep("Hit Big Stream", () => SetContents(_ => testStream(2, true)));
AddStep("Hit Medium Stream", () => SetContents(_ => testStream(5, true)));
AddStep("Hit Small Stream", () => SetContents(_ => testStream(7, true)));
}
private Drawable testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null)

View File

@ -30,54 +30,54 @@ namespace osu.Game.Rulesets.Osu.Tests
[Test]
public void TestVariousSliders()
{
AddStep("Big Single", () => SetContents(() => testSimpleBig()));
AddStep("Medium Single", () => SetContents(() => testSimpleMedium()));
AddStep("Small Single", () => SetContents(() => testSimpleSmall()));
AddStep("Big 1 Repeat", () => SetContents(() => testSimpleBig(1)));
AddStep("Medium 1 Repeat", () => SetContents(() => testSimpleMedium(1)));
AddStep("Small 1 Repeat", () => SetContents(() => testSimpleSmall(1)));
AddStep("Big 2 Repeats", () => SetContents(() => testSimpleBig(2)));
AddStep("Medium 2 Repeats", () => SetContents(() => testSimpleMedium(2)));
AddStep("Small 2 Repeats", () => SetContents(() => testSimpleSmall(2)));
AddStep("Big Single", () => SetContents(_ => testSimpleBig()));
AddStep("Medium Single", () => SetContents(_ => testSimpleMedium()));
AddStep("Small Single", () => SetContents(_ => testSimpleSmall()));
AddStep("Big 1 Repeat", () => SetContents(_ => testSimpleBig(1)));
AddStep("Medium 1 Repeat", () => SetContents(_ => testSimpleMedium(1)));
AddStep("Small 1 Repeat", () => SetContents(_ => testSimpleSmall(1)));
AddStep("Big 2 Repeats", () => SetContents(_ => testSimpleBig(2)));
AddStep("Medium 2 Repeats", () => SetContents(_ => testSimpleMedium(2)));
AddStep("Small 2 Repeats", () => SetContents(_ => testSimpleSmall(2)));
AddStep("Slow Slider", () => SetContents(testSlowSpeed)); // slow long sliders take ages already so no repeat steps
AddStep("Slow Short Slider", () => SetContents(() => testShortSlowSpeed()));
AddStep("Slow Short Slider 1 Repeats", () => SetContents(() => testShortSlowSpeed(1)));
AddStep("Slow Short Slider 2 Repeats", () => SetContents(() => testShortSlowSpeed(2)));
AddStep("Slow Slider", () => SetContents(_ => testSlowSpeed())); // slow long sliders take ages already so no repeat steps
AddStep("Slow Short Slider", () => SetContents(_ => testShortSlowSpeed()));
AddStep("Slow Short Slider 1 Repeats", () => SetContents(_ => testShortSlowSpeed(1)));
AddStep("Slow Short Slider 2 Repeats", () => SetContents(_ => testShortSlowSpeed(2)));
AddStep("Fast Slider", () => SetContents(() => testHighSpeed()));
AddStep("Fast Slider 1 Repeat", () => SetContents(() => testHighSpeed(1)));
AddStep("Fast Slider 2 Repeats", () => SetContents(() => testHighSpeed(2)));
AddStep("Fast Short Slider", () => SetContents(() => testShortHighSpeed()));
AddStep("Fast Short Slider 1 Repeat", () => SetContents(() => testShortHighSpeed(1)));
AddStep("Fast Short Slider 2 Repeats", () => SetContents(() => testShortHighSpeed(2)));
AddStep("Fast Short Slider 6 Repeats", () => SetContents(() => testShortHighSpeed(6)));
AddStep("Fast Slider", () => SetContents(_ => testHighSpeed()));
AddStep("Fast Slider 1 Repeat", () => SetContents(_ => testHighSpeed(1)));
AddStep("Fast Slider 2 Repeats", () => SetContents(_ => testHighSpeed(2)));
AddStep("Fast Short Slider", () => SetContents(_ => testShortHighSpeed()));
AddStep("Fast Short Slider 1 Repeat", () => SetContents(_ => testShortHighSpeed(1)));
AddStep("Fast Short Slider 2 Repeats", () => SetContents(_ => testShortHighSpeed(2)));
AddStep("Fast Short Slider 6 Repeats", () => SetContents(_ => testShortHighSpeed(6)));
AddStep("Perfect Curve", () => SetContents(() => testPerfect()));
AddStep("Perfect Curve 1 Repeat", () => SetContents(() => testPerfect(1)));
AddStep("Perfect Curve 2 Repeats", () => SetContents(() => testPerfect(2)));
AddStep("Perfect Curve", () => SetContents(_ => testPerfect()));
AddStep("Perfect Curve 1 Repeat", () => SetContents(_ => testPerfect(1)));
AddStep("Perfect Curve 2 Repeats", () => SetContents(_ => testPerfect(2)));
AddStep("Linear Slider", () => SetContents(() => testLinear()));
AddStep("Linear Slider 1 Repeat", () => SetContents(() => testLinear(1)));
AddStep("Linear Slider 2 Repeats", () => SetContents(() => testLinear(2)));
AddStep("Linear Slider", () => SetContents(_ => testLinear()));
AddStep("Linear Slider 1 Repeat", () => SetContents(_ => testLinear(1)));
AddStep("Linear Slider 2 Repeats", () => SetContents(_ => testLinear(2)));
AddStep("Bezier Slider", () => SetContents(() => testBezier()));
AddStep("Bezier Slider 1 Repeat", () => SetContents(() => testBezier(1)));
AddStep("Bezier Slider 2 Repeats", () => SetContents(() => testBezier(2)));
AddStep("Bezier Slider", () => SetContents(_ => testBezier()));
AddStep("Bezier Slider 1 Repeat", () => SetContents(_ => testBezier(1)));
AddStep("Bezier Slider 2 Repeats", () => SetContents(_ => testBezier(2)));
AddStep("Linear Overlapping", () => SetContents(() => testLinearOverlapping()));
AddStep("Linear Overlapping 1 Repeat", () => SetContents(() => testLinearOverlapping(1)));
AddStep("Linear Overlapping 2 Repeats", () => SetContents(() => testLinearOverlapping(2)));
AddStep("Linear Overlapping", () => SetContents(_ => testLinearOverlapping()));
AddStep("Linear Overlapping 1 Repeat", () => SetContents(_ => testLinearOverlapping(1)));
AddStep("Linear Overlapping 2 Repeats", () => SetContents(_ => testLinearOverlapping(2)));
AddStep("Catmull Slider", () => SetContents(() => testCatmull()));
AddStep("Catmull Slider 1 Repeat", () => SetContents(() => testCatmull(1)));
AddStep("Catmull Slider 2 Repeats", () => SetContents(() => testCatmull(2)));
AddStep("Catmull Slider", () => SetContents(_ => testCatmull()));
AddStep("Catmull Slider 1 Repeat", () => SetContents(_ => testCatmull(1)));
AddStep("Catmull Slider 2 Repeats", () => SetContents(_ => testCatmull(2)));
AddStep("Big Single, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset()));
AddStep("Big 1 Repeat, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset(1)));
AddStep("Big Single, Large StackOffset", () => SetContents(_ => testSimpleBigLargeStackOffset()));
AddStep("Big 1 Repeat, Large StackOffset", () => SetContents(_ => testSimpleBigLargeStackOffset(1)));
AddStep("Distance Overflow", () => SetContents(() => testDistanceOverflow()));
AddStep("Distance Overflow 1 Repeat", () => SetContents(() => testDistanceOverflow(1)));
AddStep("Distance Overflow", () => SetContents(_ => testDistanceOverflow()));
AddStep("Distance Overflow 1 Repeat", () => SetContents(_ => testDistanceOverflow(1)));
}
[Test]

View File

@ -29,15 +29,15 @@ namespace osu.Game.Rulesets.Osu.Tests
public void TestVariousSpinners(bool autoplay)
{
string term = autoplay ? "Hit" : "Miss";
AddStep($"{term} Big", () => SetContents(() => testSingle(2, autoplay)));
AddStep($"{term} Medium", () => SetContents(() => testSingle(5, autoplay)));
AddStep($"{term} Small", () => SetContents(() => testSingle(7, autoplay)));
AddStep($"{term} Big", () => SetContents(_ => testSingle(2, autoplay)));
AddStep($"{term} Medium", () => SetContents(_ => testSingle(5, autoplay)));
AddStep($"{term} Small", () => SetContents(_ => testSingle(7, autoplay)));
}
[Test]
public void TestSpinningSamplePitchShift()
{
AddStep("Add spinner", () => SetContents(() => testSingle(5, true, 4000)));
AddStep("Add spinner", () => SetContents(_ => testSingle(5, true, 4000)));
AddUntilStep("Pitch starts low", () => getSpinningSample().Frequency.Value < 0.8);
AddUntilStep("Pitch increases", () => getSpinningSample().Frequency.Value > 0.8);
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Tests
[TestCase(true)]
public void TestLongSpinner(bool autoplay)
{
AddStep("Very long spinner", () => SetContents(() => testSingle(5, autoplay, 4000)));
AddStep("Very long spinner", () => SetContents(_ => testSingle(5, autoplay, 4000)));
AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult);
AddUntilStep("Check correct progress", () => drawableSpinner.Progress == (autoplay ? 1 : 0));
}
@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Tests
[TestCase(true)]
public void TestSuperShortSpinner(bool autoplay)
{
AddStep("Very short spinner", () => SetContents(() => testSingle(5, autoplay, 200)));
AddStep("Very short spinner", () => SetContents(_ => testSingle(5, autoplay, 200)));
AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult);
AddUntilStep("Short spinner implicitly completes", () => drawableSpinner.Progress == 1);
}

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -182,8 +182,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
// todo: temporary / arbitrary, used for lifetime optimisation.
this.Delay(800).FadeOut();
(CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
switch (state)
{
case ArmedState.Idle:

View File

@ -97,8 +97,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
base.UpdateHitStateTransforms(state);
(CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
switch (state)
{
case ArmedState.Idle:

View File

@ -87,8 +87,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Debug.Assert(HitObject.HitWindows != null);
(CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
switch (state)
{
case ArmedState.Idle:

View File

@ -1,17 +0,0 @@
// 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 osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public interface IMainCirclePiece
{
/// <summary>
/// Begins animating this <see cref="IMainCirclePiece"/>.
/// </summary>
/// <param name="state">The <see cref="ArmedState"/> of the related <see cref="DrawableHitCircle"/>.</param>
void Animate(ArmedState state);
}
}

View File

@ -13,7 +13,7 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class MainCirclePiece : CompositeDrawable, IMainCirclePiece
public class MainCirclePiece : CompositeDrawable
{
private readonly CirclePiece circle;
private readonly RingPiece ring;
@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
private readonly IBindable<ArmedState> armedState = new Bindable<ArmedState>();
[Resolved]
private DrawableHitObject drawableObject { get; set; }
@ -53,6 +54,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
accentColour.BindTo(drawableObject.AccentColour);
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
armedState.BindTo(drawableObject.State);
}
protected override void LoadComplete()
@ -67,16 +69,20 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
}, true);
indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true);
armedState.BindValueChanged(animate, true);
}
public void Animate(ArmedState state)
private void animate(ValueChangedEvent<ArmedState> state)
{
ClearTransforms(true);
using (BeginAbsoluteSequence(drawableObject.StateUpdateTime))
glow.FadeOut(400);
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
{
switch (state)
switch (state.NewValue)
{
case ArmedState.Hit:
const double flash_in = 40;
@ -89,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
explode.FadeIn(flash_in);
this.ScaleTo(1.5f, 400, Easing.OutQuad);
using (BeginDelayedSequence(flash_in, true))
using (BeginDelayedSequence(flash_in))
{
// after the flash, we can hide some elements that were behind it
ring.FadeOut();

View File

@ -12,7 +12,6 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
@ -20,7 +19,7 @@ using static osu.Game.Skinning.LegacySkinConfiguration;
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
public class LegacyMainCirclePiece : CompositeDrawable, IMainCirclePiece
public class LegacyMainCirclePiece : CompositeDrawable
{
private readonly string priorityLookup;
private readonly bool hasNumber;
@ -41,6 +40,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
private readonly IBindable<ArmedState> armedState = new Bindable<ArmedState>();
[Resolved]
private DrawableHitObject drawableObject { get; set; }
@ -115,6 +115,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
accentColour.BindTo(drawableObject.AccentColour);
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
armedState.BindTo(drawableObject.State);
Texture getTextureWithFallback(string name)
{
@ -139,15 +140,19 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
if (hasNumber)
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
armedState.BindValueChanged(animate, true);
}
public void Animate(ArmedState state)
private void animate(ValueChangedEvent<ArmedState> state)
{
const double legacy_fade_duration = 240;
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime, true))
ClearTransforms(true);
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
{
switch (state)
switch (state.NewValue)
{
case ArmedState.Hit:
circleSprites.FadeOut(legacy_fade_duration, Easing.Out);

View File

@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
[BackgroundDependencyLoader]
private void load()
{
AddStep("Bar line", () => SetContents(() =>
AddStep("Bar line", () => SetContents(_ =>
{
ScrollingHitObjectContainer hoc;
@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
return cont;
}));
AddStep("Bar line (major)", () => SetContents(() =>
AddStep("Bar line (major)", () => SetContents(_ =>
{
ScrollingHitObjectContainer hoc;

View File

@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
[BackgroundDependencyLoader]
private void load()
{
AddStep("Drum roll", () => SetContents(() =>
AddStep("Drum roll", () => SetContents(_ =>
{
var hoc = new ScrollingHitObjectContainer();
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
return hoc;
}));
AddStep("Drum roll (strong)", () => SetContents(() =>
AddStep("Drum roll (strong)", () => SetContents(_ =>
{
var hoc = new ScrollingHitObjectContainer();

View File

@ -17,25 +17,25 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
[BackgroundDependencyLoader]
private void load()
{
AddStep("Centre hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime())
AddStep("Centre hit", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime())
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}));
AddStep("Centre hit (strong)", () => SetContents(() => new DrawableHit(createHitAtCurrentTime(true))
AddStep("Centre hit (strong)", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime(true))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}));
AddStep("Rim hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime())
AddStep("Rim hit", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime())
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}));
AddStep("Rim hit (strong)", () => SetContents(() => new DrawableHit(createHitAtCurrentTime(true))
AddStep("Rim hit (strong)", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime(true))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -54,16 +54,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
{
AddStep("set beatmap", () => setBeatmap());
AddStep("clear state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Clear)));
AddStep("idle state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Idle)));
AddStep("kiai state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai)));
AddStep("fail state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Fail)));
AddStep("clear state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Clear)));
AddStep("idle state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Idle)));
AddStep("kiai state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai)));
AddStep("fail state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Fail)));
}
[Test]
public void TestInitialState()
{
AddStep("create mascot", () => SetContents(() => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
AddStep("create mascot", () => SetContents(_ => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
AddAssert("mascot initially idle", () => allMascotsIn(TaikoMascotAnimationState.Idle));
}
@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
{
AddStep("set beatmap", () => setBeatmap());
AddStep("create mascot", () => SetContents(() => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
AddStep("create mascot", () => SetContents(_ => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
AddStep("set clear state", () => mascots.ForEach(mascot => mascot.State.Value = TaikoMascotAnimationState.Clear));
AddStep("miss", () => mascots.ForEach(mascot => mascot.LastResult.Value = new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss }));
@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
{
Beatmap.Value.Track.Start();
SetContents(() =>
SetContents(_ =>
{
var ruleset = new TaikoRuleset();
return new DrawableTaikoRuleset(ruleset, Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo));

View File

@ -22,16 +22,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
[Test]
public void TestNormalHit()
{
AddStep("Great", () => SetContents(() => getContentFor(createHit(HitResult.Great))));
AddStep("Ok", () => SetContents(() => getContentFor(createHit(HitResult.Ok))));
AddStep("Miss", () => SetContents(() => getContentFor(createHit(HitResult.Miss))));
AddStep("Great", () => SetContents(_ => getContentFor(createHit(HitResult.Great))));
AddStep("Ok", () => SetContents(_ => getContentFor(createHit(HitResult.Ok))));
AddStep("Miss", () => SetContents(_ => getContentFor(createHit(HitResult.Miss))));
}
[TestCase(HitResult.Great)]
[TestCase(HitResult.Ok)]
public void TestStrongHit(HitResult type)
{
AddStep("create hit", () => SetContents(() => getContentFor(createStrongHit(type))));
AddStep("create hit", () => SetContents(_ => getContentFor(createStrongHit(type))));
AddStep("visualise second hit",
() => this.ChildrenOfType<HitExplosion>()
.ForEach(e => e.VisualiseSecondHit(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement()))));

View File

@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
[BackgroundDependencyLoader]
private void load()
{
SetContents(() => new TaikoInputManager(new TaikoRuleset().RulesetInfo)
SetContents(_ => new TaikoInputManager(new TaikoRuleset().RulesetInfo)
{
RelativeSizeAxes = Axes.Both,
Child = new Container

View File

@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
[Test]
public void TestKiaiHits()
{
AddStep("rim hit", () => SetContents(() => getContentFor(createHit(HitType.Rim))));
AddStep("centre hit", () => SetContents(() => getContentFor(createHit(HitType.Centre))));
AddStep("rim hit", () => SetContents(_ => getContentFor(createHit(HitType.Rim))));
AddStep("centre hit", () => SetContents(_ => getContentFor(createHit(HitType.Centre))));
}
private Drawable getContentFor(DrawableTestHit hit)

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
Beatmap.Value.Track.Start();
});
AddStep("Load playfield", () => SetContents(() => new TaikoPlayfield(new ControlPointInfo())
AddStep("Load playfield", () => SetContents(_ => new TaikoPlayfield(new ControlPointInfo())
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,

View File

@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
public TestSceneTaikoScroller()
{
AddStep("Load scroller", () => SetContents(() =>
AddStep("Load scroller", () => SetContents(_ =>
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Scroller), _ => Empty())
{
Clock = new FramedClock(clock),

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -489,5 +489,23 @@ namespace osu.Game.Tests.Chat
Assert.AreEqual(result.Links[2].Url, "\uD83D\uDE00");
Assert.AreEqual(result.Links[3].Url, "\uD83D\uDE20");
}
[Test]
public void TestAbsoluteExternalLinks()
{
LinkDetails result = MessageFormatter.GetLinkDetails("https://google.com");
Assert.AreEqual(LinkAction.External, result.Action);
Assert.AreEqual("https://google.com", result.Argument);
}
[Test]
public void TestRelativeExternalLinks()
{
LinkDetails result = MessageFormatter.GetLinkDetails("/relative");
Assert.AreEqual(LinkAction.External, result.Action);
Assert.AreEqual("/relative", result.Argument);
}
}
}

View File

@ -92,6 +92,41 @@ namespace osu.Game.Tests.Gameplay
AddAssert("Lifetime is correct", () => dho.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY && entry.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY);
}
[Test]
public void TestDrawableLifetimeUpdateOnEntryLifetimeChange()
{
TestDrawableHitObject dho = null;
TestLifetimeEntry entry = null;
AddStep("Create DHO", () =>
{
dho = new TestDrawableHitObject(null);
dho.Apply(entry = new TestLifetimeEntry(new HitObject()));
Child = dho;
});
AddStep("Set entry lifetime", () =>
{
entry.LifetimeStart = 777;
entry.LifetimeEnd = 888;
});
AddAssert("Drawable lifetime is updated", () => dho.LifetimeStart == 777 && dho.LifetimeEnd == 888);
AddStep("KeepAlive = true", () => entry.KeepAlive = true);
AddAssert("Drawable lifetime is updated", () => dho.LifetimeStart == double.MinValue && dho.LifetimeEnd == double.MaxValue);
AddStep("Modify start time", () => entry.HitObject.StartTime = 100);
AddAssert("Drawable lifetime is correct", () => dho.LifetimeStart == double.MinValue);
AddStep("Set LifetimeStart", () => dho.LifetimeStart = 666);
AddAssert("Lifetime change is blocked", () => dho.LifetimeStart == double.MinValue);
AddStep("Set LifetimeEnd", () => dho.LifetimeEnd = 999);
AddAssert("Lifetime change is blocked", () => dho.LifetimeEnd == double.MaxValue);
AddStep("KeepAlive = false", () => entry.KeepAlive = false);
AddAssert("Drawable lifetime is restored", () => dho.LifetimeStart == 666 && dho.LifetimeEnd == 999);
}
private class TestDrawableHitObject : DrawableHitObject
{
public const double INITIAL_LIFETIME_OFFSET = 100;

View File

@ -0,0 +1,33 @@
// 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.Game.Rulesets;
using osu.Game.Rulesets.Osu;
namespace osu.Game.Tests.Visual.Gameplay
{
public abstract class SkinnableHUDComponentTestScene : SkinnableTestScene
{
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
[SetUp]
public void SetUp() => Schedule(() =>
{
SetContents(skin =>
{
var implementation = skin != null
? CreateLegacyImplementation()
: CreateDefaultImplementation();
implementation.Anchor = Anchor.Centre;
implementation.Origin = Anchor.Centre;
return implementation;
});
});
protected abstract Drawable CreateDefaultImplementation();
protected abstract Drawable CreateLegacyImplementation();
}
}

View File

@ -90,6 +90,20 @@ namespace osu.Game.Tests.Visual.Gameplay
assertChildPosition(5);
}
[TestCase("pooled")]
[TestCase("non-pooled")]
public void TestLifetimeRecomputedWhenTimeRangeChanges(string pooled)
{
var beatmap = createBeatmap(_ => pooled == "pooled" ? new TestPooledHitObject() : new TestHitObject());
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range });
createTest(beatmap);
assertDead(3);
AddStep("increase time range", () => drawableRuleset.TimeRange.Value = 3 * time_range);
assertPosition(3, 1);
}
[Test]
public void TestRelativeBeatLengthScaleSingleTimingPoint()
{

View File

@ -29,8 +29,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("allow skin lookup", () => storyboard.UseSkinSprites = false);
AddStep("create sprites", () => SetContents(
() => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
assertSpritesFromSkin(false);
}
@ -42,8 +41,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("allow skin lookup", () => storyboard.UseSkinSprites = true);
AddStep("create sprites", () => SetContents(
() => createSprite(lookup_name, Anchor.Centre, Vector2.Zero)));
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.Centre, Vector2.Zero)));
assertSpritesFromSkin(true);
}

View File

@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestToggleEditor()
{
AddStep("show available components", () => SetContents(() => new SkinComponentToolbox(300)
AddStep("show available components", () => SetContents(_ => new SkinComponentToolbox(300)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,

View File

@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
AddStep("create editor overlay", () =>
{
SetContents(() =>
SetContents(_ =>
{
var ruleset = new OsuRuleset();
var mods = new[] { ruleset.GetAutoplayMod() };

View File

@ -3,26 +3,26 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD;
using osu.Game.Skinning;
namespace osu.Game.Tests.Visual.Gameplay
{
public class TestSceneSkinnableAccuracyCounter : SkinnableTestScene
public class TestSceneSkinnableAccuracyCounter : SkinnableHUDComponentTestScene
{
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
[Cached]
private ScoreProcessor scoreProcessor = new ScoreProcessor();
protected override Drawable CreateDefaultImplementation() => new DefaultAccuracyCounter();
protected override Drawable CreateLegacyImplementation() => new LegacyAccuracyCounter();
[SetUpSteps]
public void SetUpSteps()
{
AddStep("Set initial accuracy", () => scoreProcessor.Accuracy.Value = 1);
AddStep("Create accuracy counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter))));
}
[Test]

View File

@ -3,26 +3,19 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Testing;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osu.Game.Screens.Play.HUD;
namespace osu.Game.Tests.Visual.Gameplay
{
public class TestSceneComboCounter : SkinnableTestScene
public class TestSceneSkinnableComboCounter : SkinnableHUDComponentTestScene
{
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
[Cached]
private ScoreProcessor scoreProcessor = new ScoreProcessor();
[SetUpSteps]
public void SetUpSteps()
{
AddStep("Create combo counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ComboCounter))));
}
protected override Drawable CreateDefaultImplementation() => new DefaultComboCounter();
protected override Drawable CreateLegacyImplementation() => new LegacyComboCounter();
[Test]
public void TestComboCounterIncrementing()

View File

@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
AddStep("create overlay", () =>
{
SetContents(() =>
SetContents(_ =>
{
hudOverlay = new HUDOverlay(null, Array.Empty<Mod>());

View File

@ -3,28 +3,28 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD;
using osu.Game.Skinning;
namespace osu.Game.Tests.Visual.Gameplay
{
public class TestSceneSkinnableHealthDisplay : SkinnableTestScene
public class TestSceneSkinnableHealthDisplay : SkinnableHUDComponentTestScene
{
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
[Cached(typeof(HealthProcessor))]
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
protected override Drawable CreateDefaultImplementation() => new DefaultHealthDisplay();
protected override Drawable CreateLegacyImplementation() => new LegacyHealthDisplay();
[SetUpSteps]
public void SetUpSteps()
{
AddStep("Create health displays", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.HealthDisplay))));
AddStep(@"Reset all", delegate
{
healthProcessor.Health.Value = 1;

View File

@ -3,26 +3,20 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Testing;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD;
using osu.Game.Skinning;
namespace osu.Game.Tests.Visual.Gameplay
{
public class TestSceneSkinnableScoreCounter : SkinnableTestScene
public class TestSceneSkinnableScoreCounter : SkinnableHUDComponentTestScene
{
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
[Cached]
private ScoreProcessor scoreProcessor = new ScoreProcessor();
[SetUpSteps]
public void SetUpSteps()
{
AddStep("Create score counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ScoreCounter))));
}
protected override Drawable CreateDefaultImplementation() => new DefaultScoreCounter();
protected override Drawable CreateLegacyImplementation() => new LegacyScoreCounter();
[Test]
public void TestScoreCounterIncrementing()

View File

@ -3,7 +3,7 @@
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="DeepEqual" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
</ItemGroup>

View File

@ -43,7 +43,7 @@ namespace osu.Game.Graphics.Containers
AddText(text[previousLinkEnd..link.Index]);
string displayText = text.Substring(link.Index, link.Length);
string linkArgument = link.Argument ?? link.Url;
string linkArgument = link.Argument;
string tooltip = displayText == link.Url ? null : link.Url;
AddLink(displayText, link.Action, linkArgument, tooltip);
@ -57,7 +57,7 @@ namespace osu.Game.Graphics.Containers
createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.External, url), url);
public void AddLink(string text, Action action, string tooltipText = null, Action<SpriteText> creationParameters = null)
=> createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.Custom, null), tooltipText, action);
=> createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.Custom, string.Empty), tooltipText, action);
public void AddLink(string text, LinkAction action, string argument, string tooltipText = null, Action<SpriteText> creationParameters = null)
=> createLink(AddText(text, creationParameters), new LinkDetails(action, argument), tooltipText);
@ -70,7 +70,7 @@ namespace osu.Game.Graphics.Containers
createLink(spriteText.Yield(), new LinkDetails(action, argument), tooltipText);
}
public void AddLink(IEnumerable<SpriteText> text, LinkAction action = LinkAction.External, string linkArgument = null, string tooltipText = null)
public void AddLink(IEnumerable<SpriteText> text, LinkAction action, string linkArgument, string tooltipText = null)
{
foreach (var t in text)
AddArbitraryDrawable(t);

View File

@ -4,7 +4,9 @@
using Markdig.Syntax.Inlines;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Containers.Markdown;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Overlays;
@ -12,17 +14,50 @@ namespace osu.Game.Graphics.Containers.Markdown
{
public class OsuMarkdownTextFlowContainer : MarkdownTextFlowContainer
{
[Resolved]
private OverlayColourProvider colourProvider { get; set; }
protected override void AddLinkText(string text, LinkInline linkInline)
=> AddDrawable(new OsuMarkdownLinkText(text, linkInline));
// TODO : Add background (colour B6) and change font to monospace
protected override void AddCodeInLine(CodeInline codeInline)
=> AddText(codeInline.Content, t => { t.Colour = colourProvider.Light1; });
// TODO : Change font to monospace
protected override void AddCodeInLine(CodeInline codeInline) => AddDrawable(new OsuMarkdownInlineCode
{
Text = codeInline.Content
});
protected override SpriteText CreateEmphasisedSpriteText(bool bold, bool italic)
=> CreateSpriteText().With(t => t.Font = t.Font.With(weight: bold ? FontWeight.Bold : FontWeight.Regular, italics: italic));
private class OsuMarkdownInlineCode : Container
{
[Resolved]
private IMarkdownTextComponent parentTextComponent { get; set; }
public string Text;
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
AutoSizeAxes = Axes.Both;
CornerRadius = 4;
Masking = true;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background6,
},
parentTextComponent.CreateSpriteText().With(t =>
{
t.Colour = colourProvider.Light1;
t.Text = Text;
t.Padding = new MarginPadding
{
Vertical = 1,
Horizontal = 4,
};
}),
};
}
}
}
}

View File

@ -6,6 +6,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
#nullable enable
namespace osu.Game.Online.Chat
{
public static class MessageFormatter
@ -61,7 +63,7 @@ namespace osu.Game.Online.Chat
private static string websiteRootUrl = "osu.ppy.sh";
private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null, char[] escapeChars = null)
private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null, char[]? escapeChars = null)
{
int captureOffset = 0;
@ -173,12 +175,12 @@ namespace osu.Game.Online.Chat
}
}
return new LinkDetails(LinkAction.External, url);
break;
case "osu":
// every internal link also needs some kind of argument
if (args.Length < 3)
return new LinkDetails(LinkAction.External, null);
break;
LinkAction linkType;
@ -220,7 +222,7 @@ namespace osu.Game.Online.Chat
return new LinkDetails(LinkAction.JoinMultiplayerMatch, args[1]);
}
return new LinkDetails(LinkAction.External, null);
return new LinkDetails(LinkAction.External, url);
}
private static MessageFormatterResult format(string toFormat, int startIndex = 0, int space = 3)

View File

@ -277,7 +277,7 @@ namespace osu.Game
{
case LinkAction.OpenBeatmap:
// TODO: proper query params handling
if (link.Argument != null && int.TryParse(link.Argument.Contains('?') ? link.Argument.Split('?')[0] : link.Argument, out int beatmapId))
if (int.TryParse(link.Argument.Contains('?') ? link.Argument.Split('?')[0] : link.Argument, out int beatmapId))
ShowBeatmap(beatmapId);
break;

View File

@ -35,11 +35,11 @@ namespace osu.Game.Rulesets.Objects
HitObject = hitObject;
startTimeBindable.BindTo(HitObject.StartTimeBindable);
startTimeBindable.BindValueChanged(_ => setInitialLifetime(), true);
startTimeBindable.BindValueChanged(_ => SetInitialLifetime(), true);
// Subscribe to this event before the DrawableHitObject so that the local callback is invoked before the entry is re-applied as a result of DefaultsApplied.
// This way, the DrawableHitObject can use OnApply() to overwrite the LifetimeStart that was set inside setInitialLifetime().
HitObject.DefaultsApplied += _ => setInitialLifetime();
HitObject.DefaultsApplied += _ => SetInitialLifetime();
}
// The lifetime, as set by the hitobject.
@ -94,6 +94,6 @@ namespace osu.Game.Rulesets.Objects
/// <summary>
/// Set <see cref="LifetimeEntry.LifetimeStart"/> using <see cref="InitialLifetimeOffset"/>.
/// </summary>
private void setInitialLifetime() => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset;
internal void SetInitialLifetime() => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset;
}
}

View File

@ -3,6 +3,7 @@
#nullable enable
using System;
using System.Diagnostics;
using osu.Framework.Graphics.Performance;
using osu.Framework.Graphics.Pooling;
@ -26,14 +27,13 @@ namespace osu.Game.Rulesets.Objects.Pooling
/// </summary>
protected bool HasEntryApplied { get; private set; }
// Drawable's lifetime gets out of sync with entry's lifetime if entry's lifetime is modified.
// We cannot delegate getter to `Entry.LifetimeStart` because it is incompatible with `LifetimeManagementContainer` due to how lifetime change is detected.
public override double LifetimeStart
{
get => base.LifetimeStart;
set
{
base.LifetimeStart = value;
if (Entry == null && LifetimeStart != value)
throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime<TEntry>)} when entry is not set");
if (Entry != null)
Entry.LifetimeStart = value;
@ -45,7 +45,8 @@ namespace osu.Game.Rulesets.Objects.Pooling
get => base.LifetimeEnd;
set
{
base.LifetimeEnd = value;
if (Entry == null && LifetimeEnd != value)
throw new InvalidOperationException($"Cannot modify lifetime of {nameof(PoolableDrawableWithLifetime<TEntry>)} when entry is not set");
if (Entry != null)
Entry.LifetimeEnd = value;
@ -79,9 +80,8 @@ namespace osu.Game.Rulesets.Objects.Pooling
free();
Entry = entry;
base.LifetimeStart = entry.LifetimeStart;
base.LifetimeEnd = entry.LifetimeEnd;
entry.LifetimeChanged += setLifetimeFromEntry;
setLifetimeFromEntry(entry);
OnApply(entry);
@ -117,11 +117,19 @@ namespace osu.Game.Rulesets.Objects.Pooling
OnFree(Entry);
Entry.LifetimeChanged -= setLifetimeFromEntry;
Entry = null;
base.LifetimeStart = double.MinValue;
base.LifetimeEnd = double.MaxValue;
HasEntryApplied = false;
}
private void setLifetimeFromEntry(LifetimeEntry entry)
{
Debug.Assert(entry == Entry);
base.LifetimeStart = entry.LifetimeStart;
base.LifetimeEnd = entry.LifetimeEnd;
}
}
}

View File

@ -172,11 +172,12 @@ namespace osu.Game.Rulesets.UI.Scrolling
if (layoutCache.IsValid) return;
foreach (var hitObject in Objects)
{
if (hitObject.HitObject != null)
invalidateHitObject(hitObject);
}
layoutComputed.Clear();
// Reset lifetime to the conservative estimation.
// If a drawable becomes alive by this lifetime, its lifetime will be updated to a more precise lifetime in the next update.
foreach (var entry in Entries)
entry.SetInitialLifetime();
scrollingInfo.Algorithm.Reset();

View File

@ -100,15 +100,15 @@ namespace osu.Game.Skinning
}
})
{
Children = new[]
Children = new Drawable[]
{
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ComboCounter)),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ScoreCounter)),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.SongProgress)),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.BarHitErrorMeter)),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.BarHitErrorMeter)),
new DefaultComboCounter(),
new DefaultScoreCounter(),
new DefaultAccuracyCounter(),
new DefaultHealthDisplay(),
new SongProgress(),
new BarHitErrorMeter(),
new BarHitErrorMeter(),
}
};
@ -116,35 +116,6 @@ namespace osu.Game.Skinning
}
break;
case HUDSkinComponent hudComponent:
{
switch (hudComponent.Component)
{
case HUDSkinComponents.ComboCounter:
return new DefaultComboCounter();
case HUDSkinComponents.ScoreCounter:
return new DefaultScoreCounter();
case HUDSkinComponents.AccuracyCounter:
return new DefaultAccuracyCounter();
case HUDSkinComponents.HealthDisplay:
return new DefaultHealthDisplay();
case HUDSkinComponents.SongProgress:
return new SongProgress();
case HUDSkinComponents.BarHitErrorMeter:
return new BarHitErrorMeter();
case HUDSkinComponents.ColourHitErrorMeter:
return new ColourHitErrorMeter();
}
break;
}
}
return null;

View File

@ -1,22 +0,0 @@
// 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.Linq;
namespace osu.Game.Skinning
{
public class HUDSkinComponent : ISkinComponent
{
public readonly HUDSkinComponents Component;
public HUDSkinComponent(HUDSkinComponents component)
{
Component = component;
}
protected virtual string ComponentName => Component.ToString();
public string LookupName =>
string.Join('/', new[] { "HUD", ComponentName }.Where(s => !string.IsNullOrEmpty(s)));
}
}

View File

@ -378,16 +378,26 @@ namespace osu.Game.Skinning
}
})
{
Children = new[]
{
// TODO: these should fallback to the osu!classic skin.
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ComboCounter)) ?? new DefaultComboCounter(),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ScoreCounter)) ?? new DefaultScoreCounter(),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)) ?? new DefaultAccuracyCounter(),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)) ?? new DefaultHealthDisplay(),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.SongProgress)) ?? new SongProgress(),
GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.BarHitErrorMeter)) ?? new BarHitErrorMeter(),
}
Children = this.HasFont(LegacyFont.Score)
? new Drawable[]
{
new LegacyComboCounter(),
new LegacyScoreCounter(),
new LegacyAccuracyCounter(),
new LegacyHealthDisplay(),
new SongProgress(),
new BarHitErrorMeter(),
}
: new Drawable[]
{
// TODO: these should fallback to using osu!classic skin textures, rather than doing this.
new DefaultComboCounter(),
new DefaultScoreCounter(),
new DefaultAccuracyCounter(),
new DefaultHealthDisplay(),
new SongProgress(),
new BarHitErrorMeter(),
}
};
return skinnableTargetWrapper;
@ -395,29 +405,6 @@ namespace osu.Game.Skinning
return null;
case HUDSkinComponent hudComponent:
{
if (!this.HasFont(LegacyFont.Score))
return null;
switch (hudComponent.Component)
{
case HUDSkinComponents.ComboCounter:
return new LegacyComboCounter();
case HUDSkinComponents.ScoreCounter:
return new LegacyScoreCounter();
case HUDSkinComponents.AccuracyCounter:
return new LegacyAccuracyCounter();
case HUDSkinComponents.HealthDisplay:
return new LegacyHealthDisplay();
}
return null;
}
case GameplaySkinComponent<HitResult> resultComponent:
Func<Drawable> createDrawable = () => getJudgementAnimation(resultComponent.Component);

View File

@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual
private readonly List<Drawable> createdDrawables = new List<Drawable>();
public void SetContents(Func<Drawable> creationFunction)
protected void SetContents(Func<ISkin, Drawable> creationFunction)
{
createdDrawables.Clear();
@ -67,9 +67,9 @@ namespace osu.Game.Tests.Visual
protected IEnumerable<Drawable> CreatedDrawables => createdDrawables;
private Drawable createProvider(Skin skin, Func<Drawable> creationFunction, IBeatmap beatmap)
private Drawable createProvider(Skin skin, Func<ISkin, Drawable> creationFunction, IBeatmap beatmap)
{
var created = creationFunction();
var created = creationFunction(skin);
createdDrawables.Add(created);

View File

@ -20,23 +20,23 @@
<ItemGroup Label="Package References">
<PackageReference Include="DiffPlex" Version="1.7.0" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.33" />
<PackageReference Include="Humanizer" Version="2.8.26" />
<PackageReference Include="Humanizer" Version="2.10.1" />
<PackageReference Include="MessagePack" Version="2.2.85" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="5.0.5" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="5.0.5" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="5.0.5" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="5.0.6" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="5.0.6" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="5.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.NETCore.Targets" Version="3.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="ppy.LocalisationAnalyser" Version="2021.524.0">
<PackageReference Include="ppy.LocalisationAnalyser" Version="2021.525.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="ppy.osu.Framework" Version="2021.528.0" />
<PackageReference Include="ppy.osu.Framework" Version="2021.601.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.525.0" />
<PackageReference Include="Sentry" Version="3.3.4" />
<PackageReference Include="Sentry" Version="3.4.0" />
<PackageReference Include="SharpCompress" Version="0.28.2" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />

View File

@ -70,7 +70,7 @@
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.528.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.601.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.525.0" />
</ItemGroup>
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
@ -89,11 +89,11 @@
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
<ItemGroup Label="Transitive Dependencies">
<PackageReference Include="DiffPlex" Version="1.6.3" />
<PackageReference Include="Humanizer" Version="2.8.26" />
<PackageReference Include="Humanizer" Version="2.10.1" />
<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="2021.528.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="ppy.osu.Framework" Version="2021.601.0" />
<PackageReference Include="SharpCompress" Version="0.28.2" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="SharpRaven" Version="2.4.0" />