From 993c514126ca63bb29dbe12fb7a6bf0a04ffca46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Jun 2022 16:18:50 +0900 Subject: [PATCH 1/4] Update game and ruleset dependencies --- PerformanceCalculator/PerformanceCalculator.csproj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PerformanceCalculator/PerformanceCalculator.csproj b/PerformanceCalculator/PerformanceCalculator.csproj index 94d55f5..6918378 100644 --- a/PerformanceCalculator/PerformanceCalculator.csproj +++ b/PerformanceCalculator/PerformanceCalculator.csproj @@ -7,10 +7,10 @@ - - - - - + + + + + From f0938033e4e72c34f2cb8f53dbf4dcb2c864a3d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Jun 2022 17:13:24 +0900 Subject: [PATCH 2/4] Add mod definition output command To be used for more automated web-side updates when mod definitions change. --- .../Difficulty/ModsCommand.cs | 116 ++++++++++++++++++ PerformanceCalculator/Program.cs | 1 + 2 files changed, 117 insertions(+) create mode 100644 PerformanceCalculator/Difficulty/ModsCommand.cs diff --git a/PerformanceCalculator/Difficulty/ModsCommand.cs b/PerformanceCalculator/Difficulty/ModsCommand.cs new file mode 100644 index 0000000..0b9bd99 --- /dev/null +++ b/PerformanceCalculator/Difficulty/ModsCommand.cs @@ -0,0 +1,116 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Linq; +using Humanizer; +using JetBrains.Annotations; +using McMaster.Extensions.CommandLineUtils; +using Newtonsoft.Json; +using osu.Game.Configuration; +using osu.Game.Rulesets.Mods; + +namespace PerformanceCalculator.Difficulty +{ + [Command(Name = "mods", Description = "Outputs all available mods in a consumable format.")] + public class ModsCommand : ProcessorCommand + { + [UsedImplicitly] + [Required] + [Argument( + 0, + Name = "ruleset", + Description = "The ruleset to compute the beatmap difficulty for, if it's a convertible beatmap.\n" + + "Values: 0 - osu!, 1 - osu!taiko, 2 - osu!catch, 3 - osu!mania")] + [AllowedValues("0", "1", "2", "3")] + public int Ruleset { get; } + + public override void Execute() + { + var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset); + + var allMods = ruleset.CreateAllMods(); + + var modDefinitions = allMods.Select(mod => new + { + mod.Acronym, + mod.Name, + mod.Description, + Type = mod.Type.ToString(), + Settings = getSettingsDefinitions(mod), + IncompatibleMods = getAllImplementations(mod.IncompatibleMods), + mod.RequiresConfiguration, + mod.UserPlayable, + mod.ValidForMultiplayer, + mod.ValidForMultiplayerAsFreeMod, + }); + + Console.WriteLine(JsonConvert.SerializeObject(modDefinitions, Formatting.Indented)); + + IEnumerable getAllImplementations(Type[] incompatibleTypes) + { + foreach (var mod in allMods) + { + if (incompatibleTypes.Any(t => mod.GetType().IsSubclassOf(t))) + yield return mod.Acronym; + } + } + + IEnumerable getSettingsDefinitions(Mod mod) + { + var sourceProperties = mod.GetSettingsSourceProperties(); + + foreach (var (settingSourceAttribute, propertyInfo) in sourceProperties) + { + var bindable = propertyInfo.GetValue(mod); + + Debug.Assert(bindable != null); + + object? underlyingValue = bindable.GetUnderlyingSettingValue(); + + var netType = underlyingValue?.GetType() ?? bindable.GetType().GetInterface("IBindable`1")?.GenericTypeArguments.FirstOrDefault(); + + yield return new + { + Name = propertyInfo.Name.Underscore(), + Type = getJsonType(netType) + }; + } + } + } + + private string getJsonType(Type? netType) + { + if (netType == typeof(int)) + return "number"; + if (netType == typeof(double)) + return "number"; + if (netType == typeof(float)) + return "number"; + if (netType == typeof(int?)) + return "number"; + if (netType == typeof(double?)) + return "number"; + if (netType == typeof(float?)) + return "number"; + + if (netType == typeof(bool)) + return "boolean"; + if (netType == typeof(bool?)) + return "boolean"; + + if (netType == typeof(string)) + return "string"; + + if (netType?.IsEnum == true) + return "string"; + + throw new ArgumentOutOfRangeException(nameof(netType)); + } + } +} diff --git a/PerformanceCalculator/Program.cs b/PerformanceCalculator/Program.cs index ae48e83..83a7a9e 100644 --- a/PerformanceCalculator/Program.cs +++ b/PerformanceCalculator/Program.cs @@ -17,6 +17,7 @@ namespace PerformanceCalculator { [Command("dotnet PerformanceCalculator.dll")] [Subcommand(typeof(DifficultyCommand))] + [Subcommand(typeof(ModsCommand))] [Subcommand(typeof(PerformanceCommand))] [Subcommand(typeof(ProfileCommand))] [Subcommand(typeof(SimulateListingCommand))] From 1e8e84d5dd22981ea3d7686f0dafa8cabf3a05a4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Jun 2022 17:29:32 +0900 Subject: [PATCH 3/4] Output all rulesets in a single json block --- .../Difficulty/ModsCommand.cs | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/PerformanceCalculator/Difficulty/ModsCommand.cs b/PerformanceCalculator/Difficulty/ModsCommand.cs index 0b9bd99..eacda22 100644 --- a/PerformanceCalculator/Difficulty/ModsCommand.cs +++ b/PerformanceCalculator/Difficulty/ModsCommand.cs @@ -5,14 +5,13 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; using System.Diagnostics; using System.Linq; using Humanizer; -using JetBrains.Annotations; using McMaster.Extensions.CommandLineUtils; using Newtonsoft.Json; using osu.Game.Configuration; +using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; namespace PerformanceCalculator.Difficulty @@ -20,23 +19,24 @@ namespace PerformanceCalculator.Difficulty [Command(Name = "mods", Description = "Outputs all available mods in a consumable format.")] public class ModsCommand : ProcessorCommand { - [UsedImplicitly] - [Required] - [Argument( - 0, - Name = "ruleset", - Description = "The ruleset to compute the beatmap difficulty for, if it's a convertible beatmap.\n" - + "Values: 0 - osu!, 1 - osu!taiko, 2 - osu!catch, 3 - osu!mania")] - [AllowedValues("0", "1", "2", "3")] - public int Ruleset { get; } - public override void Execute() { - var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset); + var allRulesets = Enumerable.Range(0, ILegacyRuleset.MAX_LEGACY_RULESET_ID + 1) + .Select(LegacyHelper.GetRulesetFromLegacyID); + Console.WriteLine(JsonConvert.SerializeObject(allRulesets.Select(r => new + { + Name = r.RulesetInfo.ShortName, + RulesetID = r.RulesetInfo.OnlineID, + Mods = getDefinitionsForRuleset(r) + }), Formatting.Indented)); + } + + private IEnumerable getDefinitionsForRuleset(Ruleset ruleset) + { var allMods = ruleset.CreateAllMods(); - var modDefinitions = allMods.Select(mod => new + return allMods.Select(mod => new { mod.Acronym, mod.Name, @@ -50,8 +50,6 @@ namespace PerformanceCalculator.Difficulty mod.ValidForMultiplayerAsFreeMod, }); - Console.WriteLine(JsonConvert.SerializeObject(modDefinitions, Formatting.Indented)); - IEnumerable getAllImplementations(Type[] incompatibleTypes) { foreach (var mod in allMods) @@ -65,7 +63,7 @@ namespace PerformanceCalculator.Difficulty { var sourceProperties = mod.GetSettingsSourceProperties(); - foreach (var (settingSourceAttribute, propertyInfo) in sourceProperties) + foreach (var (_, propertyInfo) in sourceProperties) { var bindable = propertyInfo.GetValue(mod); From 2cdffd2b5d66de5559b30d229e97684f80b3438a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Jun 2022 18:30:00 +0900 Subject: [PATCH 4/4] Fix incorrect incompatible mods matching --- PerformanceCalculator/Difficulty/ModsCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PerformanceCalculator/Difficulty/ModsCommand.cs b/PerformanceCalculator/Difficulty/ModsCommand.cs index eacda22..b05d603 100644 --- a/PerformanceCalculator/Difficulty/ModsCommand.cs +++ b/PerformanceCalculator/Difficulty/ModsCommand.cs @@ -54,7 +54,7 @@ namespace PerformanceCalculator.Difficulty { foreach (var mod in allMods) { - if (incompatibleTypes.Any(t => mod.GetType().IsSubclassOf(t))) + if (incompatibleTypes.Any(t => t.IsInstanceOfType(mod))) yield return mod.Acronym; } }