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

Merge branch 'master' into named-pipe

This commit is contained in:
Dean Herbert 2024-12-07 10:42:55 +09:00
commit e22f3b7d05
No known key found for this signature in database
58 changed files with 464 additions and 378 deletions

View File

@ -1,57 +0,0 @@
# .NET Code Style
# IDE styles reference: https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/
# IDE0001: Simplify names
dotnet_diagnostic.IDE0001.severity = warning
# IDE0002: Simplify member access
dotnet_diagnostic.IDE0002.severity = warning
# IDE0003: Remove qualification
dotnet_diagnostic.IDE0003.severity = warning
# IDE0004: Remove unnecessary cast
dotnet_diagnostic.IDE0004.severity = warning
# IDE0005: Remove unnecessary imports
dotnet_diagnostic.IDE0005.severity = warning
# IDE0034: Simplify default literal
dotnet_diagnostic.IDE0034.severity = warning
# IDE0036: Sort modifiers
dotnet_diagnostic.IDE0036.severity = warning
# IDE0040: Add accessibility modifier
dotnet_diagnostic.IDE0040.severity = warning
# IDE0049: Use keyword for type name
dotnet_diagnostic.IDE0040.severity = warning
# IDE0055: Fix formatting
dotnet_diagnostic.IDE0055.severity = warning
# IDE0051: Private method is unused
dotnet_diagnostic.IDE0051.severity = silent
# IDE0052: Private member is unused
dotnet_diagnostic.IDE0052.severity = silent
# IDE0073: File header
dotnet_diagnostic.IDE0073.severity = warning
# IDE0130: Namespace mismatch with folder
dotnet_diagnostic.IDE0130.severity = warning
# IDE1006: Naming style
dotnet_diagnostic.IDE1006.severity = warning
#Disable operator overloads requiring alternate named methods
dotnet_diagnostic.CA2225.severity = none
# Banned APIs
dotnet_diagnostic.RS0030.severity = error
# Temporarily disable analysing CanBeNull = true in NRT contexts due to mobile issues.
# See: https://github.com/ppy/osu/pull/19677
dotnet_diagnostic.OSUF001.severity = none

View File

@ -14,10 +14,6 @@ M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Collections.Gen
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.Char.ToLower(System.Char);char.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use char.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture.
M:System.Char.ToUpper(System.Char);char.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use char.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture.
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.

View File

@ -0,0 +1,109 @@
# .NET Code Style
# IDE styles reference: https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/
is_global = true
# IDE0001: Simplify names
dotnet_diagnostic.IDE0001.severity = warning
# IDE0002: Simplify member access
dotnet_diagnostic.IDE0002.severity = warning
# IDE0003: Remove qualification
dotnet_diagnostic.IDE0003.severity = warning
# IDE0004: Remove unnecessary cast
dotnet_diagnostic.IDE0004.severity = warning
# IDE0005: Remove unnecessary imports
dotnet_diagnostic.IDE0005.severity = warning
# IDE0034: Simplify default literal
dotnet_diagnostic.IDE0034.severity = warning
# IDE0036: Sort modifiers
dotnet_diagnostic.IDE0036.severity = warning
# IDE0040: Add accessibility modifier
dotnet_diagnostic.IDE0040.severity = warning
# IDE0049: Use keyword for type name
dotnet_diagnostic.IDE0040.severity = warning
# IDE0055: Fix formatting
dotnet_diagnostic.IDE0055.severity = warning
# IDE0051: Private method is unused
dotnet_diagnostic.IDE0051.severity = silent
# IDE0052: Private member is unused
dotnet_diagnostic.IDE0052.severity = silent
# IDE0073: File header
dotnet_diagnostic.IDE0073.severity = warning
# IDE0130: Namespace mismatch with folder
dotnet_diagnostic.IDE0130.severity = warning
# IDE1006: Naming style
dotnet_diagnostic.IDE1006.severity = warning
# CA1305: Specify IFormatProvider
# Too many noisy warnings for parsing/formatting numbers
dotnet_diagnostic.CA1305.severity = none
# CA1507: Use nameof to express symbol names
# Flaggs serialization name attributes
dotnet_diagnostic.CA1507.severity = suggestion
# CA1806: Do not ignore method results
# The usages for numeric parsing are explicitly optional
dotnet_diagnostic.CA1806.severity = suggestion
# CA1822: Mark members as static
# Potential false positive around reflection/too much noise
dotnet_diagnostic.CA1822.severity = none
# CA1826: Do not use Enumerable method on indexable collections
dotnet_diagnostic.CA1826.severity = suggestion
# CA1859: Use concrete types when possible for improved performance
# Involves design considerations
dotnet_diagnostic.CA1859.severity = suggestion
# CA1860: Avoid using 'Enumerable.Any()' extension method
dotnet_diagnostic.CA1860.severity = suggestion
# CA1861: Avoid constant arrays as arguments
# Outdated with collection expressions
dotnet_diagnostic.CA1861.severity = suggestion
# CA2007: Consider calling ConfigureAwait on the awaited task
dotnet_diagnostic.CA2007.severity = warning
# CA2016: Forward the 'CancellationToken' parameter to methods
# Some overloads are having special handling for debugger
dotnet_diagnostic.CA2016.severity = suggestion
# CA2021: Do not call Enumerable.Cast<T> or Enumerable.OfType<T> with incompatible types
# Causing a lot of false positives with generics
dotnet_diagnostic.CA2021.severity = none
# CA2101: Specify marshaling for P/Invoke string arguments
# Reports warning for all non-UTF16 usages on DllImport; consider migrating to LibraryImport
dotnet_diagnostic.CA2101.severity = none
# CA2201: Do not raise reserved exception types
dotnet_diagnostic.CA2201.severity = warning
# CA2208: Instantiate argument exceptions correctly
dotnet_diagnostic.CA2208.severity = suggestion
# CA2242: Test for NaN correctly
dotnet_diagnostic.CA2242.severity = warning
# Banned APIs
dotnet_diagnostic.RS0030.severity = error
# Temporarily disable analysing CanBeNull = true in NRT contexts due to mobile issues.
# See: https://github.com/ppy/osu/pull/19677
dotnet_diagnostic.OSUF001.severity = none

View File

@ -1,58 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="osu! Rule Set" Description=" " ToolsVersion="16.0">
<Rules AnalyzerId="Microsoft.CodeQuality.Analyzers" RuleNamespace="Microsoft.CodeQuality.Analyzers">
<Rule Id="CA1016" Action="None" />
<Rule Id="CA1028" Action="None" />
<Rule Id="CA1031" Action="None" />
<Rule Id="CA1034" Action="None" />
<Rule Id="CA1036" Action="None" />
<Rule Id="CA1040" Action="None" />
<Rule Id="CA1044" Action="None" />
<Rule Id="CA1051" Action="None" />
<Rule Id="CA1054" Action="None" />
<Rule Id="CA1056" Action="None" />
<Rule Id="CA1062" Action="None" />
<Rule Id="CA1063" Action="None" />
<Rule Id="CA1067" Action="None" />
<Rule Id="CA1707" Action="None" />
<Rule Id="CA1710" Action="None" />
<Rule Id="CA1714" Action="None" />
<Rule Id="CA1716" Action="None" />
<Rule Id="CA1717" Action="None" />
<Rule Id="CA1720" Action="None" />
<Rule Id="CA1721" Action="None" />
<Rule Id="CA1724" Action="None" />
<Rule Id="CA1801" Action="None" />
<Rule Id="CA1806" Action="None" />
<Rule Id="CA1812" Action="None" />
<Rule Id="CA1814" Action="None" />
<Rule Id="CA1815" Action="None" />
<Rule Id="CA1819" Action="None" />
<Rule Id="CA1822" Action="None" />
<Rule Id="CA1823" Action="None" />
<Rule Id="CA2007" Action="Warning" />
<Rule Id="CA2214" Action="None" />
<Rule Id="CA2227" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.CodeQuality.CSharp.Analyzers" RuleNamespace="Microsoft.CodeQuality.CSharp.Analyzers">
<Rule Id="CA1001" Action="None" />
<Rule Id="CA1032" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.NetCore.Analyzers" RuleNamespace="Microsoft.NetCore.Analyzers">
<Rule Id="CA1303" Action="None" />
<Rule Id="CA1304" Action="None" />
<Rule Id="CA1305" Action="None" />
<Rule Id="CA1307" Action="None" />
<Rule Id="CA1308" Action="None" />
<Rule Id="CA1816" Action="None" />
<Rule Id="CA1826" Action="None" />
<Rule Id="CA2000" Action="None" />
<Rule Id="CA2008" Action="None" />
<Rule Id="CA2213" Action="None" />
<Rule Id="CA2235" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.NetCore.CSharp.Analyzers" RuleNamespace="Microsoft.NetCore.CSharp.Analyzers">
<Rule Id="CA1309" Action="Warning" />
<Rule Id="CA2201" Action="Warning" />
</Rules>
</RuleSet>

View File

@ -18,9 +18,21 @@
<ItemGroup Label="Code Analysis">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" PrivateAssets="All" />
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
<!-- Rider compatibility: .globalconfig needs to be explicitly referenced instead of using the global file name. -->
<GlobalAnalyzerConfigFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\osu.globalconfig" />
</ItemGroup>
<PropertyGroup Label="Code Analysis">
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset</CodeAnalysisRuleSet>
<AnalysisMode>Default</AnalysisMode>
<AnalysisModeDesign>Default</AnalysisModeDesign>
<AnalysisModeDocumentation>Recommended</AnalysisModeDocumentation>
<AnalysisModeGlobalization>Recommended</AnalysisModeGlobalization>
<AnalysisModeInteroperability>Recommended</AnalysisModeInteroperability>
<AnalysisModeMaintainability>Recommended</AnalysisModeMaintainability>
<AnalysisModeNaming>Default</AnalysisModeNaming>
<AnalysisModePerformance>Minimum</AnalysisModePerformance>
<AnalysisModeReliability>Recommended</AnalysisModeReliability>
<AnalysisModeSecurity>Default</AnalysisModeSecurity>
<AnalysisModeUsage>Default</AnalysisModeUsage>
</PropertyGroup>
<PropertyGroup Label="Documentation">
<GenerateDocumentationFile>true</GenerateDocumentationFile>

View File

@ -10,7 +10,7 @@
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.1128.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.1205.0" />
</ItemGroup>
<PropertyGroup>
<!-- Fody does not handle Android build well, and warns when unchanged.

View File

@ -148,15 +148,7 @@ namespace osu.Desktop.Windows
foreach (var association in uri_associations)
association.UpdateDescription(getLocalisedString(association.Description));
string getLocalisedString(LocalisableString s)
{
if (localisation == null)
return s.ToString();
var b = localisation.GetLocalisedBindableString(s);
b.UnbindAll();
return b.Value;
}
string getLocalisedString(LocalisableString s) => localisation?.GetLocalisedString(s) ?? s.ToString();
}
#region Native interop

View File

@ -164,7 +164,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 1: return colour_cyan;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
case 3:
@ -176,7 +176,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 2: return colour_cyan;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
case 4:
@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 3: return colour_purple;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
case 5:
@ -206,7 +206,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 4: return colour_cyan;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
case 6:
@ -224,7 +224,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 5: return colour_pink;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
case 7:
@ -244,7 +244,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 6: return colour_pink;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
case 8:
@ -266,7 +266,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 7: return colour_purple;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
case 9:
@ -290,7 +290,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 8: return colour_purple;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
case 10:
@ -316,7 +316,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 9: return colour_purple;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
}
@ -339,7 +339,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
case 5: return colour_green;
default: throw new ArgumentOutOfRangeException();
default: throw new ArgumentOutOfRangeException(nameof(columnIndex));
}
}
}

View File

@ -0,0 +1,7 @@
# Higher global_level has higher priority, the default global_level
# is 100 for root .globalconfig and 0 for others
# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/configuration-files#precedence
is_global = true
global_level = 101
dotnet_diagnostic.CA2007.severity = none

View File

@ -221,7 +221,7 @@ namespace osu.Game.Tests.Visual.Gameplay
string? filePath = null;
// Files starting with _ are temporary, created by CreateFileSafely call.
AddUntilStep("wait for export file", () => filePath = LocalStorage.GetFiles("exports").SingleOrDefault(f => !Path.GetFileName(f).StartsWith("_", StringComparison.Ordinal)), () => Is.Not.Null);
AddUntilStep("wait for export file", () => filePath = LocalStorage.GetFiles("exports").SingleOrDefault(f => !Path.GetFileName(f).StartsWith('_')), () => Is.Not.Null);
AddUntilStep("filesize is non-zero", () =>
{
try

View File

@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Mods
protected override TestPlayer CreateModPlayer(Ruleset ruleset)
{
var player = base.CreateModPlayer(ruleset);
player.RestartRequested = _ => restartRequested = true;
player.PrepareLoaderForRestart = _ => restartRequested = true;
return player;
}

View File

@ -198,7 +198,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("make second user host", () => MultiplayerClient.TransferHost(3));
AddUntilStep("kick buttons not visible", () => this.ChildrenOfType<ParticipantPanel.KickButton>().Count(d => d.IsPresent) == 0);
AddUntilStep("kick buttons not visible", () => !this.ChildrenOfType<ParticipantPanel.KickButton>().Any(d => d.IsPresent));
AddStep("make local user host again", () => MultiplayerClient.TransferHost(API.LocalUser.Value.Id));

View File

@ -101,7 +101,16 @@ namespace osu.Game.Tests.Visual.UserInterface
}
}
}
},
}
},
new OsuMenuItem(@"Another nested option")
{
Items = new MenuItem[]
{
new OsuMenuItem(@"Sub-One"),
new OsuMenuItem(@"Sub-Two"),
new OsuMenuItem(@"Sub-Three"),
}
},
new OsuMenuItem(@"Choose me please"),

View File

@ -151,7 +151,7 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("click delete option", () =>
{
InputManager.MoveMouseTo(contextMenuContainer.ChildrenOfType<DrawableOsuMenuItem>().First(i => i.Item.Text.Value.ToString().ToLowerInvariant() == "delete"));
InputManager.MoveMouseTo(contextMenuContainer.ChildrenOfType<DrawableOsuMenuItem>().First(i => string.Equals(i.Item.Text.Value.ToString(), "delete", System.StringComparison.OrdinalIgnoreCase)));
InputManager.Click(MouseButton.Left);
});

View File

@ -12,9 +12,9 @@
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Label="Code Analysis">
<CodeAnalysisRuleSet>tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup Label="Code Analysis">
<GlobalAnalyzerConfigFiles Include="CodeAnalysis.tests.globalconfig" />
</ItemGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="osu! Rule Set" Description=" " ToolsVersion="16.0">
<Rules AnalyzerId="Microsoft.CodeQuality.Analyzers" RuleNamespace="Microsoft.CodeQuality.Analyzers">
<Rule Id="CA2007" Action="None" />
</Rules>
</RuleSet>

View File

@ -248,29 +248,30 @@ namespace osu.Game.Database
return new RealmLive<T>(realmObject, realm);
}
#pragma warning disable RS0030 // mentioning banned symbols in documentation
/// <summary>
/// Register a callback to be invoked each time this <see cref="T:Realms.IRealmCollection`1" /> changes.
/// Register a callback to be invoked each time this <see cref="IRealmCollection{T}" /> changes.
/// </summary>
/// <remarks>
/// <para>
/// This adds osu! specific thread and managed state safety checks on top of <see cref="IRealmCollection{T}.SubscribeForNotifications"/>.
/// </para>
/// <para>
/// The first callback will be invoked with the initial <see cref="T:Realms.IRealmCollection`1" /> after the asynchronous query completes,
/// The first callback will be invoked with the initial <see cref="IRealmCollection{T}" /> after the asynchronous query completes,
/// and then called again after each write transaction which changes either any of the objects in the collection, or
/// which objects are in the collection. The <c>changes</c> parameter will
/// be <c>null</c> the first time the callback is invoked with the initial results. For each call after that,
/// it will contain information about which rows in the results were added, removed or modified.
/// </para>
/// <para>
/// If a write transaction did not modify any objects in this <see cref="T:Realms.IRealmCollection`1" />, the callback is not invoked at all.
/// If a write transaction did not modify any objects in this <see cref="IRealmCollection{T}" />, the callback is not invoked at all.
/// If an error occurs the callback will be invoked with <c>null</c> for the <c>sender</c> parameter and a non-<c>null</c> <c>error</c>.
/// Currently the only errors that can occur are when opening the <see cref="T:Realms.Realm" /> on the background worker thread.
/// Currently the only errors that can occur are when opening the <see cref="Realm" /> on the background worker thread.
/// </para>
/// <para>
/// At the time when the block is called, the <see cref="T:Realms.IRealmCollection`1" /> object will be fully evaluated
/// At the time when the block is called, the <see cref="IRealmCollection{T}" /> object will be fully evaluated
/// and up-to-date, and as long as you do not perform a write transaction on the same thread
/// or explicitly call <see cref="M:Realms.Realm.Refresh" />, accessing it will never perform blocking work.
/// or explicitly call <see cref="Realm.Refresh" />, accessing it will never perform blocking work.
/// </para>
/// <para>
/// Notifications are delivered via the standard event loop, and so can't be delivered while the event loop is blocked by other activity.
@ -279,13 +280,14 @@ namespace osu.Game.Database
/// </para>
/// </remarks>
/// <param name="collection">The <see cref="IRealmCollection{T}"/> to observe for changes.</param>
/// <param name="callback">The callback to be invoked with the updated <see cref="T:Realms.IRealmCollection`1" />.</param>
/// <param name="callback">The callback to be invoked with the updated <see cref="IRealmCollection{T}" />.</param>
/// <returns>
/// A subscription token. It must be kept alive for as long as you want to receive change notifications.
/// To stop receiving notifications, call <see cref="M:System.IDisposable.Dispose" />.
/// To stop receiving notifications, call <see cref="IDisposable.Dispose" />.
/// </returns>
/// <seealso cref="M:Realms.CollectionExtensions.SubscribeForNotifications``1(System.Collections.Generic.IList{``0},Realms.NotificationCallbackDelegate{``0})" />
/// <seealso cref="M:Realms.CollectionExtensions.SubscribeForNotifications``1(System.Linq.IQueryable{``0},Realms.NotificationCallbackDelegate{``0})" />
/// <seealso cref="Realms.CollectionExtensions.SubscribeForNotifications{T}(IList{T}, NotificationCallbackDelegate{T})" />
/// <seealso cref="Realms.CollectionExtensions.SubscribeForNotifications{T}(IQueryable{T}, NotificationCallbackDelegate{T})" />
#pragma warning restore RS0030
public static IDisposable QueryAsyncWithNotifications<T>(this IRealmCollection<T> collection, NotificationCallbackDelegate<T> callback)
where T : RealmObjectBase
{

View File

@ -60,7 +60,7 @@ namespace osu.Game.Extensions
public static string ToCamelCase(this string input)
{
string word = input.ToPascalCase();
return word.Length > 0 ? word.Substring(0, 1).ToLowerInvariant() + word.Substring(1) : word;
return word.Length > 0 ? char.ToLowerInvariant(word[0]) + word.Substring(1) : word;
}
/// <summary>

View File

@ -11,16 +11,8 @@ namespace osu.Game.Graphics.Cursor
[Cached(typeof(OsuContextMenuContainer))]
public partial class OsuContextMenuContainer : ContextMenuContainer
{
[Cached]
private OsuContextMenuSamples samples = new OsuContextMenuSamples();
private OsuContextMenu menu = null!;
public OsuContextMenuContainer()
{
AddInternal(samples);
}
protected override Menu CreateMenu() => menu = new OsuContextMenu(true);
public void CloseMenu()

View File

@ -23,6 +23,9 @@ namespace osu.Game.Graphics.UserInterface
DialogCancel,
[Description("dialog-ok")]
DialogOk
DialogOk,
[Description("menu-open")]
MenuOpen,
}
}

View File

@ -15,7 +15,7 @@ namespace osu.Game.Graphics.UserInterface
private const int fade_duration = 250;
[Resolved]
private OsuContextMenuSamples samples { get; set; } = null!;
private OsuMenuSamples menuSamples { get; set; } = null!;
// todo: this shouldn't be required after https://github.com/ppy/osu-framework/issues/4519 is fixed.
private bool wasOpened;
@ -47,15 +47,14 @@ namespace osu.Game.Graphics.UserInterface
protected override void AnimateOpen()
{
wasOpened = true;
this.FadeIn(fade_duration, Easing.OutQuint);
if (playClickSample)
samples.PlayClickSample();
if (!playClickSample)
return;
if (!wasOpened)
samples.PlayOpenSample();
wasOpened = true;
menuSamples.PlayClickSample();
menuSamples.PlayOpenSample();
}
protected override void AnimateClose()
@ -63,7 +62,7 @@ namespace osu.Game.Graphics.UserInterface
this.FadeOut(fade_duration, Easing.OutQuint);
if (wasOpened)
samples.PlayCloseSample();
menuSamples.PlayCloseSample();
wasOpened = false;
}

View File

@ -1,37 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
namespace osu.Game.Graphics.UserInterface
{
public partial class OsuContextMenuSamples : Component
{
private Sample sampleClick;
private Sample sampleOpen;
private Sample sampleClose;
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
sampleClick = audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-select");
sampleOpen = audio.Samples.Get(@"UI/dropdown-open");
sampleClose = audio.Samples.Get(@"UI/dropdown-close");
}
public void PlayClickSample() => Scheduler.AddOnce(playClickSample);
private void playClickSample() => sampleClick.Play();
public void PlayOpenSample() => Scheduler.AddOnce(playOpenSample);
private void playOpenSample() => sampleOpen.Play();
public void PlayCloseSample() => Scheduler.AddOnce(playCloseSample);
private void playCloseSample() => sampleClose.Play();
}
}

View File

@ -4,8 +4,6 @@
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -20,12 +18,12 @@ namespace osu.Game.Graphics.UserInterface
{
public partial class OsuMenu : Menu
{
private Sample sampleOpen;
private Sample sampleClose;
// todo: this shouldn't be required after https://github.com/ppy/osu-framework/issues/4519 is fixed.
private bool wasOpened;
[Resolved]
private OsuMenuSamples menuSamples { get; set; } = null!;
public OsuMenu(Direction direction, bool topLevelMenu = false)
: base(direction, topLevelMenu)
{
@ -33,13 +31,8 @@ namespace osu.Game.Graphics.UserInterface
MaskingContainer.CornerRadius = 4;
ItemsContainer.Padding = new MarginPadding(5);
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
sampleOpen = audio.Samples.Get(@"UI/dropdown-open");
sampleClose = audio.Samples.Get(@"UI/dropdown-close");
OnSubmenuOpen += _ => { menuSamples?.PlaySubOpenSample(); };
}
protected override void Update()
@ -64,7 +57,7 @@ namespace osu.Game.Graphics.UserInterface
protected override void AnimateOpen()
{
if (!TopLevelMenu && !wasOpened)
sampleOpen?.Play();
menuSamples?.PlayOpenSample();
this.FadeIn(300, Easing.OutQuint);
wasOpened = true;
@ -73,7 +66,7 @@ namespace osu.Game.Graphics.UserInterface
protected override void AnimateClose()
{
if (!TopLevelMenu && wasOpened)
sampleClose?.Play();
menuSamples?.PlayCloseSample();
this.FadeOut(300, Easing.OutQuint);
wasOpened = false;

View File

@ -0,0 +1,70 @@
// 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.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
namespace osu.Game.Graphics.UserInterface
{
public partial class OsuMenuSamples : Component
{
private Sample sampleClick;
private Sample sampleOpen;
private Sample sampleSubOpen;
private Sample sampleClose;
private bool triggerOpen;
private bool triggerSubOpen;
private bool triggerClose;
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
sampleClick = audio.Samples.Get(@"UI/menu-open-select");
sampleOpen = audio.Samples.Get(@"UI/menu-open");
sampleSubOpen = audio.Samples.Get(@"UI/menu-sub-open");
sampleClose = audio.Samples.Get(@"UI/menu-close");
}
public void PlayClickSample()
{
Scheduler.AddOnce(playClickSample);
}
public void PlayOpenSample()
{
triggerOpen = true;
Scheduler.AddOnce(resolvePlayback);
}
public void PlaySubOpenSample()
{
triggerSubOpen = true;
Scheduler.AddOnce(resolvePlayback);
}
public void PlayCloseSample()
{
triggerClose = true;
Scheduler.AddOnce(resolvePlayback);
}
private void playClickSample() => sampleClick.Play();
private void resolvePlayback()
{
if (triggerSubOpen)
sampleSubOpen?.Play();
else if (triggerOpen)
sampleOpen?.Play();
else if (triggerClose)
sampleClose?.Play();
triggerOpen = triggerSubOpen = triggerClose = false;
}
}
}

View File

@ -9,21 +9,6 @@ namespace osu.Game.Localisation
{
private const string prefix = @"osu.Game.Resources.Localisation.DebugSettings";
/// <summary>
/// "Debug"
/// </summary>
public static LocalisableString DebugSectionHeader => new TranslatableString(getKey(@"debug_section_header"), @"Debug");
/// <summary>
/// "Show log overlay"
/// </summary>
public static LocalisableString ShowLogOverlay => new TranslatableString(getKey(@"show_log_overlay"), @"Show log overlay");
/// <summary>
/// "Bypass front-to-back render pass"
/// </summary>
public static LocalisableString BypassFrontToBackPass => new TranslatableString(getKey(@"bypass_front_to_back_pass"), @"Bypass front-to-back render pass");
/// <summary>
/// "Import files"
/// </summary>
@ -34,16 +19,6 @@ namespace osu.Game.Localisation
/// </summary>
public static LocalisableString RunLatencyCertifier => new TranslatableString(getKey(@"run_latency_certifier"), @"Run latency certifier");
/// <summary>
/// "Memory"
/// </summary>
public static LocalisableString MemoryHeader => new TranslatableString(getKey(@"memory_header"), @"Memory");
/// <summary>
/// "Clear all caches"
/// </summary>
public static LocalisableString ClearAllCaches => new TranslatableString(getKey(@"clear_all_caches"), @"Clear all caches");
private static string getKey(string key) => $"{prefix}:{key}";
}
}

View File

@ -44,6 +44,11 @@ namespace osu.Game.Localisation
/// </summary>
public static LocalisableString CheckUpdate => new TranslatableString(getKey(@"check_update"), @"Check for updates");
/// <summary>
/// "Checking for updates"
/// </summary>
public static LocalisableString CheckingForUpdates => new TranslatableString(getKey(@"checking_for_updates"), @"Checking for updates");
/// <summary>
/// "Open osu! folder"
/// </summary>

View File

@ -363,7 +363,7 @@ namespace osu.Game.Online.Leaderboards
return null;
default:
throw new ArgumentOutOfRangeException();
throw new ArgumentOutOfRangeException(nameof(state));
}
}

View File

@ -87,7 +87,7 @@ namespace osu.Game.Online
break;
default:
throw new ArgumentOutOfRangeException();
throw new ArgumentOutOfRangeException(nameof(state.NewValue));
}
});

View File

@ -41,6 +41,7 @@ using osu.Game.Database;
using osu.Game.Extensions;
using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input;
using osu.Game.Input.Bindings;
using osu.Game.IO;
@ -383,6 +384,10 @@ namespace osu.Game
GlobalActionContainer globalBindings;
OsuMenuSamples menuSamples;
dependencies.Cache(menuSamples = new OsuMenuSamples());
base.Content.Add(menuSamples);
base.Content.Add(SafeAreaContainer = new SafeAreaContainer
{
SafeAreaOverrideEdges = SafeAreaOverrideEdges,

View File

@ -126,7 +126,7 @@ namespace osu.Game.Overlays
break;
default:
throw new ArgumentOutOfRangeException();
throw new ArgumentOutOfRangeException(nameof(state.NewValue));
}
}
}

View File

@ -386,10 +386,8 @@ namespace osu.Game.Overlays
{
channelList.RemoveChannel(channel);
if (loadedChannels.ContainsKey(channel))
if (loadedChannels.Remove(channel, out var loaded))
{
DrawableChannel loaded = loadedChannels[channel];
loadedChannels.Remove(channel);
// DrawableChannel removed from cache must be manually disposed
loaded.Dispose();
}

View File

@ -244,7 +244,7 @@ namespace osu.Game.Overlays.Comments
protected void OnSuccess(CommentBundle response)
{
commentCounter.Current.Value = response.Total;
newCommentEditor.CommentableMeta.Value = response.CommentableMeta.SingleOrDefault(m => m.Id == id.Value && m.Type == type.Value.ToString().ToSnakeCase().ToLowerInvariant());
newCommentEditor.CommentableMeta.Value = response.CommentableMeta.SingleOrDefault(m => m.Id == id.Value && string.Equals(m.Type, type.Value.ToString().ToSnakeCase(), StringComparison.OrdinalIgnoreCase));
if (!response.Comments.Any())
{

View File

@ -5,6 +5,7 @@
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Development;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation;
@ -90,11 +91,13 @@ namespace osu.Game.Overlays.FirstRunSetup
new GraphicsSection(),
new OnlineSection(),
new MaintenanceSection(),
new DebugSection(),
},
SearchTerm = SettingsItem<bool>.CLASSIC_DEFAULT_SEARCH_TERM,
}
};
if (DebugUtils.IsDebugBuild)
searchContainer.Add(new DebugSection());
}
private void applyClassic()

View File

@ -1,19 +1,17 @@
// 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.Development;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Localisation;
using osu.Game.Overlays.Settings.Sections.DebugSettings;
namespace osu.Game.Overlays.Settings.Sections
{
public partial class DebugSection : SettingsSection
{
public override LocalisableString Header => DebugSettingsStrings.DebugSectionHeader;
public override LocalisableString Header => @"Debug";
public override Drawable CreateIcon() => new SpriteIcon
{
@ -22,12 +20,12 @@ namespace osu.Game.Overlays.Settings.Sections
public DebugSection()
{
Add(new GeneralSettings());
if (DebugUtils.IsDebugBuild)
Add(new BatchImportSettings());
Add(new MemorySettings());
Children = new Drawable[]
{
new GeneralSettings(),
new BatchImportSettings(),
new MemorySettings(),
};
}
}
}

View File

@ -5,43 +5,28 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Framework.Screens;
using osu.Game.Localisation;
using osu.Game.Screens;
using osu.Game.Screens.Import;
using osu.Game.Screens.Utility;
namespace osu.Game.Overlays.Settings.Sections.DebugSettings
{
public partial class GeneralSettings : SettingsSubsection
{
protected override LocalisableString Header => CommonStrings.General;
protected override LocalisableString Header => @"General";
[BackgroundDependencyLoader]
private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig, IPerformFromScreenRunner? performer)
private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig)
{
Children = new Drawable[]
{
new SettingsCheckbox
{
LabelText = DebugSettingsStrings.ShowLogOverlay,
LabelText = @"Show log overlay",
Current = frameworkConfig.GetBindable<bool>(FrameworkSetting.ShowLogOverlay)
},
new SettingsCheckbox
{
LabelText = DebugSettingsStrings.BypassFrontToBackPass,
LabelText = @"Bypass front-to-back render pass",
Current = config.GetBindable<bool>(DebugSetting.BypassFrontToBackPass)
},
new SettingsButton
{
Text = DebugSettingsStrings.ImportFiles,
Action = () => performer?.PerformFromScreen(menu => menu.Push(new FileImportScreen()))
},
new SettingsButton
{
Text = DebugSettingsStrings.RunLatencyCertifier,
Action = () => performer?.PerformFromScreen(menu => menu.Push(new LatencyCertifierScreen()))
}
};
}
}

View File

@ -11,13 +11,12 @@ using osu.Framework.Localisation;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.Localisation;
namespace osu.Game.Overlays.Settings.Sections.DebugSettings
{
public partial class MemorySettings : SettingsSubsection
{
protected override LocalisableString Header => DebugSettingsStrings.MemoryHeader;
protected override LocalisableString Header => @"Memory";
[BackgroundDependencyLoader]
private void load(GameHost host, RealmAccess realm)
@ -29,27 +28,27 @@ namespace osu.Game.Overlays.Settings.Sections.DebugSettings
{
new SettingsButton
{
Text = DebugSettingsStrings.ClearAllCaches,
Text = @"Clear all caches",
Action = host.Collect
},
new SettingsButton
{
Text = "Compact realm",
Text = @"Compact realm",
Action = () =>
{
// Blocking operations implicitly causes a Compact().
using (realm.BlockAllOperations("compact"))
using (realm.BlockAllOperations(@"compact"))
{
}
}
},
blockAction = new SettingsButton
{
Text = "Block realm",
Text = @"Block realm",
},
unblockAction = new SettingsButton
{
Text = "Unblock realm",
Text = @"Unblock realm",
},
};
@ -57,7 +56,7 @@ namespace osu.Game.Overlays.Settings.Sections.DebugSettings
{
try
{
IDisposable? token = realm.BlockAllOperations("maintenance");
IDisposable? token = realm.BlockAllOperations(@"maintenance");
blockAction.Enabled.Value = false;
@ -89,7 +88,7 @@ namespace osu.Game.Overlays.Settings.Sections.DebugSettings
}
catch (Exception e)
{
Logger.Error(e, "Blocking realm failed");
Logger.Error(e, @"Blocking realm failed");
}
};
}

View File

@ -4,7 +4,6 @@
using System.Threading.Tasks;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Framework.Logging;
@ -13,6 +12,7 @@ using osu.Framework.Screens;
using osu.Framework.Statistics;
using osu.Game.Configuration;
using osu.Game.Localisation;
using osu.Game.Online.Multiplayer;
using osu.Game.Overlays.Notifications;
using osu.Game.Overlays.Settings.Sections.Maintenance;
using osu.Game.Updater;
@ -36,8 +36,11 @@ namespace osu.Game.Overlays.Settings.Sections.General
[Resolved]
private Storage storage { get; set; } = null!;
[Resolved]
private OsuGame? game { get; set; }
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, OsuGame? game)
private void load(OsuConfigManager config)
{
Add(new SettingsEnumDropdown<ReleaseStream>
{
@ -50,23 +53,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
Add(checkForUpdatesButton = new SettingsButton
{
Text = GeneralSettingsStrings.CheckUpdate,
Action = () =>
{
checkForUpdatesButton.Enabled.Value = false;
Task.Run(updateManager.CheckForUpdateAsync).ContinueWith(task => Schedule(() =>
{
if (!task.GetResultSafely())
{
notifications?.Post(new SimpleNotification
{
Text = GeneralSettingsStrings.RunningLatestRelease(game!.Version),
Icon = FontAwesome.Solid.CheckCircle,
});
}
checkForUpdatesButton.Enabled.Value = true;
}));
}
Action = () => checkForUpdates().FireAndForget()
});
}
@ -94,6 +81,44 @@ namespace osu.Game.Overlays.Settings.Sections.General
}
}
private async Task checkForUpdates()
{
if (updateManager == null || game == null)
return;
checkForUpdatesButton.Enabled.Value = false;
var checkingNotification = new ProgressNotification
{
Text = GeneralSettingsStrings.CheckingForUpdates,
};
notifications?.Post(checkingNotification);
try
{
bool foundUpdate = await updateManager.CheckForUpdateAsync().ConfigureAwait(true);
if (!foundUpdate)
{
notifications?.Post(new SimpleNotification
{
Text = GeneralSettingsStrings.RunningLatestRelease(game.Version),
Icon = FontAwesome.Solid.CheckCircle,
});
}
}
catch
{
}
finally
{
// This sequence allows the notification to be immediately dismissed.
checkingNotification.State = ProgressNotificationState.Cancelled;
checkingNotification.Close(false);
checkForUpdatesButton.Enabled.Value = true;
}
}
private void exportLogs()
{
ProgressNotification notification = new ProgressNotification

View File

@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
{
protected override LocalisableString Header => BindingSettingsStrings.ShortcutAndGameplayBindings;
public override IEnumerable<LocalisableString> FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { @"keybindings", @"controls", @"keyboard", @"keys" });
public override IEnumerable<LocalisableString> FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { @"keybindings", @"controls", @"keyboard", @"keys", @"buttons" });
public BindingSettings(KeyBindingPanel keyConfig)
{

View File

@ -114,10 +114,10 @@ namespace osu.Game.Overlays.Settings.Sections.Input
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows || RuntimeInfo.OS == RuntimeInfo.Platform.Linux)
{
t.NewLine();
var formattedSource = MessageFormatter.FormatText(localisation.GetLocalisedBindableString(TabletSettingsStrings.NoTabletDetectedDescription(
var formattedSource = MessageFormatter.FormatText(localisation.GetLocalisedString(TabletSettingsStrings.NoTabletDetectedDescription(
RuntimeInfo.OS == RuntimeInfo.Platform.Windows
? @"https://opentabletdriver.net/Wiki/FAQ/Windows"
: @"https://opentabletdriver.net/Wiki/FAQ/Linux")).Value);
: @"https://opentabletdriver.net/Wiki/FAQ/Linux")));
t.AddLinks(formattedSource.Text, formattedSource.Links);
}
}),

View File

@ -0,0 +1,36 @@
// 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.Localisation;
using osu.Framework.Screens;
using osu.Game.Localisation;
using osu.Game.Screens;
using osu.Game.Screens.Import;
using osu.Game.Screens.Utility;
namespace osu.Game.Overlays.Settings.Sections.Maintenance
{
public partial class GeneralSettings : SettingsSubsection
{
protected override LocalisableString Header => CommonStrings.General;
[BackgroundDependencyLoader]
private void load(IPerformFromScreenRunner? performer)
{
Children = new[]
{
new SettingsButton
{
Text = DebugSettingsStrings.ImportFiles,
Action = () => performer?.PerformFromScreen(menu => menu.Push(new FileImportScreen()))
},
new SettingsButton
{
Text = DebugSettingsStrings.RunLatencyCertifier,
Action = () => performer?.PerformFromScreen(menu => menu.Push(new LatencyCertifierScreen()))
}
};
}
}
}

View File

@ -23,6 +23,7 @@ namespace osu.Game.Overlays.Settings.Sections
{
Children = new Drawable[]
{
new GeneralSettings(),
new BeatmapSettings(),
new SkinSettings(),
new CollectionsSettings(),

View File

@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Development;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
@ -27,7 +28,9 @@ namespace osu.Game.Overlays
public LocalisableString Title => SettingsStrings.HeaderTitle;
public LocalisableString Description => SettingsStrings.HeaderDescription;
protected override IEnumerable<SettingsSection> CreateSections() => new SettingsSection[]
protected override IEnumerable<SettingsSection> CreateSections()
{
var sections = new List<SettingsSection>
{
// This list should be kept in sync with ScreenBehaviour.
new GeneralSection(),
@ -40,9 +43,14 @@ namespace osu.Game.Overlays
new GraphicsSection(),
new OnlineSection(),
new MaintenanceSection(),
new DebugSection(),
};
if (DebugUtils.IsDebugBuild)
sections.Add(new DebugSection());
return sections;
}
private readonly List<SettingsSubPanel> subPanels = new List<SettingsSubPanel>();
private SettingsSubPanel lastOpenedSubPanel;

View File

@ -130,7 +130,7 @@ namespace osu.Game.Overlays.Toolbar
break;
default:
throw new ArgumentOutOfRangeException();
throw new ArgumentOutOfRangeException(nameof(state.NewValue));
}
});
}

View File

@ -173,10 +173,10 @@ namespace osu.Game.Rulesets.Mods
};
drawable.OnRevertResult += (_, result) =>
{
if (!ratesForRewinding.ContainsKey(result.HitObject)) return;
if (!ratesForRewinding.TryGetValue(result.HitObject, out double rate)) return;
if (!shouldProcessResult(result)) return;
recentRates.Insert(0, ratesForRewinding[result.HitObject]);
recentRates.Insert(0, rate);
ratesForRewinding.Remove(result.HitObject);
recentRates.RemoveAt(recentRates.Count - 1);

View File

@ -243,7 +243,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
return PathType.CATMULL;
case 'B':
if (input.Length > 1 && int.TryParse(input.Substring(1), out int degree) && degree > 0)
if (input.Length > 1 && int.TryParse(input.AsSpan(1), out int degree) && degree > 0)
return PathType.BSpline(degree);
return PathType.BEZIER;

View File

@ -99,7 +99,7 @@ namespace osu.Game.Screens.Edit.Components.Menus
ForegroundColourHover = colourProvider.Content1;
BackgroundColourHover = colourProvider.Background1;
AddInternal(hoverClickSounds = new HoverClickSounds());
AddInternal(hoverClickSounds = new HoverClickSounds(HoverSampleSet.MenuOpen));
}
protected override void LoadComplete()

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -80,19 +81,34 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
bool matchingFilter = true;
matchingFilter &= criteria.Ruleset == null || r.Room.PlaylistItemStats?.RulesetIDs.Any(id => id == criteria.Ruleset.OnlineID) != false;
if (!string.IsNullOrEmpty(criteria.SearchString))
{
// Room name isn't translatable, so ToString() is used here for simplicity.
matchingFilter &= r.FilterTerms.Any(term => term.ToString().Contains(criteria.SearchString, StringComparison.InvariantCultureIgnoreCase));
}
matchingFilter &= matchPermissions(r, criteria.Permissions);
// Room name isn't translatable, so ToString() is used here for simplicity.
string[] filterTerms = r.FilterTerms.Select(t => t.ToString()).ToArray();
string[] searchTerms = criteria.SearchString.Split(' ', StringSplitOptions.RemoveEmptyEntries);
matchingFilter &= searchTerms.All(searchTerm => filterTerms.Any(filterTerm => checkTerm(filterTerm, searchTerm)));
r.MatchingFilter = matchingFilter;
}
});
// Lifted from SearchContainer.
static bool checkTerm(string haystack, string needle)
{
int index = 0;
for (int i = 0; i < needle.Length; i++)
{
int found = CultureInfo.InvariantCulture.CompareInfo.IndexOf(haystack, needle[i], index, CompareOptions.OrdinalIgnoreCase);
if (found < 0)
return false;
index = found + 1;
}
return true;
}
static bool matchPermissions(DrawableLoungeRoom room, RoomPermissionsFilter accessType)
{
switch (accessType)

View File

@ -83,7 +83,7 @@ namespace osu.Game.Screens.Play
/// </summary>
protected virtual bool PauseOnFocusLost => true;
public Action<bool> RestartRequested;
public Action<bool> PrepareLoaderForRestart;
private bool isRestarting;
private bool skipExitTransition;
@ -646,7 +646,6 @@ namespace osu.Game.Screens.Play
// import current score if possible.
prepareAndImportScoreAsync();
// Screen may not be current if a restart has been performed.
if (this.IsCurrentScreen())
{
skipExitTransition = skipTransition;
@ -657,6 +656,12 @@ namespace osu.Game.Screens.Play
// - the pause / fail dialog was requested but couldn't be displayed due to the type or state of this Player instance.
this.Exit();
}
else
{
// May be restarting from results screen.
if (this.GetChildScreen() != null)
this.MakeCurrent();
}
return true;
}
@ -719,12 +724,8 @@ namespace osu.Game.Screens.Play
// stopping here is to ensure music doesn't become audible after exiting back to PlayerLoader.
musicController.Stop();
if (RestartRequested != null)
{
skipExitTransition = quickRestart;
RestartRequested?.Invoke(quickRestart);
return true;
}
PrepareLoaderForRestart?.Invoke(quickRestart);
return PerformExit(quickRestart);
}

View File

@ -457,7 +457,7 @@ namespace osu.Game.Screens.Play
CurrentPlayer = createPlayer();
CurrentPlayer.Configuration.AutomaticallySkipIntro |= quickRestart;
CurrentPlayer.RestartCount = restartCount++;
CurrentPlayer.RestartRequested = restartRequested;
CurrentPlayer.PrepareLoaderForRestart = prepareForRestart;
LoadTask = LoadComponentAsync(CurrentPlayer, _ =>
{
@ -470,13 +470,11 @@ namespace osu.Game.Screens.Play
{
}
private void restartRequested(bool quickRestartRequested)
private void prepareForRestart(bool quickRestartRequested)
{
quickRestart = quickRestartRequested;
hideOverlays = true;
ValidForResume = true;
this.MakeCurrent();
}
private void contentIn(double delayBeforeSideDisplays = 0)

View File

@ -55,6 +55,8 @@ namespace osu.Game.Screens.Ranking
[Resolved]
private Player? player { get; set; }
private bool skipExitTransition;
[Resolved]
private IAPIProvider api { get; set; } = null!;
@ -203,6 +205,7 @@ namespace osu.Game.Screens.Ranking
{
if (!this.IsCurrentScreen()) return;
skipExitTransition = true;
player?.Restart(true);
},
});
@ -313,6 +316,7 @@ namespace osu.Game.Screens.Ranking
// HitObject references from HitEvent.
Score?.HitEvents.Clear();
if (!skipExitTransition)
this.FadeOut(100);
return false;
}

View File

@ -59,14 +59,14 @@ namespace osu.Game.Screens.Ranking.Statistics.User
new SimpleStatisticTable.Spacer(),
new PerformancePointsChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
},
new Drawable[] { },
[],
new Drawable[]
{
new MaximumComboChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
new SimpleStatisticTable.Spacer(),
new AccuracyChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
},
new Drawable[] { },
[],
new Drawable[]
{
new RankedScoreChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },

View File

@ -37,7 +37,7 @@ namespace osu.Game.Skinning
protected override string[] HashableFileTypes => new[] { ".ini", ".json" };
protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path).ToLowerInvariant() == @".osk";
protected override bool ShouldDeleteArchive(string path) => string.Equals(Path.GetExtension(path), @".osk", StringComparison.OrdinalIgnoreCase);
protected override SkinInfo CreateModel(ArchiveReader archive, ImportParameters parameters) => new SkinInfo { Name = archive.Name ?? @"No name" };

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -91,7 +92,7 @@ namespace osu.Game.Storyboards
// Importantly, do this after the NullOrEmpty because EF may have stored the non-nullable value as null to the database, bypassing compile-time constraints.
backgroundPath = backgroundPath.ToLowerInvariant();
return GetLayer("Background").Elements.Any(e => e.Path.ToLowerInvariant() == backgroundPath);
return GetLayer("Background").Elements.Any(e => string.Equals(e.Path, backgroundPath, StringComparison.OrdinalIgnoreCase));
}
}

View File

@ -78,12 +78,12 @@ namespace osu.Game.Tests.Visual.Spectator
/// <param name="state">The spectator state to end play with.</param>
public void SendEndPlay(int userId, SpectatedUserState state = SpectatedUserState.Quit)
{
if (!userBeatmapDictionary.TryGetValue(userId, out int value))
if (!userBeatmapDictionary.TryGetValue(userId, out int beatmapId))
return;
((ISpectatorClient)this).UserFinishedPlaying(userId, new SpectatorState
{
BeatmapID = value,
BeatmapID = beatmapId,
RulesetID = 0,
Mods = userModsDictionary[userId],
State = state

View File

@ -666,10 +666,7 @@ namespace osu.Game.Utils
{
// 2020-10-07 jbialogrodzki #730 Since this is public API we should probably
// handle null arguments? It doesn't seem to have been done consistently in this class though.
if (coefficients == null)
{
throw new ArgumentNullException(nameof(coefficients));
}
ArgumentNullException.ThrowIfNull(coefficients);
// 2020-10-07 jbialogrodzki #730 Zero polynomials need explicit handling.
// Without this check, we attempted to peek coefficients at negative indices!

View File

@ -35,8 +35,8 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Realm" Version="11.5.0" />
<PackageReference Include="ppy.osu.Framework" Version="2024.1128.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2024.1106.0" />
<PackageReference Include="ppy.osu.Framework" Version="2024.1205.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2024.1202.0" />
<PackageReference Include="Sentry" Version="4.13.0" />
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
<PackageReference Include="SharpCompress" Version="0.38.0" />

View File

@ -17,6 +17,6 @@
<MtouchInterpreter>-all</MtouchInterpreter>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Framework.iOS" Version="2024.1128.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2024.1205.0" />
</ItemGroup>
</Project>

View File

@ -56,11 +56,10 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{10DF8F12-50FD-45D8-8A38-17BA764BF54D}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.globalconfig = .globalconfig
Directory.Build.props = Directory.Build.props
osu.Android.props = osu.Android.props
osu.iOS.props = osu.iOS.props
CodeAnalysis\osu.ruleset = CodeAnalysis\osu.ruleset
global.json = global.json
osu.sln.DotSettings = osu.sln.DotSettings
osu.TestProject.props = osu.TestProject.props
EndProjectSection
@ -95,6 +94,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Pippidon"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Pippidon.Tests", "Templates\Rulesets\ruleset-scrolling-example\osu.Game.Rulesets.Pippidon.Tests\osu.Game.Rulesets.Pippidon.Tests.csproj", "{1743BF7C-E6AE-4A06-BAD9-166D62894303}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CodeAnalysis", "CodeAnalysis", "{FB156649-D457-4D1A-969C-D3A23FD31513}"
ProjectSection(SolutionItems) = preProject
CodeAnalysis\BannedSymbols.txt = CodeAnalysis\BannedSymbols.txt
CodeAnalysis\osu.globalconfig = CodeAnalysis\osu.globalconfig
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU