1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 09:27:29 +08:00

Merge branch 'ppy:master' into enhance-randomisation

This commit is contained in:
Pascal Pomper 2022-08-10 12:22:39 +02:00 committed by GitHub
commit f98ee3ff24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1182 changed files with 20136 additions and 9991 deletions

View File

@ -16,3 +16,10 @@ 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.
M:Humanizer.InflectorExtensions.Pascalize(System.String);Humanizer's .Pascalize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToPascalCase() instead.
M:Humanizer.InflectorExtensions.Camelize(System.String);Humanizer's .Camelize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToCamelCase() instead.
M:Humanizer.InflectorExtensions.Underscore(System.String);Humanizer's .Underscore() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToSnakeCase() instead.
M:Humanizer.InflectorExtensions.Kebaberize(System.String);Humanizer's .Kebaberize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToKebabCase() instead.

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

@ -9,7 +9,6 @@
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />

View File

@ -9,7 +9,6 @@
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />

View File

@ -4,8 +4,6 @@
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Bindings;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
@ -50,9 +48,6 @@ namespace osu.Game.Rulesets.Pippidon
new KeyBinding(InputKey.X, PippidonAction.Button2),
};
public override Drawable CreateIcon() => new Sprite
{
Texture = new TextureStore(new TextureLoaderStore(CreateResourceStore()), false).Get("Textures/coin"),
};
public override Drawable CreateIcon() => new PippidonRulesetIcon(this);
}
}

View File

@ -0,0 +1,26 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics.Rendering;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
namespace osu.Game.Rulesets.Pippidon
{
public class PippidonRulesetIcon : Sprite
{
private readonly Ruleset ruleset;
public PippidonRulesetIcon(Ruleset ruleset)
{
this.ruleset = ruleset;
}
[BackgroundDependencyLoader]
private void load(IRenderer renderer)
{
Texture = new TextureStore(renderer, new TextureLoaderStore(ruleset.CreateResourceStore()), false).Get("Textures/coin");
}
}
}

View File

@ -9,7 +9,6 @@
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />

View File

@ -9,7 +9,6 @@
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />

View File

@ -4,8 +4,6 @@
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Bindings;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
@ -47,10 +45,6 @@ namespace osu.Game.Rulesets.Pippidon
new KeyBinding(InputKey.S, PippidonAction.MoveDown),
};
public override Drawable CreateIcon() => new Sprite
{
Margin = new MarginPadding { Top = 3 },
Texture = new TextureStore(new TextureLoaderStore(CreateResourceStore()), false).Get("Textures/coin"),
};
public override Drawable CreateIcon() => new PippidonRulesetIcon(this);
}
}

View File

@ -0,0 +1,29 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Rendering;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
namespace osu.Game.Rulesets.Pippidon
{
public class PippidonRulesetIcon : Sprite
{
private readonly Ruleset ruleset;
public PippidonRulesetIcon(Ruleset ruleset)
{
this.ruleset = ruleset;
Margin = new MarginPadding { Top = 3 };
}
[BackgroundDependencyLoader]
private void load(IRenderer renderer)
{
Texture = new TextureStore(renderer, new TextureLoaderStore(ruleset.CreateResourceStore()), false).Get("Textures/coin");
}
}
}

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.810.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.810.2" />
</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,76 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Android.Input;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Localisation;
using osu.Game.Overlays.Settings;
namespace osu.Android
{
public class AndroidJoystickSettings : SettingsSubsection
{
protected override LocalisableString Header => JoystickSettingsStrings.JoystickGamepad;
private readonly AndroidJoystickHandler joystickHandler;
private readonly Bindable<bool> enabled = new BindableBool(true);
private SettingsSlider<float> deadzoneSlider = null!;
private Bindable<float> handlerDeadzone = null!;
private Bindable<float> localDeadzone = null!;
public AndroidJoystickSettings(AndroidJoystickHandler joystickHandler)
{
this.joystickHandler = joystickHandler;
}
[BackgroundDependencyLoader]
private void load()
{
// use local bindable to avoid changing enabled state of game host's bindable.
handlerDeadzone = joystickHandler.DeadzoneThreshold.GetBoundCopy();
localDeadzone = handlerDeadzone.GetUnboundCopy();
Children = new Drawable[]
{
new SettingsCheckbox
{
LabelText = CommonStrings.Enabled,
Current = enabled
},
deadzoneSlider = new SettingsSlider<float>
{
LabelText = JoystickSettingsStrings.DeadzoneThreshold,
KeyboardStep = 0.01f,
DisplayAsPercentage = true,
Current = localDeadzone,
},
};
}
protected override void LoadComplete()
{
base.LoadComplete();
enabled.BindTo(joystickHandler.Enabled);
enabled.BindValueChanged(e => deadzoneSlider.Current.Disabled = !e.NewValue, true);
handlerDeadzone.BindValueChanged(val =>
{
bool disabled = localDeadzone.Disabled;
localDeadzone.Disabled = false;
localDeadzone.Value = val.NewValue;
localDeadzone.Disabled = disabled;
}, true);
localDeadzone.BindValueChanged(val => handlerDeadzone.Value = val.NewValue);
}
}
}

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,15 +79,36 @@ 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);
case AndroidJoystickHandler jh:
return new AndroidJoystickSettings(jh);
default:
return base.CreateSettingsSubsectionFor(handler);
}
}
private class AndroidBatteryInfo : BatteryInfo
{
public override double ChargeLevel => Battery.ChargeLevel;
public override double? ChargeLevel => Battery.ChargeLevel;
public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery;
public override bool OnBattery => Battery.PowerSource == BatteryPowerSource.Battery;
}
}
}

View File

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

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.Text;
using DiscordRPC;
@ -26,15 +24,15 @@ namespace osu.Desktop
{
private const string client_id = "367827983903490050";
private DiscordRpcClient client;
private DiscordRpcClient client = null!;
[Resolved]
private IBindable<RulesetInfo> ruleset { get; set; }
private IBindable<RulesetInfo> ruleset { get; set; } = null!;
private IBindable<APIUser> user;
private IBindable<APIUser> user = null!;
[Resolved]
private IAPIProvider api { get; set; }
private IAPIProvider api { get; set; } = null!;
private readonly IBindable<UserStatus> status = new Bindable<UserStatus>();
private readonly IBindable<UserActivity> activity = new Bindable<UserActivity>();
@ -57,7 +55,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);
@ -130,7 +128,7 @@ namespace osu.Desktop
presence.Assets.LargeImageText = string.Empty;
else
{
if (user.Value.RulesetsStatistics != null && user.Value.RulesetsStatistics.TryGetValue(ruleset.Value.ShortName, out UserStatistics statistics))
if (user.Value.RulesetsStatistics != null && user.Value.RulesetsStatistics.TryGetValue(ruleset.Value.ShortName, out UserStatistics? statistics))
presence.Assets.LargeImageText = $"{user.Value.Username}" + (statistics.GlobalRank > 0 ? $" (rank #{statistics.GlobalRank:N0})" : string.Empty);
else
presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.GlobalRank > 0 ? $" (rank #{user.Value.Statistics.GlobalRank:N0})" : string.Empty);
@ -164,7 +162,7 @@ namespace osu.Desktop
});
}
private IBeatmapInfo getBeatmap(UserActivity activity)
private IBeatmapInfo? getBeatmap(UserActivity activity)
{
switch (activity)
{
@ -183,10 +181,10 @@ namespace osu.Desktop
switch (activity)
{
case UserActivity.InGame game:
return game.BeatmapInfo.ToString();
return game.BeatmapInfo.ToString() ?? string.Empty;
case UserActivity.Editing edit:
return edit.BeatmapInfo.ToString();
return edit.BeatmapInfo.ToString() ?? string.Empty;
case UserActivity.InLobby lobby:
return privacyMode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value;

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
namespace osu.Desktop.LegacyIpc
{
/// <summary>
@ -13,7 +11,7 @@ namespace osu.Desktop.LegacyIpc
/// </remarks>
public class LegacyIpcDifficultyCalculationRequest
{
public string BeatmapFile { get; set; }
public string BeatmapFile { get; set; } = string.Empty;
public int RulesetId { get; set; }
public int Mods { get; set; }
}

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
namespace osu.Desktop.LegacyIpc
{
/// <summary>

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 osu.Framework.Platform;
using Newtonsoft.Json.Linq;
@ -39,17 +37,20 @@ namespace osu.Desktop.LegacyIpc
public new object Value
{
get => base.Value;
set => base.Value = new Data
{
MessageType = value.GetType().Name,
MessageData = value
};
set => base.Value = new Data(value.GetType().Name, value);
}
public class Data
{
public string MessageType { get; set; }
public object MessageData { get; set; }
public string MessageType { get; }
public object MessageData { get; }
public Data(string messageType, object messageData)
{
MessageType = messageType;
MessageData = messageData;
}
}
}
}

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,38 @@ 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.Input.Handlers.Touch;
using osu.Framework.Threading;
using osu.Game.IO;
using osu.Game.IPC;
using osu.Game.Overlays.Settings;
using osu.Game.Overlays.Settings.Sections;
using osu.Game.Overlays.Settings.Sections.Input;
using osu.Game.Utils;
using SDL2;
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 +62,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 +94,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 +129,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 +147,31 @@ 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);
case TouchHandler th:
return new InputSection.HandlerSection(th);
default:
return base.CreateSettingsSubsectionFor(handler);
}
}
protected override BatteryInfo CreateBatteryInfo() => new SDL2BatteryInfo();
private readonly List<string> importableFiles = new List<string>();
private ScheduledDelegate importSchedule;
private ScheduledDelegate? importSchedule;
private void fileDrop(string[] filePaths)
{
@ -168,5 +204,29 @@ namespace osu.Desktop
Task.Factory.StartNew(() => Import(paths), TaskCreationOptions.LongRunning);
}
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
osuSchemeLinkIPCChannel?.Dispose();
}
private class SDL2BatteryInfo : BatteryInfo
{
public override double? ChargeLevel
{
get
{
SDL.SDL_GetPowerInfo(out _, out int percentage);
if (percentage == -1)
return null;
return percentage / 100.0;
}
}
public override bool OnBattery => SDL.SDL_GetPowerInfo(out _, out _) == SDL.SDL_PowerState.SDL_POWERSTATE_ON_BATTERY;
}
}
}

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.IO;
using System.Runtime.Versioning;
@ -11,24 +9,50 @@ 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 SDL2;
using Squirrel;
namespace osu.Desktop
{
public static class Program
{
#if DEBUG
private const string base_game_name = @"osu-development";
#else
private const string base_game_name = @"osu";
#endif
private static LegacyTcpIpcProvider legacyIpc;
private static LegacyTcpIpcProvider? legacyIpc;
[STAThread]
public static void Main(string[] args)
{
// run Squirrel first, as the app may exit after these run
if (OperatingSystem.IsWindows())
{
var windowsVersion = Environment.OSVersion.Version;
// While .NET 6 still supports Windows 7 and above, we are limited by realm currently, as they choose to only support 8.1 and higher.
// See https://www.mongodb.com/docs/realm/sdk/dotnet/#supported-platforms
if (windowsVersion.Major < 6 || (windowsVersion.Major == 6 && windowsVersion.Minor <= 2))
{
// If users running in compatibility mode becomes more of a common thing, we may want to provide better guidance or even consider
// disabling it ourselves.
// We could also better detect compatibility mode if required:
// https://stackoverflow.com/questions/10744651/how-i-can-detect-if-my-application-is-running-under-compatibility-mode#comment58183249_10744730
SDL.SDL_ShowSimpleMessageBox(SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR,
"Your operating system is too old to run osu!",
"This version of osu! requires at least Windows 8.1 to run.\n"
+ "Please upgrade your operating system or consider using an older version of osu!.\n\n"
+ "If you are running a newer version of windows, please check you don't have \"Compatibility mode\" turned on for osu!", IntPtr.Zero);
return;
}
setupSquirrel();
}
// Back up the cwd before DesktopGameHost changes it
string cwd = Environment.CurrentDirectory;
@ -65,19 +89,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 +121,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

@ -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.Security.Principal;
using osu.Framework;
@ -21,7 +19,7 @@ namespace osu.Desktop.Security
public class ElevatedPrivilegesChecker : Component
{
[Resolved]
private INotificationOverlay notifications { get; set; }
private INotificationOverlay notifications { get; set; } = null!;
private bool elevated;

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.Runtime.Versioning;
using System.Threading.Tasks;
@ -26,8 +24,8 @@ namespace osu.Desktop.Updater
[SupportedOSPlatform("windows")]
public class SquirrelUpdateManager : osu.Game.Updater.UpdateManager
{
private UpdateManager updateManager;
private INotificationOverlay notificationOverlay;
private UpdateManager? updateManager;
private INotificationOverlay notificationOverlay = null!;
public Task PrepareUpdateAsync() => UpdateManager.RestartAppWhenExited();
@ -50,12 +48,12 @@ namespace osu.Desktop.Updater
protected override async Task<bool> PerformUpdateCheck() => await checkForUpdateAsync().ConfigureAwait(false);
private async Task<bool> checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null)
private async Task<bool> checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification? notification = null)
{
// should we schedule a retry on completion of this check?
bool scheduleRecheck = true;
const string github_token = null; // TODO: populate.
const string? github_token = null; // TODO: populate.
try
{
@ -145,7 +143,7 @@ namespace osu.Desktop.Updater
private class UpdateCompleteNotification : ProgressCompletionNotification
{
[Resolved]
private OsuGame game { get; set; }
private OsuGame game { get; set; } = null!;
public UpdateCompleteNotification(SquirrelUpdateManager updateManager)
{
@ -154,7 +152,7 @@ namespace osu.Desktop.Updater
Activated = () =>
{
updateManager.PrepareUpdateAsync()
.ContinueWith(_ => updateManager.Schedule(() => game?.GracefullyExit()));
.ContinueWith(_ => updateManager.Schedule(() => game.AttemptExit()));
return true;
};
}

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 osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -14,12 +12,12 @@ namespace osu.Desktop.Windows
{
public class GameplayWinKeyBlocker : Component
{
private Bindable<bool> disableWinKey;
private IBindable<bool> localUserPlaying;
private IBindable<bool> isActive;
private Bindable<bool> disableWinKey = null!;
private IBindable<bool> localUserPlaying = null!;
private IBindable<bool> isActive = null!;
[Resolved]
private GameHost host { get; set; }
private GameHost host { get; set; } = null!;
[BackgroundDependencyLoader]
private void load(ILocalUserPlayInfo localUserInfo, OsuConfigManager config)

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.Runtime.InteropServices;
@ -21,7 +19,7 @@ namespace osu.Desktop.Windows
private const int wm_syskeyup = 261;
//Resharper disable once NotAccessedField.Local
private static LowLevelKeyboardProcDelegate keyboardHookDelegate; // keeping a reference alive for the GC
private static LowLevelKeyboardProcDelegate? keyboardHookDelegate; // keeping a reference alive for the GC
private static IntPtr keyHook;
[StructLayout(LayoutKind.Explicit)]

View File

@ -24,7 +24,7 @@
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Clowd.Squirrel" Version="2.9.40" />
<PackageReference Include="Clowd.Squirrel" Version="2.9.42" />
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
<PackageReference Include="System.IO.Packaging" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.14" />

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.IO;
using BenchmarkDotNet.Attributes;
using osu.Framework.IO.Stores;

View File

@ -0,0 +1,166 @@
// 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 BenchmarkDotNet.Attributes;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Taiko.Objects;
namespace osu.Game.Benchmarks
{
public class BenchmarkHitObject : BenchmarkTest
{
[Params(1, 100, 1000)]
public int Count { get; set; }
[Params(false, true)]
public bool WithBindableAccess { get; set; }
[Benchmark]
public HitCircle[] OsuCircle()
{
var circles = new HitCircle[Count];
for (int i = 0; i < Count; i++)
{
circles[i] = new HitCircle();
if (WithBindableAccess)
{
_ = circles[i].PositionBindable;
_ = circles[i].ScaleBindable;
_ = circles[i].ComboIndexBindable;
_ = circles[i].ComboOffsetBindable;
_ = circles[i].StackHeightBindable;
_ = circles[i].LastInComboBindable;
_ = circles[i].ComboIndexWithOffsetsBindable;
_ = circles[i].IndexInCurrentComboBindable;
_ = circles[i].SamplesBindable;
_ = circles[i].StartTimeBindable;
}
else
{
_ = circles[i].Position;
_ = circles[i].Scale;
_ = circles[i].ComboIndex;
_ = circles[i].ComboOffset;
_ = circles[i].StackHeight;
_ = circles[i].LastInCombo;
_ = circles[i].ComboIndexWithOffsets;
_ = circles[i].IndexInCurrentCombo;
_ = circles[i].Samples;
_ = circles[i].StartTime;
_ = circles[i].Position;
_ = circles[i].Scale;
_ = circles[i].ComboIndex;
_ = circles[i].ComboOffset;
_ = circles[i].StackHeight;
_ = circles[i].LastInCombo;
_ = circles[i].ComboIndexWithOffsets;
_ = circles[i].IndexInCurrentCombo;
_ = circles[i].Samples;
_ = circles[i].StartTime;
}
}
return circles;
}
[Benchmark]
public Hit[] TaikoHit()
{
var hits = new Hit[Count];
for (int i = 0; i < Count; i++)
{
hits[i] = new Hit();
if (WithBindableAccess)
{
_ = hits[i].TypeBindable;
_ = hits[i].IsStrongBindable;
_ = hits[i].SamplesBindable;
_ = hits[i].StartTimeBindable;
}
else
{
_ = hits[i].Type;
_ = hits[i].IsStrong;
_ = hits[i].Samples;
_ = hits[i].StartTime;
}
}
return hits;
}
[Benchmark]
public Fruit[] CatchFruit()
{
var fruit = new Fruit[Count];
for (int i = 0; i < Count; i++)
{
fruit[i] = new Fruit();
if (WithBindableAccess)
{
_ = fruit[i].OriginalXBindable;
_ = fruit[i].XOffsetBindable;
_ = fruit[i].ScaleBindable;
_ = fruit[i].ComboIndexBindable;
_ = fruit[i].HyperDashBindable;
_ = fruit[i].LastInComboBindable;
_ = fruit[i].ComboIndexWithOffsetsBindable;
_ = fruit[i].IndexInCurrentComboBindable;
_ = fruit[i].IndexInBeatmapBindable;
_ = fruit[i].SamplesBindable;
_ = fruit[i].StartTimeBindable;
}
else
{
_ = fruit[i].OriginalX;
_ = fruit[i].XOffset;
_ = fruit[i].Scale;
_ = fruit[i].ComboIndex;
_ = fruit[i].HyperDash;
_ = fruit[i].LastInCombo;
_ = fruit[i].ComboIndexWithOffsets;
_ = fruit[i].IndexInCurrentCombo;
_ = fruit[i].IndexInBeatmap;
_ = fruit[i].Samples;
_ = fruit[i].StartTime;
}
}
return fruit;
}
[Benchmark]
public Note[] ManiaNote()
{
var notes = new Note[Count];
for (int i = 0; i < Count; i++)
{
notes[i] = new Note();
if (WithBindableAccess)
{
_ = notes[i].ColumnBindable;
_ = notes[i].SamplesBindable;
_ = notes[i].StartTimeBindable;
}
else
{
_ = notes[i].Column;
_ = notes[i].Samples;
_ = notes[i].StartTime;
}
}
return notes;
}
}
}

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 BenchmarkDotNet.Attributes;
using osu.Game.Rulesets.Osu.Mods;
@ -11,7 +9,7 @@ namespace osu.Game.Benchmarks
{
public class BenchmarkMod : BenchmarkTest
{
private OsuModDoubleTime mod;
private OsuModDoubleTime mod = null!;
[Params(1, 10, 100)]
public int Times { get; set; }

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.Linq;
using System.Threading;
using BenchmarkDotNet.Attributes;
@ -17,9 +15,9 @@ namespace osu.Game.Benchmarks
{
public class BenchmarkRealmReads : BenchmarkTest
{
private TemporaryNativeStorage storage;
private RealmAccess realm;
private UpdateThread updateThread;
private TemporaryNativeStorage storage = null!;
private RealmAccess realm = null!;
private UpdateThread updateThread = null!;
[Params(1, 100, 1000)]
public int ReadsPerFetch { get; set; }
@ -31,7 +29,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 +74,7 @@ namespace osu.Game.Benchmarks
}
});
done.Wait();
done.Wait(60000);
}
[Benchmark]
@ -115,7 +113,7 @@ namespace osu.Game.Benchmarks
}
});
done.Wait();
done.Wait(60000);
}
[Benchmark]
@ -135,9 +133,9 @@ namespace osu.Game.Benchmarks
[GlobalCleanup]
public void Cleanup()
{
realm?.Dispose();
storage?.Dispose();
updateThread?.Exit();
realm.Dispose();
storage.Dispose();
updateThread.Exit();
}
}
}

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 BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Engines;
using osu.Game.Online.API;
@ -13,9 +11,9 @@ namespace osu.Game.Benchmarks
{
public class BenchmarkRuleset : BenchmarkTest
{
private OsuRuleset ruleset;
private APIMod apiModDoubleTime;
private APIMod apiModDifficultyAdjust;
private OsuRuleset ruleset = null!;
private APIMod apiModDoubleTime = null!;
private APIMod apiModDifficultyAdjust = null!;
public override void SetUp()
{

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 BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using NUnit.Framework;

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 BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running;

View File

@ -24,21 +24,24 @@ namespace osu.Game.Rulesets.Catch.Tests
new object[] { LegacyMods.DoubleTime, new[] { typeof(CatchModDoubleTime) } },
new object[] { LegacyMods.Relax, new[] { typeof(CatchModRelax) } },
new object[] { LegacyMods.HalfTime, new[] { typeof(CatchModHalfTime) } },
new object[] { LegacyMods.Nightcore, new[] { typeof(CatchModNightcore) } },
new object[] { LegacyMods.Flashlight, new[] { typeof(CatchModFlashlight) } },
new object[] { LegacyMods.Autoplay, new[] { typeof(CatchModAutoplay) } },
new object[] { LegacyMods.Perfect, new[] { typeof(CatchModPerfect) } },
new object[] { LegacyMods.Cinema, new[] { typeof(CatchModCinema) } },
new object[] { LegacyMods.HardRock | LegacyMods.DoubleTime, new[] { typeof(CatchModHardRock), typeof(CatchModDoubleTime) } }
};
[TestCaseSource(nameof(catch_mod_mapping))]
[TestCase(LegacyMods.Cinema, new[] { typeof(CatchModCinema) })]
[TestCase(LegacyMods.Cinema | LegacyMods.Autoplay, new[] { typeof(CatchModCinema) })]
[TestCase(LegacyMods.Nightcore, new[] { typeof(CatchModNightcore) })]
[TestCase(LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(CatchModNightcore) })]
[TestCase(LegacyMods.Perfect, new[] { typeof(CatchModPerfect) })]
[TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath, new[] { typeof(CatchModPerfect) })]
public new void TestFromLegacy(LegacyMods legacyMods, Type[] expectedMods) => base.TestFromLegacy(legacyMods, expectedMods);
[TestCaseSource(nameof(catch_mod_mapping))]
[TestCase(LegacyMods.Cinema | LegacyMods.Autoplay, new[] { typeof(CatchModCinema) })]
[TestCase(LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(CatchModNightcore) })]
[TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath, new[] { typeof(CatchModPerfect) })]
public new void TestFromLegacy(LegacyMods legacyMods, Type[] expectedMods) => base.TestFromLegacy(legacyMods, expectedMods);
[TestCaseSource(nameof(catch_mod_mapping))]
public new void TestToLegacy(LegacyMods legacyMods, Type[] givenMods) => base.TestToLegacy(legacyMods, givenMods);
protected override Ruleset CreateRuleset() => new CatchRuleset();

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

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Utils;

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.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Utils;

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 NUnit.Framework;
using osu.Game.Rulesets.Catch.Mods;
using osu.Game.Rulesets.Catch.Objects;

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.Collections.Generic;
using System.Linq;
using NUnit.Framework;

View File

@ -9,7 +9,6 @@ using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Screens.Play.HUD;
using osu.Game.Skinning;
using osu.Game.Tests.Visual;
using osuTK;
@ -29,7 +28,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

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />

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

@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty
private float halfCatcherWidth;
public override int Version => 20220701;
public CatchDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
: base(ruleset, beatmap)
{

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

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Replays;

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.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;

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 osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods

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 osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods

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.Linq;
using osu.Framework.Bindables;
using osu.Game.Beatmaps;

View File

@ -1,14 +1,12 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModDoubleTime : ModDoubleTime
{
public override double ScoreMultiplier => 1.06;
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
}
}

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 osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods

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 osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Configuration;
@ -16,7 +14,7 @@ namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModFlashlight : ModFlashlight<CatchHitObject>
{
public override double ScoreMultiplier => 1.12;
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
[SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")]
public override BindableFloat SizeMultiplier { get; } = new BindableFloat
@ -39,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Mods
protected override Flashlight CreateFlashlight() => new CatchFlashlight(this, playfield);
private CatchPlayfield playfield;
private CatchPlayfield playfield = null!;
public override void ApplyToDrawableRuleset(DrawableRuleset<CatchHitObject> drawableRuleset)
{

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 osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Rulesets.Catch.Objects;

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 osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods

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 osu.Game.Rulesets.Mods;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Beatmaps;
@ -11,7 +9,7 @@ namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModHardRock : ModHardRock, IApplicableToBeatmapProcessor
{
public override double ScoreMultiplier => 1.12;
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
public void ApplyToBeatmapProcessor(IBeatmapProcessor beatmapProcessor)
{

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.Linq;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects;
@ -17,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.Mods
public class CatchModHidden : ModHidden, IApplicableToDrawableRuleset<CatchHitObject>
{
public override string Description => @"Play with fading fruits.";
public override double ScoreMultiplier => 1.06;
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
private const double fade_out_offset_multiplier = 0.6;
private const double fade_out_duration_multiplier = 0.44;

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.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Beatmaps;

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 osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Mods;

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 osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Mods;
@ -10,6 +8,6 @@ namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModNightcore : ModNightcore<CatchHitObject>
{
public override double ScoreMultiplier => 1.06;
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
}
}

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 osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods

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 osu.Framework.Bindables;
using osu.Game.Rulesets.Mods;

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 osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods

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 osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
@ -20,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Mods
{
public override string Description => @"Use the mouse to control the catcher.";
private DrawableRuleset<CatchHitObject> drawableRuleset;
private DrawableRuleset<CatchHitObject> drawableRuleset = null!;
public void ApplyToDrawableRuleset(DrawableRuleset<CatchHitObject> drawableRuleset)
{

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 osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods

View File

@ -19,7 +19,9 @@ namespace osu.Game.Rulesets.Catch.Objects
{
public const float OBJECT_RADIUS = 64;
public readonly Bindable<float> OriginalXBindable = new Bindable<float>();
private HitObjectProperty<float> originalX;
public Bindable<float> OriginalXBindable => originalX.Bindable;
/// <summary>
/// The horizontal position of the hit object between 0 and <see cref="CatchPlayfield.WIDTH"/>.
@ -31,18 +33,20 @@ namespace osu.Game.Rulesets.Catch.Objects
[JsonIgnore]
public float X
{
set => OriginalXBindable.Value = value;
set => originalX.Value = value;
}
public readonly Bindable<float> XOffsetBindable = new Bindable<float>();
private HitObjectProperty<float> xOffset;
public Bindable<float> XOffsetBindable => xOffset.Bindable;
/// <summary>
/// A random offset applied to the horizontal position, set by the beatmap processing.
/// </summary>
public float XOffset
{
get => XOffsetBindable.Value;
set => XOffsetBindable.Value = value;
get => xOffset.Value;
set => xOffset.Value = value;
}
/// <summary>
@ -54,8 +58,8 @@ namespace osu.Game.Rulesets.Catch.Objects
/// </remarks>
public float OriginalX
{
get => OriginalXBindable.Value;
set => OriginalXBindable.Value = value;
get => originalX.Value;
set => originalX.Value = value;
}
/// <summary>
@ -69,59 +73,71 @@ namespace osu.Game.Rulesets.Catch.Objects
public double TimePreempt { get; set; } = 1000;
public readonly Bindable<int> IndexInBeatmapBindable = new Bindable<int>();
private HitObjectProperty<int> indexInBeatmap;
public Bindable<int> IndexInBeatmapBindable => indexInBeatmap.Bindable;
public int IndexInBeatmap
{
get => IndexInBeatmapBindable.Value;
set => IndexInBeatmapBindable.Value = value;
get => indexInBeatmap.Value;
set => indexInBeatmap.Value = value;
}
public virtual bool NewCombo { get; set; }
public int ComboOffset { get; set; }
public Bindable<int> IndexInCurrentComboBindable { get; } = new Bindable<int>();
private HitObjectProperty<int> indexInCurrentCombo;
public Bindable<int> IndexInCurrentComboBindable => indexInCurrentCombo.Bindable;
public int IndexInCurrentCombo
{
get => IndexInCurrentComboBindable.Value;
set => IndexInCurrentComboBindable.Value = value;
get => indexInCurrentCombo.Value;
set => indexInCurrentCombo.Value = value;
}
public Bindable<int> ComboIndexBindable { get; } = new Bindable<int>();
private HitObjectProperty<int> comboIndex;
public Bindable<int> ComboIndexBindable => comboIndex.Bindable;
public int ComboIndex
{
get => ComboIndexBindable.Value;
set => ComboIndexBindable.Value = value;
get => comboIndex.Value;
set => comboIndex.Value = value;
}
public Bindable<int> ComboIndexWithOffsetsBindable { get; } = new Bindable<int>();
private HitObjectProperty<int> comboIndexWithOffsets;
public Bindable<int> ComboIndexWithOffsetsBindable => comboIndexWithOffsets.Bindable;
public int ComboIndexWithOffsets
{
get => ComboIndexWithOffsetsBindable.Value;
set => ComboIndexWithOffsetsBindable.Value = value;
get => comboIndexWithOffsets.Value;
set => comboIndexWithOffsets.Value = value;
}
public Bindable<bool> LastInComboBindable { get; } = new Bindable<bool>();
private HitObjectProperty<bool> lastInCombo;
public Bindable<bool> LastInComboBindable => lastInCombo.Bindable;
/// <summary>
/// The next fruit starts a new combo. Used for explodey.
/// </summary>
public virtual bool LastInCombo
{
get => LastInComboBindable.Value;
set => LastInComboBindable.Value = value;
get => lastInCombo.Value;
set => lastInCombo.Value = value;
}
public readonly Bindable<float> ScaleBindable = new Bindable<float>(1);
private HitObjectProperty<float> scale = new HitObjectProperty<float>(1);
public Bindable<float> ScaleBindable => scale.Bindable;
public float Scale
{
get => ScaleBindable.Value;
set => ScaleBindable.Value = value;
get => scale.Value;
set => scale.Value = value;
}
/// <summary>

View File

@ -5,6 +5,7 @@
using Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Skinning;
using osuTK.Graphics;
@ -24,12 +25,14 @@ namespace osu.Game.Rulesets.Catch.Objects
/// </summary>
public float DistanceToHyperDash { get; set; }
public readonly Bindable<bool> HyperDashBindable = new Bindable<bool>();
private HitObjectProperty<bool> hyperDash;
public Bindable<bool> HyperDashBindable => hyperDash.Bindable;
/// <summary>
/// Whether this fruit can initiate a hyperdash.
/// </summary>
public bool HyperDash => HyperDashBindable.Value;
public bool HyperDash => hyperDash.Value;
private CatchHitObject hyperDashTarget;

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.Linq;
using osu.Framework.Utils;

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.Collections.Generic;
using System.Linq;
using osu.Framework.Input.StateChanges;

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.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Replays.Legacy;
@ -22,7 +20,7 @@ namespace osu.Game.Rulesets.Catch.Replays
{
}
public CatchReplayFrame(double time, float? position = null, bool dashing = false, CatchReplayFrame lastFrame = null)
public CatchReplayFrame(double time, float? position = null, bool dashing = false, CatchReplayFrame? lastFrame = null)
: base(time)
{
Position = position ?? -1;
@ -40,7 +38,7 @@ namespace osu.Game.Rulesets.Catch.Replays
}
}
public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame? lastFrame = null)
{
Position = currentFrame.Position.X;
Dashing = currentFrame.ButtonState == ReplayButtonState.Left1;

View File

@ -6,7 +6,6 @@
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Screens.Play.HUD;
using osu.Game.Skinning;
using osuTK.Graphics;

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

@ -120,10 +120,10 @@ namespace osu.Game.Rulesets.Catch.UI
lastHyperDashState = Catcher.HyperDashing;
}
public void SetCatcherPosition(float X)
public void SetCatcherPosition(float x)
{
float lastPosition = Catcher.X;
float newPosition = Math.Clamp(X, 0, CatchPlayfield.WIDTH);
float newPosition = Math.Clamp(x, 0, CatchPlayfield.WIDTH);
Catcher.X = newPosition;

View File

@ -23,10 +23,8 @@ namespace osu.Game.Rulesets.Mania.Tests
new object[] { LegacyMods.SuddenDeath, new[] { typeof(ManiaModSuddenDeath) } },
new object[] { LegacyMods.DoubleTime, new[] { typeof(ManiaModDoubleTime) } },
new object[] { LegacyMods.HalfTime, new[] { typeof(ManiaModHalfTime) } },
new object[] { LegacyMods.Nightcore, new[] { typeof(ManiaModNightcore) } },
new object[] { LegacyMods.Flashlight, new[] { typeof(ManiaModFlashlight) } },
new object[] { LegacyMods.Autoplay, new[] { typeof(ManiaModAutoplay) } },
new object[] { LegacyMods.Perfect, new[] { typeof(ManiaModPerfect) } },
new object[] { LegacyMods.Key4, new[] { typeof(ManiaModKey4) } },
new object[] { LegacyMods.Key5, new[] { typeof(ManiaModKey5) } },
new object[] { LegacyMods.Key6, new[] { typeof(ManiaModKey6) } },
@ -34,7 +32,6 @@ namespace osu.Game.Rulesets.Mania.Tests
new object[] { LegacyMods.Key8, new[] { typeof(ManiaModKey8) } },
new object[] { LegacyMods.FadeIn, new[] { typeof(ManiaModFadeIn) } },
new object[] { LegacyMods.Random, new[] { typeof(ManiaModRandom) } },
new object[] { LegacyMods.Cinema, new[] { typeof(ManiaModCinema) } },
new object[] { LegacyMods.Key9, new[] { typeof(ManiaModKey9) } },
new object[] { LegacyMods.KeyCoop, new[] { typeof(ManiaModDualStages) } },
new object[] { LegacyMods.Key1, new[] { typeof(ManiaModKey1) } },
@ -45,12 +42,18 @@ namespace osu.Game.Rulesets.Mania.Tests
};
[TestCaseSource(nameof(mania_mod_mapping))]
[TestCase(LegacyMods.Cinema, new[] { typeof(ManiaModCinema) })]
[TestCase(LegacyMods.Cinema | LegacyMods.Autoplay, new[] { typeof(ManiaModCinema) })]
[TestCase(LegacyMods.Nightcore, new[] { typeof(ManiaModNightcore) })]
[TestCase(LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(ManiaModNightcore) })]
[TestCase(LegacyMods.Perfect, new[] { typeof(ManiaModPerfect) })]
[TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath, new[] { typeof(ManiaModPerfect) })]
public new void TestFromLegacy(LegacyMods legacyMods, Type[] expectedMods) => base.TestFromLegacy(legacyMods, expectedMods);
[TestCaseSource(nameof(mania_mod_mapping))]
[TestCase(LegacyMods.Cinema | LegacyMods.Autoplay, new[] { typeof(ManiaModCinema) })]
[TestCase(LegacyMods.Nightcore | LegacyMods.DoubleTime, new[] { typeof(ManiaModNightcore) })]
[TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath, new[] { typeof(ManiaModPerfect) })]
public new void TestToLegacy(LegacyMods legacyMods, Type[] givenMods) => base.TestToLegacy(legacyMods, givenMods);
protected override Ruleset CreateRuleset() => new ManiaRuleset();

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.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;

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.Linq;
using NUnit.Framework;
using osu.Game.Beatmaps;

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 NUnit.Framework;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Tests.Visual;

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 NUnit.Framework;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mania.Objects;

View File

@ -0,0 +1,52 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
namespace osu.Game.Rulesets.Mania.Tests.Skinning
{
public class TestSceneBarLine : ManiaSkinnableTestScene
{
[Test]
public void TestMinor()
{
AddStep("Create barlines", () => recreate());
}
private void recreate(Func<IEnumerable<BarLine>>? createBarLines = null)
{
var stageDefinitions = new List<StageDefinition>
{
new StageDefinition { Columns = 4 },
};
SetContents(_ => new ManiaPlayfield(stageDefinitions).With(s =>
{
if (createBarLines != null)
{
var barLines = createBarLines();
foreach (var b in barLines)
s.Add(b);
return;
}
for (int i = 0; i < 64; i++)
{
s.Add(new BarLine
{
StartTime = Time.Current + i * 500,
Major = i % 4 == 0,
});
}
}));
}
}
}

View File

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />

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
@ -20,12 +19,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty
[JsonProperty("great_hit_window")]
public double GreatHitWindow { get; set; }
/// <summary>
/// The score multiplier applied via score-reducing mods.
/// </summary>
[JsonProperty("score_multiplier")]
public double ScoreMultiplier { get; set; }
public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes()
{
foreach (var v in base.ToDatabaseAttributes())
@ -34,17 +27,15 @@ namespace osu.Game.Rulesets.Mania.Difficulty
yield return (ATTRIB_ID_MAX_COMBO, MaxCombo);
yield return (ATTRIB_ID_DIFFICULTY, StarRating);
yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow);
yield return (ATTRIB_ID_SCORE_MULTIPLIER, ScoreMultiplier);
}
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];
GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW];
ScoreMultiplier = values[ATTRIB_ID_SCORE_MULTIPLIER];
}
}
}

View File

@ -31,6 +31,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty
private readonly bool isForCurrentRuleset;
private readonly double originalOverallDifficulty;
public override int Version => 20220701;
public ManiaDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
@ -53,7 +55,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty
// In osu-stable mania, rate-adjustment mods don't affect the hit window.
// This is done the way it is to introduce fractional differences in order to match osu-stable for the time being.
GreatHitWindow = Math.Ceiling((int)(getHitWindow300(mods) * clockRate) / clockRate),
ScoreMultiplier = getScoreMultiplier(mods),
MaxCombo = beatmap.HitObjects.Sum(maxComboForObject)
};
}
@ -147,32 +148,5 @@ namespace osu.Game.Rulesets.Mania.Difficulty
return value;
}
}
private double getScoreMultiplier(Mod[] mods)
{
double scoreMultiplier = 1;
foreach (var m in mods)
{
switch (m)
{
case ManiaModNoFail _:
case ManiaModEasy _:
case ManiaModHalfTime _:
scoreMultiplier *= 0.5;
break;
}
}
var maniaBeatmap = (ManiaBeatmap)Beatmap;
int diff = maniaBeatmap.TotalColumns - maniaBeatmap.OriginalTotalColumns;
if (diff > 0)
scoreMultiplier *= 0.9;
else if (diff < 0)
scoreMultiplier *= 0.9 + 0.04 * diff;
return scoreMultiplier;
}
}
}

View File

@ -14,19 +14,12 @@ namespace osu.Game.Rulesets.Mania.Difficulty
[JsonProperty("difficulty")]
public double Difficulty { get; set; }
[JsonProperty("accuracy")]
public double Accuracy { get; set; }
[JsonProperty("scaled_score")]
public double ScaledScore { get; set; }
public override IEnumerable<PerformanceDisplayAttribute> GetAttributesForDisplay()
{
foreach (var attribute in base.GetAttributesForDisplay())
yield return attribute;
yield return new PerformanceDisplayAttribute(nameof(Difficulty), "Difficulty", Difficulty);
yield return new PerformanceDisplayAttribute(nameof(Accuracy), "Accuracy", Accuracy);
}
}
}

View File

@ -15,15 +15,13 @@ namespace osu.Game.Rulesets.Mania.Difficulty
{
public class ManiaPerformanceCalculator : PerformanceCalculator
{
// Score after being scaled by non-difficulty-increasing mods
private double scaledScore;
private int countPerfect;
private int countGreat;
private int countGood;
private int countOk;
private int countMeh;
private int countMiss;
private double scoreAccuracy;
public ManiaPerformanceCalculator()
: base(new ManiaRuleset())
@ -34,82 +32,47 @@ namespace osu.Game.Rulesets.Mania.Difficulty
{
var maniaAttributes = (ManiaDifficultyAttributes)attributes;
scaledScore = score.TotalScore;
countPerfect = score.Statistics.GetValueOrDefault(HitResult.Perfect);
countGreat = score.Statistics.GetValueOrDefault(HitResult.Great);
countGood = score.Statistics.GetValueOrDefault(HitResult.Good);
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok);
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
if (maniaAttributes.ScoreMultiplier > 0)
{
// Scale score up, so it's comparable to other keymods
scaledScore *= 1.0 / maniaAttributes.ScoreMultiplier;
}
scoreAccuracy = customAccuracy;
// Arbitrary initial value for scaling pp in order to standardize distributions across game modes.
// The specific number has no intrinsic meaning and can be adjusted as needed.
double multiplier = 0.8;
double multiplier = 8.0;
if (score.Mods.Any(m => m is ModNoFail))
multiplier *= 0.9;
multiplier *= 0.75;
if (score.Mods.Any(m => m is ModEasy))
multiplier *= 0.5;
double difficultyValue = computeDifficultyValue(maniaAttributes);
double accValue = computeAccuracyValue(difficultyValue, maniaAttributes);
double totalValue =
Math.Pow(
Math.Pow(difficultyValue, 1.1) +
Math.Pow(accValue, 1.1), 1.0 / 1.1
) * multiplier;
double totalValue = difficultyValue * multiplier;
return new ManiaPerformanceAttributes
{
Difficulty = difficultyValue,
Accuracy = accValue,
ScaledScore = scaledScore,
Total = totalValue
};
}
private double computeDifficultyValue(ManiaDifficultyAttributes attributes)
{
double difficultyValue = Math.Pow(5 * Math.Max(1, attributes.StarRating / 0.2) - 4.0, 2.2) / 135.0;
difficultyValue *= 1.0 + 0.1 * Math.Min(1.0, totalHits / 1500.0);
if (scaledScore <= 500000)
difficultyValue = 0;
else if (scaledScore <= 600000)
difficultyValue *= (scaledScore - 500000) / 100000 * 0.3;
else if (scaledScore <= 700000)
difficultyValue *= 0.3 + (scaledScore - 600000) / 100000 * 0.25;
else if (scaledScore <= 800000)
difficultyValue *= 0.55 + (scaledScore - 700000) / 100000 * 0.20;
else if (scaledScore <= 900000)
difficultyValue *= 0.75 + (scaledScore - 800000) / 100000 * 0.15;
else
difficultyValue *= 0.90 + (scaledScore - 900000) / 100000 * 0.1;
double difficultyValue = Math.Pow(Math.Max(attributes.StarRating - 0.15, 0.05), 2.2) // Star rating to pp curve
* Math.Max(0, 5 * scoreAccuracy - 4) // From 80% accuracy, 1/20th of total pp is awarded per additional 1% accuracy
* (1 + 0.1 * Math.Min(1, totalHits / 1500)); // Length bonus, capped at 1500 notes
return difficultyValue;
}
private double computeAccuracyValue(double difficultyValue, ManiaDifficultyAttributes attributes)
{
if (attributes.GreatHitWindow <= 0)
return 0;
// Lots of arbitrary values from testing.
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
double accuracyValue = Math.Max(0.0, 0.2 - (attributes.GreatHitWindow - 34) * 0.006667)
* difficultyValue
* Math.Pow(Math.Max(0.0, scaledScore - 960000) / 40000, 1.1);
return accuracyValue;
}
private double totalHits => countPerfect + countOk + countGreat + countGood + countMeh + countMiss;
/// <summary>
/// Accuracy used to weight judgements independently from the score's actual accuracy.
/// </summary>
private double customAccuracy => (countPerfect * 320 + countGreat * 300 + countGood * 200 + countOk * 100 + countMeh * 50) / (totalHits * 320);
}
}

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 osu.Game.Beatmaps;
using osu.Game.Rulesets.Filter;
using osu.Game.Rulesets.Mania.Beatmaps;

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

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods

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.Linq;
using osu.Game.Beatmaps;

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.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;

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.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;

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 osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods

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 osu.Framework.Graphics.Sprites;
using osu.Game.Configuration;
using osu.Game.Rulesets.Mania.Objects;

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 osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods

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 osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods

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