diff --git a/.editorconfig b/.editorconfig index 440c24f..3ac4a83 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,17 @@ # EditorConfig is awesome: http://editorconfig.org root = true +[*.{csproj,props,targets}] +charset = utf-8-bom +end_of_line = crlf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +[g_*.cs] +generated_code = true + [*.cs] end_of_line = crlf insert_final_newline = true @@ -8,6 +19,9 @@ indent_style = space indent_size = 4 trim_trailing_whitespace = true +#license header +file_header_template = Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.\nSee the LICENCE file in the repository root for full licence text. + #Roslyn naming styles #PascalCase for public and protected members @@ -121,7 +135,7 @@ dotnet_style_qualification_for_event = false:warning dotnet_style_predefined_type_for_locals_parameters_members = true:warning dotnet_style_predefined_type_for_member_access = true:warning csharp_style_var_when_type_is_apparent = true:none -csharp_style_var_for_built_in_types = true:none +csharp_style_var_for_built_in_types = false:warning csharp_style_var_elsewhere = true:silent #Style - modifiers @@ -165,7 +179,7 @@ csharp_style_unused_value_assignment_preference = discard_variable:warning #Style - variable declaration csharp_style_inlined_variable_declaration = true:warning -csharp_style_deconstructed_variable_declaration = true:warning +csharp_style_deconstructed_variable_declaration = false:silent #Style - other C# 7.x features dotnet_style_prefer_inferred_tuple_names = true:warning @@ -176,19 +190,20 @@ dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent #Style - C# 8 features csharp_prefer_static_local_function = true:warning csharp_prefer_simple_using_statement = true:silent -csharp_style_prefer_index_operator = true:warning -csharp_style_prefer_range_operator = true:warning +csharp_style_prefer_index_operator = false:silent +csharp_style_prefer_range_operator = false:silent csharp_style_prefer_switch_expression = false:none -#Supressing roslyn built-in analyzers -# Suppress: EC112 +csharp_style_namespace_declarations = block_scoped:warning -#Private method is unused -dotnet_diagnostic.IDE0051.severity = silent -#Private member is unused -dotnet_diagnostic.IDE0052.severity = silent +#Style - C# 12 features +csharp_style_prefer_primary_constructors = false -#Rules for disposable -dotnet_diagnostic.IDE0067.severity = none -dotnet_diagnostic.IDE0068.severity = none -dotnet_diagnostic.IDE0069.severity = none \ No newline at end of file +[*.{yaml,yml}] +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +dotnet_diagnostic.OLOC001.words_in_name = 5 +dotnet_diagnostic.OLOC001.license_header = // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.\n// See the LICENCE file in the repository root for full licence text. diff --git a/.gitattributes b/.gitattributes index e3ffd34..c61ca25 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,6 +15,7 @@ App.config text eol=crlf *.cmd text eol=crlf *.snippet text eol=crlf *.manifest text eol=crlf +*.licenseheader text eol=crlf # Check out with lf (UNIX) line endings *.sh text eol=lf diff --git a/.gitignore b/.gitignore index 2b9c125..3dca421 100644 --- a/.gitignore +++ b/.gitignore @@ -10,12 +10,8 @@ # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs -### Cake ### -tools/** -build/tools/** - # Build results -bin/[Dd]ebug/ +[Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ @@ -102,7 +98,6 @@ $tf/ _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user -inspectcode # JustCode is a .NET coding add-in .JustCode @@ -196,6 +191,7 @@ ClientBin/ *.publishsettings node_modules/ orleans.codegen.cs +Resource.designer.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) @@ -251,21 +247,102 @@ paket-files/ # FAKE - F# Make .fake/ -# JetBrains Rider -.idea/.idea.osu.Tools/.idea/*.xml -.idea/.idea.osu.Tools/.idea/codeStyles/*.xml -.idea/.idea.osu.Tools/.idea/dataSources/*.xml -.idea/.idea.osu.Tools/.idea/dictionaries/*.xml -.idea/.idea.osu.Tools/*.iml -*.sln.iml - -# CodeRush -.cr/ - # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc -Staging/ +# Cake # +/tools/** +/build/tools/** +/build/temp/** + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf +.idea/*/.idea/projectSettingsUpdater.xml +.idea/*/.idea/encodings.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +.idea/modules.xml +.idea/*.iml +.idea/modules +*.iml +*.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# fastlane +fastlane/report.xml + +# inspectcode inspectcodereport.xml +inspectcode + +# BenchmarkDotNet +/BenchmarkDotNet.Artifacts + +*.GeneratedMSBuildEditorConfig.editorconfig + +# Fody (pulled in by Realm) - schema file +FodyWeavers.xsd + +.idea/.idea.osu.Desktop/.idea/misc.xml +.idea/.idea.osu.Android/.idea/deploymentTargetDropDown.xml + PerformanceCalculator/cache/ diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt new file mode 100644 index 0000000..550f7c8 --- /dev/null +++ b/CodeAnalysis/BannedSymbols.txt @@ -0,0 +1,20 @@ +M:System.Object.Equals(System.Object,System.Object)~System.Boolean;Don't use object.Equals. Use IEquatable or EqualityComparer.Default instead. +M:System.Object.Equals(System.Object)~System.Boolean;Don't use object.Equals. Use IEquatable or EqualityComparer.Default instead. +M:System.ValueType.Equals(System.Object)~System.Boolean;Don't use object.Equals(Fallbacks to ValueType). Use IEquatable or EqualityComparer.Default instead. +M:System.Nullable`1.Equals(System.Object)~System.Boolean;Use == instead. +T:System.IComparable;Don't use non-generic IComparable. Use generic version instead. +T:SixLabors.ImageSharp.IDeepCloneable`1;Use osu.Game.Utils.IDeepCloneable instead. +M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText. +M:osu.Framework.Bindables.IBindableList`1.GetBoundCopy();Fails on iOS. Use manual ctor + BindTo instead. (see https://github.com/mono/mono/issues/19900) +T:NuGet.Packaging.CollectionExtensions;Don't use internal extension methods. +M:Realms.IRealmCollection`1.SubscribeForNotifications`1(Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IRealmCollection,NotificationCallbackDelegate) instead. +M:System.Guid.#ctor;Probably meaning to use Guid.NewGuid() instead. If actually wanting empty, use Guid.Empty. +M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Linq.IQueryable{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IQueryable,NotificationCallbackDelegate) instead. +M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Collections.Generic.IList{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IList,NotificationCallbackDelegate) 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: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. diff --git a/CodeAnalysis/osu.Tools.globalconfig b/CodeAnalysis/osu.Tools.globalconfig new file mode 100644 index 0000000..247a825 --- /dev/null +++ b/CodeAnalysis/osu.Tools.globalconfig @@ -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 or Enumerable.OfType 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 diff --git a/PerformanceCalculator/Difficulty/LegacyScoreAttributesCommand.cs b/PerformanceCalculator/Difficulty/LegacyScoreAttributesCommand.cs index 053287b..375b84e 100644 --- a/PerformanceCalculator/Difficulty/LegacyScoreAttributesCommand.cs +++ b/PerformanceCalculator/Difficulty/LegacyScoreAttributesCommand.cs @@ -144,7 +144,7 @@ namespace PerformanceCalculator.Difficulty foreach (var modString in Mods) { - Mod newMod = availableMods.FirstOrDefault(m => string.Equals(m.Acronym, modString, StringComparison.CurrentCultureIgnoreCase)); + Mod newMod = availableMods.FirstOrDefault(m => string.Equals(m.Acronym, modString, StringComparison.OrdinalIgnoreCase)); if (newMod == null) throw new ArgumentException($"Invalid mod provided: {modString}"); diff --git a/PerformanceCalculator/Difficulty/LegacyScoreConversionCommand.cs b/PerformanceCalculator/Difficulty/LegacyScoreConversionCommand.cs index 190baa3..a773181 100644 --- a/PerformanceCalculator/Difficulty/LegacyScoreConversionCommand.cs +++ b/PerformanceCalculator/Difficulty/LegacyScoreConversionCommand.cs @@ -99,7 +99,7 @@ namespace PerformanceCalculator.Difficulty foreach (var modString in Mods) { - Mod newMod = availableMods.FirstOrDefault(m => string.Equals(m.Acronym, modString, StringComparison.CurrentCultureIgnoreCase)); + Mod newMod = availableMods.FirstOrDefault(m => string.Equals(m.Acronym, modString, StringComparison.OrdinalIgnoreCase)); if (newMod == null) throw new ArgumentException($"Invalid mod provided: {modString}"); diff --git a/PerformanceCalculator/Difficulty/ModsCommand.cs b/PerformanceCalculator/Difficulty/ModsCommand.cs index f3f5ea5..4298ab9 100644 --- a/PerformanceCalculator/Difficulty/ModsCommand.cs +++ b/PerformanceCalculator/Difficulty/ModsCommand.cs @@ -11,6 +11,7 @@ using Humanizer; using McMaster.Extensions.CommandLineUtils; using Newtonsoft.Json; using osu.Game.Configuration; +using osu.Game.Extensions; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -75,7 +76,7 @@ namespace PerformanceCalculator.Difficulty yield return new { - Name = propertyInfo.Name.Underscore(), + Name = propertyInfo.Name.ToSnakeCase(), Type = getJsonType(netType), Label = settingsSource.Label.ToString(), Description = settingsSource.Description.ToString(), diff --git a/PerformanceCalculator/PerformanceCalculator.csproj b/PerformanceCalculator/PerformanceCalculator.csproj index ef2d471..17b3e4e 100644 --- a/PerformanceCalculator/PerformanceCalculator.csproj +++ b/PerformanceCalculator/PerformanceCalculator.csproj @@ -8,10 +8,10 @@ - - - - - + + + + + diff --git a/PerformanceCalculator/ProcessorCommand.cs b/PerformanceCalculator/ProcessorCommand.cs index 392631c..d090120 100644 --- a/PerformanceCalculator/ProcessorCommand.cs +++ b/PerformanceCalculator/ProcessorCommand.cs @@ -88,7 +88,7 @@ namespace PerformanceCalculator var ppAttributeValues = JsonConvert.DeserializeObject>(JsonConvert.SerializeObject(result.PerformanceAttributes)) ?? new Dictionary(); foreach (var attrib in ppAttributeValues) - document.Children.Add(FormatDocumentLine(attrib.Key.Humanize().ToLower(), FormattableString.Invariant($"{attrib.Value:N2}"))); + document.Children.Add(FormatDocumentLine(attrib.Key.Humanize().ToLower(CultureInfo.InvariantCulture), FormattableString.Invariant($"{attrib.Value:N2}"))); AddSectionHeader(document, "Difficulty attributes"); diff --git a/PerformanceCalculator/Simulate/OsuSimulateCommand.cs b/PerformanceCalculator/Simulate/OsuSimulateCommand.cs index c981095..337a9c2 100644 --- a/PerformanceCalculator/Simulate/OsuSimulateCommand.cs +++ b/PerformanceCalculator/Simulate/OsuSimulateCommand.cs @@ -161,19 +161,18 @@ namespace PerformanceCalculator.Simulate double total = 6 * countGreat + 2 * countGood + countMeh; double max = 6 * (countGreat + countGood + countMeh + countMiss); - if (statistics.ContainsKey(HitResult.SliderTailHit)) + if (statistics.TryGetValue(HitResult.SliderTailHit, out int countSliderTailHit)) { var countSliders = beatmap.HitObjects.Count(x => x is Slider); - var countSliderTailHit = statistics[HitResult.SliderTailHit]; total += 3 * countSliderTailHit; max += 3 * countSliders; } - if (statistics.ContainsKey(HitResult.LargeTickMiss)) + if (statistics.TryGetValue(HitResult.LargeTickMiss, out int countLargeTickMiss)) { var countLargeTicks = beatmap.HitObjects.Sum(obj => obj.NestedHitObjects.Count(x => x is SliderTick or SliderRepeat)); - var countLargeTickHit = countLargeTicks - statistics[HitResult.LargeTickMiss]; + var countLargeTickHit = countLargeTicks - countLargeTickMiss; total += 0.6 * countLargeTickHit; max += 0.6 * countLargeTicks; diff --git a/PerformanceCalculatorGUI/APIManager.cs b/PerformanceCalculatorGUI/APIManager.cs index 65aec67..02551d0 100644 --- a/PerformanceCalculatorGUI/APIManager.cs +++ b/PerformanceCalculatorGUI/APIManager.cs @@ -35,14 +35,14 @@ namespace PerformanceCalculatorGUI { if (token == null) { - await getAccessToken(); + await getAccessToken().ConfigureAwait(false); Debug.Assert(token != null); } using var req = new JsonWebRequest($"{ENDPOINT_CONFIGURATION.APIEndpointUrl}/api/v2/{request}"); req.AddHeader("x-api-version", api_version.ToString(CultureInfo.InvariantCulture)); req.AddHeader(System.Net.HttpRequestHeader.Authorization.ToString(), $"Bearer {token.AccessToken}"); - await req.PerformAsync(); + await req.PerformAsync().ConfigureAwait(false); return req.ResponseObject; } @@ -58,7 +58,7 @@ namespace PerformanceCalculatorGUI req.AddParameter("client_secret", clientSecretBindable.Value); req.AddParameter("grant_type", "client_credentials"); req.AddParameter("scope", "public"); - await req.PerformAsync(); + await req.PerformAsync().ConfigureAwait(false); token = req.ResponseObject; } diff --git a/PerformanceCalculatorGUI/Components/ExtendedProfileScore.cs b/PerformanceCalculatorGUI/Components/ExtendedProfileScore.cs index 626c52e..6f5d569 100644 --- a/PerformanceCalculatorGUI/Components/ExtendedProfileScore.cs +++ b/PerformanceCalculatorGUI/Components/ExtendedProfileScore.cs @@ -125,7 +125,7 @@ namespace PerformanceCalculatorGUI.Components Anchor = Anchor.Centre, Origin = Anchor.Centre, Colour = colourProvider.Light1, - Text = Score.PositionChange.Value.ToString() + Text = $"{Score.PositionChange.Value:+0;-0;-}" } }, new Container diff --git a/PerformanceCalculatorGUI/Components/TextBoxes/LimitedLabelledFractionalNumberBox.cs b/PerformanceCalculatorGUI/Components/TextBoxes/LimitedLabelledFractionalNumberBox.cs index f1d4841..f6fbbad 100644 --- a/PerformanceCalculatorGUI/Components/TextBoxes/LimitedLabelledFractionalNumberBox.cs +++ b/PerformanceCalculatorGUI/Components/TextBoxes/LimitedLabelledFractionalNumberBox.cs @@ -11,8 +11,6 @@ namespace PerformanceCalculatorGUI.Components.TextBoxes { private partial class FractionalNumberBox : OsuTextBox { - protected override bool AllowIme => false; - protected override bool CanAddCharacter(char character) => char.IsAsciiDigit(character) || character == CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator[0]; protected override void OnUserTextAdded(string added) diff --git a/PerformanceCalculatorGUI/PerformanceCalculatorGUI.csproj b/PerformanceCalculatorGUI/PerformanceCalculatorGUI.csproj index 8513064..adb994f 100644 --- a/PerformanceCalculatorGUI/PerformanceCalculatorGUI.csproj +++ b/PerformanceCalculatorGUI/PerformanceCalculatorGUI.csproj @@ -6,10 +6,10 @@ latest - - - - - + + + + + diff --git a/PerformanceCalculatorGUI/RulesetHelper.cs b/PerformanceCalculatorGUI/RulesetHelper.cs index 659c509..00d85da 100644 --- a/PerformanceCalculatorGUI/RulesetHelper.cs +++ b/PerformanceCalculatorGUI/RulesetHelper.cs @@ -280,19 +280,18 @@ namespace PerformanceCalculatorGUI double total = 6 * countGreat + 2 * countGood + countMeh; double max = 6 * (countGreat + countGood + countMeh + countMiss); - if (statistics.ContainsKey(HitResult.SliderTailHit)) + if (statistics.TryGetValue(HitResult.SliderTailHit, out int countSliderTailHit)) { var countSliders = beatmap.HitObjects.Count(x => x is Slider); - var countSliderTailHit = statistics[HitResult.SliderTailHit]; total += 3 * countSliderTailHit; max += 3 * countSliders; } - if (statistics.ContainsKey(HitResult.LargeTickMiss)) + if (statistics.TryGetValue(HitResult.LargeTickMiss, out int countLargeTicksMiss)) { var countLargeTicks = beatmap.HitObjects.Sum(obj => obj.NestedHitObjects.Count(x => x is SliderTick or SliderRepeat)); - var countLargeTickHit = countLargeTicks - statistics[HitResult.LargeTickMiss]; + var countLargeTickHit = countLargeTicks - countLargeTicksMiss; total += 0.6 * countLargeTickHit; max += 0.6 * countLargeTicks; diff --git a/PerformanceCalculatorGUI/Screens/BeatmapLeaderboardScreen.cs b/PerformanceCalculatorGUI/Screens/BeatmapLeaderboardScreen.cs index 00a8334..0de1d5a 100644 --- a/PerformanceCalculatorGUI/Screens/BeatmapLeaderboardScreen.cs +++ b/PerformanceCalculatorGUI/Screens/BeatmapLeaderboardScreen.cs @@ -196,7 +196,7 @@ namespace PerformanceCalculatorGUI.Screens { Schedule(() => loadingLayer.Text.Value = "Getting leaderboard..."); - var leaderboard = await apiManager.GetJsonFromApi($@"beatmaps/{beatmapIdTextBox.Current.Value}/scores?scope=global&mode={ruleset.Value.ShortName}"); + var leaderboard = await apiManager.GetJsonFromApi($@"beatmaps/{beatmapIdTextBox.Current.Value}/scores?scope=global&mode={ruleset.Value.ShortName}").ConfigureAwait(false); var plays = new List(); @@ -236,9 +236,11 @@ namespace PerformanceCalculatorGUI.Screens var difficultyAttributes = difficultyCalculator.Calculate(mods); var performanceCalculator = rulesetInstance.CreatePerformanceCalculator(); + if (performanceCalculator == null) + continue; - var perfAttributes = await performanceCalculator?.CalculateAsync(parsedScore.ScoreInfo, difficultyAttributes, token)!; - score.PP = perfAttributes?.Total ?? 0.0; + var perfAttributes = await performanceCalculator.CalculateAsync(parsedScore.ScoreInfo, difficultyAttributes, token).ConfigureAwait(false); + score.PP = perfAttributes.Total; plays.Add(score); } diff --git a/PerformanceCalculatorGUI/Screens/LeaderboardScreen.cs b/PerformanceCalculatorGUI/Screens/LeaderboardScreen.cs index 884899f..bedfe84 100644 --- a/PerformanceCalculatorGUI/Screens/LeaderboardScreen.cs +++ b/PerformanceCalculatorGUI/Screens/LeaderboardScreen.cs @@ -239,7 +239,7 @@ namespace PerformanceCalculatorGUI.Screens { Schedule(() => loadingLayer.Text.Value = "Getting leaderboard..."); - var leaderboard = await apiManager.GetJsonFromApi($"rankings/{ruleset.Value.ShortName}/performance?cursor[page]={pageTextBox.Value.Value - 1}"); + var leaderboard = await apiManager.GetJsonFromApi($"rankings/{ruleset.Value.ShortName}/performance?cursor[page]={pageTextBox.Value.Value - 1}").ConfigureAwait(false); var calculatedPlayers = new List(); var calculatedScores = new List(); @@ -253,7 +253,7 @@ namespace PerformanceCalculatorGUI.Screens Schedule(() => loadingLayer.Text.Value = $"Calculating {player.User.Username} top scores..."); - var playerData = await calculatePlayer(player, token); + var playerData = await calculatePlayer(player, token).ConfigureAwait(false); calculatedPlayers.Add(new LeaderboardUser { @@ -301,7 +301,7 @@ namespace PerformanceCalculatorGUI.Screens var plays = new List(); - var apiScores = await apiManager.GetJsonFromApi>($"users/{player.User.OnlineID}/scores/best?mode={ruleset.Value.ShortName}&limit=100"); + var apiScores = await apiManager.GetJsonFromApi>($"users/{player.User.OnlineID}/scores/best?mode={ruleset.Value.ShortName}&limit=100").ConfigureAwait(false); var rulesetInstance = ruleset.Value.CreateInstance(); diff --git a/PerformanceCalculatorGUI/Screens/ObjectInspection/ObjectDifficultyValuesContainer.cs b/PerformanceCalculatorGUI/Screens/ObjectInspection/ObjectDifficultyValuesContainer.cs index b0e9799..1cf9452 100644 --- a/PerformanceCalculatorGUI/Screens/ObjectInspection/ObjectDifficultyValuesContainer.cs +++ b/PerformanceCalculatorGUI/Screens/ObjectInspection/ObjectDifficultyValuesContainer.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Rulesets.Catch.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Preprocessing; @@ -72,7 +73,7 @@ namespace PerformanceCalculatorGUI.Screens.ObjectInspection Colour = colors.Background6, RelativeSizeAxes = Axes.Both }, - hitObjectTypeText = new SpriteText + hitObjectTypeText = new OsuSpriteText { Font = new FontUsage(size: 30), Padding = new MarginPadding(10) diff --git a/PerformanceCalculatorGUI/Screens/ObjectInspection/ObjectInspectorDifficultyValue.cs b/PerformanceCalculatorGUI/Screens/ObjectInspection/ObjectInspectorDifficultyValue.cs index 49128f1..13c38ab 100644 --- a/PerformanceCalculatorGUI/Screens/ObjectInspection/ObjectInspectorDifficultyValue.cs +++ b/PerformanceCalculatorGUI/Screens/ObjectInspection/ObjectInspectorDifficultyValue.cs @@ -3,8 +3,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osuTK; namespace PerformanceCalculatorGUI.Screens.ObjectInspection @@ -45,12 +45,12 @@ namespace PerformanceCalculatorGUI.Screens.ObjectInspection { new Drawable[] { - new SpriteText + new OsuSpriteText { Text = label, Font = OsuFont.GetFont(weight: FontWeight.SemiBold) }, - new SpriteText + new OsuSpriteText { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, diff --git a/PerformanceCalculatorGUI/Screens/ProfileScreen.cs b/PerformanceCalculatorGUI/Screens/ProfileScreen.cs index b31fdcf..885e25e 100644 --- a/PerformanceCalculatorGUI/Screens/ProfileScreen.cs +++ b/PerformanceCalculatorGUI/Screens/ProfileScreen.cs @@ -238,7 +238,7 @@ namespace PerformanceCalculatorGUI.Screens { Schedule(() => loadingLayer.Text.Value = "Getting user data..."); - var player = await apiManager.GetJsonFromApi($"users/{username}/{ruleset.Value.ShortName}"); + var player = await apiManager.GetJsonFromApi($"users/{username}/{ruleset.Value.ShortName}").ConfigureAwait(false); currentUser = player.Username; @@ -273,11 +273,11 @@ namespace PerformanceCalculatorGUI.Screens Schedule(() => loadingLayer.Text.Value = $"Calculating {player.Username} top scores..."); - var apiScores = await apiManager.GetJsonFromApi>($"users/{player.OnlineID}/scores/best?mode={ruleset.Value.ShortName}&limit=100"); + var apiScores = await apiManager.GetJsonFromApi>($"users/{player.OnlineID}/scores/best?mode={ruleset.Value.ShortName}&limit=100").ConfigureAwait(false); if (includePinnedCheckbox.Current.Value) { - var pinnedScores = await apiManager.GetJsonFromApi>($"users/{player.OnlineID}/scores/pinned?mode={ruleset.Value.ShortName}&limit=100"); + var pinnedScores = await apiManager.GetJsonFromApi>($"users/{player.OnlineID}/scores/pinned?mode={ruleset.Value.ShortName}&limit=100").ConfigureAwait(false); apiScores = apiScores.Concat(pinnedScores.Where(p => !apiScores.Any(b => b.ID == p.ID))).ToList(); } @@ -299,10 +299,12 @@ namespace PerformanceCalculatorGUI.Screens var difficultyCalculator = rulesetInstance.CreateDifficultyCalculator(working); var difficultyAttributes = difficultyCalculator.Calculate(mods); var performanceCalculator = rulesetInstance.CreatePerformanceCalculator(); + if (performanceCalculator == null) + continue; double? livePp = score.PP; - var perfAttributes = await performanceCalculator?.CalculateAsync(parsedScore.ScoreInfo, difficultyAttributes, token)!; - score.PP = perfAttributes?.Total ?? 0.0; + var perfAttributes = await performanceCalculator.CalculateAsync(parsedScore.ScoreInfo, difficultyAttributes, token).ConfigureAwait(false); + score.PP = perfAttributes.Total; var extendedScore = new ExtendedScore(score, livePp, perfAttributes); plays.Add(extendedScore); diff --git a/PerformanceCalculatorGUI/Screens/SimulateScreen.cs b/PerformanceCalculatorGUI/Screens/SimulateScreen.cs index 83b258f..e650e4b 100644 --- a/PerformanceCalculatorGUI/Screens/SimulateScreen.cs +++ b/PerformanceCalculatorGUI/Screens/SimulateScreen.cs @@ -1022,7 +1022,7 @@ namespace PerformanceCalculatorGUI.Screens { try { - var scoreInfo = await apiManager.GetJsonFromApi($"scores/{scoreId}"); + var scoreInfo = await apiManager.GetJsonFromApi($"scores/{scoreId}").ConfigureAwait(false); Schedule(() => { diff --git a/osu.Tools.props b/osu.Tools.props index 65fc2ef..6231ca4 100644 --- a/osu.Tools.props +++ b/osu.Tools.props @@ -1,15 +1,35 @@ - + - 8.0 + 12.0 osu-tools.licenseheader + + + + + + + + Default + Default + Recommended + Recommended + Recommended + Recommended + Default + Minimum + Recommended + Default + Default + ppy Pty Ltd - Copyright (c) 2020 ppy Pty Ltd + https://github.com/ppy/osu-tools + Copyright (c) 2024 ppy Pty Ltd diff --git a/osu.Tools.sln b/osu.Tools.sln index 4513456..743f448 100644 --- a/osu.Tools.sln +++ b/osu.Tools.sln @@ -7,6 +7,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerformanceCalculator", "Pe EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerformanceCalculatorGUI", "PerformanceCalculatorGUI\PerformanceCalculatorGUI.csproj", "{BB34B7BA-CB01-4F5F-9532-B070F3A2C52E}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{10DF8F12-50FD-45D8-8A38-17BA764BF54D}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + osu.Tools.props = osu.Tools.props + osu.Tools.sln.DotSettings = osu.Tools.sln.DotSettings + EndProjectSection +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.Tools.globalconfig = CodeAnalysis\osu.Tools.globalconfig + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/osu.Tools.sln.DotSettings b/osu.Tools.sln.DotSettings index 1c52afd..ccd6db3 100644 --- a/osu.Tools.sln.DotSettings +++ b/osu.Tools.sln.DotSettings @@ -15,10 +15,12 @@ HINT HINT WARNING + WARNING WARNING WARNING WARNING True + DO_NOT_SHOW WARNING WARNING HINT @@ -65,8 +67,9 @@ HINT WARNING DO_NOT_SHOW + HINT WARNING - WARNING + HINT WARNING WARNING DO_NOT_SHOW @@ -80,6 +83,7 @@ WARNING WARNING HINT + DO_NOT_SHOW WARNING HINT DO_NOT_SHOW @@ -149,7 +153,6 @@ HINT HINT WARNING - HINT HINT WARNING WARNING @@ -165,6 +168,7 @@ WARNING WARNING WARNING + HINT WARNING WARNING WARNING @@ -251,6 +255,7 @@ HINT DO_NOT_SHOW WARNING + DO_NOT_SHOW WARNING WARNING WARNING @@ -263,6 +268,7 @@ WARNING WARNING WARNING + HINT WARNING HINT HINT @@ -335,12 +341,14 @@ API ARGB BPM + DDKK EF FPS GC GL GLSL HID + HP HSL HSPA HSV @@ -352,6 +360,8 @@ IP IPC JIT + KDDK + KKDD LTRB MD5 NS @@ -370,6 +380,7 @@ QAT BNG UI + WIP False HINT <?xml version="1.0" encoding="utf-16"?> @@ -834,6 +845,7 @@ See the LICENCE file in the repository root for full licence text. True True True + True True True True @@ -841,6 +853,7 @@ See the LICENCE file in the repository root for full licence text. True True True + True True True True @@ -1011,6 +1024,7 @@ private void load() True True True + True True True True @@ -1047,4 +1061,6 @@ private void load() True True True + True + True True