Merge branch 'master' into accuracy-pp-curve

This commit is contained in:
Dan Balasescu 2022-06-29 16:48:11 +09:00
commit 5cdf98b4dc
475 changed files with 3471 additions and 2018 deletions

View File

@ -1,4 +1,2 @@
# Normalize all the line endings
32a74f95a5c80a0ed18e693f13a47522099df5c3
# Enabled NRT globally
f8830c6850128456266c82de83273204f8b74ac0

View File

@ -126,6 +126,12 @@ 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.

View File

@ -16,3 +16,6 @@ M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Linq.IQueryable
M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Collections.Generic.IList{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IList<T>,NotificationCallbackDelegate<T>) instead.
M:System.Threading.Tasks.Task.Wait();Don't use Task.Wait. Use Task.WaitSafely() to ensure we avoid deadlocks.
P:System.Threading.Tasks.Task`1.Result;Don't use Task.Result. Use Task.GetResultSafely() to ensure we avoid deadlocks.
M:System.Threading.ManualResetEventSlim.Wait();Specify a timeout to avoid waiting forever.
M:System.String.ToLower();string.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
M:System.String.ToUpper();string.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.

View File

@ -1,7 +1,7 @@
<!-- Contains required properties for osu!framework projects. -->
<Project>
<PropertyGroup Label="C#">
<LangVersion>8.0</LangVersion>
<LangVersion>9.0</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
</PropertyGroup>

View File

@ -8,20 +8,20 @@ GEM
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.2.0)
aws-partitions (1.570.0)
aws-sdk-core (3.130.0)
aws-partitions (1.601.0)
aws-sdk-core (3.131.2)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.55.0)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.57.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.113.0)
aws-sdk-s3 (1.114.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.4.0)
aws-sigv4 (1.5.0)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
claide (1.1.0)
@ -36,7 +36,7 @@ GEM
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.6)
emoji_regex (3.2.3)
excon (0.92.1)
excon (0.92.3)
faraday (1.10.0)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
@ -56,8 +56,8 @@ GEM
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.0.3)
multipart-post (>= 1.2, < 3)
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
@ -66,7 +66,7 @@ GEM
faraday_middleware (1.2.0)
faraday (~> 1.0)
fastimage (2.2.6)
fastlane (2.205.1)
fastlane (2.206.2)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
@ -110,9 +110,9 @@ GEM
souyuz (= 0.11.1)
fastlane-plugin-xamarin (0.6.3)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.16.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-core (0.4.2)
google-apis-androidpublisher_v3 (0.23.0)
google-apis-core (>= 0.6, < 2.a)
google-apis-core (0.6.0)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
@ -121,19 +121,19 @@ GEM
retriable (>= 2.0, < 4.a)
rexml
webrick
google-apis-iamcredentials_v1 (0.10.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-playcustomapp_v1 (0.7.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-storage_v1 (0.11.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-iamcredentials_v1 (0.12.0)
google-apis-core (>= 0.6, < 2.a)
google-apis-playcustomapp_v1 (0.9.0)
google-apis-core (>= 0.6, < 2.a)
google-apis-storage_v1 (0.16.0)
google-apis-core (>= 0.6, < 2.a)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.2.0)
google-cloud-storage (1.36.1)
google-cloud-storage (1.36.2)
addressable (~> 2.8)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
@ -141,7 +141,7 @@ GEM
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (1.1.2)
googleauth (1.2.0)
faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
@ -149,12 +149,12 @@ GEM
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
highline (2.0.3)
http-cookie (1.0.4)
http-cookie (1.0.5)
domain_name (~> 0.5)
httpclient (2.8.3)
jmespath (1.6.1)
json (2.6.1)
jwt (2.3.0)
json (2.6.2)
jwt (2.4.1)
memoist (0.16.2)
mini_magick (4.11.0)
mini_mime (1.1.2)
@ -169,10 +169,10 @@ GEM
optparse (0.1.1)
os (1.1.4)
plist (3.6.0)
public_suffix (4.0.6)
public_suffix (4.0.7)
racc (1.6.0)
rake (13.0.6)
representable (3.1.1)
representable (3.2.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
@ -182,9 +182,9 @@ GEM
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
security (0.1.3)
signet (0.16.1)
signet (0.17.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.0)
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.8)
@ -205,11 +205,11 @@ GEM
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.1)
unf_ext (0.0.8.2)
unicode-display_width (1.8.0)
webrick (1.7.0)
word_wrap (1.0.0)
xcodeproj (1.21.0)
xcodeproj (1.22.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)

View File

@ -51,8 +51,8 @@
<Reference Include="Java.Interop" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.616.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.615.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.628.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.628.1" />
</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. -->

View File

@ -0,0 +1,97 @@
// 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 Android.OS;
using osu.Framework.Allocation;
using osu.Framework.Android.Input;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Configuration;
using osu.Game.Localisation;
using osu.Game.Overlays.Settings;
using osu.Game.Overlays.Settings.Sections.Input;
namespace osu.Android
{
public class AndroidMouseSettings : SettingsSubsection
{
private readonly AndroidMouseHandler mouseHandler;
protected override LocalisableString Header => MouseSettingsStrings.Mouse;
private Bindable<double> handlerSensitivity = null!;
private Bindable<double> localSensitivity = null!;
private Bindable<bool> relativeMode = null!;
public AndroidMouseSettings(AndroidMouseHandler mouseHandler)
{
this.mouseHandler = mouseHandler;
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager osuConfig)
{
// use local bindable to avoid changing enabled state of game host's bindable.
handlerSensitivity = mouseHandler.Sensitivity.GetBoundCopy();
localSensitivity = handlerSensitivity.GetUnboundCopy();
relativeMode = mouseHandler.UseRelativeMode.GetBoundCopy();
// High precision/pointer capture is only available on Android 8.0 and up
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
AddRange(new Drawable[]
{
new SettingsCheckbox
{
LabelText = MouseSettingsStrings.HighPrecisionMouse,
TooltipText = MouseSettingsStrings.HighPrecisionMouseTooltip,
Current = relativeMode,
Keywords = new[] { @"raw", @"input", @"relative", @"cursor", @"captured", @"pointer" },
},
new MouseSettings.SensitivitySetting
{
LabelText = MouseSettingsStrings.CursorSensitivity,
Current = localSensitivity,
},
});
}
AddRange(new Drawable[]
{
new SettingsCheckbox
{
LabelText = MouseSettingsStrings.DisableMouseWheelVolumeAdjust,
TooltipText = MouseSettingsStrings.DisableMouseWheelVolumeAdjustTooltip,
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableWheel),
},
new SettingsCheckbox
{
LabelText = MouseSettingsStrings.DisableMouseButtons,
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons),
},
});
}
protected override void LoadComplete()
{
base.LoadComplete();
relativeMode.BindValueChanged(relative => localSensitivity.Disabled = !relative.NewValue, true);
handlerSensitivity.BindValueChanged(val =>
{
bool disabled = localSensitivity.Disabled;
localSensitivity.Disabled = false;
localSensitivity.Value = val.NewValue;
localSensitivity.Disabled = disabled;
}, true);
localSensitivity.BindValueChanged(val => handlerSensitivity.Value = val.NewValue);
}
}
}

View File

@ -7,7 +7,11 @@ using System;
using Android.App;
using Android.OS;
using osu.Framework.Allocation;
using osu.Framework.Android.Input;
using osu.Framework.Input.Handlers;
using osu.Framework.Platform;
using osu.Game;
using osu.Game.Overlays.Settings;
using osu.Game.Updater;
using osu.Game.Utils;
using Xamarin.Essentials;
@ -75,10 +79,28 @@ namespace osu.Android
LoadComponentAsync(new GameplayScreenRotationLocker(), Add);
}
public override void SetHost(GameHost host)
{
base.SetHost(host);
host.Window.CursorState |= CursorState.Hidden;
}
protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager();
protected override BatteryInfo CreateBatteryInfo() => new AndroidBatteryInfo();
public override SettingsSubsection CreateSettingsSubsectionFor(InputHandler handler)
{
switch (handler)
{
case AndroidMouseHandler mh:
return new AndroidMouseSettings(mh);
default:
return base.CreateSettingsSubsectionFor(handler);
}
}
private class AndroidBatteryInfo : BatteryInfo
{
public override double ChargeLevel => Battery.ChargeLevel;

View File

@ -26,6 +26,7 @@
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup>
<ItemGroup>
<Compile Include="AndroidMouseSettings.cs" />
<Compile Include="GameplayScreenRotationLocker.cs" />
<Compile Include="OsuGameActivity.cs" />
<Compile Include="OsuGameAndroid.cs" />

View File

@ -57,7 +57,7 @@ namespace osu.Desktop
client.OnReady += onReady;
// safety measure for now, until we performance test / improve backoff for failed connections.
client.OnConnectionFailed += (_, __) => client.Deinitialize();
client.OnConnectionFailed += (_, _) => client.Deinitialize();
client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network);

View File

@ -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.Diagnostics;
@ -20,25 +18,34 @@ using osu.Framework;
using osu.Framework.Logging;
using osu.Game.Updater;
using osu.Desktop.Windows;
using osu.Framework.Input.Handlers;
using osu.Framework.Input.Handlers.Joystick;
using osu.Framework.Input.Handlers.Mouse;
using osu.Framework.Input.Handlers.Tablet;
using osu.Framework.Threading;
using osu.Game.IO;
using osu.Game.IPC;
using osu.Game.Overlays.Settings;
using osu.Game.Overlays.Settings.Sections.Input;
namespace osu.Desktop
{
internal class OsuGameDesktop : OsuGame
{
public OsuGameDesktop(string[] args = null)
private OsuSchemeLinkIPCChannel? osuSchemeLinkIPCChannel;
public OsuGameDesktop(string[]? args = null)
: base(args)
{
}
public override StableStorage GetStorageForStableInstall()
public override StableStorage? GetStorageForStableInstall()
{
try
{
if (Host is DesktopGameHost desktopHost)
{
string stablePath = getStableInstallPath();
string? stablePath = getStableInstallPath();
if (!string.IsNullOrEmpty(stablePath))
return new StableStorage(stablePath, desktopHost);
}
@ -51,11 +58,11 @@ namespace osu.Desktop
return null;
}
private string getStableInstallPath()
private string? getStableInstallPath()
{
static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")) || File.Exists(Path.Combine(p, "osu!.cfg"));
string stableInstallPath;
string? stableInstallPath;
if (OperatingSystem.IsWindows())
{
@ -83,15 +90,15 @@ namespace osu.Desktop
}
[SupportedOSPlatform("windows")]
private string getStableInstallPathFromRegistry()
private string? getStableInstallPathFromRegistry()
{
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
using (RegistryKey? key = Registry.ClassesRoot.OpenSubKey("osu"))
return key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty)?.ToString()?.Split('"')[1].Replace("osu!.exe", "");
}
protected override UpdateManager CreateUpdateManager()
{
string packageManaged = Environment.GetEnvironmentVariable("OSU_EXTERNAL_UPDATE_PROVIDER");
string? packageManaged = Environment.GetEnvironmentVariable("OSU_EXTERNAL_UPDATE_PROVIDER");
if (!string.IsNullOrEmpty(packageManaged))
return new NoActionUpdateManager();
@ -118,6 +125,8 @@ namespace osu.Desktop
LoadComponentAsync(new GameplayWinKeyBlocker(), Add);
LoadComponentAsync(new ElevatedPrivilegesChecker(), Add);
osuSchemeLinkIPCChannel = new OsuSchemeLinkIPCChannel(Host, this);
}
public override void SetHost(GameHost host)
@ -134,8 +143,26 @@ namespace osu.Desktop
desktopWindow.DragDrop += f => fileDrop(new[] { f });
}
public override SettingsSubsection CreateSettingsSubsectionFor(InputHandler handler)
{
switch (handler)
{
case ITabletHandler th:
return new TabletSettings(th);
case MouseHandler mh:
return new MouseSettings(mh);
case JoystickHandler jh:
return new JoystickSettings(jh);
default:
return base.CreateSettingsSubsectionFor(handler);
}
}
private readonly List<string> importableFiles = new List<string>();
private ScheduledDelegate importSchedule;
private ScheduledDelegate? importSchedule;
private void fileDrop(string[] filePaths)
{
@ -168,5 +195,11 @@ namespace osu.Desktop
Task.Factory.StartNew(() => Import(paths), TaskCreationOptions.LongRunning);
}
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
osuSchemeLinkIPCChannel?.Dispose();
}
}
}

View File

@ -11,6 +11,7 @@ using osu.Framework;
using osu.Framework.Development;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game;
using osu.Game.IPC;
using osu.Game.Tournament;
using Squirrel;
@ -65,19 +66,8 @@ namespace osu.Desktop
{
if (!host.IsPrimaryInstance)
{
if (args.Length > 0 && args[0].Contains('.')) // easy way to check for a file import in args
{
var importer = new ArchiveImportIPCChannel(host);
foreach (string file in args)
{
Console.WriteLine(@"Importing {0}", file);
if (!importer.ImportAsync(Path.GetFullPath(file, cwd)).Wait(3000))
throw new TimeoutException(@"IPC took too long to send");
}
if (trySendIPCMessage(host, cwd, args))
return;
}
// we want to allow multiple instances to be started when in debug.
if (!DebugUtils.IsDebugBuild)
@ -108,21 +98,49 @@ namespace osu.Desktop
}
}
private static bool trySendIPCMessage(IIpcHost host, string cwd, string[] args)
{
if (args.Length == 1 && args[0].StartsWith(OsuGameBase.OSU_PROTOCOL, StringComparison.Ordinal))
{
var osuSchemeLinkHandler = new OsuSchemeLinkIPCChannel(host);
if (!osuSchemeLinkHandler.HandleLinkAsync(args[0]).Wait(3000))
throw new IPCTimeoutException(osuSchemeLinkHandler.GetType());
return true;
}
if (args.Length > 0 && args[0].Contains('.')) // easy way to check for a file import in args
{
var importer = new ArchiveImportIPCChannel(host);
foreach (string file in args)
{
Console.WriteLine(@"Importing {0}", file);
if (!importer.ImportAsync(Path.GetFullPath(file, cwd)).Wait(3000))
throw new IPCTimeoutException(importer.GetType());
}
return true;
}
return false;
}
[SupportedOSPlatform("windows")]
private static void setupSquirrel()
{
SquirrelAwareApp.HandleEvents(onInitialInstall: (version, tools) =>
SquirrelAwareApp.HandleEvents(onInitialInstall: (_, tools) =>
{
tools.CreateShortcutForThisExe();
tools.CreateUninstallerRegistryEntry();
}, onAppUpdate: (version, tools) =>
}, onAppUpdate: (_, tools) =>
{
tools.CreateUninstallerRegistryEntry();
}, onAppUninstall: (version, tools) =>
}, onAppUninstall: (_, tools) =>
{
tools.RemoveShortcutForThisExe();
tools.RemoveUninstallerRegistryEntry();
}, onEveryRun: (version, tools, firstRun) =>
}, onEveryRun: (_, _, _) =>
{
// While setting the `ProcessAppUserModelId` fixes duplicate icons/shortcuts on the taskbar, it currently
// causes the right-click context menu to function incorrectly.

View File

@ -154,7 +154,7 @@ namespace osu.Desktop.Updater
Activated = () =>
{
updateManager.PrepareUpdateAsync()
.ContinueWith(_ => updateManager.Schedule(() => game?.GracefullyExit()));
.ContinueWith(_ => updateManager.Schedule(() => game?.AttemptExit()));
return true;
};
}

View File

@ -31,7 +31,7 @@ namespace osu.Game.Benchmarks
realm = new RealmAccess(storage, OsuGameBase.CLIENT_DATABASE_FILENAME);
realm.Run(r =>
realm.Run(_ =>
{
realm.Write(c => c.Add(TestResources.CreateTestBeatmapSetInfo(rulesets: new[] { new OsuRuleset().RulesetInfo })));
});
@ -76,7 +76,7 @@ namespace osu.Game.Benchmarks
}
});
done.Wait();
done.Wait(60000);
}
[Benchmark]
@ -115,7 +115,7 @@ namespace osu.Game.Benchmarks
}
});
done.Wait();
done.Wait(60000);
}
[Benchmark]

View File

@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Catch.Tests
new JuiceStreamPathVertex(20, -5)
}));
removeCount = path.RemoveVertices((_, i) => true);
removeCount = path.RemoveVertices((_, _) => true);
Assert.That(removeCount, Is.EqualTo(1));
Assert.That(path.Vertices, Is.EqualTo(new[]
{

View File

@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.Tests
AddStep("change component scale", () => Player.ChildrenOfType<LegacyScoreCounter>().First().Scale = new Vector2(2f));
AddStep("update target", () => Player.ChildrenOfType<SkinnableTargetContainer>().ForEach(LegacySkin.UpdateDrawableTarget));
AddStep("exit player", () => Player.Exit());
CreateTest(null);
CreateTest();
}
AddAssert("legacy HUD combo counter hidden", () =>

View File

@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Tests
hyperDashCount = 0;
// this needs to be done within the frame stable context due to how quickly hyperdash state changes occur.
Player.DrawableRuleset.FrameStableComponents.OnUpdate += d =>
Player.DrawableRuleset.FrameStableComponents.OnUpdate += _ =>
{
var catcher = Player.ChildrenOfType<Catcher>().FirstOrDefault();

View File

@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Catch
protected override string RulesetPrefix => "catch"; // todo: use CatchRuleset.SHORT_NAME;
protected override string ComponentName => Component.ToString().ToLower();
protected override string ComponentName => Component.ToString().ToLowerInvariant();
}
}

View File

@ -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 Newtonsoft.Json;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
namespace osu.Game.Rulesets.Catch.Difficulty
@ -31,9 +30,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty
yield return (ATTRIB_ID_MAX_COMBO, MaxCombo);
}
public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> values)
public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> values, IBeatmapOnlineInfo onlineInfo)
{
base.FromDatabaseAttributes(values);
base.FromDatabaseAttributes(values, onlineInfo);
StarRating = values[ATTRIB_ID_AIM];
ApproachRate = values[ATTRIB_ID_APPROACH_RATE];

View File

@ -135,15 +135,15 @@ namespace osu.Game.Rulesets.Catch.Edit
{
switch (BlueprintContainer.CurrentTool)
{
case SelectTool _:
case SelectTool:
if (EditorBeatmap.SelectedHitObjects.Count == 0)
return null;
double minTime = EditorBeatmap.SelectedHitObjects.Min(hitObject => hitObject.StartTime);
return getLastSnappableHitObject(minTime);
case FruitCompositionTool _:
case JuiceStreamCompositionTool _:
case FruitCompositionTool:
case JuiceStreamCompositionTool:
if (!CursorInPlacementArea)
return null;

View File

@ -42,10 +42,10 @@ namespace osu.Game.Rulesets.Catch.Edit
case Droplet droplet:
return droplet is TinyDroplet ? PositionRange.EMPTY : new PositionRange(droplet.OriginalX);
case JuiceStream _:
case JuiceStream:
return GetPositionRange(hitObject.NestedHitObjects);
case BananaShower _:
case BananaShower:
// A banana shower occupies the whole screen width.
return new PositionRange(0, CatchPlayfield.WIDTH);

View File

@ -131,7 +131,7 @@ namespace osu.Game.Rulesets.Catch.Edit
{
switch (hitObject)
{
case BananaShower _:
case BananaShower:
return false;
case JuiceStream juiceStream:

View File

@ -389,13 +389,13 @@ namespace osu.Game.Rulesets.Catch.UI
{
switch (source)
{
case Fruit _:
case Fruit:
return caughtFruitPool.Get();
case Banana _:
case Banana:
return caughtBananaPool.Get();
case Droplet _:
case Droplet:
return caughtDropletPool.Get();
default:

View File

@ -173,7 +173,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
switch (original)
{
case IHasDistance _:
case IHasDistance:
{
var generator = new DistanceObjectPatternGenerator(Random, original, beatmap, lastPattern, originalBeatmap);
conversion = generator;

View File

@ -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 Newtonsoft.Json;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
namespace osu.Game.Rulesets.Mania.Difficulty
@ -30,9 +29,9 @@ namespace osu.Game.Rulesets.Mania.Difficulty
yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow);
}
public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> values)
public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> values, IBeatmapOnlineInfo onlineInfo)
{
base.FromDatabaseAttributes(values);
base.FromDatabaseAttributes(values, onlineInfo);
MaxCombo = (int)values[ATTRIB_ID_MAX_COMBO];
StarRating = values[ATTRIB_ID_DIFFICULTY];

View File

@ -146,56 +146,56 @@ namespace osu.Game.Rulesets.Mania
{
switch (mod)
{
case ManiaModKey1 _:
case ManiaModKey1:
value |= LegacyMods.Key1;
break;
case ManiaModKey2 _:
case ManiaModKey2:
value |= LegacyMods.Key2;
break;
case ManiaModKey3 _:
case ManiaModKey3:
value |= LegacyMods.Key3;
break;
case ManiaModKey4 _:
case ManiaModKey4:
value |= LegacyMods.Key4;
break;
case ManiaModKey5 _:
case ManiaModKey5:
value |= LegacyMods.Key5;
break;
case ManiaModKey6 _:
case ManiaModKey6:
value |= LegacyMods.Key6;
break;
case ManiaModKey7 _:
case ManiaModKey7:
value |= LegacyMods.Key7;
break;
case ManiaModKey8 _:
case ManiaModKey8:
value |= LegacyMods.Key8;
break;
case ManiaModKey9 _:
case ManiaModKey9:
value |= LegacyMods.Key9;
break;
case ManiaModDualStages _:
case ManiaModDualStages:
value |= LegacyMods.KeyCoop;
break;
case ManiaModFadeIn _:
case ManiaModFadeIn:
value |= LegacyMods.FadeIn;
value &= ~LegacyMods.Hidden; // this is toggled on in the base call due to inheritance, but we don't want that.
break;
case ManiaModMirror _:
case ManiaModMirror:
value |= LegacyMods.Mirror;
break;
case ManiaModRandom _:
case ManiaModRandom:
value |= LegacyMods.Random;
break;
}

View File

@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania
protected override string RulesetPrefix => ManiaRuleset.SHORT_NAME;
protected override string ComponentName => Component.ToString().ToLower();
protected override string ComponentName => Component.ToString().ToLowerInvariant();
}
public enum ManiaSkinComponents

View File

@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Mania.Mods
var rng = new Random((int)Seed.Value);
int availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => rng.Next()).ToList();
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(_ => rng.Next()).ToList();
beatmap.HitObjects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
}

View File

@ -57,11 +57,11 @@ namespace osu.Game.Rulesets.Mania.Replays
{
switch (point)
{
case HitPoint _:
case HitPoint:
actions.Add(columnActions[point.Column]);
break;
case ReleasePoint _:
case ReleasePoint:
actions.Remove(columnActions[point.Column]);
break;
}

View File

@ -10,7 +10,6 @@ using osu.Framework.Input;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Edit;
@ -41,10 +40,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
private bool editorComponentsReady => editor.ChildrenOfType<HitObjectComposer>().FirstOrDefault()?.IsLoaded == true
&& editor.ChildrenOfType<TimelineArea>().FirstOrDefault()?.IsLoaded == true
&& editor?.ChildrenOfType<Playfield>().FirstOrDefault()?.IsLoaded == true;
[TestCase(true)]
[TestCase(false)]
public void TestVelocityChangeSavesCorrectly(bool adjustVelocity)
@ -52,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
double? velocity = null;
AddStep("enter editor", () => Game.ScreenStack.Push(new EditorLoader()));
AddUntilStep("wait for editor load", () => editorComponentsReady);
AddUntilStep("wait for editor load", () => editor?.ReadyForUse == true);
AddStep("seek to first control point", () => editorClock.Seek(editorBeatmap.ControlPointInfo.TimingPoints.First().Time));
AddStep("enter slider placement mode", () => InputManager.Key(Key.Number3));
@ -91,7 +86,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddStep("exit", () => InputManager.Key(Key.Escape));
AddStep("enter editor (again)", () => Game.ScreenStack.Push(new EditorLoader()));
AddUntilStep("wait for editor load", () => editorComponentsReady);
AddUntilStep("wait for editor load", () => editor?.ReadyForUse == true);
AddStep("seek to slider", () => editorClock.Seek(slider.StartTime));
AddAssert("slider has correct velocity", () => slider.Velocity == velocity);

View File

@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Osu.Tests
skin.Setup(s => s.GetTexture(It.IsAny<string>())).CallBase();
skin.Setup(s => s.GetTexture(It.IsIn(textureFilenames), It.IsAny<WrapMode>(), It.IsAny<WrapMode>()))
.Returns((string componentName, WrapMode _, WrapMode __) => new Texture(1, 1) { AssetName = componentName });
.Returns((string componentName, WrapMode _, WrapMode _) => new Texture(1, 1) { AssetName = componentName });
Child = new DependencyProvidingContainer
{

View File

@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
lastResult = null;
spinner = nextSpinner;
spinner.OnNewResult += (o, result) => lastResult = result;
spinner.OnNewResult += (_, result) => lastResult = result;
}
return lastResult?.Type == HitResult.Great;
@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
return false;
spinner = nextSpinner;
spinner.OnNewResult += (o, result) => results.Add(result);
spinner.OnNewResult += (_, result) => results.Add(result);
results.Clear();
}

View File

@ -0,0 +1,120 @@
// 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 System.Linq;
using NUnit.Framework;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Replays;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests
{
[HeadlessTest]
public class TestSceneSliderFollowCircleInput : RateAdjustedBeatmapTestScene
{
private List<JudgementResult>? judgementResults;
private ScoreAccessibleReplayPlayer? currentPlayer;
[Test]
public void TestMaximumDistanceTrackingWithoutMovement(
[Values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)]
float circleSize,
[Values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)]
double velocity)
{
const double time_slider_start = 1000;
float circleRadius = OsuHitObject.OBJECT_RADIUS * (1.0f - 0.7f * (circleSize - 5) / 5) / 2;
float followCircleRadius = circleRadius * 1.2f;
performTest(new Beatmap<OsuHitObject>
{
HitObjects =
{
new Slider
{
StartTime = time_slider_start,
Position = new Vector2(0, 0),
DifficultyControlPoint = new DifficultyControlPoint { SliderVelocity = velocity },
Path = new SliderPath(PathType.Linear, new[]
{
Vector2.Zero,
new Vector2(followCircleRadius, 0),
}, followCircleRadius),
},
},
BeatmapInfo =
{
Difficulty = new BeatmapDifficulty
{
CircleSize = circleSize,
SliderTickRate = 1
},
Ruleset = new OsuRuleset().RulesetInfo
},
}, new List<ReplayFrame>
{
new OsuReplayFrame { Position = new Vector2(-circleRadius + 1, 0), Actions = { OsuAction.LeftButton }, Time = time_slider_start },
});
AddAssert("Tracking kept", assertMaxJudge);
}
private bool assertMaxJudge() => judgementResults?.Any() == true && judgementResults.All(t => t.Type == t.Judgement.MaxResult);
private void performTest(Beatmap<OsuHitObject> beatmap, List<ReplayFrame> frames)
{
AddStep("load player", () =>
{
Beatmap.Value = CreateWorkingBeatmap(beatmap);
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });
p.OnLoadComplete += _ =>
{
p.ScoreProcessor.NewJudgement += result =>
{
if (currentPlayer == p) judgementResults?.Add(result);
};
};
LoadScreen(currentPlayer = p);
judgementResults = new List<JudgementResult>();
});
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
AddUntilStep("Wait for completion", () => currentPlayer?.ScoreProcessor.HasCompleted.Value == true);
}
private class ScoreAccessibleReplayPlayer : ReplayPlayer
{
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
protected override bool PauseOnFocusLost => false;
public ScoreAccessibleReplayPlayer(Score score)
: base(score, new PlayerConfiguration
{
AllowPause = false,
ShowResults = false,
})
{
}
}
}
}

View File

@ -66,10 +66,7 @@ namespace osu.Game.Rulesets.Osu.Tests
drawableSlider = null;
});
[SetUpSteps]
public override void SetUpSteps()
{
}
protected override bool HasCustomSteps => true;
[TestCase(0)]
[TestCase(1)]
@ -77,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public void TestSnakingEnabled(int sliderIndex)
{
AddStep("enable autoplay", () => autoplay = true);
base.SetUpSteps();
CreateTest();
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
retrieveSlider(sliderIndex);
@ -101,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public void TestSnakingDisabled(int sliderIndex)
{
AddStep("have autoplay", () => autoplay = true);
base.SetUpSteps();
CreateTest();
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
retrieveSlider(sliderIndex);
@ -121,8 +118,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
AddStep("enable autoplay", () => autoplay = true);
setSnaking(true);
base.SetUpSteps();
CreateTest();
// repeat might have a chance to update its position depending on where in the frame its hit,
// so some leniency is allowed here instead of checking strict equality
addCheckPositionChangeSteps(() => 16600, getSliderRepeat, positionAlmostSame);
@ -133,15 +129,14 @@ namespace osu.Game.Rulesets.Osu.Tests
{
AddStep("disable autoplay", () => autoplay = false);
setSnaking(true);
base.SetUpSteps();
CreateTest();
addCheckPositionChangeSteps(() => 16600, getSliderRepeat, positionDecreased);
}
private void retrieveSlider(int index)
{
AddStep("retrieve slider at index", () => slider = (Slider)beatmap.HitObjects[index]);
addSeekStep(() => slider);
addSeekStep(() => slider.StartTime);
AddUntilStep("retrieve drawable slider", () =>
(drawableSlider = (DrawableSlider)Player.DrawableRuleset.Playfield.AllHitObjects.SingleOrDefault(d => d.HitObject == slider)) != null);
}
@ -161,7 +156,7 @@ namespace osu.Game.Rulesets.Osu.Tests
=> addCheckPositionChangeSteps(timeAtRepeat(startTime, repeatIndex), positionAtRepeat(repeatIndex), positionRemainsSame);
private Func<double> timeAtRepeat(Func<double> startTime, int repeatIndex) => () => startTime() + 100 + duration_of_span * repeatIndex;
private Func<Vector2> positionAtRepeat(int repeatIndex) => repeatIndex % 2 == 0 ? (Func<Vector2>)getSliderStart : getSliderEnd;
private Func<Vector2> positionAtRepeat(int repeatIndex) => repeatIndex % 2 == 0 ? getSliderStart : getSliderEnd;
private List<Vector2> getSliderCurve() => ((PlaySliderBody)drawableSlider.Body.Drawable).CurrentCurve;
private Vector2 getSliderStart() => getSliderCurve().First();
@ -205,16 +200,10 @@ namespace osu.Game.Rulesets.Osu.Tests
});
}
private void addSeekStep(Func<Slider> slider)
private void addSeekStep(Func<double> getTime)
{
AddStep("seek to slider", () => Player.GameplayClockContainer.Seek(slider().StartTime));
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(slider().StartTime, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
}
private void addSeekStep(Func<double> time)
{
AddStep("seek to time", () => Player.GameplayClockContainer.Seek(time()));
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time(), Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
AddStep("seek to time", () => Player.GameplayClockContainer.Seek(getTime()));
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(getTime(), Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
}
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap { HitObjects = createHitObjects() };

View File

@ -4,7 +4,6 @@
#nullable disable
using System;
using osu.Framework.Utils;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
using osu.Game.Rulesets.Osu.Objects;
@ -33,14 +32,22 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
// derive strainTime for calculation
var osuCurrObj = (OsuDifficultyHitObject)current;
var osuPrevObj = current.Index > 0 ? (OsuDifficultyHitObject)current.Previous(0) : null;
var osuNextObj = (OsuDifficultyHitObject)current.Next(0);
double strainTime = osuCurrObj.StrainTime;
double greatWindowFull = greatWindow * 2;
double speedWindowRatio = strainTime / greatWindowFull;
double doubletapness = 1;
// Aim to nerf cheesy rhythms (Very fast consecutive doubles with large deltatimes between)
if (osuPrevObj != null && strainTime < greatWindowFull && osuPrevObj.StrainTime > strainTime)
strainTime = Interpolation.Lerp(osuPrevObj.StrainTime, strainTime, speedWindowRatio);
// Nerf doubletappable doubles.
if (osuNextObj != null)
{
double currDeltaTime = Math.Max(1, osuCurrObj.DeltaTime);
double nextDeltaTime = Math.Max(1, osuNextObj.DeltaTime);
double deltaDifference = Math.Abs(nextDeltaTime - currDeltaTime);
double speedRatio = currDeltaTime / Math.Max(currDeltaTime, deltaDifference);
double windowRatio = Math.Pow(Math.Min(1, currDeltaTime / greatWindowFull), 2);
doubletapness = Math.Pow(speedRatio, 1 - windowRatio);
}
// Cap deltatime to the OD 300 hitwindow.
// 0.93 is derived from making sure 260bpm OD8 streams aren't nerfed harshly, whilst 0.92 limits the effect of the cap.
@ -55,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
double travelDistance = osuPrevObj?.TravelDistance ?? 0;
double distance = Math.Min(single_spacing_threshold, travelDistance + osuCurrObj.MinimumJumpDistance);
return (speedBonus + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / strainTime;
return (speedBonus + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) * doubletapness / strainTime;
}
}
}

View File

@ -1,12 +1,11 @@
// 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 JetBrains.Annotations;
using Newtonsoft.Json;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
@ -96,9 +95,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
yield return (ATTRIB_ID_SLIDER_FACTOR, SliderFactor);
}
public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> values)
public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> values, IBeatmapOnlineInfo onlineInfo)
{
base.FromDatabaseAttributes(values);
base.FromDatabaseAttributes(values, onlineInfo);
AimDifficulty = values[ATTRIB_ID_AIM];
SpeedDifficulty = values[ATTRIB_ID_SPEED];
@ -108,6 +107,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty
StarRating = values[ATTRIB_ID_DIFFICULTY];
FlashlightDifficulty = values.GetValueOrDefault(ATTRIB_ID_FLASHLIGHT);
SliderFactor = values[ATTRIB_ID_SLIDER_FACTOR];
DrainRate = onlineInfo.DrainRate;
HitCircleCount = onlineInfo.CircleCount;
SliderCount = onlineInfo.SliderCount;
SpinnerCount = onlineInfo.SpinnerCount;
}
#region Newtonsoft.Json implicit ShouldSerialize() methods

View File

@ -7,6 +7,7 @@ using System;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Difficulty.Evaluators;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
namespace osu.Game.Rulesets.Osu.Difficulty.Skills
{
@ -37,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
protected override double StrainValueAt(DifficultyHitObject current)
{
currentStrain *= strainDecay(current.DeltaTime);
currentStrain *= strainDecay(((OsuDifficultyHitObject)current).StrainTime);
currentStrain += SpeedEvaluator.EvaluateDifficultyOf(current, greatWindow) * skillMultiplier;
currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current, greatWindow);

View File

@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Edit
});
selectedHitObjects = EditorBeatmap.SelectedHitObjects.GetBoundCopy();
selectedHitObjects.CollectionChanged += (_, __) => updateDistanceSnapGrid();
selectedHitObjects.CollectionChanged += (_, _) => updateDistanceSnapGrid();
placementObject = EditorBeatmap.PlacementObject.GetBoundCopy();
placementObject.ValueChanged += _ => updateDistanceSnapGrid();
@ -204,7 +204,7 @@ namespace osu.Game.Rulesets.Osu.Edit
switch (BlueprintContainer.CurrentTool)
{
case SelectTool _:
case SelectTool:
if (!EditorBeatmap.SelectedHitObjects.Any())
return;

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public void ApplyToDrawableHitObject(DrawableHitObject drawable)
{
drawable.ApplyCustomUpdateState += (drawableObject, state) =>
drawable.ApplyCustomUpdateState += (drawableObject, _) =>
{
if (!(drawableObject is DrawableHitCircle drawableHitCircle)) return;

View File

@ -30,9 +30,6 @@ namespace osu.Game.Rulesets.Osu.Mods
[SettingSource("Apply classic note lock", "Applies note lock to the full hit window.")]
public Bindable<bool> ClassicNoteLock { get; } = new BindableBool(true);
[SettingSource("Use fixed slider follow circle hit area", "Makes the slider follow circle track its final size at all times.")]
public Bindable<bool> FixedFollowCircleHitArea { get; } = new BindableBool(true);
[SettingSource("Always play a slider's tail sample", "Always plays a slider's tail sample regardless of whether it was hit or not.")]
public Bindable<bool> AlwaysPlayTailSample { get; } = new BindableBool(true);
@ -62,10 +59,6 @@ namespace osu.Game.Rulesets.Osu.Mods
{
switch (obj)
{
case DrawableSlider slider:
slider.Ball.InputTracksVisualSize = !FixedFollowCircleHitArea.Value;
break;
case DrawableSliderHead head:
head.TrackFollowCircle = !NoSliderHeadMovement.Value;
break;

View File

@ -88,7 +88,7 @@ namespace osu.Game.Rulesets.Osu.Mods
switch (drawableObject)
{
case DrawableSliderTail _:
case DrawableSliderTail:
using (drawableObject.BeginAbsoluteSequence(fadeStartTime))
drawableObject.FadeOut(fadeDuration);
@ -165,14 +165,14 @@ namespace osu.Game.Rulesets.Osu.Mods
switch (hitObject)
{
case Slider _:
case Slider:
return (fadeOutStartTime, longFadeDuration);
case SliderTick _:
case SliderTick:
double tickFadeOutDuration = Math.Min(hitObject.TimePreempt - DrawableSliderTick.ANIM_DURATION, 1000);
return (hitObject.StartTime - tickFadeOutDuration, tickFadeOutDuration);
case Spinner _:
case Spinner:
return (fadeOutStartTime + longFadeDuration, fadeOutDuration);
default:

View File

@ -44,13 +44,13 @@ namespace osu.Game.Rulesets.Osu.Mods
// apply grow effect
switch (drawable)
{
case DrawableSliderHead _:
case DrawableSliderTail _:
case DrawableSliderHead:
case DrawableSliderTail:
// special cases we should *not* be scaling.
break;
case DrawableSlider _:
case DrawableHitCircle _:
case DrawableSlider:
case DrawableHitCircle:
{
using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt))
drawable.ScaleTo(StartScale.Value).Then().ScaleTo(EndScale, h.TimePreempt, Easing.OutSine);

View File

@ -374,7 +374,7 @@ namespace osu.Game.Rulesets.Osu.Mods
int i = 0;
double currentTime = timingPoint.Time;
while (!definitelyBigger(currentTime, mapEndTime) && controlPointInfo.TimingPointAt(currentTime) == timingPoint)
while (!definitelyBigger(currentTime, mapEndTime) && ReferenceEquals(controlPointInfo.TimingPointAt(currentTime), timingPoint))
{
beats.Add(Math.Floor(currentTime));
i++;

View File

@ -34,10 +34,10 @@ namespace osu.Game.Rulesets.Osu.Mods
{
switch (drawable)
{
case DrawableSliderHead _:
case DrawableSliderTail _:
case DrawableSliderTick _:
case DrawableSliderRepeat _:
case DrawableSliderHead:
case DrawableSliderTail:
case DrawableSliderTick:
case DrawableSliderRepeat:
return;
default:

View File

@ -156,7 +156,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public Slider()
{
SamplesBindable.CollectionChanged += (_, __) => UpdateNestedSamples();
SamplesBindable.CollectionChanged += (_, _) => UpdateNestedSamples();
Path.Version.ValueChanged += _ => updateNestedPositions();
}

View File

@ -120,19 +120,19 @@ namespace osu.Game.Rulesets.Osu
{
switch (mod)
{
case OsuModAutopilot _:
case OsuModAutopilot:
value |= LegacyMods.Autopilot;
break;
case OsuModSpunOut _:
case OsuModSpunOut:
value |= LegacyMods.SpunOut;
break;
case OsuModTarget _:
case OsuModTarget:
value |= LegacyMods.Target;
break;
case OsuModTouchDevice _:
case OsuModTouchDevice:
value |= LegacyMods.TouchDevice;
break;
}

View File

@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Osu
protected override string RulesetPrefix => OsuRuleset.SHORT_NAME;
protected override string ComponentName => Component.ToString().ToLower();
protected override string ComponentName => Component.ToString().ToLowerInvariant();
}
}

View File

@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.Replays
hitWindows = slider.TailCircle.HitWindows;
break;
case Spinner _:
case Spinner:
hitWindows = defaultHitWindows;
break;
}

View File

@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
{
switch (hitObject)
{
case HitCircle _:
case HitCircle:
return new OsuHitCircleJudgementResult(hitObject, judgement);
default:

View File

@ -34,13 +34,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
set => ball.Colour = value;
}
/// <summary>
/// Whether to track accurately to the visual size of this <see cref="SliderBall"/>.
/// If <c>false</c>, tracking will be performed at the final scale at all times.
/// </summary>
public bool InputTracksVisualSize = true;
private readonly Drawable followCircle;
private readonly Drawable fullSizeFollowCircle;
private readonly DrawableSlider drawableSlider;
private readonly Drawable ball;
@ -62,6 +57,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
Alpha = 0,
Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderFollowCircle), _ => new DefaultFollowCircle()),
},
fullSizeFollowCircle = new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true
},
ball = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBall), _ => new DefaultSliderBall())
{
Anchor = Anchor.Centre,
@ -104,14 +106,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
tracking = value;
if (InputTracksVisualSize)
followCircle.ScaleTo(tracking ? 2.4f : 1f, 300, Easing.OutQuint);
else
{
// We need to always be tracking the final size, at both endpoints. For now, this is achieved by removing the scale duration.
followCircle.ScaleTo(tracking ? 2.4f : 1f);
}
fullSizeFollowCircle.Scale = new Vector2(tracking ? 2.4f : 1f);
followCircle.ScaleTo(tracking ? 2.4f : 1f, 300, Easing.OutQuint);
followCircle.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint);
}
}
@ -170,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
// in valid time range
Time.Current >= drawableSlider.HitObject.StartTime && Time.Current < drawableSlider.HitObject.EndTime &&
// in valid position range
lastScreenSpaceMousePosition.HasValue && followCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) &&
lastScreenSpaceMousePosition.HasValue && fullSizeFollowCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) &&
// valid action
(actions?.Any(isValidTrackingAction) ?? false);

View File

@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
break;
case DrawableSpinnerBonusTick _:
case DrawableSpinnerBonusTick:
if (state == ArmedState.Hit)
glow.FlashColour(Color4.White, 200);

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.UI
switch (obj)
{
case DrawableSpinner _:
case DrawableSpinner:
continue;
case DrawableSlider slider:

View File

@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Osu.UI
// note: `Slider`'s `ProxiedLayer` is added when its nested `DrawableHitCircle` is loaded.
switch (drawable)
{
case DrawableSpinner _:
case DrawableSpinner:
spinnerProxies.Add(drawable.CreateProxy());
break;

View File

@ -88,11 +88,11 @@ namespace osu.Game.Rulesets.Osu.Utils
switch (hitObject)
{
case HitCircle _:
case HitCircle:
shift = clampHitCircleToPlayfield(current);
break;
case Slider _:
case Slider:
shift = clampSliderToPlayfield(current);
break;
}

View File

@ -144,7 +144,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
createDrawableRuleset();
assertStateAfterResult(new JudgementResult(new Swell(), new TaikoSwellJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Clear);
AddUntilStep($"state reverts to {expectedStateAfterClear.ToString().ToLower()}", () => allMascotsIn(expectedStateAfterClear));
AddUntilStep($"state reverts to {expectedStateAfterClear.ToString().ToLowerInvariant()}", () => allMascotsIn(expectedStateAfterClear));
}
private void setBeatmap(bool kiai = false)
@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
{
TaikoMascotAnimationState[] mascotStates = null;
AddStep($"{judgementResult.Type.ToString().ToLower()} result for {judgementResult.Judgement.GetType().Name.Humanize(LetterCasing.LowerCase)}",
AddStep($"{judgementResult.Type.ToString().ToLowerInvariant()} result for {judgementResult.Judgement.GetType().Name.Humanize(LetterCasing.LowerCase)}",
() =>
{
applyNewResult(judgementResult);
@ -204,7 +204,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
Schedule(() => mascotStates = animatedMascots.Select(mascot => mascot.State.Value).ToArray());
});
AddAssert($"state is {expectedState.ToString().ToLower()}", () => mascotStates.All(state => state == expectedState));
AddAssert($"state is {expectedState.ToString().ToLowerInvariant()}", () => mascotStates.All(state => state == expectedState));
}
private void applyNewResult(JudgementResult judgementResult)

View File

@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
AddStep("Setup judgements", () =>
{
judged = false;
Player.ScoreProcessor.NewJudgement += b => judged = true;
Player.ScoreProcessor.NewJudgement += _ => judged = true;
});
AddUntilStep("swell judged", () => judged);
AddAssert("failed", () => Player.GameplayState.HasFailed);

View File

@ -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 Newtonsoft.Json;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
namespace osu.Game.Rulesets.Taiko.Difficulty
@ -57,9 +56,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow);
}
public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> values)
public override void FromDatabaseAttributes(IReadOnlyDictionary<int, double> values, IBeatmapOnlineInfo onlineInfo)
{
base.FromDatabaseAttributes(values);
base.FromDatabaseAttributes(values, onlineInfo);
MaxCombo = (int)values[ATTRIB_ID_MAX_COMBO];
StarRating = values[ATTRIB_ID_DIFFICULTY];

View File

@ -48,8 +48,8 @@ namespace osu.Game.Rulesets.Taiko.Mods
{
switch (hitObject)
{
case DrawableDrumRollTick _:
case DrawableHit _:
case DrawableDrumRollTick:
case DrawableHit:
double preempt = drawableRuleset.TimeRange.Value / drawableRuleset.ControlPointAt(hitObject.HitObject.StartTime).Multiplier;
double start = hitObject.HitObject.StartTime - preempt * fade_out_start_time;
double duration = preempt * fade_out_duration;

View File

@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
DisplayColour.Value = Type == HitType.Centre ? COLOUR_CENTRE : COLOUR_RIM;
});
SamplesBindable.BindCollectionChanged((_, __) => updateTypeFromSamples());
SamplesBindable.BindCollectionChanged((_, _) => updateTypeFromSamples());
}
private void updateTypeFromSamples()

View File

@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
protected TaikoStrongableHitObject()
{
IsStrongBindable.BindValueChanged(_ => updateSamplesFromType());
SamplesBindable.BindCollectionChanged((_, __) => updateTypeFromSamples());
SamplesBindable.BindCollectionChanged((_, _) => updateTypeFromSamples());
}
private void updateTypeFromSamples()

View File

@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Taiko
protected override string RulesetPrefix => TaikoRuleset.SHORT_NAME;
protected override string ComponentName => Component.ToString().ToLower();
protected override string ComponentName => Component.ToString().ToLowerInvariant();
}
}

View File

@ -139,10 +139,10 @@ namespace osu.Game.Rulesets.Taiko.UI
private static Texture getAnimationFrame(ISkin skin, TaikoMascotAnimationState state, int frameIndex)
{
var texture = skin.GetTexture($"pippidon{state.ToString().ToLower()}{frameIndex}");
var texture = skin.GetTexture($"pippidon{state.ToString().ToLowerInvariant()}{frameIndex}");
if (frameIndex == 0 && texture == null)
texture = skin.GetTexture($"pippidon{state.ToString().ToLower()}");
texture = skin.GetTexture($"pippidon{state.ToString().ToLowerInvariant()}");
return texture;
}

View File

@ -245,7 +245,7 @@ namespace osu.Game.Rulesets.Taiko.UI
barLinePlayfield.Add(barLine);
break;
case DrawableTaikoHitObject _:
case DrawableTaikoHitObject:
base.Add(h);
break;
@ -261,7 +261,7 @@ namespace osu.Game.Rulesets.Taiko.UI
case DrawableBarLine barLine:
return barLinePlayfield.Remove(barLine);
case DrawableTaikoHitObject _:
case DrawableTaikoHitObject:
return base.Remove(h);
default:
@ -280,12 +280,12 @@ namespace osu.Game.Rulesets.Taiko.UI
switch (result.Judgement)
{
case TaikoStrongJudgement _:
case TaikoStrongJudgement:
if (result.IsHit)
hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).ParentHitObject)?.VisualiseSecondHit(result);
break;
case TaikoDrumRollTickJudgement _:
case TaikoDrumRollTickJudgement:
if (!result.IsHit)
break;

View File

@ -157,7 +157,7 @@ namespace osu.Game.Tests.Beatmaps
[TestCase(8.3, DifficultyRating.ExpertPlus)]
public void TestDifficultyRatingMapping(double starRating, DifficultyRating expectedBracket)
{
var actualBracket = BeatmapDifficultyCache.GetDifficultyRating(starRating);
var actualBracket = StarDifficulty.GetDifficultyRating(starRating);
Assert.AreEqual(expectedBracket, actualBracket);
}

View File

@ -99,7 +99,7 @@ namespace osu.Game.Tests.Collections.IO
public async Task TestImportMalformedDatabase()
{
bool exceptionThrown = false;
UnhandledExceptionEventHandler setException = (_, __) => exceptionThrown = true;
UnhandledExceptionEventHandler setException = (_, _) => exceptionThrown = true;
using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{

View File

@ -15,7 +15,6 @@ using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Extensions;
using osu.Game.IO.Archives;
using osu.Game.Models;
using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets;
@ -39,10 +38,7 @@ namespace osu.Game.Tests.Database
using (var importer = new BeatmapImporter(storage, realm))
using (new RealmRulesetStore(realm, storage))
{
Live<BeatmapSetInfo>? beatmapSet;
using (var reader = new ZipArchiveReader(TestResources.GetTestBeatmapStream()))
beatmapSet = await importer.Import(reader);
var beatmapSet = await importer.Import(new ImportTask(TestResources.GetTestBeatmapStream(), "renatus.osz"));
Assert.NotNull(beatmapSet);
Debug.Assert(beatmapSet != null);
@ -83,10 +79,7 @@ namespace osu.Game.Tests.Database
using (var importer = new BeatmapImporter(storage, realm))
using (new RealmRulesetStore(realm, storage))
{
Live<BeatmapSetInfo>? beatmapSet;
using (var reader = new ZipArchiveReader(TestResources.GetTestBeatmapStream()))
beatmapSet = await importer.Import(reader);
var beatmapSet = await importer.Import(new ImportTask(TestResources.GetTestBeatmapStream(), "renatus.osz"));
Assert.NotNull(beatmapSet);
Debug.Assert(beatmapSet != null);
@ -146,11 +139,8 @@ namespace osu.Game.Tests.Database
{
Task.Run(async () =>
{
Live<BeatmapSetInfo>? beatmapSet;
using (var reader = new ZipArchiveReader(TestResources.GetTestBeatmapStream()))
// ReSharper disable once AccessToDisposedClosure
beatmapSet = await importer.Import(reader);
// ReSharper disable once AccessToDisposedClosure
var beatmapSet = await importer.Import(new ImportTask(TestResources.GetTestBeatmapStream(), "renatus.osz"));
Assert.NotNull(beatmapSet);
Debug.Assert(beatmapSet != null);
@ -173,13 +163,8 @@ namespace osu.Game.Tests.Database
using (var importer = new BeatmapImporter(storage, realm))
using (new RealmRulesetStore(realm, storage))
{
Live<BeatmapSetInfo>? imported;
using (var reader = new ZipArchiveReader(TestResources.GetTestBeatmapStream()))
{
imported = await importer.Import(reader);
EnsureLoaded(realm.Realm);
}
var imported = await importer.Import(new ImportTask(TestResources.GetTestBeatmapStream(), "renatus.osz"));
EnsureLoaded(realm.Realm);
Assert.AreEqual(1, realm.Realm.All<BeatmapSetInfo>().Count());
@ -291,6 +276,42 @@ namespace osu.Game.Tests.Database
});
}
[Test]
public void TestImportDirectoryWithEmptyOsuFiles()
{
RunTestWithRealmAsync(async (realm, storage) =>
{
using var importer = new BeatmapImporter(storage, realm);
using var store = new RealmRulesetStore(realm, storage);
string? temp = TestResources.GetTestBeatmapForImport();
string extractedFolder = $"{temp}_extracted";
Directory.CreateDirectory(extractedFolder);
try
{
using (var zip = ZipArchive.Open(temp))
zip.WriteToDirectory(extractedFolder);
foreach (var file in new DirectoryInfo(extractedFolder).GetFiles("*.osu"))
{
using (file.Open(FileMode.Create))
{
// empty file.
}
}
var imported = await importer.Import(new ImportTask(extractedFolder));
Assert.IsNull(imported);
}
finally
{
Directory.Delete(extractedFolder, true);
}
});
}
[Test]
public void TestImportThenImportWithReZip()
{
@ -586,6 +607,12 @@ namespace osu.Game.Tests.Database
using (var outStream = File.Open(brokenTempFilename, FileMode.CreateNew))
using (var zip = ZipArchive.Open(brokenOsz))
{
foreach (var entry in zip.Entries.ToArray())
{
if (entry.Key.EndsWith(".osu", StringComparison.InvariantCulture))
zip.RemoveEntry(entry);
}
zip.AddEntry("broken.osu", brokenOsu, false);
zip.SaveTo(outStream, CompressionType.Deflate);
}
@ -606,7 +633,7 @@ namespace osu.Game.Tests.Database
checkSingleReferencedFileCount(realm.Realm, 18);
Assert.AreEqual(1, loggedExceptionCount);
Assert.AreEqual(0, loggedExceptionCount);
File.Delete(brokenTempFilename);
});
@ -760,7 +787,7 @@ namespace osu.Game.Tests.Database
}
};
var imported = importer.Import(toImport);
var imported = importer.ImportModel(toImport);
realm.Run(r => r.Refresh());

View File

@ -46,7 +46,7 @@ namespace osu.Game.Tests.Database
realm.RegisterCustomSubscription(r =>
{
var subscription = r.All<BeatmapInfo>().QueryAsyncWithNotifications((sender, changes, error) =>
var subscription = r.All<BeatmapInfo>().QueryAsyncWithNotifications((_, _, _) =>
{
realm.Run(_ =>
{
@ -79,11 +79,11 @@ namespace osu.Game.Tests.Database
{
hasThreadedUsage.Set();
stopThreadedUsage.Wait();
stopThreadedUsage.Wait(60000);
});
}, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler);
hasThreadedUsage.Wait();
hasThreadedUsage.Wait(60000);
Assert.Throws<TimeoutException>(() =>
{

View File

@ -189,7 +189,7 @@ namespace osu.Game.Tests.Database
});
// Can't be used, even from within a valid context.
realm.Run(threadContext =>
realm.Run(_ =>
{
Assert.Throws<InvalidOperationException>(() =>
{

View File

@ -192,7 +192,7 @@ namespace osu.Game.Tests.Gameplay
AddStep("apply perfect hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Perfect }));
AddAssert("not failed", () => !processor.HasFailed);
AddStep($"apply {resultApplied.ToString().ToLower()} hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = resultApplied }));
AddStep($"apply {resultApplied.ToString().ToLowerInvariant()} hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = resultApplied }));
AddAssert("failed", () => processor.HasFailed);
}

View File

@ -254,7 +254,7 @@ namespace osu.Game.Tests.NonVisual
Assert.That(File.Exists(Path.Combine(customPath, OsuGameBase.CLIENT_DATABASE_FILENAME)));
Directory.CreateDirectory(customPath2);
File.Copy(Path.Combine(customPath, OsuGameBase.CLIENT_DATABASE_FILENAME), Path.Combine(customPath2, OsuGameBase.CLIENT_DATABASE_FILENAME));
File.WriteAllText(Path.Combine(customPath2, OsuGameBase.CLIENT_DATABASE_FILENAME), "I am a text");
// Fails because file already exists.
Assert.Throws<ArgumentException>(() => osu.Migrate(customPath2));

View File

@ -83,14 +83,14 @@ namespace osu.Game.Tests.NonVisual
public override event Action<JudgementResult> NewResult
{
add => throw new InvalidOperationException();
remove => throw new InvalidOperationException();
add => throw new InvalidOperationException($"{nameof(NewResult)} operations not supported in test context");
remove => throw new InvalidOperationException($"{nameof(NewResult)} operations not supported in test context");
}
public override event Action<JudgementResult> RevertResult
{
add => throw new InvalidOperationException();
remove => throw new InvalidOperationException();
add => throw new InvalidOperationException($"{nameof(RevertResult)} operations not supported in test context");
remove => throw new InvalidOperationException($"{nameof(RevertResult)} operations not supported in test context");
}
public override Playfield Playfield { get; }

View File

@ -364,12 +364,12 @@ namespace osu.Game.Tests.NonVisual
private void confirmCurrentFrame(int? frame)
{
Assert.AreEqual(frame is int x ? replay.Frames[x].Time : (double?)null, handler.CurrentFrame?.Time, "Unexpected current frame");
Assert.AreEqual(frame is int x ? replay.Frames[x].Time : null, handler.CurrentFrame?.Time, "Unexpected current frame");
}
private void confirmNextFrame(int? frame)
{
Assert.AreEqual(frame is int x ? replay.Frames[x].Time : (double?)null, handler.NextFrame?.Time, "Unexpected next frame");
Assert.AreEqual(frame is int x ? replay.Frames[x].Time : null, handler.NextFrame?.Time, "Unexpected next frame");
}
private class TestReplayFrame : ReplayFrame

View File

@ -157,7 +157,7 @@ namespace osu.Game.Tests.NonVisual.Skinning
{
// use an incrementing width to allow assertion matching on correct textures as they turn from uploads into actual textures.
int width = 1;
Textures = fileNames.ToDictionary(fileName => fileName, fileName => new TextureUpload(new Image<Rgba32>(width, width++)));
Textures = fileNames.ToDictionary(fileName => fileName, _ => new TextureUpload(new Image<Rgba32>(width, width++)));
}
public TextureUpload Get(string name) => Textures.GetValueOrDefault(name);

View File

@ -227,10 +227,10 @@ namespace osu.Game.Tests.Online
this.testBeatmapManager = testBeatmapManager;
}
public override Live<BeatmapSetInfo> Import(BeatmapSetInfo item, ArchiveReader archive = null, bool batchImport = false, CancellationToken cancellationToken = default)
public override Live<BeatmapSetInfo> ImportModel(BeatmapSetInfo item, ArchiveReader archive = null, bool batchImport = false, CancellationToken cancellationToken = default)
{
testBeatmapManager.AllowImport.Task.WaitSafely();
return (testBeatmapManager.CurrentImport = base.Import(item, archive, batchImport, cancellationToken));
return (testBeatmapManager.CurrentImport = base.ImportModel(item, archive, batchImport, cancellationToken));
}
}
}

View File

@ -15,7 +15,6 @@ using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.Extensions;
using osu.Game.IO;
using osu.Game.IO.Archives;
using osu.Game.Skinning;
using SharpCompress.Archives.Zip;
@ -28,7 +27,7 @@ namespace osu.Game.Tests.Skins.IO
[Test]
public Task TestSingleImportDifferentFilename() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk"));
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("test skin", "skinner"), "skin.osk"));
// When the import filename doesn't match, it should be appended (and update the skin.ini).
assertCorrectMetadata(import1, "test skin [skin]", "skinner", osu);
@ -37,7 +36,7 @@ namespace osu.Game.Tests.Skins.IO
[Test]
public Task TestSingleImportWeirdIniFileCase() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner", iniFilename: "Skin.InI"), "skin.osk"));
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("test skin", "skinner", iniFilename: "Skin.InI"), "skin.osk"));
// When the import filename doesn't match, it should be appended (and update the skin.ini).
assertCorrectMetadata(import1, "test skin [skin]", "skinner", osu);
@ -46,7 +45,7 @@ namespace osu.Game.Tests.Skins.IO
[Test]
public Task TestSingleImportMissingSectionHeader() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner", includeSectionHeader: false), "skin.osk"));
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("test skin", "skinner", includeSectionHeader: false), "skin.osk"));
// When the import filename doesn't match, it should be appended (and update the skin.ini).
assertCorrectMetadata(import1, "test skin [skin]", "skinner", osu);
@ -55,7 +54,7 @@ namespace osu.Game.Tests.Skins.IO
[Test]
public Task TestSingleImportMatchingFilename() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "test skin.osk"));
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("test skin", "skinner"), "test skin.osk"));
// When the import filename matches it shouldn't be appended.
assertCorrectMetadata(import1, "test skin", "skinner", osu);
@ -64,7 +63,7 @@ namespace osu.Game.Tests.Skins.IO
[Test]
public Task TestSingleImportNoIniFile() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithNonIniFile(), "test skin.osk"));
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithNonIniFile(), "test skin.osk"));
// When the import filename matches it shouldn't be appended.
assertCorrectMetadata(import1, "test skin", "Unknown", osu);
@ -73,7 +72,7 @@ namespace osu.Game.Tests.Skins.IO
[Test]
public Task TestEmptyImportImportsWithFilename() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createEmptyOsk(), "test skin.osk"));
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createEmptyOsk(), "test skin.osk"));
// When the import filename matches it shouldn't be appended.
assertCorrectMetadata(import1, "test skin", "Unknown", osu);
@ -86,8 +85,8 @@ namespace osu.Game.Tests.Skins.IO
[Test]
public Task TestImportTwiceWithSameMetadataAndFilename() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk"));
var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk"));
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"));
assertImportedOnce(import1, import2);
});
@ -96,8 +95,8 @@ namespace osu.Game.Tests.Skins.IO
public Task TestImportTwiceWithNoMetadataSameDownloadFilename() => 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 ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download.osk"));
var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download.osk"));
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"));
assertImportedOnce(import1, import2);
});
@ -105,10 +104,10 @@ namespace osu.Game.Tests.Skins.IO
[Test]
public Task TestImportUpperCasedOskArchive() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "name 1.OsK"));
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("name 1", "author 1"), "name 1.OsK"));
assertCorrectMetadata(import1, "name 1", "author 1", osu);
var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "name 1.oSK"));
var import2 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("name 1", "author 1"), "name 1.oSK"));
assertImportedOnce(import1, import2);
});
@ -118,7 +117,7 @@ namespace osu.Game.Tests.Skins.IO
{
MemoryStream exportStream = new MemoryStream();
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "custom.osk"));
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("name 1", "author 1"), "custom.osk"));
assertCorrectMetadata(import1, "name 1 [custom]", "author 1", osu);
import1.PerformRead(s =>
@ -128,7 +127,7 @@ namespace osu.Game.Tests.Skins.IO
string exportFilename = import1.GetDisplayString();
var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(exportStream, $"{exportFilename}.osk"));
var import2 = await loadSkinIntoOsu(osu, new ImportTask(exportStream, $"{exportFilename}.osk"));
assertCorrectMetadata(import2, "name 1 [custom]", "author 1", osu);
assertImportedOnce(import1, import2);
@ -137,8 +136,8 @@ namespace osu.Game.Tests.Skins.IO
[Test]
public Task TestSameMetadataNameSameFolderName() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 1"));
var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(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"));
var import2 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("name 1", "author 1"), "my custom skin 1"));
assertImportedOnce(import1, import2);
assertCorrectMetadata(import1, "name 1 [my custom skin 1]", "author 1", osu);
@ -151,8 +150,8 @@ namespace osu.Game.Tests.Skins.IO
[Test]
public Task TestImportTwiceWithSameMetadataButDifferentFilename() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk"));
var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin2.osk"));
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("test skin", "skinner"), "skin.osk"));
var import2 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("test skin", "skinner"), "skin2.osk"));
assertImportedBoth(import1, import2);
});
@ -161,8 +160,8 @@ namespace osu.Game.Tests.Skins.IO
public Task TestImportTwiceWithNoMetadataDifferentDownloadFilename() => 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 ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download.osk"));
var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download2.osk"));
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), "download2.osk"));
assertImportedBoth(import1, import2);
});
@ -170,8 +169,8 @@ namespace osu.Game.Tests.Skins.IO
[Test]
public Task TestImportTwiceWithSameFilenameDifferentMetadata() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin v2", "skinner"), "skin.osk"));
var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin v2.1", "skinner"), "skin.osk"));
var import1 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("test skin v2", "skinner"), "skin.osk"));
var import2 = await loadSkinIntoOsu(osu, new ImportTask(createOskWithIni("test skin v2.1", "skinner"), "skin.osk"));
assertImportedBoth(import1, import2);
assertCorrectMetadata(import1, "test skin v2 [skin]", "skinner", osu);
@ -181,8 +180,8 @@ namespace osu.Game.Tests.Skins.IO
[Test]
public Task TestSameMetadataNameDifferentFolderName() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 1"));
var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 2"));
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 2"));
assertImportedBoth(import1, import2);
assertCorrectMetadata(import1, "name 1 [my custom skin 1]", "author 1", osu);
@ -358,10 +357,10 @@ namespace osu.Game.Tests.Skins.IO
}
}
private async Task<Live<SkinInfo>> loadSkinIntoOsu(OsuGameBase osu, ArchiveReader archive = null)
private async Task<Live<SkinInfo>> loadSkinIntoOsu(OsuGameBase osu, ImportTask import)
{
var skinManager = osu.Dependencies.Get<SkinManager>();
return await skinManager.Import(archive);
return await skinManager.Import(import);
}
}
}

View File

@ -58,7 +58,7 @@ namespace osu.Game.Tests.Skins
{
AddStep($"Set beatmap skin enabled to {allowBeatmapLookups}", () => config.SetValue(OsuSetting.BeatmapSkins, allowBeatmapLookups));
ISkin expected() => allowBeatmapLookups ? (ISkin)beatmapSource : userSource;
ISkin expected() => allowBeatmapLookups ? beatmapSource : userSource;
AddAssert("Check lookup is from correct source", () => requester.FindProvider(s => s.GetDrawableComponent(new TestSkinComponent()) != null) == expected());
}

View File

@ -10,7 +10,7 @@ using osu.Framework.Extensions;
using osu.Framework.Testing;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.IO.Archives;
using osu.Game.Database;
using osu.Game.Tests.Resources;
using osu.Game.Tests.Visual;
@ -27,7 +27,7 @@ namespace osu.Game.Tests.Skins
[BackgroundDependencyLoader]
private void load()
{
var imported = beatmaps.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-beatmap.osz"))).GetResultSafely();
var imported = beatmaps.Import(new ImportTask(TestResources.OpenResource("Archives/ogg-beatmap.osz"), "ogg-beatmap.osz")).GetResultSafely();
imported?.PerformRead(s =>
{

View File

@ -8,7 +8,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Testing;
using osu.Game.Audio;
using osu.Game.IO.Archives;
using osu.Game.Database;
using osu.Game.Skinning;
using osu.Game.Tests.Resources;
using osu.Game.Tests.Visual;
@ -26,7 +26,7 @@ namespace osu.Game.Tests.Skins
[BackgroundDependencyLoader]
private void load()
{
var imported = skins.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-skin.osk"))).GetResultSafely();
var imported = skins.Import(new ImportTask(TestResources.OpenResource("Archives/ogg-skin.osk"), "ogg-skin.osk")).GetResultSafely();
skin = imported.PerformRead(skinInfo => skins.GetSkin(skinInfo));
}

View File

@ -36,8 +36,6 @@ namespace osu.Game.Tests.Visual.Editing
{
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
protected override bool EditorComponentsReady => Editor.ChildrenOfType<SetupScreen>().SingleOrDefault()?.IsLoaded == true;
protected override bool IsolateSavingFromDatabase => false;
[Resolved]
@ -95,15 +93,23 @@ namespace osu.Game.Tests.Visual.Editing
string extractedFolder = $"{temp}_extracted";
Directory.CreateDirectory(extractedFolder);
using (var zip = ZipArchive.Open(temp))
zip.WriteToDirectory(extractedFolder);
try
{
using (var zip = ZipArchive.Open(temp))
zip.WriteToDirectory(extractedFolder);
bool success = setup.ChildrenOfType<ResourcesSection>().First().ChangeAudioTrack(Path.Combine(extractedFolder, "03. Renatus - Soleily 192kbps.mp3"));
bool success = setup.ChildrenOfType<ResourcesSection>().First().ChangeAudioTrack(new FileInfo(Path.Combine(extractedFolder, "03. Renatus - Soleily 192kbps.mp3")));
File.Delete(temp);
Directory.Delete(extractedFolder, true);
// ensure audio file is copied to beatmap as "audio.mp3" rather than original filename.
Assert.That(Beatmap.Value.Metadata.AudioFile == "audio.mp3");
return success;
return success;
}
finally
{
File.Delete(temp);
Directory.Delete(extractedFolder, true);
}
});
AddAssert("track length changed", () => Beatmap.Value.Track.Length > 60000);

View File

@ -0,0 +1,60 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using NUnit.Framework;
using osu.Framework.Extensions;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Components.Timelines.Summary;
using osu.Game.Screens.Edit.GameplayTest;
using osu.Game.Screens.Select;
using osu.Game.Tests.Resources;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Editing
{
public class TestSceneEditorNavigation : OsuGameTestScene
{
[Test]
public void TestEditorGameplayTestAlwaysUsesOriginalRuleset()
{
BeatmapSetInfo beatmapSet = null!;
AddStep("import test beatmap", () => Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely());
AddStep("retrieve beatmap", () => beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach());
AddStep("present beatmap", () => Game.PresentBeatmap(beatmapSet));
AddUntilStep("wait for song select",
() => Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet)
&& Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect
&& songSelect.IsLoaded);
AddUntilStep("wait for completion notification", () => Game.Notifications.ChildrenOfType<ProgressCompletionNotification>().Count() == 1);
AddStep("dismiss notifications", () => Game.Notifications.Hide());
AddStep("switch ruleset", () => Game.Ruleset.Value = new ManiaRuleset().RulesetInfo);
AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0)));
AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse);
AddStep("test gameplay", () =>
{
var testGameplayButton = this.ChildrenOfType<TestGameplayButton>().Single();
InputManager.MoveMouseTo(testGameplayButton);
InputManager.Click(MouseButton.Left);
});
AddUntilStep("wait for player", () => Game.ScreenStack.CurrentScreen is EditorPlayer editorPlayer && editorPlayer.IsLoaded);
AddAssert("current ruleset is osu!", () => Game.Ruleset.Value.Equals(new OsuRuleset().RulesetInfo));
AddStep("exit to song select", () => Game.PerformFromScreen(_ => { }, typeof(PlaySongSelect).Yield()));
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect);
AddAssert("previous ruleset restored", () => Game.Ruleset.Value.Equals(new ManiaRuleset().RulesetInfo));
}
}
}

View File

@ -117,8 +117,8 @@ namespace osu.Game.Tests.Visual.Editing
// After placement these must be non-default as defaults are read-only.
AddAssert("Placed object has non-default control points", () =>
EditorBeatmap.HitObjects[0].SampleControlPoint != SampleControlPoint.DEFAULT &&
EditorBeatmap.HitObjects[0].DifficultyControlPoint != DifficultyControlPoint.DEFAULT);
!ReferenceEquals(EditorBeatmap.HitObjects[0].SampleControlPoint, SampleControlPoint.DEFAULT) &&
!ReferenceEquals(EditorBeatmap.HitObjects[0].DifficultyControlPoint, DifficultyControlPoint.DEFAULT));
ReloadEditorToSameBeatmap();
@ -126,8 +126,8 @@ namespace osu.Game.Tests.Visual.Editing
// After placement these must be non-default as defaults are read-only.
AddAssert("Placed object still has non-default control points", () =>
EditorBeatmap.HitObjects[0].SampleControlPoint != SampleControlPoint.DEFAULT &&
EditorBeatmap.HitObjects[0].DifficultyControlPoint != DifficultyControlPoint.DEFAULT);
!ReferenceEquals(EditorBeatmap.HitObjects[0].SampleControlPoint, SampleControlPoint.DEFAULT) &&
!ReferenceEquals(EditorBeatmap.HitObjects[0].DifficultyControlPoint, DifficultyControlPoint.DEFAULT));
}
[Test]

View File

@ -322,8 +322,8 @@ namespace osu.Game.Tests.Visual.Gameplay
{
switch (h)
{
case TestPooledHitObject _:
case TestPooledParentHitObject _:
case TestPooledHitObject:
case TestPooledParentHitObject:
return null;
case TestParentHitObject p:

View File

@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Gameplay
protected override void LoadComplete()
{
base.LoadComplete();
HealthProcessor.FailConditions += (_, __) => true;
HealthProcessor.FailConditions += (_, _) => true;
}
private double lastFrequency = double.MaxValue;

View File

@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Gameplay
protected override void LoadComplete()
{
base.LoadComplete();
HealthProcessor.FailConditions += (_, __) => true;
HealthProcessor.FailConditions += (_, _) => true;
}
}
}

View File

@ -273,14 +273,14 @@ namespace osu.Game.Tests.Visual.Gameplay
public override event Action<JudgementResult> NewResult
{
add => throw new InvalidOperationException();
remove => throw new InvalidOperationException();
add => throw new InvalidOperationException($"{nameof(NewResult)} operations not supported in test context");
remove => throw new InvalidOperationException($"{nameof(NewResult)} operations not supported in test context");
}
public override event Action<JudgementResult> RevertResult
{
add => throw new InvalidOperationException();
remove => throw new InvalidOperationException();
add => throw new InvalidOperationException($"{nameof(RevertResult)} operations not supported in test context");
remove => throw new InvalidOperationException($"{nameof(RevertResult)} operations not supported in test context");
}
public override Playfield Playfield { get; }

View File

@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestToggleEditor()
{
AddToggleStep("toggle editor visibility", visible => skinEditor.ToggleVisibility());
AddToggleStep("toggle editor visibility", _ => skinEditor.ToggleVisibility());
}
[Test]

View File

@ -103,7 +103,7 @@ namespace osu.Game.Tests.Visual.Gameplay
Child = new SkinProvidingContainer(secondarySource)
{
RelativeSizeAxes = Axes.Both,
Child = consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"))
Child = consumer = new SkinConsumer("test", _ => new NamedBox("Default Implementation"))
}
};
});
@ -132,7 +132,7 @@ namespace osu.Game.Tests.Visual.Gameplay
};
});
AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"))));
AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", _ => new NamedBox("Default Implementation"))));
AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox);
AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1);
}
@ -155,7 +155,7 @@ namespace osu.Game.Tests.Visual.Gameplay
};
});
AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"))));
AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", _ => new NamedBox("Default Implementation"))));
AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox);
AddStep("disable", () => target.Disable());
AddAssert("consumer using base source", () => consumer.Drawable is BaseSourceBox);

View File

@ -167,11 +167,16 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("start failing sends", () =>
{
spectatorClient.ShouldFailSendingFrames = true;
framesReceivedSoFar = replay.Frames.Count;
frameSendAttemptsSoFar = spectatorClient.FrameSendAttempts;
});
AddUntilStep("wait for send attempts", () => spectatorClient.FrameSendAttempts > frameSendAttemptsSoFar + 5);
AddUntilStep("wait for next send attempt", () =>
{
framesReceivedSoFar = replay.Frames.Count;
return spectatorClient.FrameSendAttempts > frameSendAttemptsSoFar + 1;
});
AddUntilStep("wait for more send attempts", () => spectatorClient.FrameSendAttempts > frameSendAttemptsSoFar + 10);
AddAssert("frames did not increase", () => framesReceivedSoFar == replay.Frames.Count);
AddStep("stop failing sends", () => spectatorClient.ShouldFailSendingFrames = false);

View File

@ -121,7 +121,7 @@ namespace osu.Game.Tests.Visual.Gameplay
private void createPlayerTest()
{
CreateTest(null);
CreateTest();
AddAssert("storyboard loaded", () => Player.Beatmap.Value.Storyboard != null);
waitUntilStoryboardSamplesPlay();

View File

@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Gameplay
base.SetUpSteps();
AddStep("enable storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, true));
AddStep("set dim level to 0", () => LocalConfig.SetValue<double>(OsuSetting.DimLevel, 0));
AddStep("reset fail conditions", () => currentFailConditions = (_, __) => false);
AddStep("reset fail conditions", () => currentFailConditions = (_, _) => false);
AddStep("set storyboard duration to 2s", () => currentStoryboardDuration = 2000);
AddStep("set ShowResults = true", () => showResults = true);
}
@ -52,17 +52,18 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestStoryboardSkipOutro()
{
CreateTest(null);
AddStep("set storyboard duration to long", () => currentStoryboardDuration = 200000);
CreateTest();
AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value);
AddStep("skip outro", () => InputManager.Key(osuTK.Input.Key.Space));
AddAssert("player is no longer current screen", () => !Player.IsCurrentScreen());
AddUntilStep("player is no longer current screen", () => !Player.IsCurrentScreen());
AddUntilStep("wait for score shown", () => Player.IsScoreShown);
}
[Test]
public void TestStoryboardNoSkipOutro()
{
CreateTest(null);
CreateTest();
AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= currentStoryboardDuration);
AddUntilStep("wait for score shown", () => Player.IsScoreShown);
}
@ -70,7 +71,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestStoryboardExitDuringOutroStillExits()
{
CreateTest(null);
CreateTest();
AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value);
AddStep("exit via pause", () => Player.ExitViaPause());
AddAssert("player exited", () => !Player.IsCurrentScreen() && Player.GetChildScreen() == null);
@ -80,7 +81,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[TestCase(true)]
public void TestStoryboardToggle(bool enabledAtBeginning)
{
CreateTest(null);
CreateTest();
AddStep($"{(enabledAtBeginning ? "enable" : "disable")} storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, enabledAtBeginning));
AddStep("toggle storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, !enabledAtBeginning));
AddUntilStep("wait for score shown", () => Player.IsScoreShown);
@ -91,7 +92,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
CreateTest(() =>
{
AddStep("fail on first judgement", () => currentFailConditions = (_, __) => true);
AddStep("fail on first judgement", () => currentFailConditions = (_, _) => true);
// Fail occurs at 164ms with the provided beatmap.
// Fail animation runs for 2.5s realtime but the gameplay time change is *variable* due to the frequency transform being applied, so we need a bit of lenience.
@ -129,7 +130,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
SkipOverlay.FadeContainer fadeContainer() => Player.ChildrenOfType<SkipOverlay.FadeContainer>().First();
CreateTest(null);
CreateTest();
AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value);
AddUntilStep("skip overlay content becomes visible", () => fadeContainer().State == Visibility.Visible);
@ -143,7 +144,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestPerformExitNoOutro()
{
CreateTest(null);
CreateTest();
AddStep("disable storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, false));
AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value);
AddStep("exit via pause", () => Player.ExitViaPause());

View File

@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
// To emulate `MultiplayerClient.CurrentMatchPlayingUserIds` we need a bindable list of *only IDs*.
// This tracks the list of users 1:1.
MultiplayerUsers.BindCollectionChanged((c, e) =>
MultiplayerUsers.BindCollectionChanged((_, e) =>
{
switch (e.Action)
{

View File

@ -42,11 +42,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
var mockLounge = new Mock<LoungeSubScreen>();
mockLounge
.Setup(l => l.Join(It.IsAny<Room>(), It.IsAny<string>(), It.IsAny<Action<Room>>(), It.IsAny<Action<string>>()))
.Callback<Room, string, Action<Room>, Action<string>>((a, b, c, d) =>
.Callback<Room, string, Action<Room>, Action<string>>((_, _, _, d) =>
{
Task.Run(() =>
{
allowResponseCallback.Wait();
allowResponseCallback.Wait(10000);
allowResponseCallback.Reset();
Schedule(() => d?.Invoke("Incorrect password"));
});

View File

@ -90,7 +90,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
setRoomCountdown(countdownStart.Duration);
break;
case StopCountdownRequest _:
case StopCountdownRequest:
multiplayerRoom.Countdown = null;
raiseRoomUpdated();
break;

View File

@ -0,0 +1,86 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using NUnit.Framework;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics.Containers;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.IPC;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.Navigation
{
[TestFixture]
[Ignore("This test cannot be run headless, as it requires the game host running the nested game to have IPC bound.")]
public class TestSceneInterProcessCommunication : OsuGameTestScene
{
private HeadlessGameHost ipcSenderHost = null!;
private OsuSchemeLinkIPCChannel osuSchemeLinkIPCReceiver = null!;
private OsuSchemeLinkIPCChannel osuSchemeLinkIPCSender = null!;
private const int requested_beatmap_set_id = 1;
[Resolved]
private GameHost gameHost { get; set; } = null!;
public override void SetUpSteps()
{
base.SetUpSteps();
AddStep("set up request handling", () =>
{
((DummyAPIAccess)API).HandleRequest = request =>
{
switch (request)
{
case GetBeatmapSetRequest gbr:
var apiBeatmapSet = CreateAPIBeatmapSet();
apiBeatmapSet.OnlineID = requested_beatmap_set_id;
apiBeatmapSet.Beatmaps = apiBeatmapSet.Beatmaps.Append(new APIBeatmap
{
DifficultyName = "Target difficulty",
OnlineID = 75,
}).ToArray();
gbr.TriggerSuccess(apiBeatmapSet);
return true;
}
return false;
};
});
AddStep("create IPC receiver channel", () => osuSchemeLinkIPCReceiver = new OsuSchemeLinkIPCChannel(gameHost, Game));
AddStep("create IPC sender channel", () =>
{
ipcSenderHost = new HeadlessGameHost(gameHost.Name, new HostOptions { BindIPC = true });
osuSchemeLinkIPCSender = new OsuSchemeLinkIPCChannel(ipcSenderHost);
});
}
[Test]
public void TestOsuSchemeLinkIPCChannel()
{
AddStep("open beatmap via IPC", () => osuSchemeLinkIPCSender.HandleLinkAsync($@"osu://s/{requested_beatmap_set_id}").WaitSafely());
AddUntilStep("beatmap overlay displayed", () => Game.ChildrenOfType<BeatmapSetOverlay>().FirstOrDefault()?.State.Value == Visibility.Visible);
AddUntilStep("beatmap overlay showing content", () => Game.ChildrenOfType<BeatmapSetOverlay>().FirstOrDefault()?.Header.BeatmapSet.Value.OnlineID == requested_beatmap_set_id);
}
public override void TearDownSteps()
{
AddStep("dispose IPC receiver", () => osuSchemeLinkIPCReceiver.Dispose());
AddStep("dispose IPC sender", () =>
{
osuSchemeLinkIPCSender.Dispose();
ipcSenderHost.Dispose();
});
base.TearDownSteps();
}
}
}

View File

@ -8,7 +8,9 @@ using NUnit.Framework;
using osu.Framework.Extensions;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Framework.Threading;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
@ -16,6 +18,7 @@ using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD.HitErrorMeters;
using osu.Game.Skinning.Editor;
using osu.Game.Tests.Beatmaps.IO;
using osuTK;
using osuTK.Input;
using static osu.Game.Tests.Visual.Navigation.TestSceneScreenNavigation;
@ -26,29 +29,6 @@ namespace osu.Game.Tests.Visual.Navigation
private TestPlaySongSelect songSelect;
private SkinEditor skinEditor => Game.ChildrenOfType<SkinEditor>().FirstOrDefault();
private void advanceToSongSelect()
{
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
}
private void openSkinEditor()
{
AddStep("open skin editor", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.PressKey(Key.ShiftLeft);
InputManager.Key(Key.S);
InputManager.ReleaseKey(Key.ControlLeft);
InputManager.ReleaseKey(Key.ShiftLeft);
});
AddUntilStep("skin editor loaded", () => skinEditor != null);
}
[Test]
public void TestEditComponentDuringGameplay()
{
@ -88,6 +68,68 @@ namespace osu.Game.Tests.Visual.Navigation
AddAssert("value is less than default", () => hitErrorMeter.JudgementLineThickness.Value < hitErrorMeter.JudgementLineThickness.Default);
}
[Test]
public void TestComponentsDeselectedOnSkinEditorHide()
{
advanceToSongSelect();
openSkinEditor();
switchToGameplayScene();
AddUntilStep("wait for components", () => skinEditor.ChildrenOfType<SkinBlueprint>().Any());
AddStep("select all components", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.Key(Key.A);
InputManager.ReleaseKey(Key.ControlLeft);
});
AddUntilStep("components selected", () => skinEditor.SelectedComponents.Count > 0);
toggleSkinEditor();
AddUntilStep("no components selected", () => skinEditor.SelectedComponents.Count == 0);
}
[Test]
public void TestSwitchScreenWhileDraggingComponent()
{
Vector2 firstBlueprintCentre = Vector2.Zero;
ScheduledDelegate movementDelegate = null;
advanceToSongSelect();
openSkinEditor();
AddStep("add skinnable component", () =>
{
skinEditor.ChildrenOfType<SkinComponentToolbox.ToolboxComponentButton>().First().TriggerClick();
});
AddUntilStep("newly added component selected", () => skinEditor.SelectedComponents.Count == 1);
AddStep("start drag", () =>
{
firstBlueprintCentre = skinEditor.ChildrenOfType<SkinBlueprint>().First().ScreenSpaceDrawQuad.Centre;
InputManager.MoveMouseTo(firstBlueprintCentre);
InputManager.PressButton(MouseButton.Left);
});
AddStep("start movement", () => movementDelegate = Scheduler.AddDelayed(() => { InputManager.MoveMouseTo(firstBlueprintCentre += new Vector2(1)); }, 10, true));
toggleSkinEditor();
AddStep("exit song select", () => songSelect.Exit());
AddUntilStep("wait for blueprints removed", () => !skinEditor.ChildrenOfType<SkinBlueprint>().Any());
AddStep("stop drag", () =>
{
InputManager.ReleaseButton(MouseButton.Left);
movementDelegate?.Cancel();
});
}
[Test]
public void TestAutoplayCompatibleModsRetainedOnEnteringGameplay()
{
@ -146,9 +188,40 @@ namespace osu.Game.Tests.Visual.Navigation
AddUntilStep("mod overlay closed", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden);
}
private void advanceToSongSelect()
{
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
}
private void openSkinEditor()
{
toggleSkinEditor();
AddUntilStep("skin editor loaded", () => skinEditor != null);
}
private void toggleSkinEditor()
{
AddStep("toggle skin editor", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.PressKey(Key.ShiftLeft);
InputManager.Key(Key.S);
InputManager.ReleaseKey(Key.ControlLeft);
InputManager.ReleaseKey(Key.ShiftLeft);
});
}
private void switchToGameplayScene()
{
AddStep("Click gameplay scene button", () => skinEditor.ChildrenOfType<SkinEditorSceneLibrary.SceneButton>().First(b => b.Text == "Gameplay").TriggerClick());
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
AddStep("Click gameplay scene button", () =>
{
InputManager.MoveMouseTo(skinEditor.ChildrenOfType<SkinEditorSceneLibrary.SceneButton>().First(b => b.Text == "Gameplay"));
InputManager.Click(MouseButton.Left);
});
AddUntilStep("wait for player", () =>
{

View File

@ -40,7 +40,6 @@ namespace osu.Game.Tests.Visual.Online
private ChannelManager channelManager;
private APIUser testUser;
private Channel testPMChannel;
private Channel[] testChannels;
private Channel testChannel1 => testChannels[0];
@ -53,7 +52,6 @@ namespace osu.Game.Tests.Visual.Online
public void SetUp() => Schedule(() =>
{
testUser = new APIUser { Username = "test user", Id = 5071479 };
testPMChannel = new Channel(testUser);
testChannels = Enumerable.Range(1, 10).Select(createPublicChannel).ToArray();
Child = new DependencyProvidingContainer
@ -80,6 +78,14 @@ namespace osu.Game.Tests.Visual.Online
{
switch (req)
{
case CreateChannelRequest createRequest:
createRequest.TriggerSuccess(new APIChatChannel
{
ChannelID = ((int)createRequest.Channel.Id),
RecentMessages = new List<Message>()
});
return true;
case GetUpdatesRequest getUpdates:
getUpdates.TriggerFailure(new WebException());
return true;
@ -181,7 +187,7 @@ namespace osu.Game.Tests.Visual.Online
{
AddStep("Show overlay", () => chatOverlay.Show());
AddAssert("Listing is visible", () => listingIsVisible);
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
joinTestChannel(0);
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
waitForChannel1Visible();
}
@ -203,12 +209,11 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestChannelCloseButton()
{
var testPMChannel = new Channel(testUser);
AddStep("Show overlay", () => chatOverlay.Show());
AddStep("Join PM and public channels", () =>
{
channelManager.JoinChannel(testChannel1);
channelManager.JoinChannel(testPMChannel);
});
joinTestChannel(0);
joinChannel(testPMChannel);
AddStep("Select PM channel", () => clickDrawable(getChannelListItem(testPMChannel)));
AddStep("Click close button", () =>
{
@ -229,7 +234,7 @@ namespace osu.Game.Tests.Visual.Online
public void TestChatCommand()
{
AddStep("Show overlay", () => chatOverlay.Show());
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
joinTestChannel(0);
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
AddStep("Open chat with user", () => channelManager.PostCommand($"chat {testUser.Username}"));
AddAssert("PM channel is selected", () =>
@ -248,14 +253,16 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestMultiplayerChannelIsNotShown()
{
Channel multiplayerChannel = null;
Channel multiplayerChannel;
AddStep("Show overlay", () => chatOverlay.Show());
AddStep("Join multiplayer channel", () => channelManager.JoinChannel(multiplayerChannel = new Channel(new APIUser())
joinChannel(multiplayerChannel = new Channel(new APIUser())
{
Name = "#mp_1",
Type = ChannelType.Multiplayer,
}));
});
AddAssert("Channel is joined", () => channelManager.JoinedChannels.Contains(multiplayerChannel));
AddUntilStep("Channel not present in listing", () => !chatOverlay.ChildrenOfType<ChannelListingItem>()
.Where(item => item.IsPresent)
@ -269,7 +276,7 @@ namespace osu.Game.Tests.Visual.Online
Message message = null;
AddStep("Show overlay", () => chatOverlay.Show());
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
joinTestChannel(0);
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
AddStep("Send message in channel 1", () =>
{
@ -291,8 +298,8 @@ namespace osu.Game.Tests.Visual.Online
Message message = null;
AddStep("Show overlay", () => chatOverlay.Show());
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
AddStep("Join channel 2", () => channelManager.JoinChannel(testChannel2));
joinTestChannel(0);
joinTestChannel(1);
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
AddStep("Send message in channel 2", () =>
{
@ -314,8 +321,8 @@ namespace osu.Game.Tests.Visual.Online
Message message = null;
AddStep("Show overlay", () => chatOverlay.Show());
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
AddStep("Join channel 2", () => channelManager.JoinChannel(testChannel2));
joinTestChannel(0);
joinTestChannel(1);
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
AddStep("Send message in channel 2", () =>
{
@ -337,7 +344,7 @@ namespace osu.Game.Tests.Visual.Online
{
Message message = null;
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
joinTestChannel(0);
AddStep("Send message in channel 1", () =>
{
testChannel1.AddNewMessages(message = new Message
@ -357,7 +364,7 @@ namespace osu.Game.Tests.Visual.Online
{
Message message = null;
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
joinTestChannel(0);
AddStep("Send message in channel 1", () =>
{
testChannel1.AddNewMessages(message = new Message
@ -378,7 +385,7 @@ namespace osu.Game.Tests.Visual.Online
{
AddStep("Show overlay", () => chatOverlay.Show());
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
joinTestChannel(0);
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
waitForChannel1Visible();
AddAssert("TextBox is focused", () => InputManager.FocusedDrawable == chatOverlayTextBox);
@ -404,11 +411,11 @@ namespace osu.Game.Tests.Visual.Online
chatOverlay.Show();
chatOverlay.SlowLoading = true;
});
AddStep("Join channel 1", () => channelManager.JoinChannel(testChannel1));
joinTestChannel(0);
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
AddUntilStep("Channel 1 loading", () => !channelIsVisible && chatOverlay.GetSlowLoadingChannel(testChannel1).LoadState == LoadState.Loading);
AddStep("Join channel 2", () => channelManager.JoinChannel(testChannel2));
joinTestChannel(1);
AddStep("Select channel 2", () => clickDrawable(getChannelListItem(testChannel2)));
AddUntilStep("Channel 2 loading", () => !channelIsVisible && chatOverlay.GetSlowLoadingChannel(testChannel2).LoadState == LoadState.Loading);
@ -461,19 +468,17 @@ namespace osu.Game.Tests.Visual.Online
Channel pmChannel1 = createPrivateChannel();
Channel pmChannel2 = createPrivateChannel();
AddStep("Show overlay with channels", () =>
{
channelManager.JoinChannel(testChannel1);
channelManager.JoinChannel(testChannel2);
channelManager.JoinChannel(pmChannel1);
channelManager.JoinChannel(pmChannel2);
channelManager.JoinChannel(announceChannel);
chatOverlay.Show();
});
joinTestChannel(0);
joinTestChannel(1);
joinChannel(pmChannel1);
joinChannel(pmChannel2);
joinChannel(announceChannel);
AddStep("Show overlay", () => chatOverlay.Show());
AddStep("Select channel 1", () => clickDrawable(getChannelListItem(testChannel1)));
waitForChannel1Visible();
AddStep("Press document next keys", () => InputManager.Keys(PlatformAction.DocumentNext));
waitForChannel2Visible();
@ -490,6 +495,18 @@ namespace osu.Game.Tests.Visual.Online
waitForChannel1Visible();
}
private void joinTestChannel(int i)
{
AddStep($"Join test channel {i}", () => channelManager.JoinChannel(testChannels[i]));
AddUntilStep("wait for join completed", () => testChannels[i].Joined.Value);
}
private void joinChannel(Channel channel)
{
AddStep($"Join channel {channel}", () => channelManager.JoinChannel(channel));
AddUntilStep("wait for join completed", () => channel.Joined.Value);
}
private void waitForChannel1Visible() =>
AddUntilStep("Channel 1 is visible", () => channelIsVisible && currentDrawableChannel?.Channel == testChannel1);
@ -549,7 +566,7 @@ namespace osu.Game.Tests.Visual.Online
private Channel createPrivateChannel()
{
int id = RNG.Next(0, 10000);
int id = RNG.Next(0, DummyAPIAccess.DUMMY_USER_ID - 1);
return new Channel(new APIUser
{
Id = id,
@ -559,7 +576,7 @@ namespace osu.Game.Tests.Visual.Online
private Channel createAnnounceChannel()
{
int id = RNG.Next(0, 10000);
int id = RNG.Next(0, DummyAPIAccess.DUMMY_USER_ID - 1);
return new Channel
{
Name = $"Announce {id}",

View File

@ -219,7 +219,7 @@ namespace osu.Game.Tests.Visual.Online
{
base.LoadComplete();
Metadata.BindValueChanged(metadata =>
Metadata.BindValueChanged(_ =>
{
foreach (var b in this.ChildrenOfType<YearButton>())
b.Action = () => YearChanged?.Invoke(b.Year);

View File

@ -187,8 +187,8 @@ namespace osu.Game.Tests.Visual.Playlists
// pre-check for requests we should be handling (as they are scheduled below).
switch (request)
{
case ShowPlaylistUserScoreRequest _:
case IndexPlaylistScoresRequest _:
case ShowPlaylistUserScoreRequest:
case IndexPlaylistScoresRequest:
break;
default:

View File

@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.Settings
public void ToggleVisibility()
{
AddWaitStep("wait some", 5);
AddToggleStep("toggle visibility", visible => settings.ToggleVisibility());
AddToggleStep("toggle visibility", _ => settings.ToggleVisibility());
}
[Test]

View File

@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
AddStep("set beatmap", () => advancedStats.BeatmapInfo = new BeatmapInfo
{
Ruleset = rulesets.GetRuleset(3) ?? throw new InvalidOperationException(),
Ruleset = rulesets.GetRuleset(3) ?? throw new InvalidOperationException("osu!mania ruleset not found"),
Difficulty = new BeatmapDifficulty
{
CircleSize = 5,

Some files were not shown because too many files have changed in this diff Show More