mirror of
https://github.com/osukey/osukey.git
synced 2025-08-04 15:16:38 +09:00
Merge branch 'ppy:master' into android-joystick-settings
This commit is contained in:
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@ -126,12 +126,6 @@ jobs:
|
||||
with:
|
||||
dotnet-version: "6.0.x"
|
||||
|
||||
# macOS agents recently have empty NuGet config files, resulting in restore failures,
|
||||
# see https://github.com/actions/virtual-environments/issues/5768
|
||||
# Add the global nuget package source manually for now.
|
||||
- name: Setup NuGet.Config
|
||||
run: echo '<configuration><packageSources><add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /></packageSources></configuration>' > ~/.config/NuGet/NuGet.Config
|
||||
|
||||
# Contrary to seemingly any other msbuild, msbuild running on macOS/Mono
|
||||
# cannot accept .sln(f) files as arguments.
|
||||
# Build just the main game for now.
|
||||
|
@ -9,7 +9,6 @@
|
||||
<GenerateProgramFile>false</GenerateProgramFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||
|
@ -9,7 +9,6 @@
|
||||
<GenerateProgramFile>false</GenerateProgramFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||
|
@ -9,7 +9,6 @@
|
||||
<GenerateProgramFile>false</GenerateProgramFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||
|
@ -9,7 +9,6 @@
|
||||
<GenerateProgramFile>false</GenerateProgramFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||
|
@ -51,8 +51,8 @@
|
||||
<Reference Include="Java.Interop" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.628.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.629.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.702.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.713.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
|
@ -14,6 +14,7 @@ using osu.Framework.Platform;
|
||||
using osu.Game;
|
||||
using osu.Game.IPC;
|
||||
using osu.Game.Tournament;
|
||||
using SDL2;
|
||||
using Squirrel;
|
||||
|
||||
namespace osu.Desktop
|
||||
@ -29,7 +30,21 @@ namespace osu.Desktop
|
||||
{
|
||||
// run Squirrel first, as the app may exit after these run
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
var windowsVersion = Environment.OSVersion.Version;
|
||||
|
||||
// While .NET 6 still supports Windows 7 and above, we are limited by realm currently, as they choose to only support 8.1 and higher.
|
||||
// See https://www.mongodb.com/docs/realm/sdk/dotnet/#supported-platforms
|
||||
if (windowsVersion.Major < 6 || (windowsVersion.Major == 6 && windowsVersion.Minor <= 2))
|
||||
{
|
||||
SDL.SDL_ShowSimpleMessageBox(SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR,
|
||||
"Your operating system is too old to run osu!",
|
||||
"This version of osu! requires at least Windows 8.1 to run.\nPlease upgrade your operating system or consider using an older version of osu!.", IntPtr.Zero);
|
||||
return;
|
||||
}
|
||||
|
||||
setupSquirrel();
|
||||
}
|
||||
|
||||
// Back up the cwd before DesktopGameHost changes it
|
||||
string cwd = Environment.CurrentDirectory;
|
||||
|
@ -24,7 +24,7 @@
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Clowd.Squirrel" Version="2.9.40" />
|
||||
<PackageReference Include="Clowd.Squirrel" Version="2.9.42" />
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||
<PackageReference Include="System.IO.Packaging" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.14" />
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.IO;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using osu.Framework.IO.Stores;
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
@ -11,7 +9,7 @@ namespace osu.Game.Benchmarks
|
||||
{
|
||||
public class BenchmarkMod : BenchmarkTest
|
||||
{
|
||||
private OsuModDoubleTime mod;
|
||||
private OsuModDoubleTime mod = null!;
|
||||
|
||||
[Params(1, 10, 100)]
|
||||
public int Times { get; set; }
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
@ -17,9 +15,9 @@ namespace osu.Game.Benchmarks
|
||||
{
|
||||
public class BenchmarkRealmReads : BenchmarkTest
|
||||
{
|
||||
private TemporaryNativeStorage storage;
|
||||
private RealmAccess realm;
|
||||
private UpdateThread updateThread;
|
||||
private TemporaryNativeStorage storage = null!;
|
||||
private RealmAccess realm = null!;
|
||||
private UpdateThread updateThread = null!;
|
||||
|
||||
[Params(1, 100, 1000)]
|
||||
public int ReadsPerFetch { get; set; }
|
||||
@ -135,9 +133,9 @@ namespace osu.Game.Benchmarks
|
||||
[GlobalCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
realm?.Dispose();
|
||||
storage?.Dispose();
|
||||
updateThread?.Exit();
|
||||
realm.Dispose();
|
||||
storage.Dispose();
|
||||
updateThread.Exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Engines;
|
||||
using osu.Game.Online.API;
|
||||
@ -13,9 +11,9 @@ namespace osu.Game.Benchmarks
|
||||
{
|
||||
public class BenchmarkRuleset : BenchmarkTest
|
||||
{
|
||||
private OsuRuleset ruleset;
|
||||
private APIMod apiModDoubleTime;
|
||||
private APIMod apiModDifficultyAdjust;
|
||||
private OsuRuleset ruleset = null!;
|
||||
private APIMod apiModDoubleTime = null!;
|
||||
private APIMod apiModDifficultyAdjust = null!;
|
||||
|
||||
public override void SetUp()
|
||||
{
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Running;
|
||||
using NUnit.Framework;
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Running;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Utils;
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Input.StateChanges;
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Replays.Legacy;
|
||||
@ -22,7 +20,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
{
|
||||
}
|
||||
|
||||
public CatchReplayFrame(double time, float? position = null, bool dashing = false, CatchReplayFrame lastFrame = null)
|
||||
public CatchReplayFrame(double time, float? position = null, bool dashing = false, CatchReplayFrame? lastFrame = null)
|
||||
: base(time)
|
||||
{
|
||||
Position = position ?? -1;
|
||||
@ -40,7 +38,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
}
|
||||
}
|
||||
|
||||
public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
|
||||
public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame? lastFrame = null)
|
||||
{
|
||||
Position = currentFrame.Position.X;
|
||||
Dashing = currentFrame.ButtonState == ReplayButtonState.Left1;
|
||||
|
@ -120,10 +120,10 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
lastHyperDashState = Catcher.HyperDashing;
|
||||
}
|
||||
|
||||
public void SetCatcherPosition(float X)
|
||||
public void SetCatcherPosition(float x)
|
||||
{
|
||||
float lastPosition = Catcher.X;
|
||||
float newPosition = Math.Clamp(X, 0, CatchPlayfield.WIDTH);
|
||||
float newPosition = Math.Clamp(x, 0, CatchPlayfield.WIDTH);
|
||||
|
||||
Catcher.X = newPosition;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Filter;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
|
@ -1,10 +1,9 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@ -85,7 +84,7 @@ namespace osu.Game.Rulesets.Mania.Replays
|
||||
}
|
||||
}
|
||||
|
||||
private double calculateReleaseTime(HitObject currentObject, HitObject nextObject)
|
||||
private double calculateReleaseTime(HitObject currentObject, HitObject? nextObject)
|
||||
{
|
||||
double endTime = currentObject.GetEndTime();
|
||||
|
||||
@ -96,10 +95,10 @@ namespace osu.Game.Rulesets.Mania.Replays
|
||||
bool canDelayKeyUpFully = nextObject == null ||
|
||||
nextObject.StartTime > endTime + RELEASE_DELAY;
|
||||
|
||||
return endTime + (canDelayKeyUpFully ? RELEASE_DELAY : (nextObject.StartTime - endTime) * 0.9);
|
||||
return endTime + (canDelayKeyUpFully ? RELEASE_DELAY : (nextObject.AsNonNull().StartTime - endTime) * 0.9);
|
||||
}
|
||||
|
||||
protected override HitObject GetNextObject(int currentIndex)
|
||||
protected override HitObject? GetNextObject(int currentIndex)
|
||||
{
|
||||
int desiredColumn = Beatmap.HitObjects[currentIndex].Column;
|
||||
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Input.StateChanges;
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -27,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Replays
|
||||
Actions.AddRange(actions);
|
||||
}
|
||||
|
||||
public void FromLegacy(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
|
||||
public void FromLegacy(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame? lastFrame = null)
|
||||
{
|
||||
var maniaBeatmap = (ManiaBeatmap)beatmap;
|
||||
|
||||
|
27
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModRepel.cs
Normal file
27
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModRepel.cs
Normal file
@ -0,0 +1,27 @@
|
||||
// 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.Game.Rulesets.Osu.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
{
|
||||
public class TestSceneOsuModRepel : OsuModTestScene
|
||||
{
|
||||
[TestCase(0.1f)]
|
||||
[TestCase(0.5f)]
|
||||
[TestCase(1)]
|
||||
public void TestRepel(float strength)
|
||||
{
|
||||
CreateModTest(new ModTestData
|
||||
{
|
||||
Mod = new OsuModRepel
|
||||
{
|
||||
RepulsionStrength = { Value = strength },
|
||||
},
|
||||
PassCondition = () => true,
|
||||
Autoplay = false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
175
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSingleTap.cs
Normal file
175
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSingleTap.cs
Normal file
@ -0,0 +1,175 @@
|
||||
// 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.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Replays;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
{
|
||||
public class TestSceneOsuModSingleTap : OsuModTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestInputSingular() => CreateModTest(new ModTestData
|
||||
{
|
||||
Mod = new OsuModSingleTap(),
|
||||
PassCondition = () => Player.ScoreProcessor.Combo.Value == 2,
|
||||
Autoplay = false,
|
||||
Beatmap = new Beatmap
|
||||
{
|
||||
HitObjects = new List<HitObject>
|
||||
{
|
||||
new HitCircle
|
||||
{
|
||||
StartTime = 500,
|
||||
Position = new Vector2(100),
|
||||
},
|
||||
new HitCircle
|
||||
{
|
||||
StartTime = 1000,
|
||||
Position = new Vector2(200, 100),
|
||||
},
|
||||
new HitCircle
|
||||
{
|
||||
StartTime = 1500,
|
||||
Position = new Vector2(300, 100),
|
||||
},
|
||||
new HitCircle
|
||||
{
|
||||
StartTime = 2000,
|
||||
Position = new Vector2(400, 100),
|
||||
},
|
||||
},
|
||||
},
|
||||
ReplayFrames = new List<ReplayFrame>
|
||||
{
|
||||
new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton),
|
||||
new OsuReplayFrame(501, new Vector2(100)),
|
||||
new OsuReplayFrame(1000, new Vector2(200, 100), OsuAction.LeftButton),
|
||||
}
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestInputAlternating() => CreateModTest(new ModTestData
|
||||
{
|
||||
Mod = new OsuModSingleTap(),
|
||||
PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 1,
|
||||
Autoplay = false,
|
||||
Beatmap = new Beatmap
|
||||
{
|
||||
HitObjects = new List<HitObject>
|
||||
{
|
||||
new HitCircle
|
||||
{
|
||||
StartTime = 500,
|
||||
Position = new Vector2(100),
|
||||
},
|
||||
new HitCircle
|
||||
{
|
||||
StartTime = 1000,
|
||||
Position = new Vector2(200, 100),
|
||||
},
|
||||
},
|
||||
},
|
||||
ReplayFrames = new List<ReplayFrame>
|
||||
{
|
||||
new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton),
|
||||
new OsuReplayFrame(501, new Vector2(100)),
|
||||
new OsuReplayFrame(1000, new Vector2(200, 100), OsuAction.RightButton),
|
||||
new OsuReplayFrame(1001, new Vector2(200, 100)),
|
||||
new OsuReplayFrame(1500, new Vector2(300, 100), OsuAction.LeftButton),
|
||||
new OsuReplayFrame(1501, new Vector2(300, 100)),
|
||||
new OsuReplayFrame(2000, new Vector2(400, 100), OsuAction.RightButton),
|
||||
new OsuReplayFrame(2001, new Vector2(400, 100)),
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Ensures singletapping is reset before the first hitobject after intro.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestInputAlternatingAtIntro() => CreateModTest(new ModTestData
|
||||
{
|
||||
Mod = new OsuModSingleTap(),
|
||||
PassCondition = () => Player.ScoreProcessor.Combo.Value == 1,
|
||||
Autoplay = false,
|
||||
Beatmap = new Beatmap
|
||||
{
|
||||
HitObjects = new List<HitObject>
|
||||
{
|
||||
new HitCircle
|
||||
{
|
||||
StartTime = 1000,
|
||||
Position = new Vector2(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
ReplayFrames = new List<ReplayFrame>
|
||||
{
|
||||
// first press during intro.
|
||||
new OsuReplayFrame(500, new Vector2(200), OsuAction.LeftButton),
|
||||
new OsuReplayFrame(501, new Vector2(200)),
|
||||
// press different key at hitobject and ensure it has been hit.
|
||||
new OsuReplayFrame(1000, new Vector2(100), OsuAction.RightButton),
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Ensures singletapping is reset before the first hitobject after a break.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestInputAlternatingWithBreak() => CreateModTest(new ModTestData
|
||||
{
|
||||
Mod = new OsuModSingleTap(),
|
||||
PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 2,
|
||||
Autoplay = false,
|
||||
Beatmap = new Beatmap
|
||||
{
|
||||
Breaks = new List<BreakPeriod>
|
||||
{
|
||||
new BreakPeriod(500, 2000),
|
||||
},
|
||||
HitObjects = new List<HitObject>
|
||||
{
|
||||
new HitCircle
|
||||
{
|
||||
StartTime = 500,
|
||||
Position = new Vector2(100),
|
||||
},
|
||||
new HitCircle
|
||||
{
|
||||
StartTime = 2500,
|
||||
Position = new Vector2(500, 100),
|
||||
},
|
||||
new HitCircle
|
||||
{
|
||||
StartTime = 3000,
|
||||
Position = new Vector2(500, 100),
|
||||
},
|
||||
}
|
||||
},
|
||||
ReplayFrames = new List<ReplayFrame>
|
||||
{
|
||||
// first press to start singletap lock.
|
||||
new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton),
|
||||
new OsuReplayFrame(501, new Vector2(100)),
|
||||
// press different key after break but before hit object.
|
||||
new OsuReplayFrame(2250, new Vector2(300, 100), OsuAction.RightButton),
|
||||
new OsuReplayFrame(2251, new Vector2(300, 100)),
|
||||
// press same key at second hitobject and ensure it has been hit.
|
||||
new OsuReplayFrame(2500, new Vector2(500, 100), OsuAction.LeftButton),
|
||||
new OsuReplayFrame(2501, new Vector2(500, 100)),
|
||||
// press different key at third hitobject and ensure it has been missed.
|
||||
new OsuReplayFrame(3000, new Vector2(500, 100), OsuAction.RightButton),
|
||||
new OsuReplayFrame(3001, new Vector2(500, 100)),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
using osu.Game.Storyboards;
|
||||
using osu.Game.Tests;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
@ -71,6 +72,16 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[TestCase(0)]
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[FlakyTest]
|
||||
/*
|
||||
* Fail rate around 0.15%
|
||||
*
|
||||
* TearDown : System.TimeoutException : "wait for seek to finish" timed out
|
||||
* --TearDown
|
||||
* at osu.Framework.Testing.Drawables.Steps.UntilStepButton.<>c__DisplayClass11_0.<.ctor>b__0()
|
||||
* at osu.Framework.Testing.Drawables.Steps.StepButton.PerformStep(Boolean userTriggered)
|
||||
* at osu.Framework.Testing.TestScene.runNextStep(Action onCompletion, Action`1 onError, Func`2 stopCondition)
|
||||
*/
|
||||
public void TestSnakingEnabled(int sliderIndex)
|
||||
{
|
||||
AddStep("enable autoplay", () => autoplay = true);
|
||||
@ -95,6 +106,16 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[TestCase(0)]
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[FlakyTest]
|
||||
/*
|
||||
* Fail rate around 0.15%
|
||||
*
|
||||
* TearDown : System.TimeoutException : "wait for seek to finish" timed out
|
||||
* --TearDown
|
||||
* at osu.Framework.Testing.Drawables.Steps.UntilStepButton.<>c__DisplayClass11_0.<.ctor>b__0()
|
||||
* at osu.Framework.Testing.Drawables.Steps.StepButton.PerformStep(Boolean userTriggered)
|
||||
* at osu.Framework.Testing.TestScene.runNextStep(Action onCompletion, Action`1 onError, Func`2 stopCondition)
|
||||
*/
|
||||
public void TestSnakingDisabled(int sliderIndex)
|
||||
{
|
||||
AddStep("have autoplay", () => autoplay = true);
|
||||
|
@ -1,9 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="Moq" Version="4.17.2" />
|
||||
<PackageReference Include="Moq" Version="4.18.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
114
osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs
Normal file
114
osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs
Normal file
@ -0,0 +1,114 @@
|
||||
// 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 osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public abstract class InputBlockingMod : Mod, IApplicableToDrawableRuleset<OsuHitObject>
|
||||
{
|
||||
public override double ScoreMultiplier => 1.0;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax), typeof(OsuModCinema) };
|
||||
public override ModType Type => ModType.Conversion;
|
||||
|
||||
private const double flash_duration = 1000;
|
||||
|
||||
private DrawableRuleset<OsuHitObject> ruleset = null!;
|
||||
|
||||
protected OsuAction? LastAcceptedAction { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A tracker for periods where alternate should not be forced (i.e. non-gameplay periods).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is different from <see cref="Player.IsBreakTime"/> in that the periods here end strictly at the first object after the break, rather than the break's end time.
|
||||
/// </remarks>
|
||||
private PeriodTracker nonGameplayPeriods = null!;
|
||||
|
||||
private IFrameStableClock gameplayClock = null!;
|
||||
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||
{
|
||||
ruleset = drawableRuleset;
|
||||
drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this));
|
||||
|
||||
var periods = new List<Period>();
|
||||
|
||||
if (drawableRuleset.Objects.Any())
|
||||
{
|
||||
periods.Add(new Period(int.MinValue, getValidJudgementTime(ruleset.Objects.First()) - 1));
|
||||
|
||||
foreach (BreakPeriod b in drawableRuleset.Beatmap.Breaks)
|
||||
periods.Add(new Period(b.StartTime, getValidJudgementTime(ruleset.Objects.First(h => h.StartTime >= b.EndTime)) - 1));
|
||||
|
||||
static double getValidJudgementTime(HitObject hitObject) => hitObject.StartTime - hitObject.HitWindows.WindowFor(HitResult.Meh);
|
||||
}
|
||||
|
||||
nonGameplayPeriods = new PeriodTracker(periods);
|
||||
|
||||
gameplayClock = drawableRuleset.FrameStableClock;
|
||||
}
|
||||
|
||||
protected abstract bool CheckValidNewAction(OsuAction action);
|
||||
|
||||
private bool checkCorrectAction(OsuAction action)
|
||||
{
|
||||
if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime))
|
||||
{
|
||||
LastAcceptedAction = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case OsuAction.LeftButton:
|
||||
case OsuAction.RightButton:
|
||||
break;
|
||||
|
||||
// Any action which is not left or right button should be ignored.
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CheckValidNewAction(action))
|
||||
{
|
||||
LastAcceptedAction = action;
|
||||
return true;
|
||||
}
|
||||
|
||||
ruleset.Cursor.FlashColour(Colour4.Red, flash_duration, Easing.OutQuint);
|
||||
return false;
|
||||
}
|
||||
|
||||
private class InputInterceptor : Component, IKeyBindingHandler<OsuAction>
|
||||
{
|
||||
private readonly InputBlockingMod mod;
|
||||
|
||||
public InputInterceptor(InputBlockingMod mod)
|
||||
{
|
||||
this.mod = mod;
|
||||
}
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
|
||||
// if the pressed action is incorrect, block it from reaching gameplay.
|
||||
=> !mod.checkCorrectAction(e.Action);
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,119 +1,20 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModAlternate : Mod, IApplicableToDrawableRuleset<OsuHitObject>
|
||||
public class OsuModAlternate : InputBlockingMod
|
||||
{
|
||||
public override string Name => @"Alternate";
|
||||
public override string Acronym => @"AL";
|
||||
public override string Description => @"Don't use the same key twice in a row!";
|
||||
public override double ScoreMultiplier => 1.0;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax) };
|
||||
public override ModType Type => ModType.Conversion;
|
||||
public override IconUsage? Icon => FontAwesome.Solid.Keyboard;
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModSingleTap) }).ToArray();
|
||||
|
||||
private const double flash_duration = 1000;
|
||||
|
||||
/// <summary>
|
||||
/// A tracker for periods where alternate should not be forced (i.e. non-gameplay periods).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is different from <see cref="Player.IsBreakTime"/> in that the periods here end strictly at the first object after the break, rather than the break's end time.
|
||||
/// </remarks>
|
||||
private PeriodTracker nonGameplayPeriods;
|
||||
|
||||
private OsuAction? lastActionPressed;
|
||||
private DrawableRuleset<OsuHitObject> ruleset;
|
||||
|
||||
private IFrameStableClock gameplayClock;
|
||||
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||
{
|
||||
ruleset = drawableRuleset;
|
||||
drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this));
|
||||
|
||||
var periods = new List<Period>();
|
||||
|
||||
if (drawableRuleset.Objects.Any())
|
||||
{
|
||||
periods.Add(new Period(int.MinValue, getValidJudgementTime(ruleset.Objects.First()) - 1));
|
||||
|
||||
foreach (BreakPeriod b in drawableRuleset.Beatmap.Breaks)
|
||||
periods.Add(new Period(b.StartTime, getValidJudgementTime(ruleset.Objects.First(h => h.StartTime >= b.EndTime)) - 1));
|
||||
|
||||
static double getValidJudgementTime(HitObject hitObject) => hitObject.StartTime - hitObject.HitWindows.WindowFor(HitResult.Meh);
|
||||
}
|
||||
|
||||
nonGameplayPeriods = new PeriodTracker(periods);
|
||||
|
||||
gameplayClock = drawableRuleset.FrameStableClock;
|
||||
}
|
||||
|
||||
private bool checkCorrectAction(OsuAction action)
|
||||
{
|
||||
if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime))
|
||||
{
|
||||
lastActionPressed = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case OsuAction.LeftButton:
|
||||
case OsuAction.RightButton:
|
||||
break;
|
||||
|
||||
// Any action which is not left or right button should be ignored.
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lastActionPressed != action)
|
||||
{
|
||||
// User alternated correctly.
|
||||
lastActionPressed = action;
|
||||
return true;
|
||||
}
|
||||
|
||||
ruleset.Cursor.FlashColour(Colour4.Red, flash_duration, Easing.OutQuint);
|
||||
return false;
|
||||
}
|
||||
|
||||
private class InputInterceptor : Component, IKeyBindingHandler<OsuAction>
|
||||
{
|
||||
private readonly OsuModAlternate mod;
|
||||
|
||||
public InputInterceptor(OsuModAlternate mod)
|
||||
{
|
||||
this.mod = mod;
|
||||
}
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
|
||||
// if the pressed action is incorrect, block it from reaching gameplay.
|
||||
=> !mod.checkCorrectAction(e.Action);
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
protected override bool CheckValidNewAction(OsuAction action) => LastAcceptedAction != action;
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override ModType Type => ModType.Automation;
|
||||
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail), typeof(ModAutoplay), typeof(OsuModMagnetised) };
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail), typeof(ModAutoplay), typeof(OsuModMagnetised), typeof(OsuModRepel) };
|
||||
|
||||
public bool PerformFail() => false;
|
||||
|
||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModAutoplay : ModAutoplay
|
||||
{
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModAutopilot), typeof(OsuModSpunOut), typeof(OsuModAlternate) }).ToArray();
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModRepel), typeof(OsuModAutopilot), typeof(OsuModSpunOut), typeof(OsuModAlternate), typeof(OsuModSingleTap) }).ToArray();
|
||||
|
||||
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
=> new ModReplayData(new OsuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" });
|
||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModCinema : ModCinema<OsuHitObject>
|
||||
{
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModAutopilot), typeof(OsuModSpunOut), typeof(OsuModAlternate) }).ToArray();
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModAutopilot), typeof(OsuModSpunOut), typeof(OsuModAlternate), typeof(OsuModSingleTap) }).ToArray();
|
||||
|
||||
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
=> new ModReplayData(new OsuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" });
|
||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override string Description => "No need to chase the circles – your cursor is a magnet!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModRelax) };
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModRelax), typeof(OsuModRepel) };
|
||||
|
||||
private IFrameStableClock gameplayClock;
|
||||
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawableRuleset<OsuHitObject>, IApplicableToPlayer
|
||||
{
|
||||
public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things.";
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModMagnetised), typeof(OsuModAlternate) }).ToArray();
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModMagnetised), typeof(OsuModAlternate), typeof(OsuModSingleTap) }).ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// How early before a hitobject's start time to trigger a hit.
|
||||
|
98
osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs
Normal file
98
osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs
Normal file
@ -0,0 +1,98 @@
|
||||
// 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.Diagnostics;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.Osu.Utils;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
internal class OsuModRepel : Mod, IUpdatableByPlayfield, IApplicableToDrawableRuleset<OsuHitObject>
|
||||
{
|
||||
public override string Name => "Repel";
|
||||
public override string Acronym => "RP";
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override string Description => "Hit objects run away!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModMagnetised) };
|
||||
|
||||
private IFrameStableClock? gameplayClock;
|
||||
|
||||
[SettingSource("Repulsion strength", "How strong the repulsion is.", 0)]
|
||||
public BindableFloat RepulsionStrength { get; } = new BindableFloat(0.5f)
|
||||
{
|
||||
Precision = 0.05f,
|
||||
MinValue = 0.05f,
|
||||
MaxValue = 1.0f,
|
||||
};
|
||||
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||
{
|
||||
gameplayClock = drawableRuleset.FrameStableClock;
|
||||
|
||||
// Hide judgment displays and follow points as they won't make any sense.
|
||||
// Judgements can potentially be turned on in a future where they display at a position relative to their drawable counterpart.
|
||||
drawableRuleset.Playfield.DisplayJudgements.Value = false;
|
||||
(drawableRuleset.Playfield as OsuPlayfield)?.FollowPoints.Hide();
|
||||
}
|
||||
|
||||
public void Update(Playfield playfield)
|
||||
{
|
||||
var cursorPos = playfield.Cursor.ActiveCursor.DrawPosition;
|
||||
|
||||
foreach (var drawable in playfield.HitObjectContainer.AliveObjects)
|
||||
{
|
||||
var destination = Vector2.Clamp(2 * drawable.Position - cursorPos, Vector2.Zero, OsuPlayfield.BASE_SIZE);
|
||||
|
||||
if (drawable.HitObject is Slider thisSlider)
|
||||
{
|
||||
var possibleMovementBounds = OsuHitObjectGenerationUtils.CalculatePossibleMovementBounds(thisSlider);
|
||||
|
||||
destination = Vector2.Clamp(
|
||||
destination,
|
||||
new Vector2(possibleMovementBounds.Left, possibleMovementBounds.Top),
|
||||
new Vector2(possibleMovementBounds.Right, possibleMovementBounds.Bottom)
|
||||
);
|
||||
}
|
||||
|
||||
switch (drawable)
|
||||
{
|
||||
case DrawableHitCircle circle:
|
||||
easeTo(circle, destination, cursorPos);
|
||||
break;
|
||||
|
||||
case DrawableSlider slider:
|
||||
|
||||
if (!slider.HeadCircle.Result.HasResult)
|
||||
easeTo(slider, destination, cursorPos);
|
||||
else
|
||||
easeTo(slider, destination - slider.Ball.DrawPosition, cursorPos);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void easeTo(DrawableHitObject hitObject, Vector2 destination, Vector2 cursorPos)
|
||||
{
|
||||
Debug.Assert(gameplayClock != null);
|
||||
|
||||
double dampLength = Vector2.Distance(hitObject.Position, cursorPos) / (0.04 * RepulsionStrength.Value + 0.04);
|
||||
|
||||
float x = (float)Interpolation.DampContinuously(hitObject.X, destination.X, dampLength, gameplayClock.ElapsedFrameTime);
|
||||
float y = (float)Interpolation.DampContinuously(hitObject.Y, destination.Y, dampLength, gameplayClock.ElapsedFrameTime);
|
||||
|
||||
hitObject.Position = new Vector2(x, y);
|
||||
}
|
||||
}
|
||||
}
|
18
osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs
Normal file
18
osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs
Normal file
@ -0,0 +1,18 @@
|
||||
// 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.Linq;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModSingleTap : InputBlockingMod
|
||||
{
|
||||
public override string Name => @"Single Tap";
|
||||
public override string Acronym => @"ST";
|
||||
public override string Description => @"You must only use one key!";
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAlternate) }).ToArray();
|
||||
|
||||
protected override bool CheckValidNewAction(OsuAction action) => LastAcceptedAction == null || LastAcceptedAction == action;
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override string Description => "Everything rotates. EVERYTHING.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModWiggle), typeof(OsuModMagnetised) };
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModWiggle), typeof(OsuModMagnetised), typeof(OsuModRepel) };
|
||||
|
||||
private float theta;
|
||||
|
||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override string Description => "They just won't stay still...";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModTransform), typeof(OsuModMagnetised) };
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModTransform), typeof(OsuModMagnetised), typeof(OsuModRepel) };
|
||||
|
||||
private const int wiggle_duration = 90; // (ms) Higher = fewer wiggles
|
||||
private const int wiggle_strength = 10; // Higher = stronger wiggles
|
||||
|
@ -319,13 +319,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
const float fade_out_time = 450;
|
||||
|
||||
// intentionally pile on an extra FadeOut to make it happen much faster.
|
||||
Ball.FadeOut(fade_out_time / 4, Easing.Out);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
Ball.ScaleTo(HitObject.Scale * 1.4f, fade_out_time, Easing.Out);
|
||||
if (SliderBody?.SnakingOut.Value == true)
|
||||
Body.FadeOut(40); // short fade to allow for any body colour to smoothly disappear.
|
||||
break;
|
||||
|
@ -23,6 +23,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
public class DrawableSliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition, IHasAccentColour
|
||||
{
|
||||
public const float FOLLOW_AREA = 2.4f;
|
||||
|
||||
public Func<OsuAction?> GetInitialHitAction;
|
||||
|
||||
public Color4 AccentColour
|
||||
@ -31,7 +33,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
set => ball.Colour = value;
|
||||
}
|
||||
|
||||
private Drawable followCircle;
|
||||
private Drawable followCircleReceptor;
|
||||
private DrawableSlider drawableSlider;
|
||||
private Drawable ball;
|
||||
@ -47,12 +48,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
Children = new[]
|
||||
{
|
||||
followCircle = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderFollowCircle), _ => new DefaultFollowCircle())
|
||||
new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderFollowCircle), _ => new DefaultFollowCircle())
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
},
|
||||
followCircleReceptor = new CircularContainer
|
||||
{
|
||||
@ -103,10 +103,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
tracking = value;
|
||||
|
||||
followCircleReceptor.Scale = new Vector2(tracking ? 2.4f : 1f);
|
||||
|
||||
followCircle.ScaleTo(tracking ? 2.4f : 1f, 300, Easing.OutQuint);
|
||||
followCircle.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint);
|
||||
followCircleReceptor.Scale = new Vector2(tracking ? FOLLOW_AREA : 1f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
new OsuModClassic(),
|
||||
new OsuModRandom(),
|
||||
new OsuModMirror(),
|
||||
new OsuModAlternate(),
|
||||
new MultiMod(new OsuModAlternate(), new OsuModSingleTap())
|
||||
};
|
||||
|
||||
case ModType.Automation:
|
||||
@ -197,7 +197,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
new OsuModApproachDifferent(),
|
||||
new OsuModMuted(),
|
||||
new OsuModNoScope(),
|
||||
new OsuModMagnetised(),
|
||||
new MultiMod(new OsuModMagnetised(), new OsuModRepel()),
|
||||
new ModAdaptiveSpeed()
|
||||
};
|
||||
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osuTK;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -95,7 +93,7 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
{
|
||||
double endTime = prev.GetEndTime();
|
||||
|
||||
HitWindows hitWindows = null;
|
||||
HitWindows? hitWindows = null;
|
||||
|
||||
switch (h)
|
||||
{
|
||||
@ -245,7 +243,7 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
}
|
||||
|
||||
double timeDifference = ApplyModsToTimeDelta(lastFrame.Time, h.StartTime);
|
||||
OsuReplayFrame lastLastFrame = Frames.Count >= 2 ? (OsuReplayFrame)Frames[^2] : null;
|
||||
OsuReplayFrame? lastLastFrame = Frames.Count >= 2 ? (OsuReplayFrame)Frames[^2] : null;
|
||||
|
||||
if (timeDifference > 0)
|
||||
{
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osuTK;
|
||||
using osu.Game.Beatmaps;
|
||||
using System;
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Input.StateChanges;
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Replays.Legacy;
|
||||
@ -28,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
Actions.AddRange(actions);
|
||||
}
|
||||
|
||||
public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
|
||||
public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame? lastFrame = null)
|
||||
{
|
||||
Position = currentFrame.Position;
|
||||
if (currentFrame.MouseLeft) Actions.Add(OsuAction.LeftButton);
|
||||
|
@ -1,19 +1,19 @@
|
||||
// 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.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
{
|
||||
public class DefaultFollowCircle : CompositeDrawable
|
||||
public class DefaultFollowCircle : FollowCircle
|
||||
{
|
||||
public DefaultFollowCircle()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChild = new CircularContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@ -29,5 +29,22 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void OnTrackingChanged(ValueChangedEvent<bool> tracking)
|
||||
{
|
||||
const float scale_duration = 300f;
|
||||
const float fade_duration = 300f;
|
||||
|
||||
this.ScaleTo(tracking.NewValue ? DrawableSliderBall.FOLLOW_AREA : 1f, scale_duration, Easing.OutQuint)
|
||||
.FadeTo(tracking.NewValue ? 1f : 0, fade_duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void OnSliderEnd()
|
||||
{
|
||||
const float fade_duration = 450f;
|
||||
|
||||
// intentionally pile on an extra FadeOut to make it happen much faster
|
||||
this.FadeOut(fade_duration / 4, Easing.Out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@ -19,13 +17,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
{
|
||||
public class DefaultSliderBall : CompositeDrawable
|
||||
{
|
||||
private Box box;
|
||||
private Box box = null!;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private DrawableHitObject? parentObject { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(DrawableHitObject drawableObject, ISkinSource skin)
|
||||
private void load(ISkinSource skin)
|
||||
{
|
||||
var slider = (DrawableSlider)drawableObject;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
float radius = skin.GetConfig<OsuSkinConfiguration, float>(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS;
|
||||
@ -51,10 +50,62 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
}
|
||||
};
|
||||
|
||||
slider.Tracking.BindValueChanged(trackingChanged, true);
|
||||
if (parentObject != null)
|
||||
{
|
||||
var slider = (DrawableSlider)parentObject;
|
||||
slider.Tracking.BindValueChanged(trackingChanged, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
if (parentObject != null)
|
||||
{
|
||||
parentObject.ApplyCustomUpdateState += updateStateTransforms;
|
||||
updateStateTransforms(parentObject, parentObject.State.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void trackingChanged(ValueChangedEvent<bool> tracking) =>
|
||||
box.FadeTo(tracking.NewValue ? 0.3f : 0.05f, 200, Easing.OutQuint);
|
||||
|
||||
private void updateStateTransforms(DrawableHitObject drawableObject, ArmedState state)
|
||||
{
|
||||
// Gets called by slider ticks, tails, etc., leading to duplicated
|
||||
// animations which may negatively affect performance
|
||||
if (drawableObject is not DrawableSlider)
|
||||
return;
|
||||
|
||||
const float fade_duration = 450f;
|
||||
|
||||
using (BeginAbsoluteSequence(drawableObject.StateUpdateTime))
|
||||
{
|
||||
this.FadeIn()
|
||||
.ScaleTo(1f);
|
||||
}
|
||||
|
||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||
{
|
||||
// intentionally pile on an extra FadeOut to make it happen much faster
|
||||
this.FadeOut(fade_duration / 4, Easing.Out);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
this.ScaleTo(1.4f, fade_duration, Easing.Out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (parentObject != null)
|
||||
parentObject.ApplyCustomUpdateState -= updateStateTransforms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
complete.BindValueChanged(complete => updateComplete(complete.NewValue, 200));
|
||||
complete.BindValueChanged(complete => updateDiscColour(complete.NewValue, 200));
|
||||
drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
|
||||
|
||||
updateStateTransforms(drawableSpinner, drawableSpinner.State.Value);
|
||||
@ -137,6 +137,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
this.ScaleTo(initial_scale);
|
||||
this.RotateTo(0);
|
||||
|
||||
updateDiscColour(false);
|
||||
|
||||
using (BeginDelayedSequence(spinner.TimePreempt / 2))
|
||||
{
|
||||
// constant ambient rotation to give the spinner "spinning" character.
|
||||
@ -177,12 +179,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
}
|
||||
}
|
||||
|
||||
// transforms we have from completing the spinner will be rolled back, so reapply immediately.
|
||||
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt))
|
||||
updateComplete(state == ArmedState.Hit, 0);
|
||||
if (drawableSpinner.Result?.TimeCompleted is double completionTime)
|
||||
{
|
||||
using (BeginAbsoluteSequence(completionTime))
|
||||
updateDiscColour(true, 200);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateComplete(bool complete, double duration)
|
||||
private void updateDiscColour(bool complete, double duration = 0)
|
||||
{
|
||||
var colour = complete ? completeColour : normalColour;
|
||||
|
||||
|
75
osu.Game.Rulesets.Osu/Skinning/FollowCircle.cs
Normal file
75
osu.Game.Rulesets.Osu/Skinning/FollowCircle.cs
Normal file
@ -0,0 +1,75 @@
|
||||
// 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.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning
|
||||
{
|
||||
public abstract class FollowCircle : CompositeDrawable
|
||||
{
|
||||
[Resolved]
|
||||
protected DrawableHitObject? ParentObject { get; private set; }
|
||||
|
||||
protected FollowCircle()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
((DrawableSlider?)ParentObject)?.Tracking.BindValueChanged(OnTrackingChanged, true);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
if (ParentObject != null)
|
||||
{
|
||||
ParentObject.HitObjectApplied += onHitObjectApplied;
|
||||
onHitObjectApplied(ParentObject);
|
||||
|
||||
ParentObject.ApplyCustomUpdateState += updateStateTransforms;
|
||||
updateStateTransforms(ParentObject, ParentObject.State.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void onHitObjectApplied(DrawableHitObject drawableObject)
|
||||
{
|
||||
this.ScaleTo(1f)
|
||||
.FadeOut();
|
||||
}
|
||||
|
||||
private void updateStateTransforms(DrawableHitObject drawableObject, ArmedState state)
|
||||
{
|
||||
// Gets called by slider ticks, tails, etc., leading to duplicated
|
||||
// animations which may negatively affect performance
|
||||
if (drawableObject is not DrawableSlider)
|
||||
return;
|
||||
|
||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||
OnSliderEnd();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (ParentObject != null)
|
||||
{
|
||||
ParentObject.HitObjectApplied -= onHitObjectApplied;
|
||||
ParentObject.ApplyCustomUpdateState -= updateStateTransforms;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void OnTrackingChanged(ValueChangedEvent<bool> tracking);
|
||||
|
||||
protected abstract void OnSliderEnd();
|
||||
}
|
||||
}
|
@ -1,12 +1,14 @@
|
||||
// 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.Diagnostics;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
public class LegacyFollowCircle : CompositeDrawable
|
||||
public class LegacyFollowCircle : FollowCircle
|
||||
{
|
||||
public LegacyFollowCircle(Drawable animationContent)
|
||||
{
|
||||
@ -18,5 +20,36 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
InternalChild = animationContent;
|
||||
}
|
||||
|
||||
protected override void OnTrackingChanged(ValueChangedEvent<bool> tracking)
|
||||
{
|
||||
Debug.Assert(ParentObject != null);
|
||||
|
||||
if (ParentObject.Judged)
|
||||
return;
|
||||
|
||||
double remainingTime = ParentObject.HitStateUpdateTime - Time.Current;
|
||||
|
||||
// Note that the scale adjust here is 2 instead of DrawableSliderBall.FOLLOW_AREA to match legacy behaviour.
|
||||
// This means the actual tracking area for gameplay purposes is larger than the sprite (but skins may be accounting for this).
|
||||
if (tracking.NewValue)
|
||||
{
|
||||
// TODO: Follow circle should bounce on each slider tick.
|
||||
this.ScaleTo(0.5f).ScaleTo(2f, Math.Min(180f, remainingTime), Easing.Out)
|
||||
.FadeTo(0).FadeTo(1f, Math.Min(60f, remainingTime));
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Should animate only at the next slider tick if we want to match stable perfectly.
|
||||
this.ScaleTo(4f, 100)
|
||||
.FadeTo(0f, 100);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSliderEnd()
|
||||
{
|
||||
this.ScaleTo(1.6f, 200, Easing.Out)
|
||||
.FadeOut(200, Easing.In);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -18,8 +18,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
private readonly ISkin skin;
|
||||
|
||||
private Sprite layerNd;
|
||||
private Sprite layerSpec;
|
||||
[Resolved(canBeNull: true)]
|
||||
private DrawableHitObject? parentObject { get; set; }
|
||||
|
||||
private Sprite layerNd = null!;
|
||||
private Sprite layerSpec = null!;
|
||||
|
||||
public LegacySliderBall(Drawable animationContent, ISkin skin)
|
||||
{
|
||||
@ -58,6 +61,17 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
if (parentObject != null)
|
||||
{
|
||||
parentObject.ApplyCustomUpdateState += updateStateTransforms;
|
||||
updateStateTransforms(parentObject, parentObject.State.Value);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
@ -68,5 +82,28 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
layerNd.Rotation = -appliedRotation;
|
||||
layerSpec.Rotation = -appliedRotation;
|
||||
}
|
||||
|
||||
private void updateStateTransforms(DrawableHitObject drawableObject, ArmedState _)
|
||||
{
|
||||
// Gets called by slider ticks, tails, etc., leading to duplicated
|
||||
// animations which in this case have no visual impact (due to
|
||||
// instant fade) but may negatively affect performance
|
||||
if (drawableObject is not DrawableSlider)
|
||||
return;
|
||||
|
||||
using (BeginAbsoluteSequence(drawableObject.StateUpdateTime))
|
||||
this.FadeIn();
|
||||
|
||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||
this.FadeOut();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (parentObject != null)
|
||||
parentObject.ApplyCustomUpdateState -= updateStateTransforms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +194,28 @@ namespace osu.Game.Rulesets.Osu.Utils
|
||||
private static Vector2 clampSliderToPlayfield(WorkingObject workingObject)
|
||||
{
|
||||
var slider = (Slider)workingObject.HitObject;
|
||||
var possibleMovementBounds = calculatePossibleMovementBounds(slider);
|
||||
var possibleMovementBounds = CalculatePossibleMovementBounds(slider);
|
||||
|
||||
// The slider rotation applied in computeModifiedPosition might make it impossible to fit the slider into the playfield
|
||||
// For example, a long horizontal slider will be off-screen when rotated by 90 degrees
|
||||
// In this case, limit the rotation to either 0 or 180 degrees
|
||||
if (possibleMovementBounds.Width < 0 || possibleMovementBounds.Height < 0)
|
||||
{
|
||||
float currentRotation = getSliderRotation(slider);
|
||||
float diff1 = getAngleDifference(workingObject.RotationOriginal, currentRotation);
|
||||
float diff2 = getAngleDifference(workingObject.RotationOriginal + MathF.PI, currentRotation);
|
||||
|
||||
if (diff1 < diff2)
|
||||
{
|
||||
RotateSlider(slider, workingObject.RotationOriginal - getSliderRotation(slider));
|
||||
}
|
||||
else
|
||||
{
|
||||
RotateSlider(slider, workingObject.RotationOriginal + MathF.PI - getSliderRotation(slider));
|
||||
}
|
||||
|
||||
possibleMovementBounds = CalculatePossibleMovementBounds(slider);
|
||||
}
|
||||
|
||||
var previousPosition = workingObject.PositionModified;
|
||||
|
||||
@ -239,10 +260,12 @@ namespace osu.Game.Rulesets.Osu.Utils
|
||||
/// Calculates a <see cref="RectangleF"/> which contains all of the possible movements of the slider (in relative X/Y coordinates)
|
||||
/// such that the entire slider is inside the playfield.
|
||||
/// </summary>
|
||||
/// <param name="slider">The <see cref="Slider"/> for which to calculate a movement bounding box.</param>
|
||||
/// <returns>A <see cref="RectangleF"/> which contains all of the possible movements of the slider such that the entire slider is inside the playfield.</returns>
|
||||
/// <remarks>
|
||||
/// If the slider is larger than the playfield, the returned <see cref="RectangleF"/> may have negative width/height.
|
||||
/// </remarks>
|
||||
private static RectangleF calculatePossibleMovementBounds(Slider slider)
|
||||
public static RectangleF CalculatePossibleMovementBounds(Slider slider)
|
||||
{
|
||||
var pathPositions = new List<Vector2>();
|
||||
slider.Path.GetPathToProgress(pathPositions, 0, 1);
|
||||
@ -353,6 +376,18 @@ namespace osu.Game.Rulesets.Osu.Utils
|
||||
return MathF.Atan2(endPositionVector.Y, endPositionVector.X);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the absolute difference between 2 angles measured in Radians.
|
||||
/// </summary>
|
||||
/// <param name="angle1">The first angle</param>
|
||||
/// <param name="angle2">The second angle</param>
|
||||
/// <returns>The absolute difference with interval <c>[0, MathF.PI)</c></returns>
|
||||
private static float getAngleDifference(float angle1, float angle2)
|
||||
{
|
||||
float diff = MathF.Abs(angle1 - angle2) % (MathF.PI * 2);
|
||||
return MathF.Min(diff, MathF.PI * 2 - diff);
|
||||
}
|
||||
|
||||
public class ObjectPositionInfo
|
||||
{
|
||||
/// <summary>
|
||||
@ -395,6 +430,7 @@ namespace osu.Game.Rulesets.Osu.Utils
|
||||
|
||||
private class WorkingObject
|
||||
{
|
||||
public float RotationOriginal { get; }
|
||||
public Vector2 PositionOriginal { get; }
|
||||
public Vector2 PositionModified { get; set; }
|
||||
public Vector2 EndPositionModified { get; set; }
|
||||
@ -405,6 +441,7 @@ namespace osu.Game.Rulesets.Osu.Utils
|
||||
public WorkingObject(ObjectPositionInfo positionInfo)
|
||||
{
|
||||
PositionInfo = positionInfo;
|
||||
RotationOriginal = HitObject is Slider slider ? getSliderRotation(slider) : 0;
|
||||
PositionModified = PositionOriginal = HitObject.Position;
|
||||
EndPositionModified = HitObject.EndPosition;
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||
|
@ -1,10 +1,9 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
@ -119,7 +118,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
||||
var nextHitObject = GetNextObject(i); // Get the next object that requires pressing the same button
|
||||
|
||||
bool canDelayKeyUp = nextHitObject == null || nextHitObject.StartTime > endTime + KEY_UP_DELAY;
|
||||
double calculatedDelay = canDelayKeyUp ? KEY_UP_DELAY : (nextHitObject.StartTime - endTime) * 0.9;
|
||||
double calculatedDelay = canDelayKeyUp ? KEY_UP_DELAY : (nextHitObject.AsNonNull().StartTime - endTime) * 0.9;
|
||||
Frames.Add(new TaikoReplayFrame(endTime + calculatedDelay));
|
||||
|
||||
hitButton = !hitButton;
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Replays.Legacy;
|
||||
@ -25,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
||||
Actions.AddRange(actions);
|
||||
}
|
||||
|
||||
public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
|
||||
public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame? lastFrame = null)
|
||||
{
|
||||
if (currentFrame.MouseRight1) Actions.Add(TaikoAction.LeftRim);
|
||||
if (currentFrame.MouseRight2) Actions.Add(TaikoAction.RightRim);
|
||||
|
@ -138,7 +138,7 @@ namespace osu.Game.Tests.Collections.IO
|
||||
{
|
||||
string firstRunName;
|
||||
|
||||
using (var host = new CleanRunHeadlessGameHost(bypassCleanup: true))
|
||||
using (var host = new CleanRunHeadlessGameHost(bypassCleanupOnDispose: true))
|
||||
{
|
||||
firstRunName = host.Name;
|
||||
|
||||
|
@ -35,7 +35,8 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using (var importer = new BeatmapImporter(storage, realm))
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
|
||||
using (new RealmRulesetStore(realm, storage))
|
||||
{
|
||||
var beatmapSet = await importer.Import(new ImportTask(TestResources.GetTestBeatmapStream(), "renatus.osz"));
|
||||
@ -76,7 +77,8 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using (var importer = new BeatmapImporter(storage, realm))
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
|
||||
using (new RealmRulesetStore(realm, storage))
|
||||
{
|
||||
var beatmapSet = await importer.Import(new ImportTask(TestResources.GetTestBeatmapStream(), "renatus.osz"));
|
||||
@ -134,7 +136,8 @@ namespace osu.Game.Tests.Database
|
||||
|
||||
var manager = new ModelManager<BeatmapSetInfo>(storage, realm);
|
||||
|
||||
using (var importer = new BeatmapImporter(storage, realm))
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
|
||||
using (new RealmRulesetStore(realm, storage))
|
||||
{
|
||||
Task.Run(async () =>
|
||||
@ -160,7 +163,8 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using (var importer = new BeatmapImporter(storage, realm))
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
|
||||
using (new RealmRulesetStore(realm, storage))
|
||||
{
|
||||
var imported = await importer.Import(new ImportTask(TestResources.GetTestBeatmapStream(), "renatus.osz"));
|
||||
@ -187,7 +191,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
await LoadOszIntoStore(importer, realm.Realm);
|
||||
@ -199,7 +203,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||
@ -217,7 +221,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||
@ -231,7 +235,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
string? tempPath = TestResources.GetTestBeatmapForImport();
|
||||
@ -261,7 +265,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||
@ -281,7 +285,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
string? temp = TestResources.GetTestBeatmapForImport();
|
||||
@ -317,7 +321,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
string? temp = TestResources.GetTestBeatmapForImport();
|
||||
@ -366,7 +370,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
string? temp = TestResources.GetTestBeatmapForImport();
|
||||
@ -417,7 +421,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
string? temp = TestResources.GetTestBeatmapForImport();
|
||||
@ -465,7 +469,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
string? temp = TestResources.GetTestBeatmapForImport();
|
||||
@ -513,7 +517,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||
@ -548,7 +552,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
var progressNotification = new ImportProgressNotification();
|
||||
@ -586,7 +590,7 @@ namespace osu.Game.Tests.Database
|
||||
Interlocked.Increment(ref loggedExceptionCount);
|
||||
};
|
||||
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||
@ -644,7 +648,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
var imported = await LoadOszIntoStore(importer, realm.Realm, batchImport: true);
|
||||
@ -671,7 +675,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realmFactory, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realmFactory);
|
||||
var importer = new BeatmapImporter(storage, realmFactory);
|
||||
using var store = new RealmRulesetStore(realmFactory, storage);
|
||||
|
||||
var imported = await LoadOszIntoStore(importer, realmFactory.Realm);
|
||||
@ -703,7 +707,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||
@ -730,7 +734,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||
@ -756,7 +760,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealm((realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
var metadata = new BeatmapMetadata
|
||||
@ -804,7 +808,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
string? temp = TestResources.GetTestBeatmapForImport();
|
||||
@ -821,7 +825,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
string? temp = TestResources.GetTestBeatmapForImport();
|
||||
@ -857,7 +861,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
string? temp = TestResources.GetTestBeatmapForImport();
|
||||
@ -899,7 +903,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
string? temp = TestResources.GetTestBeatmapForImport();
|
||||
@ -950,7 +954,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using var importer = new BeatmapImporter(storage, realm);
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
string? temp = TestResources.GetTestBeatmapForImport();
|
||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
RunTestWithRealm((realm, _) =>
|
||||
{
|
||||
using (realm.BlockAllOperations())
|
||||
using (realm.BlockAllOperations("testing"))
|
||||
{
|
||||
}
|
||||
});
|
||||
@ -56,7 +56,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
Task writeTask;
|
||||
|
||||
using (realm.BlockAllOperations())
|
||||
using (realm.BlockAllOperations("testing"))
|
||||
{
|
||||
writeTask = realm.WriteAsync(r => r.Add(TestResources.CreateTestBeatmapSetInfo()));
|
||||
Thread.Sleep(100);
|
||||
@ -169,7 +169,7 @@ namespace osu.Game.Tests.Database
|
||||
|
||||
Assert.Throws<TimeoutException>(() =>
|
||||
{
|
||||
using (realm.BlockAllOperations())
|
||||
using (realm.BlockAllOperations("testing"))
|
||||
{
|
||||
}
|
||||
});
|
||||
@ -177,7 +177,7 @@ namespace osu.Game.Tests.Database
|
||||
stopThreadedUsage.Set();
|
||||
|
||||
// Ensure we can block a second time after the usage has ended.
|
||||
using (realm.BlockAllOperations())
|
||||
using (realm.BlockAllOperations("testing"))
|
||||
{
|
||||
}
|
||||
});
|
||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Tests.Database
|
||||
{
|
||||
migratedStorage.DeleteDirectory(string.Empty);
|
||||
|
||||
using (realm.BlockAllOperations())
|
||||
using (realm.BlockAllOperations("testing"))
|
||||
{
|
||||
storage.Migrate(migratedStorage);
|
||||
}
|
||||
@ -59,6 +59,64 @@ namespace osu.Game.Tests.Database
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFailedWritePerformsRollback()
|
||||
{
|
||||
RunTestWithRealm((realm, _) =>
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
realm.Write(r =>
|
||||
{
|
||||
r.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata()));
|
||||
throw new InvalidOperationException();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.That(realm.Run(r => r.All<BeatmapInfo>()), Is.Empty);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFailedNestedWritePerformsRollback()
|
||||
{
|
||||
RunTestWithRealm((realm, _) =>
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
realm.Write(r =>
|
||||
{
|
||||
realm.Write(_ =>
|
||||
{
|
||||
r.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata()));
|
||||
throw new InvalidOperationException();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Assert.That(realm.Run(r => r.All<BeatmapInfo>()), Is.Empty);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNestedWriteCalls()
|
||||
{
|
||||
RunTestWithRealm((realm, _) =>
|
||||
{
|
||||
var beatmap = new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata());
|
||||
|
||||
var liveBeatmap = beatmap.ToLive(realm);
|
||||
|
||||
realm.Run(r =>
|
||||
r.Write(_ =>
|
||||
r.Write(_ =>
|
||||
r.Add(beatmap)))
|
||||
);
|
||||
|
||||
Assert.IsFalse(liveBeatmap.PerformRead(l => l.Hidden));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAccessAfterAttach()
|
||||
{
|
||||
|
@ -136,7 +136,7 @@ namespace osu.Game.Tests.Database
|
||||
resolvedItems = null;
|
||||
lastChanges = null;
|
||||
|
||||
using (realm.BlockAllOperations())
|
||||
using (realm.BlockAllOperations("testing"))
|
||||
Assert.That(resolvedItems, Is.Empty);
|
||||
|
||||
realm.Write(r => r.Add(TestResources.CreateTestBeatmapSetInfo()));
|
||||
@ -154,7 +154,7 @@ namespace osu.Game.Tests.Database
|
||||
testEventsArriving(false);
|
||||
|
||||
// And make sure even after another context loss we don't get firings.
|
||||
using (realm.BlockAllOperations())
|
||||
using (realm.BlockAllOperations("testing"))
|
||||
Assert.That(resolvedItems, Is.Null);
|
||||
|
||||
realm.Write(r => r.Add(TestResources.CreateTestBeatmapSetInfo()));
|
||||
@ -212,7 +212,7 @@ namespace osu.Game.Tests.Database
|
||||
|
||||
Assert.That(beatmapSetInfo, Is.Not.Null);
|
||||
|
||||
using (realm.BlockAllOperations())
|
||||
using (realm.BlockAllOperations("testing"))
|
||||
{
|
||||
// custom disposal action fired when context lost.
|
||||
Assert.That(beatmapSetInfo, Is.Null);
|
||||
@ -226,7 +226,7 @@ namespace osu.Game.Tests.Database
|
||||
|
||||
Assert.That(beatmapSetInfo, Is.Null);
|
||||
|
||||
using (realm.BlockAllOperations())
|
||||
using (realm.BlockAllOperations("testing"))
|
||||
Assert.That(beatmapSetInfo, Is.Null);
|
||||
|
||||
realm.Run(r => r.Refresh());
|
||||
@ -251,7 +251,7 @@ namespace osu.Game.Tests.Database
|
||||
Assert.That(receivedValue, Is.Not.Null);
|
||||
receivedValue = null;
|
||||
|
||||
using (realm.BlockAllOperations())
|
||||
using (realm.BlockAllOperations("testing"))
|
||||
{
|
||||
}
|
||||
|
||||
@ -262,7 +262,7 @@ namespace osu.Game.Tests.Database
|
||||
subscription.Dispose();
|
||||
receivedValue = null;
|
||||
|
||||
using (realm.BlockAllOperations())
|
||||
using (realm.BlockAllOperations("testing"))
|
||||
Assert.That(receivedValue, Is.Null);
|
||||
|
||||
realm.Run(r => r.Refresh());
|
||||
|
@ -9,7 +9,6 @@ using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Play;
|
||||
@ -78,6 +77,16 @@ namespace osu.Game.Tests.Gameplay
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest]
|
||||
/*
|
||||
* Fail rate around 0.15%
|
||||
*
|
||||
* TearDown : osu.Framework.Testing.Drawables.Steps.AssertButton+TracedException : gameplay clock time = 2500
|
||||
* --TearDown
|
||||
* at osu.Framework.Threading.ScheduledDelegate.RunTaskInternal()
|
||||
* at osu.Framework.Threading.Scheduler.Update()
|
||||
* at osu.Framework.Graphics.Drawable.UpdateSubTree()
|
||||
*/
|
||||
public void TestSeekPerformsInGameplayTime(
|
||||
[Values(1.0, 0.5, 2.0)] double clockRate,
|
||||
[Values(0.0, 200.0, -200.0)] double userOffset,
|
||||
@ -106,10 +115,10 @@ namespace osu.Game.Tests.Gameplay
|
||||
AddStep($"set audio offset to {userOffset}", () => localConfig.SetValue(OsuSetting.AudioOffset, userOffset));
|
||||
|
||||
AddStep("seek to 2500", () => gameplayClockContainer.Seek(2500));
|
||||
AddAssert("gameplay clock time = 2500", () => Precision.AlmostEquals(gameplayClockContainer.CurrentTime, 2500, 10f));
|
||||
AddStep("gameplay clock time = 2500", () => Assert.AreEqual(gameplayClockContainer.CurrentTime, 2500, 10f));
|
||||
|
||||
AddStep("seek to 10000", () => gameplayClockContainer.Seek(10000));
|
||||
AddAssert("gameplay clock time = 10000", () => Precision.AlmostEquals(gameplayClockContainer.CurrentTime, 10000, 10f));
|
||||
AddStep("gameplay clock time = 10000", () => Assert.AreEqual(gameplayClockContainer.CurrentTime, 10000, 10f));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
|
@ -4,9 +4,12 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Models;
|
||||
using osu.Game.Tests.Resources;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
@ -23,6 +26,47 @@ namespace osu.Game.Tests.NonVisual
|
||||
Assert.IsTrue(ourInfo.MatchesOnlineID(otherInfo));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAudioEqualityNoFile()
|
||||
{
|
||||
var beatmapSetA = TestResources.CreateTestBeatmapSetInfo(1);
|
||||
var beatmapSetB = TestResources.CreateTestBeatmapSetInfo(1);
|
||||
|
||||
Assert.AreNotEqual(beatmapSetA, beatmapSetB);
|
||||
Assert.IsTrue(beatmapSetA.Beatmaps.Single().AudioEquals(beatmapSetB.Beatmaps.Single()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAudioEqualitySameHash()
|
||||
{
|
||||
var beatmapSetA = TestResources.CreateTestBeatmapSetInfo(1);
|
||||
var beatmapSetB = TestResources.CreateTestBeatmapSetInfo(1);
|
||||
|
||||
addAudioFile(beatmapSetA, "abc");
|
||||
addAudioFile(beatmapSetB, "abc");
|
||||
|
||||
Assert.AreNotEqual(beatmapSetA, beatmapSetB);
|
||||
Assert.IsTrue(beatmapSetA.Beatmaps.Single().AudioEquals(beatmapSetB.Beatmaps.Single()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAudioEqualityDifferentHash()
|
||||
{
|
||||
var beatmapSetA = TestResources.CreateTestBeatmapSetInfo(1);
|
||||
var beatmapSetB = TestResources.CreateTestBeatmapSetInfo(1);
|
||||
|
||||
addAudioFile(beatmapSetA);
|
||||
addAudioFile(beatmapSetB);
|
||||
|
||||
Assert.AreNotEqual(beatmapSetA, beatmapSetB);
|
||||
Assert.IsTrue(beatmapSetA.Beatmaps.Single().AudioEquals(beatmapSetB.Beatmaps.Single()));
|
||||
}
|
||||
|
||||
private static void addAudioFile(BeatmapSetInfo beatmapSetInfo, string hash = null)
|
||||
{
|
||||
beatmapSetInfo.Files.Add(new RealmNamedFileUsage(new RealmFile { Hash = hash ?? Guid.NewGuid().ToString() }, "audio.mp3"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDatabasedWithDatabased()
|
||||
{
|
||||
|
@ -315,6 +315,26 @@ namespace osu.Game.Tests.NonVisual
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBackupCreatedOnCorruptRealm()
|
||||
{
|
||||
using (var host = new CustomTestHeadlessGameHost())
|
||||
{
|
||||
try
|
||||
{
|
||||
File.WriteAllText(host.InitialStorage.GetFullPath(OsuGameBase.CLIENT_DATABASE_FILENAME, true), "i am definitely not a realm file");
|
||||
|
||||
LoadOsuIntoHost(host);
|
||||
|
||||
Assert.That(host.InitialStorage.GetFiles(string.Empty, "*_corrupt.realm"), Has.One.Items);
|
||||
}
|
||||
finally
|
||||
{
|
||||
host.Exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string getDefaultLocationFor(CustomTestHeadlessGameHost host)
|
||||
{
|
||||
string path = Path.Combine(TestRunHeadlessGameHost.TemporaryTestDirectory, host.Name);
|
||||
@ -347,7 +367,7 @@ namespace osu.Game.Tests.NonVisual
|
||||
public Storage InitialStorage { get; }
|
||||
|
||||
public CustomTestHeadlessGameHost([CallerMemberName] string callingMethodName = @"")
|
||||
: base(callingMethodName: callingMethodName)
|
||||
: base(callingMethodName: callingMethodName, bypassCleanupOnSetup: true)
|
||||
{
|
||||
string defaultStorageLocation = getDefaultLocationFor(this);
|
||||
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -256,7 +254,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
|
||||
|
||||
private class CustomFilterCriteria : IRulesetFilterCriteria
|
||||
{
|
||||
public string CustomValue { get; set; }
|
||||
public string? CustomValue { get; set; }
|
||||
|
||||
public bool Matches(BeatmapInfo beatmapInfo) => true;
|
||||
|
||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
|
||||
var user = new APIUser { Id = 33 };
|
||||
|
||||
AddRepeatStep("add user multiple times", () => MultiplayerClient.AddUser(user), 3);
|
||||
AddAssert("room has 2 users", () => MultiplayerClient.Room?.Users.Count == 2);
|
||||
AddUntilStep("room has 2 users", () => MultiplayerClient.ClientRoom?.Users.Count == 2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -33,10 +33,10 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
|
||||
var user = new APIUser { Id = 44 };
|
||||
|
||||
AddStep("add user", () => MultiplayerClient.AddUser(user));
|
||||
AddAssert("room has 2 users", () => MultiplayerClient.Room?.Users.Count == 2);
|
||||
AddUntilStep("room has 2 users", () => MultiplayerClient.ClientRoom?.Users.Count == 2);
|
||||
|
||||
AddRepeatStep("remove user multiple times", () => MultiplayerClient.RemoveUser(user), 3);
|
||||
AddAssert("room has 1 user", () => MultiplayerClient.Room?.Users.Count == 1);
|
||||
AddStep("remove user", () => MultiplayerClient.RemoveUser(user));
|
||||
AddUntilStep("room has 1 user", () => MultiplayerClient.ClientRoom?.Users.Count == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -59,7 +59,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
|
||||
changeState(6, MultiplayerUserState.WaitingForLoad);
|
||||
checkPlayingUserCount(6);
|
||||
|
||||
AddStep("another user left", () => MultiplayerClient.RemoveUser((MultiplayerClient.Room?.Users.Last().User).AsNonNull()));
|
||||
AddStep("another user left", () => MultiplayerClient.RemoveUser((MultiplayerClient.ServerRoom?.Users.Last().User).AsNonNull()));
|
||||
checkPlayingUserCount(5);
|
||||
|
||||
AddStep("leave room", () => MultiplayerClient.LeaveRoom());
|
||||
@ -103,7 +103,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
|
||||
{
|
||||
for (int i = 0; i < userCount; ++i)
|
||||
{
|
||||
int userId = MultiplayerClient.Room?.Users[i].UserID ?? throw new AssertionException("Room cannot be null!");
|
||||
int userId = MultiplayerClient.ServerRoom?.Users[i].UserID ?? throw new AssertionException("Room cannot be null!");
|
||||
MultiplayerClient.ChangeUserState(userId, state);
|
||||
}
|
||||
});
|
||||
|
@ -212,17 +212,17 @@ namespace osu.Game.Tests.Online
|
||||
{
|
||||
}
|
||||
|
||||
protected override BeatmapImporter CreateBeatmapImporter(Storage storage, RealmAccess realm, RulesetStore rulesets, BeatmapOnlineLookupQueue onlineLookupQueue)
|
||||
protected override BeatmapImporter CreateBeatmapImporter(Storage storage, RealmAccess realm)
|
||||
{
|
||||
return new TestBeatmapImporter(this, storage, realm, onlineLookupQueue);
|
||||
return new TestBeatmapImporter(this, storage, realm);
|
||||
}
|
||||
|
||||
internal class TestBeatmapImporter : BeatmapImporter
|
||||
{
|
||||
private readonly TestBeatmapManager testBeatmapManager;
|
||||
|
||||
public TestBeatmapImporter(TestBeatmapManager testBeatmapManager, Storage storage, RealmAccess databaseAccess, BeatmapOnlineLookupQueue beatmapOnlineLookupQueue)
|
||||
: base(storage, databaseAccess, beatmapOnlineLookupQueue)
|
||||
public TestBeatmapImporter(TestBeatmapManager testBeatmapManager, Storage storage, RealmAccess databaseAccess)
|
||||
: base(storage, databaseAccess)
|
||||
{
|
||||
this.testBeatmapManager = testBeatmapManager;
|
||||
}
|
||||
|
@ -134,6 +134,7 @@ namespace osu.Game.Tests.Resources
|
||||
DifficultyName = $"{version} {beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})",
|
||||
StarRating = diff,
|
||||
Length = length,
|
||||
BeatmapSet = beatmapSet,
|
||||
BPM = bpm,
|
||||
Hash = Guid.NewGuid().ToString().ComputeMD5Hash(),
|
||||
Ruleset = rulesetInfo,
|
||||
|
@ -83,20 +83,20 @@ namespace osu.Game.Tests.Skins.IO
|
||||
#region Cases where imports should match existing
|
||||
|
||||
[Test]
|
||||
public Task TestImportTwiceWithSameMetadataAndFilename() => runSkinTest(async osu =>
|
||||
public Task TestImportTwiceWithSameMetadataAndFilename([Values] bool batchImport) => runSkinTest(async osu =>
|
||||
{
|
||||
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("test skin", "skinner"), "skin.osk"));
|
||||
var import2 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("test skin", "skinner"), "skin.osk"));
|
||||
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("test skin", "skinner"), "skin.osk"), batchImport);
|
||||
var import2 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("test skin", "skinner"), "skin.osk"), batchImport);
|
||||
|
||||
assertImportedOnce(import1, import2);
|
||||
});
|
||||
|
||||
[Test]
|
||||
public Task TestImportTwiceWithNoMetadataSameDownloadFilename() => runSkinTest(async osu =>
|
||||
public Task TestImportTwiceWithNoMetadataSameDownloadFilename([Values] bool batchImport) => runSkinTest(async osu =>
|
||||
{
|
||||
// if a user downloads two skins that do have skin.ini files but don't have any creator metadata in the skin.ini, they should both import separately just for safety.
|
||||
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni(string.Empty, string.Empty), "download.osk"));
|
||||
var import2 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni(string.Empty, string.Empty), "download.osk"));
|
||||
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni(string.Empty, string.Empty), "download.osk"), batchImport);
|
||||
var import2 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni(string.Empty, string.Empty), "download.osk"), batchImport);
|
||||
|
||||
assertImportedOnce(import1, import2);
|
||||
});
|
||||
@ -134,10 +134,10 @@ namespace osu.Game.Tests.Skins.IO
|
||||
});
|
||||
|
||||
[Test]
|
||||
public Task TestSameMetadataNameSameFolderName() => runSkinTest(async osu =>
|
||||
public Task TestSameMetadataNameSameFolderName([Values] bool batchImport) => runSkinTest(async osu =>
|
||||
{
|
||||
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("name 1", "author 1"), "my custom skin 1"));
|
||||
var import2 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("name 1", "author 1"), "my custom skin 1"));
|
||||
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("name 1", "author 1"), "my custom skin 1"), batchImport);
|
||||
var import2 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("name 1", "author 1"), "my custom skin 1"), batchImport);
|
||||
|
||||
assertImportedOnce(import1, import2);
|
||||
assertCorrectMetadata(import1, "name 1 [my custom skin 1]", "author 1", osu);
|
||||
@ -357,10 +357,10 @@ namespace osu.Game.Tests.Skins.IO
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Live<SkinInfo>> loadSkinIntoOsu(OsuGameBase osu, ImportTask import)
|
||||
private async Task<Live<SkinInfo>> loadSkinIntoOsu(OsuGameBase osu, ImportTask import, bool batchImport = false)
|
||||
{
|
||||
var skinManager = osu.Dependencies.Get<SkinManager>();
|
||||
return await skinManager.Import(import);
|
||||
return await skinManager.Import(import, batchImport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
@ -24,7 +22,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
[TestFixture]
|
||||
public class TestSceneComposeScreen : EditorClockTestScene
|
||||
{
|
||||
private EditorBeatmap editorBeatmap;
|
||||
private EditorBeatmap editorBeatmap = null!;
|
||||
|
||||
[Cached]
|
||||
private EditorClipboard clipboard = new EditorClipboard();
|
||||
|
@ -1,13 +1,12 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Screens;
|
||||
@ -39,7 +38,9 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
protected override bool IsolateSavingFromDatabase => false;
|
||||
|
||||
[Resolved]
|
||||
private BeatmapManager beatmapManager { get; set; }
|
||||
private BeatmapManager beatmapManager { get; set; } = null!;
|
||||
|
||||
private Guid currentBeatmapSetID => EditorBeatmap.BeatmapInfo.BeatmapSet?.ID ?? Guid.Empty;
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
@ -50,19 +51,19 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddStep("make new beatmap unique", () => EditorBeatmap.Metadata.Title = Guid.NewGuid().ToString());
|
||||
}
|
||||
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) => new DummyWorkingBeatmap(Audio, null);
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard? storyboard = null) => new DummyWorkingBeatmap(Audio, null);
|
||||
|
||||
[Test]
|
||||
public void TestCreateNewBeatmap()
|
||||
{
|
||||
AddStep("save beatmap", () => Editor.Save());
|
||||
AddAssert("new beatmap in database", () => beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID)?.Value.DeletePending == false);
|
||||
AddAssert("new beatmap in database", () => beatmapManager.QueryBeatmapSet(s => s.ID == currentBeatmapSetID)?.Value.DeletePending == false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExitWithoutSave()
|
||||
{
|
||||
EditorBeatmap editorBeatmap = null;
|
||||
EditorBeatmap editorBeatmap = null!;
|
||||
|
||||
AddStep("store editor beatmap", () => editorBeatmap = EditorBeatmap);
|
||||
|
||||
@ -78,12 +79,33 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddUntilStep("wait for exit", () => !Editor.IsCurrentScreen());
|
||||
AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddAssert("new beatmap not persisted", () => beatmapManager.QueryBeatmapSet(s => s.ID == editorBeatmap.BeatmapInfo.BeatmapSet.ID)?.Value.DeletePending == true);
|
||||
AddAssert("new beatmap not persisted", () => beatmapManager.QueryBeatmapSet(s => s.ID == editorBeatmap.BeatmapInfo.BeatmapSet.AsNonNull().ID)?.Value.DeletePending == true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest]
|
||||
/*
|
||||
* Fail rate around 1.2%.
|
||||
*
|
||||
* Failing with realm refetch occasionally being null.
|
||||
* My only guess is that the WorkingBeatmap at SetupScreen is dummy instead of the true one.
|
||||
* If it's something else, we have larger issues with realm, but I don't think that's the case.
|
||||
*
|
||||
* at osu.Framework.Logging.ThrowingTraceListener.Fail(String message1, String message2)
|
||||
* at System.Diagnostics.TraceInternal.Fail(String message, String detailMessage)
|
||||
* at System.Diagnostics.TraceInternal.TraceProvider.Fail(String message, String detailMessage)
|
||||
* at System.Diagnostics.Debug.Fail(String message, String detailMessage)
|
||||
* at osu.Game.Database.ModelManager`1.<>c__DisplayClass8_0.<performFileOperation>b__0(Realm realm) ModelManager.cs:line 50
|
||||
* at osu.Game.Database.RealmExtensions.Write(Realm realm, Action`1 function) RealmExtensions.cs:line 14
|
||||
* at osu.Game.Database.ModelManager`1.performFileOperation(TModel item, Action`1 operation) ModelManager.cs:line 47
|
||||
* at osu.Game.Database.ModelManager`1.AddFile(TModel item, Stream contents, String filename) ModelManager.cs:line 37
|
||||
* at osu.Game.Screens.Edit.Setup.ResourcesSection.ChangeAudioTrack(FileInfo source) ResourcesSection.cs:line 115
|
||||
* at osu.Game.Tests.Visual.Editing.TestSceneEditorBeatmapCreation.<TestAddAudioTrack>b__11_0() TestSceneEditorBeatmapCreation.cs:line 101
|
||||
*/
|
||||
public void TestAddAudioTrack()
|
||||
{
|
||||
AddAssert("track is virtual", () => Beatmap.Value.Track is TrackVirtual);
|
||||
|
||||
AddAssert("switch track to real track", () =>
|
||||
{
|
||||
var setup = Editor.ChildrenOfType<SetupScreen>().First();
|
||||
@ -112,6 +134,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
}
|
||||
});
|
||||
|
||||
AddAssert("track is not virtual", () => Beatmap.Value.Track is not TrackVirtual);
|
||||
AddAssert("track length changed", () => Beatmap.Value.Track.Length > 60000);
|
||||
}
|
||||
|
||||
@ -141,7 +164,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddAssert("new beatmap persisted", () =>
|
||||
{
|
||||
var beatmap = beatmapManager.QueryBeatmap(b => b.DifficultyName == firstDifficultyName);
|
||||
var set = beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID);
|
||||
var set = beatmapManager.QueryBeatmapSet(s => s.ID == currentBeatmapSetID);
|
||||
|
||||
return beatmap != null
|
||||
&& beatmap.DifficultyName == firstDifficultyName
|
||||
@ -160,7 +183,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
AddUntilStep("wait for created", () =>
|
||||
{
|
||||
string difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||
return difficultyName != null && difficultyName != firstDifficultyName;
|
||||
});
|
||||
|
||||
@ -176,7 +199,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddAssert("new beatmap persisted", () =>
|
||||
{
|
||||
var beatmap = beatmapManager.QueryBeatmap(b => b.DifficultyName == secondDifficultyName);
|
||||
var set = beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID);
|
||||
var set = beatmapManager.QueryBeatmapSet(s => s.ID == currentBeatmapSetID);
|
||||
|
||||
return beatmap != null
|
||||
&& beatmap.DifficultyName == secondDifficultyName
|
||||
@ -227,7 +250,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddAssert("new beatmap persisted", () =>
|
||||
{
|
||||
var beatmap = beatmapManager.QueryBeatmap(b => b.DifficultyName == originalDifficultyName);
|
||||
var set = beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID);
|
||||
var set = beatmapManager.QueryBeatmapSet(s => s.ID == currentBeatmapSetID);
|
||||
|
||||
return beatmap != null
|
||||
&& beatmap.DifficultyName == originalDifficultyName
|
||||
@ -243,7 +266,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
AddUntilStep("wait for created", () =>
|
||||
{
|
||||
string difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||
return difficultyName != null && difficultyName != originalDifficultyName;
|
||||
});
|
||||
|
||||
@ -262,13 +285,13 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
AddStep("save beatmap", () => Editor.Save());
|
||||
|
||||
BeatmapInfo refetchedBeatmap = null;
|
||||
Live<BeatmapSetInfo> refetchedBeatmapSet = null;
|
||||
BeatmapInfo? refetchedBeatmap = null;
|
||||
Live<BeatmapSetInfo>? refetchedBeatmapSet = null;
|
||||
|
||||
AddStep("refetch from database", () =>
|
||||
{
|
||||
refetchedBeatmap = beatmapManager.QueryBeatmap(b => b.DifficultyName == copyDifficultyName);
|
||||
refetchedBeatmapSet = beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID);
|
||||
refetchedBeatmapSet = beatmapManager.QueryBeatmapSet(s => s.ID == currentBeatmapSetID);
|
||||
});
|
||||
|
||||
AddAssert("new beatmap persisted", () =>
|
||||
@ -304,7 +327,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
AddUntilStep("wait for created", () =>
|
||||
{
|
||||
string difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||
return difficultyName != null && difficultyName != "New Difficulty";
|
||||
});
|
||||
AddAssert("new difficulty has correct name", () => EditorBeatmap.BeatmapInfo.DifficultyName == "New Difficulty (1)");
|
||||
@ -340,7 +363,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
AddUntilStep("wait for created", () =>
|
||||
{
|
||||
string difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||
return difficultyName != null && difficultyName != duplicate_difficulty_name;
|
||||
});
|
||||
|
||||
|
@ -6,10 +6,13 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||
using osu.Game.Screens.Select;
|
||||
@ -22,7 +25,9 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
[Test]
|
||||
public void TestCantExitWithoutSaving()
|
||||
{
|
||||
AddUntilStep("Wait for dialog overlay load", () => ((Drawable)Game.Dependencies.Get<IDialogOverlay>()).IsLoaded);
|
||||
AddRepeatStep("Exit", () => InputManager.Key(Key.Escape), 10);
|
||||
AddAssert("Sample playback disabled", () => Editor.SamplePlaybackDisabled.Value);
|
||||
AddAssert("Editor is still active screen", () => Game.ScreenStack.CurrentScreen is Editor);
|
||||
}
|
||||
|
||||
@ -39,6 +44,8 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
SaveEditor();
|
||||
|
||||
AddAssert("Hash updated", () => !string.IsNullOrEmpty(EditorBeatmap.BeatmapInfo.BeatmapSet?.Hash));
|
||||
|
||||
AddAssert("Beatmap has correct metadata", () => EditorBeatmap.BeatmapInfo.Metadata.Artist == "artist" && EditorBeatmap.BeatmapInfo.Metadata.Title == "title");
|
||||
AddAssert("Beatmap has correct author", () => EditorBeatmap.BeatmapInfo.Metadata.Author.Username == "author");
|
||||
AddAssert("Beatmap has correct difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName == "difficulty");
|
||||
@ -130,6 +137,54 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
!ReferenceEquals(EditorBeatmap.HitObjects[0].DifficultyControlPoint, DifficultyControlPoint.DEFAULT));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLengthAndStarRatingUpdated()
|
||||
{
|
||||
WorkingBeatmap working = null;
|
||||
double lastStarRating = 0;
|
||||
double lastLength = 0;
|
||||
|
||||
AddStep("Add timing point", () => EditorBeatmap.ControlPointInfo.Add(500, new TimingControlPoint()));
|
||||
AddStep("Change to placement mode", () => InputManager.Key(Key.Number2));
|
||||
AddStep("Move to playfield", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre));
|
||||
AddStep("Place single hitcircle", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("One hitobject placed", () => EditorBeatmap.HitObjects.Count == 1);
|
||||
|
||||
SaveEditor();
|
||||
AddStep("Get working beatmap", () => working = Game.BeatmapManager.GetWorkingBeatmap(EditorBeatmap.BeatmapInfo, true));
|
||||
|
||||
AddAssert("Beatmap length is zero", () => working.BeatmapInfo.Length == 0);
|
||||
checkDifficultyIncreased();
|
||||
|
||||
AddStep("Move forward", () => InputManager.Key(Key.Right));
|
||||
AddStep("Place another hitcircle", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("Two hitobjects placed", () => EditorBeatmap.HitObjects.Count == 2);
|
||||
|
||||
SaveEditor();
|
||||
AddStep("Get working beatmap", () => working = Game.BeatmapManager.GetWorkingBeatmap(EditorBeatmap.BeatmapInfo, true));
|
||||
|
||||
checkDifficultyIncreased();
|
||||
checkLengthIncreased();
|
||||
|
||||
void checkLengthIncreased()
|
||||
{
|
||||
AddStep("Beatmap length increased", () =>
|
||||
{
|
||||
Assert.That(working.BeatmapInfo.Length, Is.GreaterThan(lastLength));
|
||||
lastLength = working.BeatmapInfo.Length;
|
||||
});
|
||||
}
|
||||
|
||||
void checkDifficultyIncreased()
|
||||
{
|
||||
AddStep("Beatmap difficulty increased", () =>
|
||||
{
|
||||
Assert.That(working.BeatmapInfo.StarRating, Is.GreaterThan(lastStarRating));
|
||||
lastStarRating = working.BeatmapInfo.StarRating;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExitWithoutSaveFromExistingBeatmap()
|
||||
{
|
||||
|
@ -14,6 +14,16 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
public override Drawable CreateTestComponent() => Empty();
|
||||
|
||||
[Test]
|
||||
[FlakyTest]
|
||||
/*
|
||||
* Fail rate around 0.3%
|
||||
*
|
||||
* TearDown : osu.Framework.Testing.Drawables.Steps.AssertButton+TracedException : range halved
|
||||
* --TearDown
|
||||
* at osu.Framework.Threading.ScheduledDelegate.RunTaskInternal()
|
||||
* at osu.Framework.Threading.Scheduler.Update()
|
||||
* at osu.Framework.Graphics.Drawable.UpdateSubTree()
|
||||
*/
|
||||
public void TestVisibleRangeUpdatesOnZoomChange()
|
||||
{
|
||||
double initialVisibleRange = 0;
|
||||
|
@ -61,6 +61,16 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
/// Tests whether can still pause after cancelling completion by reverting <see cref="IScreen.ValidForResume"/> back to true.
|
||||
/// </summary>
|
||||
[Test]
|
||||
[FlakyTest]
|
||||
/*
|
||||
* Fail rate around 0.45%
|
||||
*
|
||||
* TearDown : System.TimeoutException : "completion set by processor" timed out
|
||||
* --TearDown
|
||||
* at osu.Framework.Testing.Drawables.Steps.UntilStepButton.<>c__DisplayClass11_0.<.ctor>b__0()
|
||||
* at osu.Framework.Testing.Drawables.Steps.StepButton.PerformStep(Boolean userTriggered)
|
||||
* at osu.Framework.Testing.TestScene.runNextStep(Action onCompletion, Action`1 onError, Func`2 stopCondition)
|
||||
*/
|
||||
public void TestCanPauseAfterCancellation()
|
||||
{
|
||||
complete();
|
||||
|
@ -0,0 +1,122 @@
|
||||
// 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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Tests.Resources;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestScenePlayerLocalScoreImport : PlayerTestScene
|
||||
{
|
||||
private BeatmapManager beatmaps = null!;
|
||||
private RulesetStore rulesets = null!;
|
||||
|
||||
private BeatmapSetInfo? importedSet;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, AudioManager audio)
|
||||
{
|
||||
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
||||
Dependencies.Cache(new ScoreManager(rulesets, () => beatmaps, LocalStorage, Realm, Scheduler, API));
|
||||
Dependencies.Cache(Realm);
|
||||
}
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("import beatmap", () =>
|
||||
{
|
||||
beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely();
|
||||
importedSet = beatmaps.GetAllUsableBeatmapSets().First();
|
||||
});
|
||||
}
|
||||
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => beatmaps.GetWorkingBeatmap(importedSet?.Beatmaps.First()).Beatmap;
|
||||
|
||||
private Ruleset? customRuleset;
|
||||
|
||||
protected override Ruleset CreatePlayerRuleset() => customRuleset ?? new OsuRuleset();
|
||||
|
||||
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(false);
|
||||
|
||||
protected override bool HasCustomSteps => true;
|
||||
|
||||
protected override bool AllowFail => false;
|
||||
|
||||
[Test]
|
||||
public void TestLastPlayedUpdated()
|
||||
{
|
||||
DateTimeOffset? getLastPlayed() => Realm.Run(r => r.Find<BeatmapInfo>(Beatmap.Value.BeatmapInfo.ID)?.LastPlayed);
|
||||
|
||||
AddStep("set no custom ruleset", () => customRuleset = null);
|
||||
AddAssert("last played is null", () => getLastPlayed() == null);
|
||||
|
||||
CreateTest();
|
||||
|
||||
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
||||
AddUntilStep("wait for last played to update", () => getLastPlayed() != null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestScoreStoredLocally()
|
||||
{
|
||||
AddStep("set no custom ruleset", () => customRuleset = null);
|
||||
|
||||
CreateTest();
|
||||
|
||||
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
||||
|
||||
AddStep("seek to completion", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.Objects.Last().GetEndTime()));
|
||||
|
||||
AddUntilStep("results displayed", () => Player.GetChildScreen() is ResultsScreen);
|
||||
AddUntilStep("score in database", () => Realm.Run(r => r.Find<ScoreInfo>(Player.Score.ScoreInfo.ID) != null));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestScoreStoredLocallyCustomRuleset()
|
||||
{
|
||||
Ruleset createCustomRuleset() => new CustomRuleset();
|
||||
|
||||
AddStep("import custom ruleset", () => Realm.Write(r => r.Add(createCustomRuleset().RulesetInfo)));
|
||||
AddStep("set custom ruleset", () => customRuleset = createCustomRuleset());
|
||||
|
||||
CreateTest();
|
||||
|
||||
AddAssert("score has custom ruleset", () => Player.Score.ScoreInfo.Ruleset.Equals(customRuleset.AsNonNull().RulesetInfo));
|
||||
|
||||
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
||||
|
||||
AddStep("seek to completion", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.Objects.Last().GetEndTime()));
|
||||
|
||||
AddUntilStep("results displayed", () => Player.GetChildScreen() is ResultsScreen);
|
||||
AddUntilStep("score in database", () => Realm.Run(r => r.Find<ScoreInfo>(Player.Score.ScoreInfo.ID) != null));
|
||||
}
|
||||
|
||||
private class CustomRuleset : OsuRuleset, ILegacyRuleset
|
||||
{
|
||||
public override string Description => "custom";
|
||||
public override string ShortName => "custom";
|
||||
|
||||
int ILegacyRuleset.LegacyID => -1;
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -365,21 +365,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
ImportedScore = score;
|
||||
|
||||
// It was discovered that Score members could sometimes be half-populated.
|
||||
// In particular, the RulesetID property could be set to 0 even on non-osu! maps.
|
||||
// We want to test that the state of that property is consistent in this test.
|
||||
// EF makes this impossible.
|
||||
//
|
||||
// First off, because of the EF navigational property-explicit foreign key field duality,
|
||||
// it can happen that - for example - the Ruleset navigational property is correctly initialised to mania,
|
||||
// but the RulesetID foreign key property is not initialised and remains 0.
|
||||
// EF silently bypasses this by prioritising the Ruleset navigational property over the RulesetID foreign key one.
|
||||
//
|
||||
// Additionally, adding an entity to an EF DbSet CAUSES SIDE EFFECTS with regard to the foreign key property.
|
||||
// In the above instance, if a ScoreInfo with Ruleset = {mania} and RulesetID = 0 is attached to an EF context,
|
||||
// RulesetID WILL BE SILENTLY SET TO THE CORRECT VALUE of 3.
|
||||
//
|
||||
// For the above reasons, actual importing is disabled in this test.
|
||||
// Calling base.ImportScore is omitted as it will fail for the test method which uses a custom ruleset.
|
||||
// This can be resolved by doing something similar to what TestScenePlayerLocalScoreImport is doing,
|
||||
// but requires a bit of restructuring.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,6 +142,28 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLocallyAvailableWithoutReplay()
|
||||
{
|
||||
Live<ScoreInfo> imported = null;
|
||||
|
||||
AddStep("import score", () => imported = scoreManager.Import(getScoreInfo(false, false)));
|
||||
|
||||
AddStep("create button without replay", () =>
|
||||
{
|
||||
Child = downloadButton = new TestReplayDownloadButton(imported.Value)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
});
|
||||
|
||||
AddUntilStep("wait for load", () => downloadButton.IsLoaded);
|
||||
|
||||
AddUntilStep("state is not downloaded", () => downloadButton.State.Value == DownloadState.NotDownloaded);
|
||||
AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestScoreImportThenDelete()
|
||||
{
|
||||
@ -189,11 +211,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value);
|
||||
}
|
||||
|
||||
private ScoreInfo getScoreInfo(bool replayAvailable)
|
||||
private ScoreInfo getScoreInfo(bool replayAvailable, bool hasOnlineId = true)
|
||||
{
|
||||
return new APIScore
|
||||
{
|
||||
OnlineID = online_score_id,
|
||||
OnlineID = hasOnlineId ? online_score_id : 0,
|
||||
RulesetID = 0,
|
||||
Beatmap = CreateAPIBeatmapSet(new OsuRuleset().RulesetInfo).Beatmaps.First(),
|
||||
HasReplay = replayAvailable,
|
||||
|
@ -24,10 +24,11 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
public void TestMusicPlayAction()
|
||||
{
|
||||
AddStep("ensure playing something", () => Game.MusicController.EnsurePlayingSomething());
|
||||
AddUntilStep("music playing", () => Game.MusicController.IsPlaying);
|
||||
AddStep("toggle playback", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPlay));
|
||||
AddAssert("music paused", () => !Game.MusicController.IsPlaying && Game.MusicController.UserPauseRequested);
|
||||
AddUntilStep("music paused", () => !Game.MusicController.IsPlaying && Game.MusicController.UserPauseRequested);
|
||||
AddStep("toggle playback", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPlay));
|
||||
AddAssert("music resumed", () => Game.MusicController.IsPlaying && !Game.MusicController.UserPauseRequested);
|
||||
AddUntilStep("music resumed", () => Game.MusicController.IsPlaying && !Game.MusicController.UserPauseRequested);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestCreatedWithCorrectMode()
|
||||
{
|
||||
AddAssert("room created with correct mode", () => MultiplayerClient.APIRoom?.QueueMode.Value == Mode);
|
||||
AddUntilStep("room created with correct mode", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == Mode);
|
||||
}
|
||||
|
||||
protected void RunGameplay()
|
||||
|
@ -30,19 +30,19 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestFirstItemSelectedByDefault()
|
||||
{
|
||||
AddAssert("first item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID);
|
||||
AddUntilStep("first item selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestItemAddedToTheEndOfQueue()
|
||||
{
|
||||
addItem(() => OtherBeatmap);
|
||||
AddAssert("playlist has 2 items", () => MultiplayerClient.APIRoom?.Playlist.Count == 2);
|
||||
AddUntilStep("playlist has 2 items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 2);
|
||||
|
||||
addItem(() => InitialBeatmap);
|
||||
AddAssert("playlist has 3 items", () => MultiplayerClient.APIRoom?.Playlist.Count == 3);
|
||||
AddUntilStep("playlist has 3 items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 3);
|
||||
|
||||
AddAssert("first item still selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID);
|
||||
AddUntilStep("first item still selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -50,9 +50,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
RunGameplay();
|
||||
|
||||
AddAssert("playlist has only one item", () => MultiplayerClient.APIRoom?.Playlist.Count == 1);
|
||||
AddAssert("playlist item is expired", () => MultiplayerClient.APIRoom?.Playlist[0].Expired == true);
|
||||
AddAssert("last item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID);
|
||||
AddUntilStep("playlist has only one item", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 1);
|
||||
AddUntilStep("playlist item is expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[0].Expired == true);
|
||||
AddUntilStep("last item selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -63,13 +63,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
RunGameplay();
|
||||
|
||||
AddAssert("first item expired", () => MultiplayerClient.APIRoom?.Playlist[0].Expired == true);
|
||||
AddAssert("next item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[1].ID);
|
||||
AddUntilStep("first item expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[0].Expired == true);
|
||||
AddUntilStep("next item selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[1].ID);
|
||||
|
||||
RunGameplay();
|
||||
|
||||
AddAssert("second item expired", () => MultiplayerClient.APIRoom?.Playlist[1].Expired == true);
|
||||
AddAssert("next item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[2].ID);
|
||||
AddUntilStep("second item expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Expired == true);
|
||||
AddUntilStep("next item selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[2].ID);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -82,9 +82,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
RunGameplay();
|
||||
|
||||
AddStep("change queue mode", () => MultiplayerClient.ChangeSettings(queueMode: QueueMode.HostOnly));
|
||||
AddAssert("playlist has 3 items", () => MultiplayerClient.APIRoom?.Playlist.Count == 3);
|
||||
AddAssert("item 2 is not expired", () => MultiplayerClient.APIRoom?.Playlist[1].Expired == false);
|
||||
AddAssert("current item is the other beatmap", () => MultiplayerClient.Room?.Settings.PlaylistItemId == 2);
|
||||
AddUntilStep("playlist has 3 items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 3);
|
||||
AddUntilStep("item 2 is not expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Expired == false);
|
||||
AddUntilStep("current item is the other beatmap", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == 2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -18,6 +18,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Models;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
@ -195,12 +196,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestDownloadButtonHiddenWhenBeatmapExists()
|
||||
{
|
||||
var beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo;
|
||||
Live<BeatmapSetInfo> imported = null;
|
||||
|
||||
Debug.Assert(beatmap.BeatmapSet != null);
|
||||
AddStep("import beatmap", () =>
|
||||
{
|
||||
var beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo;
|
||||
|
||||
AddStep("import beatmap", () => imported = manager.Import(beatmap.BeatmapSet));
|
||||
Debug.Assert(beatmap.BeatmapSet != null);
|
||||
imported = manager.Import(beatmap.BeatmapSet);
|
||||
});
|
||||
|
||||
createPlaylistWithBeatmaps(() => imported.PerformRead(s => s.Beatmaps.Detach()));
|
||||
|
||||
@ -245,40 +249,35 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestExpiredItems()
|
||||
{
|
||||
AddStep("create playlist", () =>
|
||||
createPlaylist(p =>
|
||||
{
|
||||
Child = playlist = new TestPlaylist
|
||||
p.Items.Clear();
|
||||
p.Items.AddRange(new[]
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(500, 300),
|
||||
Items =
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
ID = 0,
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
Expired = true,
|
||||
RequiredMods = new[]
|
||||
{
|
||||
ID = 0,
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
Expired = true,
|
||||
RequiredMods = new[]
|
||||
{
|
||||
new APIMod(new OsuModHardRock()),
|
||||
new APIMod(new OsuModDoubleTime()),
|
||||
new APIMod(new OsuModAutoplay())
|
||||
}
|
||||
},
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
new APIMod(new OsuModHardRock()),
|
||||
new APIMod(new OsuModDoubleTime()),
|
||||
new APIMod(new OsuModAutoplay())
|
||||
}
|
||||
},
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
ID = 1,
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
RequiredMods = new[]
|
||||
{
|
||||
ID = 1,
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
RequiredMods = new[]
|
||||
{
|
||||
new APIMod(new OsuModHardRock()),
|
||||
new APIMod(new OsuModDoubleTime()),
|
||||
new APIMod(new OsuModAutoplay())
|
||||
}
|
||||
new APIMod(new OsuModHardRock()),
|
||||
new APIMod(new OsuModDoubleTime()),
|
||||
new APIMod(new OsuModAutoplay())
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
AddUntilStep("wait for items to load", () => playlist.ItemMap.Values.All(i => i.IsLoaded));
|
||||
@ -321,19 +320,44 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
=> AddAssert($"delete button {index} {(visible ? "is" : "is not")} visible",
|
||||
() => (playlist.ChildrenOfType<DrawableRoomPlaylistItem.PlaylistRemoveButton>().ElementAt(2 + index * 2).Alpha > 0) == visible);
|
||||
|
||||
private void createPlaylistWithBeatmaps(Func<IEnumerable<IBeatmapInfo>> beatmaps) => createPlaylist(p =>
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
p.Items.Clear();
|
||||
|
||||
foreach (var b in beatmaps())
|
||||
{
|
||||
p.Items.Add(new PlaylistItem(b)
|
||||
{
|
||||
ID = index++,
|
||||
OwnerID = 2,
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
RequiredMods = new[]
|
||||
{
|
||||
new APIMod(new OsuModHardRock()),
|
||||
new APIMod(new OsuModDoubleTime()),
|
||||
new APIMod(new OsuModAutoplay())
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
private void createPlaylist(Action<TestPlaylist> setupPlaylist = null)
|
||||
{
|
||||
AddStep("create playlist", () =>
|
||||
{
|
||||
Child = playlist = new TestPlaylist
|
||||
Child = new OsuContextMenuContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(500, 300)
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = playlist = new TestPlaylist
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(500, 300)
|
||||
}
|
||||
};
|
||||
|
||||
setupPlaylist?.Invoke(playlist);
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
playlist.Items.Add(new PlaylistItem(i % 2 == 1
|
||||
@ -360,39 +384,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
AddUntilStep("wait for items to load", () => playlist.ItemMap.Values.All(i => i.IsLoaded));
|
||||
}
|
||||
|
||||
private void createPlaylistWithBeatmaps(Func<IEnumerable<IBeatmapInfo>> beatmaps)
|
||||
{
|
||||
AddStep("create playlist", () =>
|
||||
{
|
||||
Child = playlist = new TestPlaylist
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(500, 300)
|
||||
};
|
||||
|
||||
int index = 0;
|
||||
|
||||
foreach (var b in beatmaps())
|
||||
{
|
||||
playlist.Items.Add(new PlaylistItem(b)
|
||||
{
|
||||
ID = index++,
|
||||
OwnerID = 2,
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
RequiredMods = new[]
|
||||
{
|
||||
new APIMod(new OsuModHardRock()),
|
||||
new APIMod(new OsuModDoubleTime()),
|
||||
new APIMod(new OsuModAutoplay())
|
||||
}
|
||||
});
|
||||
}
|
||||
setupPlaylist?.Invoke(playlist);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for items to load", () => playlist.ItemMap.Values.All(i => i.IsLoaded));
|
||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestFirstItemSelectedByDefault()
|
||||
{
|
||||
AddAssert("first item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID);
|
||||
AddUntilStep("first item selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
selectNewItem(() => InitialBeatmap);
|
||||
|
||||
AddAssert("playlist item still selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID);
|
||||
AddUntilStep("playlist item still selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
selectNewItem(() => OtherBeatmap);
|
||||
|
||||
AddAssert("playlist item still selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[0].ID);
|
||||
AddUntilStep("playlist item still selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[0].ID);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -48,10 +48,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
RunGameplay();
|
||||
|
||||
AddAssert("playlist contains two items", () => MultiplayerClient.APIRoom?.Playlist.Count == 2);
|
||||
AddAssert("first playlist item expired", () => MultiplayerClient.APIRoom?.Playlist[0].Expired == true);
|
||||
AddAssert("second playlist item not expired", () => MultiplayerClient.APIRoom?.Playlist[1].Expired == false);
|
||||
AddAssert("second playlist item selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == MultiplayerClient.APIRoom?.Playlist[1].ID);
|
||||
AddUntilStep("playlist contains two items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 2);
|
||||
AddUntilStep("first playlist item expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[0].Expired == true);
|
||||
AddUntilStep("second playlist item not expired", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Expired == false);
|
||||
AddUntilStep("second playlist item selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == MultiplayerClient.ClientAPIRoom?.Playlist[1].ID);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -60,12 +60,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
RunGameplay();
|
||||
|
||||
IBeatmapInfo firstBeatmap = null;
|
||||
AddStep("get first playlist item beatmap", () => firstBeatmap = MultiplayerClient.APIRoom?.Playlist[0].Beatmap);
|
||||
AddStep("get first playlist item beatmap", () => firstBeatmap = MultiplayerClient.ServerAPIRoom?.Playlist[0].Beatmap);
|
||||
|
||||
selectNewItem(() => OtherBeatmap);
|
||||
|
||||
AddAssert("first playlist item hasn't changed", () => MultiplayerClient.APIRoom?.Playlist[0].Beatmap == firstBeatmap);
|
||||
AddAssert("second playlist item changed", () => MultiplayerClient.APIRoom?.Playlist[1].Beatmap != firstBeatmap);
|
||||
AddUntilStep("first playlist item hasn't changed", () => MultiplayerClient.ServerAPIRoom?.Playlist[0].Beatmap == firstBeatmap);
|
||||
AddUntilStep("second playlist item changed", () => MultiplayerClient.ClientAPIRoom?.Playlist[1].Beatmap != firstBeatmap);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
QueueMode = QueueMode.AllPlayers
|
||||
}).WaitSafely());
|
||||
|
||||
AddUntilStep("api room updated", () => MultiplayerClient.APIRoom?.QueueMode.Value == QueueMode.AllPlayers);
|
||||
AddUntilStep("api room updated", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -84,7 +84,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
addItem(() => OtherBeatmap);
|
||||
|
||||
AddAssert("playlist contains two items", () => MultiplayerClient.APIRoom?.Playlist.Count == 2);
|
||||
AddUntilStep("playlist contains two items", () => MultiplayerClient.ClientAPIRoom?.Playlist.Count == 2);
|
||||
}
|
||||
|
||||
private void selectNewItem(Func<BeatmapInfo> beatmap)
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -33,20 +31,21 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public class TestSceneMultiSpectatorScreen : MultiplayerTestScene
|
||||
{
|
||||
[Resolved]
|
||||
private OsuGameBase game { get; set; }
|
||||
private OsuGameBase game { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; }
|
||||
private OsuConfigManager config { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private BeatmapManager beatmapManager { get; set; }
|
||||
private BeatmapManager beatmapManager { get; set; } = null!;
|
||||
|
||||
private MultiSpectatorScreen spectatorScreen;
|
||||
private MultiSpectatorScreen spectatorScreen = null!;
|
||||
|
||||
private readonly List<MultiplayerRoomUser> playingUsers = new List<MultiplayerRoomUser>();
|
||||
|
||||
private BeatmapSetInfo importedSet;
|
||||
private BeatmapInfo importedBeatmap;
|
||||
private BeatmapSetInfo importedSet = null!;
|
||||
private BeatmapInfo importedBeatmap = null!;
|
||||
|
||||
private int importedBeatmapId;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -340,7 +339,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
sendFrames(getPlayerIds(count), 300);
|
||||
}
|
||||
|
||||
Player player = null;
|
||||
Player? player = null;
|
||||
|
||||
AddStep($"get {PLAYER_1_ID} player instance", () => player = getInstance(PLAYER_1_ID).ChildrenOfType<Player>().Single());
|
||||
|
||||
@ -369,7 +368,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
b.Storyboard.GetLayer("Background").Add(sprite);
|
||||
});
|
||||
|
||||
private void testLeadIn(Action<WorkingBeatmap> applyToBeatmap = null)
|
||||
private void testLeadIn(Action<WorkingBeatmap>? applyToBeatmap = null)
|
||||
{
|
||||
start(PLAYER_1_ID);
|
||||
|
||||
@ -387,7 +386,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
assertRunning(PLAYER_1_ID);
|
||||
}
|
||||
|
||||
private void loadSpectateScreen(bool waitForPlayerLoad = true, Action<WorkingBeatmap> applyToBeatmap = null)
|
||||
private void loadSpectateScreen(bool waitForPlayerLoad = true, Action<WorkingBeatmap>? applyToBeatmap = null)
|
||||
{
|
||||
AddStep("load screen", () =>
|
||||
{
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
@ -51,17 +49,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiplayer : ScreenTestScene
|
||||
{
|
||||
private BeatmapManager beatmaps;
|
||||
private RulesetStore rulesets;
|
||||
private BeatmapSetInfo importedSet;
|
||||
private BeatmapManager beatmaps = null!;
|
||||
private RulesetStore rulesets = null!;
|
||||
private BeatmapSetInfo importedSet = null!;
|
||||
|
||||
private TestMultiplayerComponents multiplayerComponents;
|
||||
private TestMultiplayerComponents multiplayerComponents = null!;
|
||||
|
||||
private TestMultiplayerClient multiplayerClient => multiplayerComponents.MultiplayerClient;
|
||||
private TestMultiplayerRoomManager roomManager => multiplayerComponents.RoomManager;
|
||||
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; }
|
||||
private OsuConfigManager config { get; set; } = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, AudioManager audio)
|
||||
@ -116,25 +114,25 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
// all ready
|
||||
AddUntilStep("all players ready", () =>
|
||||
{
|
||||
var nextUnready = multiplayerClient.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle);
|
||||
var nextUnready = multiplayerClient.ClientRoom?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle);
|
||||
if (nextUnready != null)
|
||||
multiplayerClient.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready);
|
||||
|
||||
return multiplayerClient.Room?.Users.All(u => u.State == MultiplayerUserState.Ready) == true;
|
||||
return multiplayerClient.ClientRoom?.Users.All(u => u.State == MultiplayerUserState.Ready) == true;
|
||||
});
|
||||
|
||||
AddStep("unready all players at once", () =>
|
||||
{
|
||||
Debug.Assert(multiplayerClient.Room != null);
|
||||
Debug.Assert(multiplayerClient.ServerRoom != null);
|
||||
|
||||
foreach (var u in multiplayerClient.Room.Users) multiplayerClient.ChangeUserState(u.UserID, MultiplayerUserState.Idle);
|
||||
foreach (var u in multiplayerClient.ServerRoom.Users) multiplayerClient.ChangeUserState(u.UserID, MultiplayerUserState.Idle);
|
||||
});
|
||||
|
||||
AddStep("ready all players at once", () =>
|
||||
{
|
||||
Debug.Assert(multiplayerClient.Room != null);
|
||||
Debug.Assert(multiplayerClient.ServerRoom != null);
|
||||
|
||||
foreach (var u in multiplayerClient.Room.Users) multiplayerClient.ChangeUserState(u.UserID, MultiplayerUserState.Ready);
|
||||
foreach (var u in multiplayerClient.ServerRoom.Users) multiplayerClient.ChangeUserState(u.UserID, MultiplayerUserState.Ready);
|
||||
});
|
||||
}
|
||||
|
||||
@ -146,7 +144,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private void removeLastUser()
|
||||
{
|
||||
APIUser lastUser = multiplayerClient.Room?.Users.Last().User;
|
||||
APIUser? lastUser = multiplayerClient.ServerRoom?.Users.Last().User;
|
||||
|
||||
if (lastUser == null || lastUser == multiplayerClient.LocalUser?.User)
|
||||
return;
|
||||
@ -156,7 +154,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private void kickLastUser()
|
||||
{
|
||||
APIUser lastUser = multiplayerClient.Room?.Users.Last().User;
|
||||
APIUser? lastUser = multiplayerClient.ServerRoom?.Users.Last().User;
|
||||
|
||||
if (lastUser == null || lastUser == multiplayerClient.LocalUser?.User)
|
||||
return;
|
||||
@ -166,14 +164,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private void markNextPlayerReady()
|
||||
{
|
||||
var nextUnready = multiplayerClient.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle);
|
||||
var nextUnready = multiplayerClient.ServerRoom?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Idle);
|
||||
if (nextUnready != null)
|
||||
multiplayerClient.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Ready);
|
||||
}
|
||||
|
||||
private void markNextPlayerIdle()
|
||||
{
|
||||
var nextUnready = multiplayerClient.Room?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Ready);
|
||||
var nextUnready = multiplayerClient.ServerRoom?.Users.FirstOrDefault(c => c.State == MultiplayerUserState.Ready);
|
||||
if (nextUnready != null)
|
||||
multiplayerClient.ChangeUserState(nextUnready.UserID, MultiplayerUserState.Idle);
|
||||
}
|
||||
@ -243,8 +241,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
});
|
||||
|
||||
AddAssert("Check participant count correct", () => multiplayerClient.APIRoom?.ParticipantCount.Value == 1);
|
||||
AddAssert("Check participant list contains user", () => multiplayerClient.APIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1);
|
||||
AddUntilStep("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount.Value == 1);
|
||||
AddUntilStep("Check participant list contains user", () => multiplayerClient.ClientAPIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -303,8 +301,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("wait for room open", () => this.ChildrenOfType<MultiplayerMatchSubScreen>().FirstOrDefault()?.IsLoaded == true);
|
||||
AddUntilStep("wait for join", () => multiplayerClient.RoomJoined);
|
||||
|
||||
AddAssert("Check participant count correct", () => multiplayerClient.APIRoom?.ParticipantCount.Value == 1);
|
||||
AddAssert("Check participant list contains user", () => multiplayerClient.APIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1);
|
||||
AddUntilStep("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount.Value == 1);
|
||||
AddUntilStep("Check participant list contains user", () => multiplayerClient.ClientAPIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -323,7 +321,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
});
|
||||
|
||||
AddAssert("room has password", () => multiplayerClient.APIRoom?.Password.Value == "password");
|
||||
AddUntilStep("room has password", () => multiplayerClient.ClientAPIRoom?.Password.Value == "password");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -351,7 +349,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
AddStep("join room", () => InputManager.Key(Key.Enter));
|
||||
|
||||
DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
|
||||
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
|
||||
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
|
||||
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "password");
|
||||
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType<OsuButton>().First().TriggerClick());
|
||||
@ -377,7 +375,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
});
|
||||
|
||||
AddStep("change password", () => multiplayerClient.ChangeSettings(password: "password2"));
|
||||
AddUntilStep("local password changed", () => multiplayerClient.APIRoom?.Password.Value == "password2");
|
||||
AddUntilStep("local password changed", () => multiplayerClient.ClientAPIRoom?.Password.Value == "password2");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -421,22 +419,22 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("Enter song select", () =>
|
||||
{
|
||||
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen;
|
||||
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.Room?.Settings.PlaylistItemId);
|
||||
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.ClientRoom?.Settings.PlaylistItemId);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for song select", () => this.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault()?.BeatmapSetsLoaded == true);
|
||||
|
||||
AddAssert("Beatmap matches current item", () => Beatmap.Value.BeatmapInfo.OnlineID == multiplayerClient.Room?.Playlist.First().BeatmapID);
|
||||
AddUntilStep("Beatmap matches current item", () => Beatmap.Value.BeatmapInfo.OnlineID == multiplayerClient.ClientRoom?.Playlist.First().BeatmapID);
|
||||
|
||||
AddStep("Select next beatmap", () => InputManager.Key(Key.Down));
|
||||
|
||||
AddUntilStep("Beatmap doesn't match current item", () => Beatmap.Value.BeatmapInfo.OnlineID != multiplayerClient.Room?.Playlist.First().BeatmapID);
|
||||
AddUntilStep("Beatmap doesn't match current item", () => Beatmap.Value.BeatmapInfo.OnlineID != multiplayerClient.ClientRoom?.Playlist.First().BeatmapID);
|
||||
|
||||
AddStep("start match externally", () => multiplayerClient.StartMatch().WaitSafely());
|
||||
|
||||
AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is Player);
|
||||
|
||||
AddAssert("Beatmap matches current item", () => Beatmap.Value.BeatmapInfo.OnlineID == multiplayerClient.Room?.Playlist.First().BeatmapID);
|
||||
AddUntilStep("Beatmap matches current item", () => Beatmap.Value.BeatmapInfo.OnlineID == multiplayerClient.ClientRoom?.Playlist.First().BeatmapID);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -459,22 +457,22 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("Enter song select", () =>
|
||||
{
|
||||
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen;
|
||||
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.Room?.Settings.PlaylistItemId);
|
||||
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.ClientRoom?.Settings.PlaylistItemId);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for song select", () => this.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault()?.BeatmapSetsLoaded == true);
|
||||
|
||||
AddAssert("Ruleset matches current item", () => Ruleset.Value.OnlineID == multiplayerClient.Room?.Playlist.First().RulesetID);
|
||||
AddUntilStep("Ruleset matches current item", () => Ruleset.Value.OnlineID == multiplayerClient.ClientRoom?.Playlist.First().RulesetID);
|
||||
|
||||
AddStep("Switch ruleset", () => ((MultiplayerMatchSongSelect)multiplayerComponents.MultiplayerScreen.CurrentSubScreen).Ruleset.Value = new CatchRuleset().RulesetInfo);
|
||||
|
||||
AddUntilStep("Ruleset doesn't match current item", () => Ruleset.Value.OnlineID != multiplayerClient.Room?.Playlist.First().RulesetID);
|
||||
AddUntilStep("Ruleset doesn't match current item", () => Ruleset.Value.OnlineID != multiplayerClient.ClientRoom?.Playlist.First().RulesetID);
|
||||
|
||||
AddStep("start match externally", () => multiplayerClient.StartMatch().WaitSafely());
|
||||
|
||||
AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is Player);
|
||||
|
||||
AddAssert("Ruleset matches current item", () => Ruleset.Value.OnlineID == multiplayerClient.Room?.Playlist.First().RulesetID);
|
||||
AddUntilStep("Ruleset matches current item", () => Ruleset.Value.OnlineID == multiplayerClient.ClientRoom?.Playlist.First().RulesetID);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -497,25 +495,25 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("Enter song select", () =>
|
||||
{
|
||||
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen;
|
||||
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.Room?.Settings.PlaylistItemId);
|
||||
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.ClientRoom?.Settings.PlaylistItemId);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for song select", () => this.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault()?.BeatmapSetsLoaded == true);
|
||||
|
||||
AddAssert("Mods match current item",
|
||||
() => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym)));
|
||||
AddUntilStep("Mods match current item",
|
||||
() => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.ClientRoom.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym)));
|
||||
|
||||
AddStep("Switch required mods", () => ((MultiplayerMatchSongSelect)multiplayerComponents.MultiplayerScreen.CurrentSubScreen).Mods.Value = new Mod[] { new OsuModDoubleTime() });
|
||||
|
||||
AddAssert("Mods don't match current item",
|
||||
() => !SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym)));
|
||||
AddUntilStep("Mods don't match current item",
|
||||
() => !SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.ClientRoom.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym)));
|
||||
|
||||
AddStep("start match externally", () => multiplayerClient.StartMatch().WaitSafely());
|
||||
|
||||
AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is Player);
|
||||
|
||||
AddAssert("Mods match current item",
|
||||
() => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym)));
|
||||
AddUntilStep("Mods match current item",
|
||||
() => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(multiplayerClient.ClientRoom.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -678,7 +676,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestGameplayExitFlow()
|
||||
{
|
||||
Bindable<double> holdDelay = null;
|
||||
Bindable<double>? holdDelay = null;
|
||||
|
||||
AddStep("Set hold delay to zero", () =>
|
||||
{
|
||||
@ -709,7 +707,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("wait for lounge", () => multiplayerComponents.CurrentScreen is Screens.OnlinePlay.Multiplayer.Multiplayer);
|
||||
|
||||
AddStep("stop holding", () => InputManager.ReleaseKey(Key.Escape));
|
||||
AddStep("set hold delay to default", () => holdDelay.SetDefault());
|
||||
AddStep("set hold delay to default", () => holdDelay?.SetDefault());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -890,7 +888,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
})).WaitSafely());
|
||||
|
||||
AddUntilStep("item arrived in playlist", () => multiplayerClient.Room?.Playlist.Count == 2);
|
||||
AddUntilStep("item arrived in playlist", () => multiplayerClient.ClientRoom?.Playlist.Count == 2);
|
||||
|
||||
AddStep("exit gameplay as initial user", () => multiplayerComponents.MultiplayerScreen.MakeCurrent());
|
||||
AddUntilStep("queue contains item", () => this.ChildrenOfType<MultiplayerQueueList>().Single().Items.Single().ID == 2);
|
||||
@ -921,10 +919,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
})).WaitSafely());
|
||||
|
||||
AddUntilStep("item arrived in playlist", () => multiplayerClient.Room?.Playlist.Count == 2);
|
||||
AddUntilStep("item arrived in playlist", () => multiplayerClient.ClientRoom?.Playlist.Count == 2);
|
||||
|
||||
AddStep("delete item as other user", () => multiplayerClient.RemoveUserPlaylistItem(1234, 2).WaitSafely());
|
||||
AddUntilStep("item removed from playlist", () => multiplayerClient.Room?.Playlist.Count == 1);
|
||||
AddUntilStep("item removed from playlist", () => multiplayerClient.ClientRoom?.Playlist.Count == 1);
|
||||
|
||||
AddStep("exit gameplay as initial user", () => multiplayerComponents.MultiplayerScreen.MakeCurrent());
|
||||
AddUntilStep("queue is empty", () => this.ChildrenOfType<MultiplayerQueueList>().Single().Items.Count == 0);
|
||||
@ -957,7 +955,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
runGameplay();
|
||||
|
||||
AddStep("exit gameplay for other user", () => multiplayerClient.ChangeUserState(1234, MultiplayerUserState.Idle));
|
||||
AddUntilStep("wait for room to be idle", () => multiplayerClient.Room?.State == MultiplayerRoomState.Open);
|
||||
AddUntilStep("wait for room to be idle", () => multiplayerClient.ClientRoom?.State == MultiplayerRoomState.Open);
|
||||
|
||||
runGameplay();
|
||||
|
||||
@ -969,9 +967,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
multiplayerClient.StartMatch().WaitSafely();
|
||||
});
|
||||
|
||||
AddUntilStep("wait for loading", () => multiplayerClient.Room?.State == MultiplayerRoomState.WaitingForLoad);
|
||||
AddUntilStep("wait for loading", () => multiplayerClient.ClientRoom?.State == MultiplayerRoomState.WaitingForLoad);
|
||||
AddStep("set player loaded", () => multiplayerClient.ChangeUserState(1234, MultiplayerUserState.Loaded));
|
||||
AddUntilStep("wait for gameplay to start", () => multiplayerClient.Room?.State == MultiplayerRoomState.Playing);
|
||||
AddUntilStep("wait for gameplay to start", () => multiplayerClient.ClientRoom?.State == MultiplayerRoomState.Playing);
|
||||
AddUntilStep("wait for local user to enter spectator", () => multiplayerComponents.CurrentScreen is MultiSpectatorScreen);
|
||||
}
|
||||
}
|
||||
@ -992,11 +990,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("wait for ready button to be enabled", () => readyButton.Enabled.Value);
|
||||
|
||||
MultiplayerUserState lastState = MultiplayerUserState.Idle;
|
||||
MultiplayerRoomUser user = null;
|
||||
MultiplayerRoomUser? user = null;
|
||||
|
||||
AddStep("click ready button", () =>
|
||||
{
|
||||
user = playingUserId == null ? multiplayerClient.LocalUser : multiplayerClient.Room?.Users.Single(u => u.UserID == playingUserId);
|
||||
user = playingUserId == null ? multiplayerClient.LocalUser : multiplayerClient.ServerRoom?.Users.Single(u => u.UserID == playingUserId);
|
||||
lastState = user?.State ?? MultiplayerUserState.Idle;
|
||||
|
||||
InputManager.MoveMouseTo(readyButton);
|
||||
|
@ -74,6 +74,25 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest]
|
||||
/*
|
||||
* Fail rate around 1.5%
|
||||
*
|
||||
* TearDown : System.AggregateException : One or more errors occurred. (Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index'))
|
||||
----> System.ArgumentOutOfRangeException : Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
|
||||
* --TearDown
|
||||
* at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
|
||||
* at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
|
||||
* at osu.Framework.Extensions.TaskExtensions.WaitSafely(Task task)
|
||||
* at osu.Framework.Testing.TestScene.checkForErrors()
|
||||
* at osu.Framework.Testing.TestScene.RunTestsFromNUnit()
|
||||
*--ArgumentOutOfRangeException
|
||||
* at osu.Framework.Bindables.BindableList`1.removeAt(Int32 index, BindableList`1 caller)
|
||||
* at osu.Framework.Bindables.BindableList`1.removeAt(Int32 index, BindableList`1 caller)
|
||||
* at osu.Framework.Bindables.BindableList`1.removeAt(Int32 index, BindableList`1 caller)
|
||||
* at osu.Game.Online.Multiplayer.MultiplayerClient.<>c__DisplayClass106_0.<PlaylistItemChanged>b__0() in C:\BuildAgent\work\ecd860037212ac52\osu.Game\Online\Multiplayer\MultiplayerClient .cs:line 702
|
||||
* at osu.Framework.Threading.ScheduledDelegate.RunTaskInternal()
|
||||
*/
|
||||
public void TestCreatedRoom()
|
||||
{
|
||||
AddStep("add playlist item", () =>
|
||||
@ -90,6 +109,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestTaikoOnlyMod()
|
||||
{
|
||||
AddStep("add playlist item", () =>
|
||||
@ -110,6 +130,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestSettingValidity()
|
||||
{
|
||||
AddAssert("create button not enabled", () => !this.ChildrenOfType<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>().Single().Enabled.Value);
|
||||
@ -126,6 +147,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestStartMatchWhileSpectating()
|
||||
{
|
||||
AddStep("set playlist", () =>
|
||||
@ -152,10 +174,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
ClickButtonWhenEnabled<MultiplayerReadyButton>();
|
||||
|
||||
AddUntilStep("match started", () => MultiplayerClient.Room?.State == MultiplayerRoomState.WaitingForLoad);
|
||||
AddUntilStep("match started", () => MultiplayerClient.ClientRoom?.State == MultiplayerRoomState.WaitingForLoad);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestFreeModSelectionHasAllowedMods()
|
||||
{
|
||||
AddStep("add playlist item with allowed mod", () =>
|
||||
@ -182,6 +205,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestModSelectKeyWithAllowedMods()
|
||||
{
|
||||
AddStep("add playlist item with allowed mod", () =>
|
||||
@ -203,6 +227,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestModSelectKeyWithNoAllowedMods()
|
||||
{
|
||||
AddStep("add playlist item with no allowed mods", () =>
|
||||
@ -223,6 +248,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestNextPlaylistItemSelectedAfterCompletion()
|
||||
{
|
||||
AddStep("add two playlist items", () =>
|
||||
@ -253,7 +279,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddUntilStep("last playlist item selected", () =>
|
||||
{
|
||||
var lastItem = this.ChildrenOfType<DrawableRoomPlaylistItem>().Single(p => p.Item.ID == MultiplayerClient.APIRoom?.Playlist.Last().ID);
|
||||
var lastItem = this.ChildrenOfType<DrawableRoomPlaylistItem>().Single(p => p.Item.ID == MultiplayerClient.ServerAPIRoom?.Playlist.Last().ID);
|
||||
return lastItem.IsSelectedItem;
|
||||
});
|
||||
}
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
@ -11,6 +9,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
@ -53,20 +52,20 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddAssert("one unique panel", () => this.ChildrenOfType<ParticipantPanel>().Select(p => p.User).Distinct().Count() == 1);
|
||||
|
||||
AddStep("add non-resolvable user", () => MultiplayerClient.TestAddUnresolvedUser());
|
||||
AddAssert("null user added", () => MultiplayerClient.Room.AsNonNull().Users.Count(u => u.User == null) == 1);
|
||||
AddUntilStep("null user added", () => MultiplayerClient.ClientRoom.AsNonNull().Users.Count(u => u.User == null) == 1);
|
||||
|
||||
AddUntilStep("two unique panels", () => this.ChildrenOfType<ParticipantPanel>().Select(p => p.User).Distinct().Count() == 2);
|
||||
|
||||
AddStep("kick null user", () => this.ChildrenOfType<ParticipantPanel>().Single(p => p.User.User == null)
|
||||
.ChildrenOfType<ParticipantPanel.KickButton>().Single().TriggerClick());
|
||||
|
||||
AddAssert("null user kicked", () => MultiplayerClient.Room.AsNonNull().Users.Count == 1);
|
||||
AddUntilStep("null user kicked", () => MultiplayerClient.ClientRoom.AsNonNull().Users.Count == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRemoveUser()
|
||||
{
|
||||
APIUser secondUser = null;
|
||||
APIUser? secondUser = null;
|
||||
|
||||
AddStep("add a user", () =>
|
||||
{
|
||||
@ -80,7 +79,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("remove host", () => MultiplayerClient.RemoveUser(API.LocalUser.Value));
|
||||
|
||||
AddAssert("single panel is for second user", () => this.ChildrenOfType<ParticipantPanel>().Single().User.User == secondUser);
|
||||
AddAssert("single panel is for second user", () => this.ChildrenOfType<ParticipantPanel>().Single().User.UserID == secondUser?.Id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -217,7 +216,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("kick second user", () => this.ChildrenOfType<ParticipantPanel.KickButton>().Single(d => d.IsPresent).TriggerClick());
|
||||
|
||||
AddAssert("second user kicked", () => MultiplayerClient.Room?.Users.Single().UserID == API.LocalUser.Value.Id);
|
||||
AddUntilStep("second user kicked", () => MultiplayerClient.ClientRoom?.Users.Single().UserID == API.LocalUser.Value.Id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -368,17 +367,21 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private void createNewParticipantsList()
|
||||
{
|
||||
ParticipantsList participantsList = null;
|
||||
ParticipantsList? participantsList = null;
|
||||
|
||||
AddStep("create new list", () => Child = participantsList = new ParticipantsList
|
||||
AddStep("create new list", () => Child = new OsuContextMenuContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Size = new Vector2(380, 0.7f)
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = participantsList = new ParticipantsList
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Size = new Vector2(380, 0.7f)
|
||||
}
|
||||
});
|
||||
|
||||
AddUntilStep("wait for list to load", () => participantsList.IsLoaded);
|
||||
AddUntilStep("wait for list to load", () => participantsList?.IsLoaded == true);
|
||||
}
|
||||
|
||||
private void checkProgressBarVisibility(bool visible) =>
|
||||
|
@ -30,10 +30,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("initialise gameplay", () =>
|
||||
{
|
||||
Stack.Push(player = new MultiplayerPlayer(MultiplayerClient.APIRoom, new PlaylistItem(Beatmap.Value.BeatmapInfo)
|
||||
Stack.Push(player = new MultiplayerPlayer(MultiplayerClient.ServerAPIRoom, new PlaylistItem(Beatmap.Value.BeatmapInfo)
|
||||
{
|
||||
RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID,
|
||||
}, MultiplayerClient.Room?.Users.ToArray()));
|
||||
}, MultiplayerClient.ServerRoom?.Users.ToArray()));
|
||||
});
|
||||
|
||||
AddUntilStep("wait for player to be current", () => player.IsCurrentScreen() && player.IsLoaded);
|
||||
|
@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(500, 300),
|
||||
Items = { BindTarget = MultiplayerClient.APIRoom!.Playlist }
|
||||
Items = { BindTarget = MultiplayerClient.ClientAPIRoom!.Playlist }
|
||||
};
|
||||
});
|
||||
|
||||
@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public void TestDeleteButtonAlwaysVisibleForHost()
|
||||
{
|
||||
AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
|
||||
AddUntilStep("wait for queue mode change", () => MultiplayerClient.APIRoom?.QueueMode.Value == QueueMode.AllPlayers);
|
||||
AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers);
|
||||
|
||||
addPlaylistItem(() => API.LocalUser.Value.OnlineID);
|
||||
assertDeleteButtonVisibility(1, true);
|
||||
@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public void TestDeleteButtonOnlyVisibleForItemOwnerIfNotHost()
|
||||
{
|
||||
AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
|
||||
AddUntilStep("wait for queue mode change", () => MultiplayerClient.APIRoom?.QueueMode.Value == QueueMode.AllPlayers);
|
||||
AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers);
|
||||
|
||||
AddStep("join other user", () => MultiplayerClient.AddUser(new APIUser { Id = 1234 }));
|
||||
AddStep("set other user as host", () => MultiplayerClient.TransferHost(1234));
|
||||
@ -101,7 +101,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public void TestCurrentItemDoesNotHaveDeleteButton()
|
||||
{
|
||||
AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
|
||||
AddUntilStep("wait for queue mode change", () => MultiplayerClient.APIRoom?.QueueMode.Value == QueueMode.AllPlayers);
|
||||
AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers);
|
||||
|
||||
addPlaylistItem(() => API.LocalUser.Value.OnlineID);
|
||||
|
||||
@ -109,7 +109,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
assertDeleteButtonVisibility(1, true);
|
||||
|
||||
AddStep("finish current item", () => MultiplayerClient.FinishCurrentItem().WaitSafely());
|
||||
AddUntilStep("wait for next item to be selected", () => MultiplayerClient.Room?.Settings.PlaylistItemId == 2);
|
||||
AddUntilStep("wait for next item to be selected", () => MultiplayerClient.ClientRoom?.Settings.PlaylistItemId == 2);
|
||||
AddUntilStep("wait for two items in playlist", () => playlist.ChildrenOfType<DrawableRoomPlaylistItem>().Count() == 2);
|
||||
|
||||
assertDeleteButtonVisibility(0, false);
|
||||
|
@ -99,10 +99,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public void TestToggleWhenIdle(MultiplayerUserState initialState)
|
||||
{
|
||||
ClickButtonWhenEnabled<MultiplayerSpectateButton>();
|
||||
AddUntilStep("user is spectating", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Spectating);
|
||||
AddUntilStep("user is spectating", () => MultiplayerClient.ClientRoom?.Users[0].State == MultiplayerUserState.Spectating);
|
||||
|
||||
ClickButtonWhenEnabled<MultiplayerSpectateButton>();
|
||||
AddUntilStep("user is idle", () => MultiplayerClient.Room?.Users[0].State == MultiplayerUserState.Idle);
|
||||
AddUntilStep("user is idle", () => MultiplayerClient.ClientRoom?.Users[0].State == MultiplayerUserState.Idle);
|
||||
}
|
||||
|
||||
[TestCase(MultiplayerRoomState.Closed)]
|
||||
|
@ -76,8 +76,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
});
|
||||
|
||||
AddUntilStep("room type is team vs", () => multiplayerClient.Room?.Settings.MatchType == MatchType.TeamVersus);
|
||||
AddAssert("user state arrived", () => multiplayerClient.Room?.Users.FirstOrDefault()?.MatchState is TeamVersusUserState);
|
||||
AddUntilStep("room type is team vs", () => multiplayerClient.ClientRoom?.Settings.MatchType == MatchType.TeamVersus);
|
||||
AddUntilStep("user state arrived", () => multiplayerClient.ClientRoom?.Users.FirstOrDefault()?.MatchState is TeamVersusUserState);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
});
|
||||
|
||||
AddAssert("user on team 0", () => (multiplayerClient.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0);
|
||||
AddUntilStep("user on team 0", () => (multiplayerClient.ClientRoom?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0);
|
||||
AddStep("add another user", () => multiplayerClient.AddUser(new APIUser { Username = "otheruser", Id = 44 }));
|
||||
|
||||
AddStep("press own button", () =>
|
||||
@ -104,17 +104,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
InputManager.MoveMouseTo(multiplayerComponents.ChildrenOfType<TeamDisplay>().First());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddAssert("user on team 1", () => (multiplayerClient.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 1);
|
||||
AddUntilStep("user on team 1", () => (multiplayerClient.ClientRoom?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 1);
|
||||
|
||||
AddStep("press own button again", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("user on team 0", () => (multiplayerClient.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0);
|
||||
AddUntilStep("user on team 0", () => (multiplayerClient.ClientRoom?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0);
|
||||
|
||||
AddStep("press other user's button", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(multiplayerComponents.ChildrenOfType<TeamDisplay>().ElementAt(1));
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddAssert("user still on team 0", () => (multiplayerClient.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0);
|
||||
AddUntilStep("user still on team 0", () => (multiplayerClient.ClientRoom?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -133,14 +133,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
});
|
||||
|
||||
AddUntilStep("match type head to head", () => multiplayerClient.APIRoom?.Type.Value == MatchType.HeadToHead);
|
||||
AddUntilStep("match type head to head", () => multiplayerClient.ClientAPIRoom?.Type.Value == MatchType.HeadToHead);
|
||||
|
||||
AddStep("change match type", () => multiplayerClient.ChangeSettings(new MultiplayerRoomSettings
|
||||
{
|
||||
MatchType = MatchType.TeamVersus
|
||||
}).WaitSafely());
|
||||
|
||||
AddUntilStep("api room updated to team versus", () => multiplayerClient.APIRoom?.Type.Value == MatchType.TeamVersus);
|
||||
AddUntilStep("api room updated to team versus", () => multiplayerClient.ClientAPIRoom?.Type.Value == MatchType.TeamVersus);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -158,13 +158,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
});
|
||||
|
||||
AddUntilStep("room type is head to head", () => multiplayerClient.Room?.Settings.MatchType == MatchType.HeadToHead);
|
||||
AddUntilStep("room type is head to head", () => multiplayerClient.ClientRoom?.Settings.MatchType == MatchType.HeadToHead);
|
||||
|
||||
AddUntilStep("team displays are not displaying teams", () => multiplayerComponents.ChildrenOfType<TeamDisplay>().All(d => d.DisplayedTeam == null));
|
||||
|
||||
AddStep("change to team vs", () => multiplayerClient.ChangeSettings(matchType: MatchType.TeamVersus));
|
||||
|
||||
AddUntilStep("room type is team vs", () => multiplayerClient.Room?.Settings.MatchType == MatchType.TeamVersus);
|
||||
AddUntilStep("room type is team vs", () => multiplayerClient.ClientRoom?.Settings.MatchType == MatchType.TeamVersus);
|
||||
|
||||
AddUntilStep("team displays are displaying teams", () => multiplayerComponents.ChildrenOfType<TeamDisplay>().All(d => d.DisplayedTeam != null));
|
||||
}
|
||||
|
@ -576,12 +576,13 @@ namespace osu.Game.Tests.Visual.Online
|
||||
|
||||
private Channel createAnnounceChannel()
|
||||
{
|
||||
int id = RNG.Next(0, DummyAPIAccess.DUMMY_USER_ID - 1);
|
||||
const int announce_channel_id = 133337;
|
||||
|
||||
return new Channel
|
||||
{
|
||||
Name = $"Announce {id}",
|
||||
Name = $"Announce {announce_channel_id}",
|
||||
Type = ChannelType.Announce,
|
||||
Id = id,
|
||||
Id = announce_channel_id,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Game.Overlays.BeatmapSet;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
@ -29,7 +27,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
LeaderboardModSelector modSelector;
|
||||
FillFlowContainer<SpriteText> selectedMods;
|
||||
|
||||
var ruleset = new Bindable<IRulesetInfo>();
|
||||
var ruleset = new Bindable<IRulesetInfo?>();
|
||||
|
||||
Add(selectedMods = new FillFlowContainer<SpriteText>
|
||||
{
|
||||
|
@ -18,6 +18,7 @@ using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.BeatmapSet.Scores;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
using osuTK.Graphics;
|
||||
@ -146,12 +147,12 @@ namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
var scores = new APIScoresCollection
|
||||
{
|
||||
Scores = new List<APIScore>
|
||||
Scores = new List<SoloScoreInfo>
|
||||
{
|
||||
new APIScore
|
||||
new SoloScoreInfo
|
||||
{
|
||||
Date = DateTimeOffset.Now,
|
||||
OnlineID = onlineID++,
|
||||
EndedAt = DateTimeOffset.Now,
|
||||
ID = onlineID++,
|
||||
User = new APIUser
|
||||
{
|
||||
Id = 6602580,
|
||||
@ -175,10 +176,10 @@ namespace osu.Game.Tests.Visual.Online
|
||||
TotalScore = 1234567890,
|
||||
Accuracy = 1,
|
||||
},
|
||||
new APIScore
|
||||
new SoloScoreInfo
|
||||
{
|
||||
Date = DateTimeOffset.Now,
|
||||
OnlineID = onlineID++,
|
||||
EndedAt = DateTimeOffset.Now,
|
||||
ID = onlineID++,
|
||||
User = new APIUser
|
||||
{
|
||||
Id = 4608074,
|
||||
@ -201,10 +202,10 @@ namespace osu.Game.Tests.Visual.Online
|
||||
TotalScore = 1234789,
|
||||
Accuracy = 0.9997,
|
||||
},
|
||||
new APIScore
|
||||
new SoloScoreInfo
|
||||
{
|
||||
Date = DateTimeOffset.Now,
|
||||
OnlineID = onlineID++,
|
||||
EndedAt = DateTimeOffset.Now,
|
||||
ID = onlineID++,
|
||||
User = new APIUser
|
||||
{
|
||||
Id = 1014222,
|
||||
@ -226,10 +227,10 @@ namespace osu.Game.Tests.Visual.Online
|
||||
TotalScore = 12345678,
|
||||
Accuracy = 0.9854,
|
||||
},
|
||||
new APIScore
|
||||
new SoloScoreInfo
|
||||
{
|
||||
Date = DateTimeOffset.Now,
|
||||
OnlineID = onlineID++,
|
||||
EndedAt = DateTimeOffset.Now,
|
||||
ID = onlineID++,
|
||||
User = new APIUser
|
||||
{
|
||||
Id = 1541390,
|
||||
@ -250,10 +251,10 @@ namespace osu.Game.Tests.Visual.Online
|
||||
TotalScore = 1234567,
|
||||
Accuracy = 0.8765,
|
||||
},
|
||||
new APIScore
|
||||
new SoloScoreInfo
|
||||
{
|
||||
Date = DateTimeOffset.Now,
|
||||
OnlineID = onlineID++,
|
||||
EndedAt = DateTimeOffset.Now,
|
||||
ID = onlineID++,
|
||||
User = new APIUser
|
||||
{
|
||||
Id = 7151382,
|
||||
@ -275,12 +276,12 @@ namespace osu.Game.Tests.Visual.Online
|
||||
|
||||
foreach (var s in scores.Scores)
|
||||
{
|
||||
s.Statistics = new Dictionary<string, int>
|
||||
s.Statistics = new Dictionary<HitResult, int>
|
||||
{
|
||||
{ "count_300", RNG.Next(2000) },
|
||||
{ "count_100", RNG.Next(2000) },
|
||||
{ "count_50", RNG.Next(2000) },
|
||||
{ "count_miss", RNG.Next(2000) }
|
||||
{ HitResult.Great, RNG.Next(2000) },
|
||||
{ HitResult.Ok, RNG.Next(2000) },
|
||||
{ HitResult.Meh, RNG.Next(2000) },
|
||||
{ HitResult.Miss, RNG.Next(2000) }
|
||||
};
|
||||
}
|
||||
|
||||
@ -289,10 +290,10 @@ namespace osu.Game.Tests.Visual.Online
|
||||
|
||||
private APIScoreWithPosition createUserBest() => new APIScoreWithPosition
|
||||
{
|
||||
Score = new APIScore
|
||||
Score = new SoloScoreInfo
|
||||
{
|
||||
Date = DateTimeOffset.Now,
|
||||
OnlineID = onlineID++,
|
||||
EndedAt = DateTimeOffset.Now,
|
||||
ID = onlineID++,
|
||||
User = new APIUser
|
||||
{
|
||||
Id = 7151382,
|
||||
|
@ -53,13 +53,8 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
Margin = new MarginPadding { Top = 20 }
|
||||
});
|
||||
|
||||
AddStep("show", () =>
|
||||
{
|
||||
infoWedge.Show();
|
||||
infoWedge.Beatmap = Beatmap.Value;
|
||||
});
|
||||
AddStep("show", () => infoWedge.Show());
|
||||
|
||||
// select part is redundant, but wait for load isn't
|
||||
selectBeatmap(Beatmap.Value.Beatmap);
|
||||
|
||||
AddWaitStep("wait for select", 3);
|
||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
dependencies.Cache(rulesetStore = new RealmRulesetStore(Realm));
|
||||
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, rulesetStore, null, dependencies.Get<AudioManager>(), Resources, dependencies.Get<GameHost>(), Beatmap.Default));
|
||||
dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, Realm, Scheduler));
|
||||
dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, Realm, Scheduler, API));
|
||||
Dependencies.Cache(Realm);
|
||||
|
||||
return dependencies;
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
@ -166,15 +167,22 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
var beatmapSet = TestResources.CreateTestBeatmapSetInfo(rulesets.Length, rulesets);
|
||||
|
||||
for (int i = 0; i < rulesets.Length; i++)
|
||||
var importedBeatmapSet = Game.BeatmapManager.Import(beatmapSet);
|
||||
|
||||
Debug.Assert(importedBeatmapSet != null);
|
||||
|
||||
importedBeatmapSet.PerformWrite(s =>
|
||||
{
|
||||
var beatmap = beatmapSet.Beatmaps[i];
|
||||
for (int i = 0; i < rulesets.Length; i++)
|
||||
{
|
||||
var beatmap = s.Beatmaps[i];
|
||||
|
||||
beatmap.StarRating = i + 1;
|
||||
beatmap.DifficultyName = $"SR{i + 1}";
|
||||
}
|
||||
beatmap.StarRating = i + 1;
|
||||
beatmap.DifficultyName = $"SR{i + 1}";
|
||||
}
|
||||
});
|
||||
|
||||
return Game.BeatmapManager.Import(beatmapSet)?.Value;
|
||||
return importedBeatmapSet.Value;
|
||||
}
|
||||
|
||||
private bool ensureAllBeatmapSetsImported(IEnumerable<BeatmapSetInfo> beatmapSets) => beatmapSets.All(set => set != null);
|
||||
|
@ -0,0 +1,28 @@
|
||||
// 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.Screens.Select;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.SongSelect
|
||||
{
|
||||
public class TestSceneDifficultyRangeFilterControl : OsuTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestBasic()
|
||||
{
|
||||
AddStep("create control", () =>
|
||||
{
|
||||
Child = new DifficultyRangeFilterControl
|
||||
{
|
||||
Width = 200,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(3),
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
{
|
||||
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
||||
Dependencies.Cache(scoreManager = new ScoreManager(rulesets, () => beatmapManager, LocalStorage, Realm, Scheduler));
|
||||
Dependencies.Cache(scoreManager = new ScoreManager(rulesets, () => beatmapManager, LocalStorage, Realm, Scheduler, API));
|
||||
Dependencies.Cache(Realm);
|
||||
|
||||
beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user