diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt
index 8b5431e2d6..e779ee6658 100644
--- a/CodeAnalysis/BannedSymbols.txt
+++ b/CodeAnalysis/BannedSymbols.txt
@@ -19,3 +19,7 @@ P:System.Threading.Tasks.Task`1.Result;Don't use Task.Result. Use Task.GetResult
M:System.Threading.ManualResetEventSlim.Wait();Specify a timeout to avoid waiting forever.
M:System.String.ToLower();string.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
M:System.String.ToUpper();string.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
+M:Humanizer.InflectorExtensions.Pascalize(System.String);Humanizer's .Pascalize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToPascalCase() instead.
+M:Humanizer.InflectorExtensions.Camelize(System.String);Humanizer's .Camelize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToCamelCase() instead.
+M:Humanizer.InflectorExtensions.Underscore(System.String);Humanizer's .Underscore() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToSnakeCase() instead.
+M:Humanizer.InflectorExtensions.Kebaberize(System.String);Humanizer's .Kebaberize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToKebabCase() instead.
diff --git a/osu.Android.props b/osu.Android.props
index d5390e6a3d..013a7d1419 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -51,8 +51,8 @@
-
-
+
+
diff --git a/osu.Android/AndroidJoystickSettings.cs b/osu.Android/AndroidJoystickSettings.cs
new file mode 100644
index 0000000000..26e921a426
--- /dev/null
+++ b/osu.Android/AndroidJoystickSettings.cs
@@ -0,0 +1,76 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Android.Input;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Localisation;
+using osu.Game.Localisation;
+using osu.Game.Overlays.Settings;
+
+namespace osu.Android
+{
+ public class AndroidJoystickSettings : SettingsSubsection
+ {
+ protected override LocalisableString Header => JoystickSettingsStrings.JoystickGamepad;
+
+ private readonly AndroidJoystickHandler joystickHandler;
+
+ private readonly Bindable enabled = new BindableBool(true);
+
+ private SettingsSlider deadzoneSlider = null!;
+
+ private Bindable handlerDeadzone = null!;
+
+ private Bindable localDeadzone = null!;
+
+ public AndroidJoystickSettings(AndroidJoystickHandler joystickHandler)
+ {
+ this.joystickHandler = joystickHandler;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ // use local bindable to avoid changing enabled state of game host's bindable.
+ handlerDeadzone = joystickHandler.DeadzoneThreshold.GetBoundCopy();
+ localDeadzone = handlerDeadzone.GetUnboundCopy();
+
+ Children = new Drawable[]
+ {
+ new SettingsCheckbox
+ {
+ LabelText = CommonStrings.Enabled,
+ Current = enabled
+ },
+ deadzoneSlider = new SettingsSlider
+ {
+ LabelText = JoystickSettingsStrings.DeadzoneThreshold,
+ KeyboardStep = 0.01f,
+ DisplayAsPercentage = true,
+ Current = localDeadzone,
+ },
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ enabled.BindTo(joystickHandler.Enabled);
+ enabled.BindValueChanged(e => deadzoneSlider.Current.Disabled = !e.NewValue, true);
+
+ handlerDeadzone.BindValueChanged(val =>
+ {
+ bool disabled = localDeadzone.Disabled;
+
+ localDeadzone.Disabled = false;
+ localDeadzone.Value = val.NewValue;
+ localDeadzone.Disabled = disabled;
+ }, true);
+
+ localDeadzone.BindValueChanged(val => handlerDeadzone.Value = val.NewValue);
+ }
+ }
+}
diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs
index 636fc7d2df..062f2ce10c 100644
--- a/osu.Android/OsuGameAndroid.cs
+++ b/osu.Android/OsuGameAndroid.cs
@@ -96,6 +96,9 @@ namespace osu.Android
case AndroidMouseHandler mh:
return new AndroidMouseSettings(mh);
+ case AndroidJoystickHandler jh:
+ return new AndroidJoystickSettings(jh);
+
default:
return base.CreateSettingsSubsectionFor(handler);
}
diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj
index 90b02c527b..004cc8c39c 100644
--- a/osu.Android/osu.Android.csproj
+++ b/osu.Android/osu.Android.csproj
@@ -26,6 +26,7 @@
true
+
diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs
index 314a03a73e..524436235e 100644
--- a/osu.Desktop/OsuGameDesktop.cs
+++ b/osu.Desktop/OsuGameDesktop.cs
@@ -22,10 +22,12 @@ using osu.Framework.Input.Handlers;
using osu.Framework.Input.Handlers.Joystick;
using osu.Framework.Input.Handlers.Mouse;
using osu.Framework.Input.Handlers.Tablet;
+using osu.Framework.Input.Handlers.Touch;
using osu.Framework.Threading;
using osu.Game.IO;
using osu.Game.IPC;
using osu.Game.Overlays.Settings;
+using osu.Game.Overlays.Settings.Sections;
using osu.Game.Overlays.Settings.Sections.Input;
namespace osu.Desktop
@@ -156,6 +158,9 @@ namespace osu.Desktop
case JoystickHandler jh:
return new JoystickSettings(jh);
+ case TouchHandler th:
+ return new InputSection.HandlerSection(th);
+
default:
return base.CreateSettingsSubsectionFor(handler);
}
diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs
index cebbcb40b7..19cf7f5d46 100644
--- a/osu.Desktop/Program.cs
+++ b/osu.Desktop/Program.cs
@@ -37,9 +37,15 @@ namespace osu.Desktop
// See https://www.mongodb.com/docs/realm/sdk/dotnet/#supported-platforms
if (windowsVersion.Major < 6 || (windowsVersion.Major == 6 && windowsVersion.Minor <= 2))
{
+ // If users running in compatibility mode becomes more of a common thing, we may want to provide better guidance or even consider
+ // disabling it ourselves.
+ // We could also better detect compatibility mode if required:
+ // https://stackoverflow.com/questions/10744651/how-i-can-detect-if-my-application-is-running-under-compatibility-mode#comment58183249_10744730
SDL.SDL_ShowSimpleMessageBox(SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR,
"Your operating system is too old to run osu!",
- "This version of osu! requires at least Windows 8.1 to run.\nPlease upgrade your operating system or consider using an older version of osu!.", IntPtr.Zero);
+ "This version of osu! requires at least Windows 8.1 to run.\n"
+ + "Please upgrade your operating system or consider using an older version of osu!.\n\n"
+ + "If you are running a newer version of windows, please check you don't have \"Compatibility mode\" turned on for osu!", IntPtr.Zero);
return;
}
diff --git a/osu.Game.Benchmarks/BenchmarkHitObject.cs b/osu.Game.Benchmarks/BenchmarkHitObject.cs
new file mode 100644
index 0000000000..65c78e39b3
--- /dev/null
+++ b/osu.Game.Benchmarks/BenchmarkHitObject.cs
@@ -0,0 +1,166 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using BenchmarkDotNet.Attributes;
+using osu.Game.Rulesets.Catch.Objects;
+using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Taiko.Objects;
+
+namespace osu.Game.Benchmarks
+{
+ public class BenchmarkHitObject : BenchmarkTest
+ {
+ [Params(1, 100, 1000)]
+ public int Count { get; set; }
+
+ [Params(false, true)]
+ public bool WithBindableAccess { get; set; }
+
+ [Benchmark]
+ public HitCircle[] OsuCircle()
+ {
+ var circles = new HitCircle[Count];
+
+ for (int i = 0; i < Count; i++)
+ {
+ circles[i] = new HitCircle();
+
+ if (WithBindableAccess)
+ {
+ _ = circles[i].PositionBindable;
+ _ = circles[i].ScaleBindable;
+ _ = circles[i].ComboIndexBindable;
+ _ = circles[i].ComboOffsetBindable;
+ _ = circles[i].StackHeightBindable;
+ _ = circles[i].LastInComboBindable;
+ _ = circles[i].ComboIndexWithOffsetsBindable;
+ _ = circles[i].IndexInCurrentComboBindable;
+ _ = circles[i].SamplesBindable;
+ _ = circles[i].StartTimeBindable;
+ }
+ else
+ {
+ _ = circles[i].Position;
+ _ = circles[i].Scale;
+ _ = circles[i].ComboIndex;
+ _ = circles[i].ComboOffset;
+ _ = circles[i].StackHeight;
+ _ = circles[i].LastInCombo;
+ _ = circles[i].ComboIndexWithOffsets;
+ _ = circles[i].IndexInCurrentCombo;
+ _ = circles[i].Samples;
+ _ = circles[i].StartTime;
+ _ = circles[i].Position;
+ _ = circles[i].Scale;
+ _ = circles[i].ComboIndex;
+ _ = circles[i].ComboOffset;
+ _ = circles[i].StackHeight;
+ _ = circles[i].LastInCombo;
+ _ = circles[i].ComboIndexWithOffsets;
+ _ = circles[i].IndexInCurrentCombo;
+ _ = circles[i].Samples;
+ _ = circles[i].StartTime;
+ }
+ }
+
+ return circles;
+ }
+
+ [Benchmark]
+ public Hit[] TaikoHit()
+ {
+ var hits = new Hit[Count];
+
+ for (int i = 0; i < Count; i++)
+ {
+ hits[i] = new Hit();
+
+ if (WithBindableAccess)
+ {
+ _ = hits[i].TypeBindable;
+ _ = hits[i].IsStrongBindable;
+ _ = hits[i].SamplesBindable;
+ _ = hits[i].StartTimeBindable;
+ }
+ else
+ {
+ _ = hits[i].Type;
+ _ = hits[i].IsStrong;
+ _ = hits[i].Samples;
+ _ = hits[i].StartTime;
+ }
+ }
+
+ return hits;
+ }
+
+ [Benchmark]
+ public Fruit[] CatchFruit()
+ {
+ var fruit = new Fruit[Count];
+
+ for (int i = 0; i < Count; i++)
+ {
+ fruit[i] = new Fruit();
+
+ if (WithBindableAccess)
+ {
+ _ = fruit[i].OriginalXBindable;
+ _ = fruit[i].XOffsetBindable;
+ _ = fruit[i].ScaleBindable;
+ _ = fruit[i].ComboIndexBindable;
+ _ = fruit[i].HyperDashBindable;
+ _ = fruit[i].LastInComboBindable;
+ _ = fruit[i].ComboIndexWithOffsetsBindable;
+ _ = fruit[i].IndexInCurrentComboBindable;
+ _ = fruit[i].IndexInBeatmapBindable;
+ _ = fruit[i].SamplesBindable;
+ _ = fruit[i].StartTimeBindable;
+ }
+ else
+ {
+ _ = fruit[i].OriginalX;
+ _ = fruit[i].XOffset;
+ _ = fruit[i].Scale;
+ _ = fruit[i].ComboIndex;
+ _ = fruit[i].HyperDash;
+ _ = fruit[i].LastInCombo;
+ _ = fruit[i].ComboIndexWithOffsets;
+ _ = fruit[i].IndexInCurrentCombo;
+ _ = fruit[i].IndexInBeatmap;
+ _ = fruit[i].Samples;
+ _ = fruit[i].StartTime;
+ }
+ }
+
+ return fruit;
+ }
+
+ [Benchmark]
+ public Note[] ManiaNote()
+ {
+ var notes = new Note[Count];
+
+ for (int i = 0; i < Count; i++)
+ {
+ notes[i] = new Note();
+
+ if (WithBindableAccess)
+ {
+ _ = notes[i].ColumnBindable;
+ _ = notes[i].SamplesBindable;
+ _ = notes[i].StartTimeBindable;
+ }
+ else
+ {
+ _ = notes[i].Column;
+ _ = notes[i].Samples;
+ _ = notes[i].StartTime;
+ }
+ }
+
+ return notes;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
index f5a3426305..6e01c44e1f 100644
--- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
@@ -19,7 +19,9 @@ namespace osu.Game.Rulesets.Catch.Objects
{
public const float OBJECT_RADIUS = 64;
- public readonly Bindable OriginalXBindable = new Bindable();
+ private HitObjectProperty originalX;
+
+ public Bindable OriginalXBindable => originalX.Bindable;
///
/// The horizontal position of the hit object between 0 and .
@@ -31,18 +33,20 @@ namespace osu.Game.Rulesets.Catch.Objects
[JsonIgnore]
public float X
{
- set => OriginalXBindable.Value = value;
+ set => originalX.Value = value;
}
- public readonly Bindable XOffsetBindable = new Bindable();
+ private HitObjectProperty xOffset;
+
+ public Bindable XOffsetBindable => xOffset.Bindable;
///
/// A random offset applied to the horizontal position, set by the beatmap processing.
///
public float XOffset
{
- get => XOffsetBindable.Value;
- set => XOffsetBindable.Value = value;
+ get => xOffset.Value;
+ set => xOffset.Value = value;
}
///
@@ -54,8 +58,8 @@ namespace osu.Game.Rulesets.Catch.Objects
///
public float OriginalX
{
- get => OriginalXBindable.Value;
- set => OriginalXBindable.Value = value;
+ get => originalX.Value;
+ set => originalX.Value = value;
}
///
@@ -69,59 +73,71 @@ namespace osu.Game.Rulesets.Catch.Objects
public double TimePreempt { get; set; } = 1000;
- public readonly Bindable IndexInBeatmapBindable = new Bindable();
+ private HitObjectProperty indexInBeatmap;
+
+ public Bindable IndexInBeatmapBindable => indexInBeatmap.Bindable;
public int IndexInBeatmap
{
- get => IndexInBeatmapBindable.Value;
- set => IndexInBeatmapBindable.Value = value;
+ get => indexInBeatmap.Value;
+ set => indexInBeatmap.Value = value;
}
public virtual bool NewCombo { get; set; }
public int ComboOffset { get; set; }
- public Bindable IndexInCurrentComboBindable { get; } = new Bindable();
+ private HitObjectProperty indexInCurrentCombo;
+
+ public Bindable IndexInCurrentComboBindable => indexInCurrentCombo.Bindable;
public int IndexInCurrentCombo
{
- get => IndexInCurrentComboBindable.Value;
- set => IndexInCurrentComboBindable.Value = value;
+ get => indexInCurrentCombo.Value;
+ set => indexInCurrentCombo.Value = value;
}
- public Bindable ComboIndexBindable { get; } = new Bindable();
+ private HitObjectProperty comboIndex;
+
+ public Bindable ComboIndexBindable => comboIndex.Bindable;
public int ComboIndex
{
- get => ComboIndexBindable.Value;
- set => ComboIndexBindable.Value = value;
+ get => comboIndex.Value;
+ set => comboIndex.Value = value;
}
- public Bindable ComboIndexWithOffsetsBindable { get; } = new Bindable();
+ private HitObjectProperty comboIndexWithOffsets;
+
+ public Bindable ComboIndexWithOffsetsBindable => comboIndexWithOffsets.Bindable;
public int ComboIndexWithOffsets
{
- get => ComboIndexWithOffsetsBindable.Value;
- set => ComboIndexWithOffsetsBindable.Value = value;
+ get => comboIndexWithOffsets.Value;
+ set => comboIndexWithOffsets.Value = value;
}
- public Bindable LastInComboBindable { get; } = new Bindable();
+ private HitObjectProperty lastInCombo;
+
+ public Bindable LastInComboBindable => lastInCombo.Bindable;
///
/// The next fruit starts a new combo. Used for explodey.
///
public virtual bool LastInCombo
{
- get => LastInComboBindable.Value;
- set => LastInComboBindable.Value = value;
+ get => lastInCombo.Value;
+ set => lastInCombo.Value = value;
}
- public readonly Bindable ScaleBindable = new Bindable(1);
+ private HitObjectProperty scale = new HitObjectProperty(1);
+
+ public Bindable ScaleBindable => scale.Bindable;
public float Scale
{
- get => ScaleBindable.Value;
- set => ScaleBindable.Value = value;
+ get => scale.Value;
+ set => scale.Value = value;
}
///
diff --git a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs
index 1ededa1438..c9bc9ca2ac 100644
--- a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs
@@ -5,6 +5,7 @@
using Newtonsoft.Json;
using osu.Framework.Bindables;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Skinning;
using osuTK.Graphics;
@@ -24,12 +25,14 @@ namespace osu.Game.Rulesets.Catch.Objects
///
public float DistanceToHyperDash { get; set; }
- public readonly Bindable HyperDashBindable = new Bindable();
+ private HitObjectProperty hyperDash;
+
+ public Bindable HyperDashBindable => hyperDash.Bindable;
///
/// Whether this fruit can initiate a hyperdash.
///
- public bool HyperDash => HyperDashBindable.Value;
+ public bool HyperDash => hyperDash.Value;
private CatchHitObject hyperDashTarget;
diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs
index 0efaeac026..ebff5cf4e9 100644
--- a/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs
+++ b/osu.Game.Rulesets.Mania/Objects/ManiaHitObject.cs
@@ -13,12 +13,14 @@ namespace osu.Game.Rulesets.Mania.Objects
{
public abstract class ManiaHitObject : HitObject, IHasColumn, IHasXPosition
{
- public readonly Bindable ColumnBindable = new Bindable();
+ private HitObjectProperty column;
+
+ public Bindable ColumnBindable => column.Bindable;
public virtual int Column
{
- get => ColumnBindable.Value;
- set => ColumnBindable.Value = value;
+ get => column.Value;
+ set => column.Value = value;
}
protected override HitWindows CreateHitWindows() => new ManiaHitWindows();
diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
index bb593c2fb3..46f7c461f8 100644
--- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
+++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
@@ -17,18 +17,18 @@ namespace osu.Game.Rulesets.Osu.Tests
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
- [TestCase(6.6972307565739273d, 206, "diffcalc-test")]
- [TestCase(1.4484754139145539d, 45, "zero-length-sliders")]
+ [TestCase(6.6369583000323935d, 206, "diffcalc-test")]
+ [TestCase(1.4476531024675374d, 45, "zero-length-sliders")]
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
=> base.Test(expectedStarRating, expectedMaxCombo, name);
- [TestCase(8.9382559208689809d, 206, "diffcalc-test")]
- [TestCase(1.7548875851757628d, 45, "zero-length-sliders")]
+ [TestCase(8.8816128335486386d, 206, "diffcalc-test")]
+ [TestCase(1.7540389962596916d, 45, "zero-length-sliders")]
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime());
- [TestCase(6.6972307218715166d, 239, "diffcalc-test")]
- [TestCase(1.4484754139145537d, 54, "zero-length-sliders")]
+ [TestCase(6.6369583000323935d, 239, "diffcalc-test")]
+ [TestCase(1.4476531024675374d, 54, "zero-length-sliders")]
public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name)
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic());
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs
index 0694746cbf..76d5ccf682 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs
@@ -108,13 +108,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
// Reward for % distance up to 125 / strainTime for overlaps where velocity is still changing.
double overlapVelocityBuff = Math.Min(125 / Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime), Math.Abs(prevVelocity - currVelocity));
- // Reward for % distance slowed down compared to previous, paying attention to not award overlap
- double nonOverlapVelocityBuff = Math.Abs(prevVelocity - currVelocity)
- // do not award overlap
- * Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, Math.Min(osuCurrObj.LazyJumpDistance, osuLastObj.LazyJumpDistance) / 100)), 2);
-
- // Choose the largest bonus, multiplied by ratio.
- velocityChangeBonus = Math.Max(overlapVelocityBuff, nonOverlapVelocityBuff) * distRatio;
+ velocityChangeBonus = overlapVelocityBuff * distRatio;
// Penalize for rhythm changes.
velocityChangeBonus *= Math.Pow(Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime) / Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime), 2);
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs b/osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs
index 704b922ee5..d5096619b9 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs
@@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModCinema : ModCinema
{
- public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModAutopilot), typeof(OsuModSpunOut), typeof(OsuModAlternate), typeof(OsuModSingleTap) }).ToArray();
+ public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModAutopilot), typeof(OsuModSpunOut), typeof(OsuModAlternate), typeof(OsuModSingleTap), typeof(OsuModRepel) }).ToArray();
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList mods)
=> new ModReplayData(new OsuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" });
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModPerfect.cs b/osu.Game.Rulesets.Osu/Mods/OsuModPerfect.cs
index c5795177d0..bde7718da5 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModPerfect.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModPerfect.cs
@@ -3,11 +3,14 @@
#nullable disable
+using System;
+using System.Linq;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModPerfect : ModPerfect
{
+ public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
}
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs
index 051ceb968c..b170d30448 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModSingleTap : InputBlockingMod
{
public override string Name => @"Single Tap";
- public override string Acronym => @"ST";
+ public override string Acronym => @"SG";
public override string Description => @"You must only use one key!";
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAlternate) }).ToArray();
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index 387342b4a9..7b98fc48e0 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -7,12 +7,12 @@ using System;
using System.Linq;
using osu.Framework.Bindables;
using osu.Game.Beatmaps;
-using osu.Game.Rulesets.Objects;
-using osuTK;
-using osu.Game.Rulesets.Objects.Types;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Scoring;
+using osuTK;
namespace osu.Game.Rulesets.Osu.Objects
{
@@ -36,12 +36,14 @@ namespace osu.Game.Rulesets.Osu.Objects
public double TimePreempt = 600;
public double TimeFadeIn = 400;
- public readonly Bindable PositionBindable = new Bindable();
+ private HitObjectProperty position;
+
+ public Bindable PositionBindable => position.Bindable;
public virtual Vector2 Position
{
- get => PositionBindable.Value;
- set => PositionBindable.Value = value;
+ get => position.Value;
+ set => position.Value = value;
}
public float X => Position.X;
@@ -53,66 +55,80 @@ namespace osu.Game.Rulesets.Osu.Objects
public Vector2 StackedEndPosition => EndPosition + StackOffset;
- public readonly Bindable StackHeightBindable = new Bindable();
+ private HitObjectProperty stackHeight;
+
+ public Bindable StackHeightBindable => stackHeight.Bindable;
public int StackHeight
{
- get => StackHeightBindable.Value;
- set => StackHeightBindable.Value = value;
+ get => stackHeight.Value;
+ set => stackHeight.Value = value;
}
public virtual Vector2 StackOffset => new Vector2(StackHeight * Scale * -6.4f);
public double Radius => OBJECT_RADIUS * Scale;
- public readonly Bindable ScaleBindable = new BindableFloat(1);
+ private HitObjectProperty scale = new HitObjectProperty(1);
+
+ public Bindable ScaleBindable => scale.Bindable;
public float Scale
{
- get => ScaleBindable.Value;
- set => ScaleBindable.Value = value;
+ get => scale.Value;
+ set => scale.Value = value;
}
public virtual bool NewCombo { get; set; }
- public readonly Bindable ComboOffsetBindable = new Bindable();
+ private HitObjectProperty comboOffset;
+
+ public Bindable ComboOffsetBindable => comboOffset.Bindable;
public int ComboOffset
{
- get => ComboOffsetBindable.Value;
- set => ComboOffsetBindable.Value = value;
+ get => comboOffset.Value;
+ set => comboOffset.Value = value;
}
- public Bindable IndexInCurrentComboBindable { get; } = new Bindable();
+ private HitObjectProperty indexInCurrentCombo;
+
+ public Bindable IndexInCurrentComboBindable => indexInCurrentCombo.Bindable;
public virtual int IndexInCurrentCombo
{
- get => IndexInCurrentComboBindable.Value;
- set => IndexInCurrentComboBindable.Value = value;
+ get => indexInCurrentCombo.Value;
+ set => indexInCurrentCombo.Value = value;
}
- public Bindable ComboIndexBindable { get; } = new Bindable();
+ private HitObjectProperty comboIndex;
+
+ public Bindable ComboIndexBindable => comboIndex.Bindable;
public virtual int ComboIndex
{
- get => ComboIndexBindable.Value;
- set => ComboIndexBindable.Value = value;
+ get => comboIndex.Value;
+ set => comboIndex.Value = value;
}
- public Bindable ComboIndexWithOffsetsBindable { get; } = new Bindable();
+ private HitObjectProperty comboIndexWithOffsets;
+
+ public Bindable ComboIndexWithOffsetsBindable => comboIndexWithOffsets.Bindable;
public int ComboIndexWithOffsets
{
- get => ComboIndexWithOffsetsBindable.Value;
- set => ComboIndexWithOffsetsBindable.Value = value;
+ get => comboIndexWithOffsets.Value;
+ set => comboIndexWithOffsets.Value = value;
}
- public Bindable LastInComboBindable { get; } = new Bindable();
+ private HitObjectProperty lastInCombo;
+
+ public Bindable LastInComboBindable => lastInCombo.Bindable;
public bool LastInCombo
{
- get => LastInComboBindable.Value;
- set => LastInComboBindable.Value = value;
+ get => lastInCombo.Value;
+ set => lastInCombo.Value = value;
}
protected OsuHitObject()
diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs
index 324f2525bc..5b7da5a1ba 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
if (ParentObject.Judged)
return;
- double remainingTime = ParentObject.HitStateUpdateTime - Time.Current;
+ double remainingTime = Math.Max(0, ParentObject.HitStateUpdateTime - Time.Current);
// Note that the scale adjust here is 2 instead of DrawableSliderBall.FOLLOW_AREA to match legacy behaviour.
// This means the actual tracking area for gameplay purposes is larger than the sprite (but skins may be accounting for this).
diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
index a9cde62f44..2c2dbddf13 100644
--- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs
@@ -35,13 +35,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
- double multiplier = 1.1; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things
-
- if (score.Mods.Any(m => m is ModNoFail))
- multiplier *= 0.90;
+ double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things
if (score.Mods.Any(m => m is ModHidden))
- multiplier *= 1.10;
+ multiplier *= 1.075;
+
+ if (score.Mods.Any(m => m is ModEasy))
+ multiplier *= 0.975;
double difficultyValue = computeDifficultyValue(score, taikoAttributes);
double accuracyValue = computeAccuracyValue(score, taikoAttributes);
@@ -61,12 +61,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private double computeDifficultyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
{
- double difficultyValue = Math.Pow(5 * Math.Max(1.0, attributes.StarRating / 0.175) - 4.0, 2.25) / 450.0;
+ double difficultyValue = Math.Pow(5 * Math.Max(1.0, attributes.StarRating / 0.115) - 4.0, 2.25) / 1150.0;
double lengthBonus = 1 + 0.1 * Math.Min(1.0, totalHits / 1500.0);
difficultyValue *= lengthBonus;
- difficultyValue *= Math.Pow(0.985, countMiss);
+ difficultyValue *= Math.Pow(0.986, countMiss);
+
+ if (score.Mods.Any(m => m is ModEasy))
+ difficultyValue *= 0.980;
if (score.Mods.Any(m => m is ModHidden))
difficultyValue *= 1.025;
@@ -74,7 +77,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (score.Mods.Any(m => m is ModFlashlight))
difficultyValue *= 1.05 * lengthBonus;
- return difficultyValue * score.Accuracy;
+ return difficultyValue * Math.Pow(score.Accuracy, 1.5);
}
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
@@ -82,10 +85,16 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (attributes.GreatHitWindow <= 0)
return 0;
- double accValue = Math.Pow(150.0 / attributes.GreatHitWindow, 1.1) * Math.Pow(score.Accuracy, 15) * 22.0;
+ double accuracyValue = Math.Pow(140.0 / attributes.GreatHitWindow, 1.1) * Math.Pow(score.Accuracy, 12.0) * 27;
- // Bonus for many objects - it's harder to keep good accuracy up for longer
- return accValue * Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
+ double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
+ accuracyValue *= lengthBonus;
+
+ // Slight HDFL Bonus for accuracy.
+ if (score.Mods.Any(m => m is ModFlashlight) && score.Mods.Any(m => m is ModHidden))
+ accuracyValue *= 1.10 * lengthBonus;
+
+ return accuracyValue;
}
private int totalHits => countGreat + countOk + countMeh + countMiss;
diff --git a/osu.Game.Rulesets.Taiko/Objects/BarLine.cs b/osu.Game.Rulesets.Taiko/Objects/BarLine.cs
index 382035119e..d2eba0eb54 100644
--- a/osu.Game.Rulesets.Taiko/Objects/BarLine.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/BarLine.cs
@@ -11,14 +11,16 @@ namespace osu.Game.Rulesets.Taiko.Objects
{
public class BarLine : TaikoHitObject, IBarLine
{
+ private HitObjectProperty major;
+
+ public Bindable MajorBindable => major.Bindable;
+
public bool Major
{
- get => MajorBindable.Value;
- set => MajorBindable.Value = value;
+ get => major.Value;
+ set => major.Value = value;
}
- public readonly Bindable MajorBindable = new BindableBool();
-
public override Judgement CreateJudgement() => new IgnoreJudgement();
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs
index 20f3304c30..787079bfee 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs
@@ -7,6 +7,7 @@ using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Audio;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osuTK.Graphics;
@@ -14,19 +15,21 @@ namespace osu.Game.Rulesets.Taiko.Objects
{
public class Hit : TaikoStrongableHitObject, IHasDisplayColour
{
- public readonly Bindable TypeBindable = new Bindable();
+ private HitObjectProperty type;
- public Bindable DisplayColour { get; } = new Bindable(COLOUR_CENTRE);
+ public Bindable TypeBindable => type.Bindable;
///
/// The that actuates this .
///
public HitType Type
{
- get => TypeBindable.Value;
- set => TypeBindable.Value = value;
+ get => type.Value;
+ set => type.Value = value;
}
+ public Bindable DisplayColour { get; } = new Bindable(COLOUR_CENTRE);
+
public static readonly Color4 COLOUR_CENTRE = Color4Extensions.FromHex(@"bb1177");
public static readonly Color4 COLOUR_RIM = Color4Extensions.FromHex(@"2299bb");
diff --git a/osu.Game.Tests/Extensions/StringDehumanizeExtensionsTest.cs b/osu.Game.Tests/Extensions/StringDehumanizeExtensionsTest.cs
new file mode 100644
index 0000000000..e7490b461b
--- /dev/null
+++ b/osu.Game.Tests/Extensions/StringDehumanizeExtensionsTest.cs
@@ -0,0 +1,85 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Globalization;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Game.Extensions;
+
+namespace osu.Game.Tests.Extensions
+{
+ [TestFixture]
+ public class StringDehumanizeExtensionsTest
+ {
+ [Test]
+ [TestCase("single", "Single")]
+ [TestCase("example word", "ExampleWord")]
+ [TestCase("mixed Casing test", "MixedCasingTest")]
+ [TestCase("PascalCase", "PascalCase")]
+ [TestCase("camelCase", "CamelCase")]
+ [TestCase("snake_case", "SnakeCase")]
+ [TestCase("kebab-case", "KebabCase")]
+ [TestCase("i will not break in a different culture", "IWillNotBreakInADifferentCulture", "tr-TR")]
+ public void TestToPascalCase(string input, string expectedOutput, string? culture = null)
+ {
+ using (temporaryCurrentCulture(culture))
+ Assert.That(input.ToPascalCase(), Is.EqualTo(expectedOutput));
+ }
+
+ [Test]
+ [TestCase("single", "single")]
+ [TestCase("example word", "exampleWord")]
+ [TestCase("mixed Casing test", "mixedCasingTest")]
+ [TestCase("PascalCase", "pascalCase")]
+ [TestCase("camelCase", "camelCase")]
+ [TestCase("snake_case", "snakeCase")]
+ [TestCase("kebab-case", "kebabCase")]
+ [TestCase("I will not break in a different culture", "iWillNotBreakInADifferentCulture", "tr-TR")]
+ public void TestToCamelCase(string input, string expectedOutput, string? culture = null)
+ {
+ using (temporaryCurrentCulture(culture))
+ Assert.That(input.ToCamelCase(), Is.EqualTo(expectedOutput));
+ }
+
+ [Test]
+ [TestCase("single", "single")]
+ [TestCase("example word", "example_word")]
+ [TestCase("mixed Casing test", "mixed_casing_test")]
+ [TestCase("PascalCase", "pascal_case")]
+ [TestCase("camelCase", "camel_case")]
+ [TestCase("snake_case", "snake_case")]
+ [TestCase("kebab-case", "kebab_case")]
+ [TestCase("I will not break in a different culture", "i_will_not_break_in_a_different_culture", "tr-TR")]
+ public void TestToSnakeCase(string input, string expectedOutput, string? culture = null)
+ {
+ using (temporaryCurrentCulture(culture))
+ Assert.That(input.ToSnakeCase(), Is.EqualTo(expectedOutput));
+ }
+
+ [Test]
+ [TestCase("single", "single")]
+ [TestCase("example word", "example-word")]
+ [TestCase("mixed Casing test", "mixed-casing-test")]
+ [TestCase("PascalCase", "pascal-case")]
+ [TestCase("camelCase", "camel-case")]
+ [TestCase("snake_case", "snake-case")]
+ [TestCase("kebab-case", "kebab-case")]
+ [TestCase("I will not break in a different culture", "i-will-not-break-in-a-different-culture", "tr-TR")]
+ public void TestToKebabCase(string input, string expectedOutput, string? culture = null)
+ {
+ using (temporaryCurrentCulture(culture))
+ Assert.That(input.ToKebabCase(), Is.EqualTo(expectedOutput));
+ }
+
+ private IDisposable temporaryCurrentCulture(string? cultureName)
+ {
+ var storedCulture = CultureInfo.CurrentCulture;
+
+ if (cultureName != null)
+ CultureInfo.CurrentCulture = new CultureInfo(cultureName);
+
+ return new InvokeOnDisposal(() => CultureInfo.CurrentCulture = storedCulture);
+ }
+ }
+}
diff --git a/osu.Game.Tests/NonVisual/BeatmapSetInfoEqualityTest.cs b/osu.Game.Tests/NonVisual/BeatmapSetInfoEqualityTest.cs
index c887105da6..461102124a 100644
--- a/osu.Game.Tests/NonVisual/BeatmapSetInfoEqualityTest.cs
+++ b/osu.Game.Tests/NonVisual/BeatmapSetInfoEqualityTest.cs
@@ -62,9 +62,45 @@ namespace osu.Game.Tests.NonVisual
Assert.IsTrue(beatmapSetA.Beatmaps.Single().AudioEquals(beatmapSetB.Beatmaps.Single()));
}
- private static void addAudioFile(BeatmapSetInfo beatmapSetInfo, string hash = null)
+ [Test]
+ public void TestAudioEqualityBeatmapInfoSameHash()
{
- beatmapSetInfo.Files.Add(new RealmNamedFileUsage(new RealmFile { Hash = hash ?? Guid.NewGuid().ToString() }, "audio.mp3"));
+ var beatmapSet = TestResources.CreateTestBeatmapSetInfo(2);
+
+ addAudioFile(beatmapSet);
+
+ var beatmap1 = beatmapSet.Beatmaps.First();
+ var beatmap2 = beatmapSet.Beatmaps.Last();
+
+ Assert.AreNotEqual(beatmap1, beatmap2);
+ Assert.IsTrue(beatmap1.AudioEquals(beatmap2));
+ }
+
+ [Test]
+ public void TestAudioEqualityBeatmapInfoDifferentHash()
+ {
+ var beatmapSet = TestResources.CreateTestBeatmapSetInfo(2);
+
+ const string filename1 = "audio1.mp3";
+ const string filename2 = "audio2.mp3";
+
+ addAudioFile(beatmapSet, filename: filename1);
+ addAudioFile(beatmapSet, filename: filename2);
+
+ var beatmap1 = beatmapSet.Beatmaps.First();
+ var beatmap2 = beatmapSet.Beatmaps.Last();
+
+ Assert.AreNotEqual(beatmap1, beatmap2);
+
+ beatmap1.Metadata.AudioFile = filename1;
+ beatmap2.Metadata.AudioFile = filename2;
+
+ Assert.IsFalse(beatmap1.AudioEquals(beatmap2));
+ }
+
+ private static void addAudioFile(BeatmapSetInfo beatmapSetInfo, string hash = null, string filename = null)
+ {
+ beatmapSetInfo.Files.Add(new RealmNamedFileUsage(new RealmFile { Hash = hash ?? Guid.NewGuid().ToString() }, filename ?? "audio.mp3"));
}
[Test]
diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs
index 41404b2636..ee29cc8644 100644
--- a/osu.Game.Tests/Resources/TestResources.cs
+++ b/osu.Game.Tests/Resources/TestResources.cs
@@ -138,7 +138,7 @@ namespace osu.Game.Tests.Resources
BPM = bpm,
Hash = Guid.NewGuid().ToString().ComputeMD5Hash(),
Ruleset = rulesetInfo,
- Metadata = metadata,
+ Metadata = metadata.DeepClone(),
Difficulty = new BeatmapDifficulty
{
OverallDifficulty = diff,
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneNoConflictingModAcronyms.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneNoConflictingModAcronyms.cs
new file mode 100644
index 0000000000..b2ba3d99ad
--- /dev/null
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneNoConflictingModAcronyms.cs
@@ -0,0 +1,27 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+#nullable disable
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Testing;
+
+namespace osu.Game.Tests.Visual.Gameplay
+{
+ [HeadlessTest]
+ public class TestSceneNoConflictingModAcronyms : TestSceneAllRulesetPlayers
+ {
+ protected override void AddCheckSteps()
+ {
+ AddStep("Check all mod acronyms are unique", () =>
+ {
+ var mods = Ruleset.Value.CreateInstance().AllMods;
+
+ IEnumerable acronyms = mods.Select(m => m.Acronym);
+
+ Assert.That(acronyms, Is.Unique);
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs
index 5ec9e88728..6491987abe 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs
@@ -8,14 +8,18 @@ using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Extensions;
using osu.Framework.Extensions.ObjectExtensions;
+using osu.Framework.Graphics.Containers;
using osu.Framework.Platform;
using osu.Framework.Screens;
+using osu.Framework.Testing;
using osu.Game.Beatmaps;
+using osu.Game.Graphics.Containers;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
+using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osu.Game.Tests.Resources;
@@ -58,14 +62,35 @@ namespace osu.Game.Tests.Visual.Gameplay
protected override bool HasCustomSteps => true;
- protected override bool AllowFail => false;
+ protected override bool AllowFail => allowFail;
+
+ private bool allowFail;
+
+ [SetUp]
+ public void SetUp()
+ {
+ allowFail = false;
+ customRuleset = null;
+ }
+
+ [Test]
+ public void TestSaveFailedReplay()
+ {
+ AddStep("allow fail", () => allowFail = true);
+
+ CreateTest();
+
+ AddUntilStep("fail screen displayed", () => Player.ChildrenOfType().First().State.Value == Visibility.Visible);
+ AddUntilStep("score not in database", () => Realm.Run(r => r.Find(Player.Score.ScoreInfo.ID) == null));
+ AddStep("click save button", () => Player.ChildrenOfType().First().ChildrenOfType().First().TriggerClick());
+ AddUntilStep("score not in database", () => Realm.Run(r => r.Find(Player.Score.ScoreInfo.ID) != null));
+ }
[Test]
public void TestLastPlayedUpdated()
{
DateTimeOffset? getLastPlayed() => Realm.Run(r => r.Find(Beatmap.Value.BeatmapInfo.ID)?.LastPlayed);
- AddStep("set no custom ruleset", () => customRuleset = null);
AddAssert("last played is null", () => getLastPlayed() == null);
CreateTest();
@@ -77,8 +102,6 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestScoreStoredLocally()
{
- AddStep("set no custom ruleset", () => customRuleset = null);
-
CreateTest();
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
diff --git a/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs
index c5c61cdd72..5454c87dff 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs
@@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual.Online
Id = 3103765,
IsOnline = true,
Statistics = new UserStatistics { GlobalRank = 1111 },
- Country = new Country { FlagName = "JP" },
+ CountryCode = CountryCode.JP,
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
},
new APIUser
@@ -64,7 +64,7 @@ namespace osu.Game.Tests.Visual.Online
Id = 2,
IsOnline = false,
Statistics = new UserStatistics { GlobalRank = 2222 },
- Country = new Country { FlagName = "AU" },
+ CountryCode = CountryCode.AU,
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
IsSupporter = true,
SupportLevel = 3,
@@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual.Online
{
Username = "Evast",
Id = 8195163,
- Country = new Country { FlagName = "BY" },
+ CountryCode = CountryCode.BY,
CoverUrl = "https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg",
IsOnline = false,
LastVisit = DateTimeOffset.Now
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs
index e7d799222a..6a39db4870 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsCountryFilter.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Online
public TestSceneRankingsCountryFilter()
{
- var countryBindable = new Bindable();
+ var countryBindable = new Bindable();
AddRange(new Drawable[]
{
@@ -56,20 +56,12 @@ namespace osu.Game.Tests.Visual.Online
}
});
- var country = new Country
- {
- FlagName = "BY",
- FullName = "Belarus"
- };
- var unknownCountry = new Country
- {
- FlagName = "CK",
- FullName = "Cook Islands"
- };
+ const CountryCode country = CountryCode.BY;
+ const CountryCode unknown_country = CountryCode.CK;
AddStep("Set country", () => countryBindable.Value = country);
- AddStep("Set null country", () => countryBindable.Value = null);
- AddStep("Set country with no flag", () => countryBindable.Value = unknownCountry);
+ AddStep("Set default country", () => countryBindable.Value = default);
+ AddStep("Set country with no flag", () => countryBindable.Value = unknown_country);
}
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs
index c8f08d70be..c776cfe377 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Online
public TestSceneRankingsHeader()
{
- var countryBindable = new Bindable();
+ var countryBindable = new Bindable();
var ruleset = new Bindable();
var scope = new Bindable();
@@ -30,21 +30,12 @@ namespace osu.Game.Tests.Visual.Online
Ruleset = { BindTarget = ruleset }
});
- var country = new Country
- {
- FlagName = "BY",
- FullName = "Belarus"
- };
-
- var unknownCountry = new Country
- {
- FlagName = "CK",
- FullName = "Cook Islands"
- };
+ const CountryCode country = CountryCode.BY;
+ const CountryCode unknown_country = CountryCode.CK;
AddStep("Set country", () => countryBindable.Value = country);
AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
- AddStep("Set country with no flag", () => countryBindable.Value = unknownCountry);
+ AddStep("Set country with no flag", () => countryBindable.Value = unknown_country);
}
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs
index 62dad7b458..5476049882 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Online
private TestRankingsOverlay rankingsOverlay;
- private readonly Bindable countryBindable = new Bindable();
+ private readonly Bindable countryBindable = new Bindable();
private readonly Bindable scope = new Bindable();
[SetUp]
@@ -48,15 +48,15 @@ namespace osu.Game.Tests.Visual.Online
public void TestFlagScopeDependency()
{
AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
- AddAssert("Check country is Null", () => countryBindable.Value == null);
- AddStep("Set country", () => countryBindable.Value = us_country);
+ AddAssert("Check country is default", () => countryBindable.IsDefault);
+ AddStep("Set country", () => countryBindable.Value = CountryCode.US);
AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance);
}
[Test]
public void TestShowCountry()
{
- AddStep("Show US", () => rankingsOverlay.ShowCountry(us_country));
+ AddStep("Show US", () => rankingsOverlay.ShowCountry(CountryCode.US));
}
private void loadRankingsOverlay()
@@ -69,15 +69,9 @@ namespace osu.Game.Tests.Visual.Online
};
}
- private static readonly Country us_country = new Country
- {
- FlagName = "US",
- FullName = "United States"
- };
-
private class TestRankingsOverlay : RankingsOverlay
{
- public new Bindable Country => base.Country;
+ public new Bindable Country => base.Country;
}
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs
index e357b0fffc..81b76d19ac 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs
@@ -57,8 +57,7 @@ namespace osu.Game.Tests.Visual.Online
{
new CountryStatistics
{
- Country = new Country { FlagName = "US", FullName = "United States" },
- FlagName = "US",
+ Code = CountryCode.US,
ActiveUsers = 2_972_623,
PlayCount = 3_086_515_743,
RankedScore = 449_407_643_332_546,
@@ -66,8 +65,7 @@ namespace osu.Game.Tests.Visual.Online
},
new CountryStatistics
{
- Country = new Country { FlagName = "RU", FullName = "Russian Federation" },
- FlagName = "RU",
+ Code = CountryCode.RU,
ActiveUsers = 1_609_989,
PlayCount = 1_637_052_841,
RankedScore = 221_660_827_473_004,
@@ -86,7 +84,7 @@ namespace osu.Game.Tests.Visual.Online
User = new APIUser
{
Username = "first active user",
- Country = new Country { FlagName = "JP" },
+ CountryCode = CountryCode.JP,
Active = true,
},
Accuracy = 0.9972,
@@ -106,7 +104,7 @@ namespace osu.Game.Tests.Visual.Online
User = new APIUser
{
Username = "inactive user",
- Country = new Country { FlagName = "AU" },
+ CountryCode = CountryCode.AU,
Active = false,
},
Accuracy = 0.9831,
@@ -126,7 +124,7 @@ namespace osu.Game.Tests.Visual.Online
User = new APIUser
{
Username = "second active user",
- Country = new Country { FlagName = "PL" },
+ CountryCode = CountryCode.PL,
Active = true,
},
Accuracy = 0.9584,
diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs
index be03328caa..beca3a8700 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs
@@ -157,11 +157,7 @@ namespace osu.Game.Tests.Visual.Online
{
Id = 6602580,
Username = @"waaiiru",
- Country = new Country
- {
- FullName = @"Spain",
- FlagName = @"ES",
- },
+ CountryCode = CountryCode.ES,
},
Mods = new[]
{
@@ -184,11 +180,7 @@ namespace osu.Game.Tests.Visual.Online
{
Id = 4608074,
Username = @"Skycries",
- Country = new Country
- {
- FullName = @"Brazil",
- FlagName = @"BR",
- },
+ CountryCode = CountryCode.BR,
},
Mods = new[]
{
@@ -210,11 +202,7 @@ namespace osu.Game.Tests.Visual.Online
{
Id = 1014222,
Username = @"eLy",
- Country = new Country
- {
- FullName = @"Japan",
- FlagName = @"JP",
- },
+ CountryCode = CountryCode.JP,
},
Mods = new[]
{
@@ -235,11 +223,7 @@ namespace osu.Game.Tests.Visual.Online
{
Id = 1541390,
Username = @"Toukai",
- Country = new Country
- {
- FullName = @"Canada",
- FlagName = @"CA",
- },
+ CountryCode = CountryCode.CA,
},
Mods = new[]
{
@@ -259,11 +243,7 @@ namespace osu.Game.Tests.Visual.Online
{
Id = 7151382,
Username = @"Mayuri Hana",
- Country = new Country
- {
- FullName = @"Thailand",
- FlagName = @"TH",
- },
+ CountryCode = CountryCode.TH,
},
Rank = ScoreRank.D,
PP = 160,
@@ -274,14 +254,18 @@ namespace osu.Game.Tests.Visual.Online
}
};
+ const int initial_great_count = 2000;
+
+ int greatCount = initial_great_count;
+
foreach (var s in scores.Scores)
{
s.Statistics = new Dictionary
{
- { HitResult.Great, RNG.Next(2000) },
- { HitResult.Ok, RNG.Next(2000) },
- { HitResult.Meh, RNG.Next(2000) },
- { HitResult.Miss, RNG.Next(2000) }
+ { HitResult.Great, greatCount -= 100 },
+ { HitResult.Ok, RNG.Next(100) },
+ { HitResult.Meh, RNG.Next(100) },
+ { HitResult.Miss, initial_great_count - greatCount }
};
}
@@ -298,11 +282,7 @@ namespace osu.Game.Tests.Visual.Online
{
Id = 7151382,
Username = @"Mayuri Hana",
- Country = new Country
- {
- FullName = @"Thailand",
- FlagName = @"TH",
- },
+ CountryCode = CountryCode.TH,
},
Rank = ScoreRank.D,
PP = 160,
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
index fff40b3c74..2a70fd7df3 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
@@ -60,7 +60,7 @@ namespace osu.Game.Tests.Visual.Online
{
Username = @"flyte",
Id = 3103765,
- Country = new Country { FlagName = @"JP" },
+ CountryCode = CountryCode.JP,
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg",
Status = { Value = new UserStatusOnline() }
}) { Width = 300 },
@@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual.Online
{
Username = @"peppy",
Id = 2,
- Country = new Country { FlagName = @"AU" },
+ CountryCode = CountryCode.AU,
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
IsSupporter = true,
SupportLevel = 3,
@@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Online
{
Username = @"Evast",
Id = 8195163,
- Country = new Country { FlagName = @"BY" },
+ CountryCode = CountryCode.BY,
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg",
IsOnline = false,
LastVisit = DateTimeOffset.Now
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs
index ad3215b1ef..caa2d2571d 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Online
{
Username = @"Somebody",
Id = 1,
- Country = new Country { FullName = @"Alien" },
+ CountryCode = CountryCode.Unknown,
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg",
JoinDate = DateTimeOffset.Now.AddDays(-1),
LastVisit = DateTimeOffset.Now,
@@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.Online
Username = @"peppy",
Id = 2,
IsSupporter = true,
- Country = new Country { FullName = @"Australia", FlagName = @"AU" },
+ CountryCode = CountryCode.AU,
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg"
}));
@@ -90,7 +90,7 @@ namespace osu.Game.Tests.Visual.Online
{
Username = @"flyte",
Id = 3103765,
- Country = new Country { FullName = @"Japan", FlagName = @"JP" },
+ CountryCode = CountryCode.JP,
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
}));
@@ -99,7 +99,7 @@ namespace osu.Game.Tests.Visual.Online
Username = @"BanchoBot",
Id = 3,
IsBot = true,
- Country = new Country { FullName = @"Saint Helena", FlagName = @"SH" },
+ CountryCode = CountryCode.SH,
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c4.jpg"
}));
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs
index fa28df3061..0eb6ec3c04 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Online
{
public TestSceneUserProfileScores()
{
- var firstScore = new APIScore
+ var firstScore = new SoloScoreInfo
{
PP = 1047.21,
Rank = ScoreRank.SH,
@@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.Online
},
DifficultyName = "Extreme"
},
- Date = DateTimeOffset.Now,
+ EndedAt = DateTimeOffset.Now,
Mods = new[]
{
new APIMod { Acronym = new OsuModHidden().Acronym },
@@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Online
Accuracy = 0.9813
};
- var secondScore = new APIScore
+ var secondScore = new SoloScoreInfo
{
PP = 134.32,
Rank = ScoreRank.A,
@@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Online
},
DifficultyName = "[4K] Regret"
},
- Date = DateTimeOffset.Now,
+ EndedAt = DateTimeOffset.Now,
Mods = new[]
{
new APIMod { Acronym = new OsuModHardRock().Acronym },
@@ -66,7 +66,7 @@ namespace osu.Game.Tests.Visual.Online
Accuracy = 0.998546
};
- var thirdScore = new APIScore
+ var thirdScore = new SoloScoreInfo
{
PP = 96.83,
Rank = ScoreRank.S,
@@ -79,11 +79,11 @@ namespace osu.Game.Tests.Visual.Online
},
DifficultyName = "Insane"
},
- Date = DateTimeOffset.Now,
+ EndedAt = DateTimeOffset.Now,
Accuracy = 0.9726
};
- var noPPScore = new APIScore
+ var noPPScore = new SoloScoreInfo
{
Rank = ScoreRank.B,
Beatmap = new APIBeatmap
@@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.Online
},
DifficultyName = "[4K] Cataclysmic Hypernova"
},
- Date = DateTimeOffset.Now,
+ EndedAt = DateTimeOffset.Now,
Accuracy = 0.55879
};
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
index ef0c7d7d4d..abcb888cd4 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
@@ -140,11 +140,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
Id = 6602580,
Username = @"waaiiru",
- Country = new Country
- {
- FullName = @"Spain",
- FlagName = @"ES",
- },
+ CountryCode = CountryCode.ES,
},
});
}
@@ -164,12 +160,8 @@ namespace osu.Game.Tests.Visual.SongSelect
{
Id = 6602580,
Username = @"waaiiru",
- Country = new Country
- {
- FullName = @"Spain",
- FlagName = @"ES",
- },
- },
+ CountryCode = CountryCode.ES,
+ }
});
}
@@ -225,11 +217,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
Id = 6602580,
Username = @"waaiiru",
- Country = new Country
- {
- FullName = @"Spain",
- FlagName = @"ES",
- },
+ CountryCode = CountryCode.ES,
},
},
new ScoreInfo
@@ -246,11 +234,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
Id = 4608074,
Username = @"Skycries",
- Country = new Country
- {
- FullName = @"Brazil",
- FlagName = @"BR",
- },
+ CountryCode = CountryCode.BR,
},
},
new ScoreInfo
@@ -268,11 +252,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
Id = 1014222,
Username = @"eLy",
- Country = new Country
- {
- FullName = @"Japan",
- FlagName = @"JP",
- },
+ CountryCode = CountryCode.JP,
},
},
new ScoreInfo
@@ -290,11 +270,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
Id = 1541390,
Username = @"Toukai",
- Country = new Country
- {
- FullName = @"Canada",
- FlagName = @"CA",
- },
+ CountryCode = CountryCode.CA,
},
},
new ScoreInfo
@@ -312,11 +288,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
Id = 2243452,
Username = @"Satoruu",
- Country = new Country
- {
- FullName = @"Venezuela",
- FlagName = @"VE",
- },
+ CountryCode = CountryCode.VE,
},
},
new ScoreInfo
@@ -334,11 +306,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
Id = 2705430,
Username = @"Mooha",
- Country = new Country
- {
- FullName = @"France",
- FlagName = @"FR",
- },
+ CountryCode = CountryCode.FR,
},
},
new ScoreInfo
@@ -356,11 +324,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
Id = 7151382,
Username = @"Mayuri Hana",
- Country = new Country
- {
- FullName = @"Thailand",
- FlagName = @"TH",
- },
+ CountryCode = CountryCode.TH,
},
},
new ScoreInfo
@@ -378,11 +342,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
Id = 2051389,
Username = @"FunOrange",
- Country = new Country
- {
- FullName = @"Canada",
- FlagName = @"CA",
- },
+ CountryCode = CountryCode.CA,
},
},
new ScoreInfo
@@ -400,11 +360,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
Id = 6169483,
Username = @"-Hebel-",
- Country = new Country
- {
- FullName = @"Mexico",
- FlagName = @"MX",
- },
+ CountryCode = CountryCode.MX,
},
},
new ScoreInfo
@@ -422,11 +378,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
Id = 6702666,
Username = @"prhtnsm",
- Country = new Country
- {
- FullName = @"Germany",
- FlagName = @"DE",
- },
+ CountryCode = CountryCode.DE,
},
},
};
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs
index 16966e489a..39fd9fda2b 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs
@@ -69,11 +69,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
Id = 6602580,
Username = @"waaiiru",
- Country = new Country
- {
- FullName = @"Spain",
- FlagName = @"ES",
- },
+ CountryCode = CountryCode.ES,
},
},
new ScoreInfo
@@ -88,11 +84,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
Id = 4608074,
Username = @"Skycries",
- Country = new Country
- {
- FullName = @"Brazil",
- FlagName = @"BR",
- },
+ CountryCode = CountryCode.BR,
},
},
new ScoreInfo
@@ -107,11 +99,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{
Id = 1541390,
Username = @"Toukai",
- Country = new Country
- {
- FullName = @"Canada",
- FlagName = @"CA",
- },
+ CountryCode = CountryCode.CA,
},
}
};
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs
index 44f2da2b95..e8454e8d0f 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs
@@ -4,13 +4,13 @@
#nullable disable
using System.Linq;
-using Humanizer;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
+using osu.Game.Extensions;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
@@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.UserInterface
};
control.Query.BindValueChanged(q => query.Text = $"Query: {q.NewValue}", true);
- control.General.BindCollectionChanged((_, _) => general.Text = $"General: {(control.General.Any() ? string.Join('.', control.General.Select(i => i.ToString().Underscore())) : "")}", true);
+ control.General.BindCollectionChanged((_, _) => general.Text = $"General: {(control.General.Any() ? string.Join('.', control.General.Select(i => i.ToString().ToSnakeCase())) : "")}", true);
control.Ruleset.BindValueChanged(r => ruleset.Text = $"Ruleset: {r.NewValue}", true);
control.Category.BindValueChanged(c => category.Text = $"Category: {c.NewValue}", true);
control.Genre.BindValueChanged(g => genre.Text = $"Genre: {g.NewValue}", true);
diff --git a/osu.Game.Tournament/CountryExtensions.cs b/osu.Game.Tournament/CountryExtensions.cs
new file mode 100644
index 0000000000..f2a583c8a5
--- /dev/null
+++ b/osu.Game.Tournament/CountryExtensions.cs
@@ -0,0 +1,770 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osu.Game.Users;
+
+namespace osu.Game.Tournament
+{
+ public static class CountryExtensions
+ {
+ public static string GetAcronym(this CountryCode country)
+ {
+ switch (country)
+ {
+ case CountryCode.BD:
+ return "BGD";
+
+ case CountryCode.BE:
+ return "BEL";
+
+ case CountryCode.BF:
+ return "BFA";
+
+ case CountryCode.BG:
+ return "BGR";
+
+ case CountryCode.BA:
+ return "BIH";
+
+ case CountryCode.BB:
+ return "BRB";
+
+ case CountryCode.WF:
+ return "WLF";
+
+ case CountryCode.BL:
+ return "BLM";
+
+ case CountryCode.BM:
+ return "BMU";
+
+ case CountryCode.BN:
+ return "BRN";
+
+ case CountryCode.BO:
+ return "BOL";
+
+ case CountryCode.BH:
+ return "BHR";
+
+ case CountryCode.BI:
+ return "BDI";
+
+ case CountryCode.BJ:
+ return "BEN";
+
+ case CountryCode.BT:
+ return "BTN";
+
+ case CountryCode.JM:
+ return "JAM";
+
+ case CountryCode.BV:
+ return "BVT";
+
+ case CountryCode.BW:
+ return "BWA";
+
+ case CountryCode.WS:
+ return "WSM";
+
+ case CountryCode.BQ:
+ return "BES";
+
+ case CountryCode.BR:
+ return "BRA";
+
+ case CountryCode.BS:
+ return "BHS";
+
+ case CountryCode.JE:
+ return "JEY";
+
+ case CountryCode.BY:
+ return "BLR";
+
+ case CountryCode.BZ:
+ return "BLZ";
+
+ case CountryCode.RU:
+ return "RUS";
+
+ case CountryCode.RW:
+ return "RWA";
+
+ case CountryCode.RS:
+ return "SRB";
+
+ case CountryCode.TL:
+ return "TLS";
+
+ case CountryCode.RE:
+ return "REU";
+
+ case CountryCode.TM:
+ return "TKM";
+
+ case CountryCode.TJ:
+ return "TJK";
+
+ case CountryCode.RO:
+ return "ROU";
+
+ case CountryCode.TK:
+ return "TKL";
+
+ case CountryCode.GW:
+ return "GNB";
+
+ case CountryCode.GU:
+ return "GUM";
+
+ case CountryCode.GT:
+ return "GTM";
+
+ case CountryCode.GS:
+ return "SGS";
+
+ case CountryCode.GR:
+ return "GRC";
+
+ case CountryCode.GQ:
+ return "GNQ";
+
+ case CountryCode.GP:
+ return "GLP";
+
+ case CountryCode.JP:
+ return "JPN";
+
+ case CountryCode.GY:
+ return "GUY";
+
+ case CountryCode.GG:
+ return "GGY";
+
+ case CountryCode.GF:
+ return "GUF";
+
+ case CountryCode.GE:
+ return "GEO";
+
+ case CountryCode.GD:
+ return "GRD";
+
+ case CountryCode.GB:
+ return "GBR";
+
+ case CountryCode.GA:
+ return "GAB";
+
+ case CountryCode.SV:
+ return "SLV";
+
+ case CountryCode.GN:
+ return "GIN";
+
+ case CountryCode.GM:
+ return "GMB";
+
+ case CountryCode.GL:
+ return "GRL";
+
+ case CountryCode.GI:
+ return "GIB";
+
+ case CountryCode.GH:
+ return "GHA";
+
+ case CountryCode.OM:
+ return "OMN";
+
+ case CountryCode.TN:
+ return "TUN";
+
+ case CountryCode.JO:
+ return "JOR";
+
+ case CountryCode.HR:
+ return "HRV";
+
+ case CountryCode.HT:
+ return "HTI";
+
+ case CountryCode.HU:
+ return "HUN";
+
+ case CountryCode.HK:
+ return "HKG";
+
+ case CountryCode.HN:
+ return "HND";
+
+ case CountryCode.HM:
+ return "HMD";
+
+ case CountryCode.VE:
+ return "VEN";
+
+ case CountryCode.PR:
+ return "PRI";
+
+ case CountryCode.PS:
+ return "PSE";
+
+ case CountryCode.PW:
+ return "PLW";
+
+ case CountryCode.PT:
+ return "PRT";
+
+ case CountryCode.SJ:
+ return "SJM";
+
+ case CountryCode.PY:
+ return "PRY";
+
+ case CountryCode.IQ:
+ return "IRQ";
+
+ case CountryCode.PA:
+ return "PAN";
+
+ case CountryCode.PF:
+ return "PYF";
+
+ case CountryCode.PG:
+ return "PNG";
+
+ case CountryCode.PE:
+ return "PER";
+
+ case CountryCode.PK:
+ return "PAK";
+
+ case CountryCode.PH:
+ return "PHL";
+
+ case CountryCode.PN:
+ return "PCN";
+
+ case CountryCode.PL:
+ return "POL";
+
+ case CountryCode.PM:
+ return "SPM";
+
+ case CountryCode.ZM:
+ return "ZMB";
+
+ case CountryCode.EH:
+ return "ESH";
+
+ case CountryCode.EE:
+ return "EST";
+
+ case CountryCode.EG:
+ return "EGY";
+
+ case CountryCode.ZA:
+ return "ZAF";
+
+ case CountryCode.EC:
+ return "ECU";
+
+ case CountryCode.IT:
+ return "ITA";
+
+ case CountryCode.VN:
+ return "VNM";
+
+ case CountryCode.SB:
+ return "SLB";
+
+ case CountryCode.ET:
+ return "ETH";
+
+ case CountryCode.SO:
+ return "SOM";
+
+ case CountryCode.ZW:
+ return "ZWE";
+
+ case CountryCode.SA:
+ return "SAU";
+
+ case CountryCode.ES:
+ return "ESP";
+
+ case CountryCode.ER:
+ return "ERI";
+
+ case CountryCode.ME:
+ return "MNE";
+
+ case CountryCode.MD:
+ return "MDA";
+
+ case CountryCode.MG:
+ return "MDG";
+
+ case CountryCode.MF:
+ return "MAF";
+
+ case CountryCode.MA:
+ return "MAR";
+
+ case CountryCode.MC:
+ return "MCO";
+
+ case CountryCode.UZ:
+ return "UZB";
+
+ case CountryCode.MM:
+ return "MMR";
+
+ case CountryCode.ML:
+ return "MLI";
+
+ case CountryCode.MO:
+ return "MAC";
+
+ case CountryCode.MN:
+ return "MNG";
+
+ case CountryCode.MH:
+ return "MHL";
+
+ case CountryCode.MK:
+ return "MKD";
+
+ case CountryCode.MU:
+ return "MUS";
+
+ case CountryCode.MT:
+ return "MLT";
+
+ case CountryCode.MW:
+ return "MWI";
+
+ case CountryCode.MV:
+ return "MDV";
+
+ case CountryCode.MQ:
+ return "MTQ";
+
+ case CountryCode.MP:
+ return "MNP";
+
+ case CountryCode.MS:
+ return "MSR";
+
+ case CountryCode.MR:
+ return "MRT";
+
+ case CountryCode.IM:
+ return "IMN";
+
+ case CountryCode.UG:
+ return "UGA";
+
+ case CountryCode.TZ:
+ return "TZA";
+
+ case CountryCode.MY:
+ return "MYS";
+
+ case CountryCode.MX:
+ return "MEX";
+
+ case CountryCode.IL:
+ return "ISR";
+
+ case CountryCode.FR:
+ return "FRA";
+
+ case CountryCode.IO:
+ return "IOT";
+
+ case CountryCode.SH:
+ return "SHN";
+
+ case CountryCode.FI:
+ return "FIN";
+
+ case CountryCode.FJ:
+ return "FJI";
+
+ case CountryCode.FK:
+ return "FLK";
+
+ case CountryCode.FM:
+ return "FSM";
+
+ case CountryCode.FO:
+ return "FRO";
+
+ case CountryCode.NI:
+ return "NIC";
+
+ case CountryCode.NL:
+ return "NLD";
+
+ case CountryCode.NO:
+ return "NOR";
+
+ case CountryCode.NA:
+ return "NAM";
+
+ case CountryCode.VU:
+ return "VUT";
+
+ case CountryCode.NC:
+ return "NCL";
+
+ case CountryCode.NE:
+ return "NER";
+
+ case CountryCode.NF:
+ return "NFK";
+
+ case CountryCode.NG:
+ return "NGA";
+
+ case CountryCode.NZ:
+ return "NZL";
+
+ case CountryCode.NP:
+ return "NPL";
+
+ case CountryCode.NR:
+ return "NRU";
+
+ case CountryCode.NU:
+ return "NIU";
+
+ case CountryCode.CK:
+ return "COK";
+
+ case CountryCode.XK:
+ return "XKX";
+
+ case CountryCode.CI:
+ return "CIV";
+
+ case CountryCode.CH:
+ return "CHE";
+
+ case CountryCode.CO:
+ return "COL";
+
+ case CountryCode.CN:
+ return "CHN";
+
+ case CountryCode.CM:
+ return "CMR";
+
+ case CountryCode.CL:
+ return "CHL";
+
+ case CountryCode.CC:
+ return "CCK";
+
+ case CountryCode.CA:
+ return "CAN";
+
+ case CountryCode.CG:
+ return "COG";
+
+ case CountryCode.CF:
+ return "CAF";
+
+ case CountryCode.CD:
+ return "COD";
+
+ case CountryCode.CZ:
+ return "CZE";
+
+ case CountryCode.CY:
+ return "CYP";
+
+ case CountryCode.CX:
+ return "CXR";
+
+ case CountryCode.CR:
+ return "CRI";
+
+ case CountryCode.CW:
+ return "CUW";
+
+ case CountryCode.CV:
+ return "CPV";
+
+ case CountryCode.CU:
+ return "CUB";
+
+ case CountryCode.SZ:
+ return "SWZ";
+
+ case CountryCode.SY:
+ return "SYR";
+
+ case CountryCode.SX:
+ return "SXM";
+
+ case CountryCode.KG:
+ return "KGZ";
+
+ case CountryCode.KE:
+ return "KEN";
+
+ case CountryCode.SS:
+ return "SSD";
+
+ case CountryCode.SR:
+ return "SUR";
+
+ case CountryCode.KI:
+ return "KIR";
+
+ case CountryCode.KH:
+ return "KHM";
+
+ case CountryCode.KN:
+ return "KNA";
+
+ case CountryCode.KM:
+ return "COM";
+
+ case CountryCode.ST:
+ return "STP";
+
+ case CountryCode.SK:
+ return "SVK";
+
+ case CountryCode.KR:
+ return "KOR";
+
+ case CountryCode.SI:
+ return "SVN";
+
+ case CountryCode.KP:
+ return "PRK";
+
+ case CountryCode.KW:
+ return "KWT";
+
+ case CountryCode.SN:
+ return "SEN";
+
+ case CountryCode.SM:
+ return "SMR";
+
+ case CountryCode.SL:
+ return "SLE";
+
+ case CountryCode.SC:
+ return "SYC";
+
+ case CountryCode.KZ:
+ return "KAZ";
+
+ case CountryCode.KY:
+ return "CYM";
+
+ case CountryCode.SG:
+ return "SGP";
+
+ case CountryCode.SE:
+ return "SWE";
+
+ case CountryCode.SD:
+ return "SDN";
+
+ case CountryCode.DO:
+ return "DOM";
+
+ case CountryCode.DM:
+ return "DMA";
+
+ case CountryCode.DJ:
+ return "DJI";
+
+ case CountryCode.DK:
+ return "DNK";
+
+ case CountryCode.VG:
+ return "VGB";
+
+ case CountryCode.DE:
+ return "DEU";
+
+ case CountryCode.YE:
+ return "YEM";
+
+ case CountryCode.DZ:
+ return "DZA";
+
+ case CountryCode.US:
+ return "USA";
+
+ case CountryCode.UY:
+ return "URY";
+
+ case CountryCode.YT:
+ return "MYT";
+
+ case CountryCode.UM:
+ return "UMI";
+
+ case CountryCode.LB:
+ return "LBN";
+
+ case CountryCode.LC:
+ return "LCA";
+
+ case CountryCode.LA:
+ return "LAO";
+
+ case CountryCode.TV:
+ return "TUV";
+
+ case CountryCode.TW:
+ return "TWN";
+
+ case CountryCode.TT:
+ return "TTO";
+
+ case CountryCode.TR:
+ return "TUR";
+
+ case CountryCode.LK:
+ return "LKA";
+
+ case CountryCode.LI:
+ return "LIE";
+
+ case CountryCode.LV:
+ return "LVA";
+
+ case CountryCode.TO:
+ return "TON";
+
+ case CountryCode.LT:
+ return "LTU";
+
+ case CountryCode.LU:
+ return "LUX";
+
+ case CountryCode.LR:
+ return "LBR";
+
+ case CountryCode.LS:
+ return "LSO";
+
+ case CountryCode.TH:
+ return "THA";
+
+ case CountryCode.TF:
+ return "ATF";
+
+ case CountryCode.TG:
+ return "TGO";
+
+ case CountryCode.TD:
+ return "TCD";
+
+ case CountryCode.TC:
+ return "TCA";
+
+ case CountryCode.LY:
+ return "LBY";
+
+ case CountryCode.VA:
+ return "VAT";
+
+ case CountryCode.VC:
+ return "VCT";
+
+ case CountryCode.AE:
+ return "ARE";
+
+ case CountryCode.AD:
+ return "AND";
+
+ case CountryCode.AG:
+ return "ATG";
+
+ case CountryCode.AF:
+ return "AFG";
+
+ case CountryCode.AI:
+ return "AIA";
+
+ case CountryCode.VI:
+ return "VIR";
+
+ case CountryCode.IS:
+ return "ISL";
+
+ case CountryCode.IR:
+ return "IRN";
+
+ case CountryCode.AM:
+ return "ARM";
+
+ case CountryCode.AL:
+ return "ALB";
+
+ case CountryCode.AO:
+ return "AGO";
+
+ case CountryCode.AQ:
+ return "ATA";
+
+ case CountryCode.AS:
+ return "ASM";
+
+ case CountryCode.AR:
+ return "ARG";
+
+ case CountryCode.AU:
+ return "AUS";
+
+ case CountryCode.AT:
+ return "AUT";
+
+ case CountryCode.AW:
+ return "ABW";
+
+ case CountryCode.IN:
+ return "IND";
+
+ case CountryCode.AX:
+ return "ALA";
+
+ case CountryCode.AZ:
+ return "AZE";
+
+ case CountryCode.IE:
+ return "IRL";
+
+ case CountryCode.ID:
+ return "IDN";
+
+ case CountryCode.UA:
+ return "UKR";
+
+ case CountryCode.QA:
+ return "QAT";
+
+ case CountryCode.MZ:
+ return "MOZ";
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(country));
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tournament/Models/TournamentUser.cs b/osu.Game.Tournament/Models/TournamentUser.cs
index 80e58538e5..78ca014860 100644
--- a/osu.Game.Tournament/Models/TournamentUser.cs
+++ b/osu.Game.Tournament/Models/TournamentUser.cs
@@ -22,7 +22,8 @@ namespace osu.Game.Tournament.Models
///
/// The player's country.
///
- public Country? Country { get; set; }
+ [JsonProperty("country_code")]
+ public CountryCode CountryCode { get; set; }
///
/// The player's global rank, or null if not available.
@@ -40,7 +41,7 @@ namespace osu.Game.Tournament.Models
{
Id = OnlineID,
Username = Username,
- Country = Country,
+ CountryCode = CountryCode,
CoverUrl = CoverUrl,
};
diff --git a/osu.Game.Tournament/Resources/countries.json b/osu.Game.Tournament/Resources/countries.json
deleted file mode 100644
index 7306a8bec5..0000000000
--- a/osu.Game.Tournament/Resources/countries.json
+++ /dev/null
@@ -1,1252 +0,0 @@
-[
- {
- "FlagName": "BD",
- "FullName": "Bangladesh",
- "Acronym": "BGD"
- },
- {
- "FlagName": "BE",
- "FullName": "Belgium",
- "Acronym": "BEL"
- },
- {
- "FlagName": "BF",
- "FullName": "Burkina Faso",
- "Acronym": "BFA"
- },
- {
- "FlagName": "BG",
- "FullName": "Bulgaria",
- "Acronym": "BGR"
- },
- {
- "FlagName": "BA",
- "FullName": "Bosnia and Herzegovina",
- "Acronym": "BIH"
- },
- {
- "FlagName": "BB",
- "FullName": "Barbados",
- "Acronym": "BRB"
- },
- {
- "FlagName": "WF",
- "FullName": "Wallis and Futuna",
- "Acronym": "WLF"
- },
- {
- "FlagName": "BL",
- "FullName": "Saint Barthelemy",
- "Acronym": "BLM"
- },
- {
- "FlagName": "BM",
- "FullName": "Bermuda",
- "Acronym": "BMU"
- },
- {
- "FlagName": "BN",
- "FullName": "Brunei",
- "Acronym": "BRN"
- },
- {
- "FlagName": "BO",
- "FullName": "Bolivia",
- "Acronym": "BOL"
- },
- {
- "FlagName": "BH",
- "FullName": "Bahrain",
- "Acronym": "BHR"
- },
- {
- "FlagName": "BI",
- "FullName": "Burundi",
- "Acronym": "BDI"
- },
- {
- "FlagName": "BJ",
- "FullName": "Benin",
- "Acronym": "BEN"
- },
- {
- "FlagName": "BT",
- "FullName": "Bhutan",
- "Acronym": "BTN"
- },
- {
- "FlagName": "JM",
- "FullName": "Jamaica",
- "Acronym": "JAM"
- },
- {
- "FlagName": "BV",
- "FullName": "Bouvet Island",
- "Acronym": "BVT"
- },
- {
- "FlagName": "BW",
- "FullName": "Botswana",
- "Acronym": "BWA"
- },
- {
- "FlagName": "WS",
- "FullName": "Samoa",
- "Acronym": "WSM"
- },
- {
- "FlagName": "BQ",
- "FullName": "Bonaire, Saint Eustatius and Saba",
- "Acronym": "BES"
- },
- {
- "FlagName": "BR",
- "FullName": "Brazil",
- "Acronym": "BRA"
- },
- {
- "FlagName": "BS",
- "FullName": "Bahamas",
- "Acronym": "BHS"
- },
- {
- "FlagName": "JE",
- "FullName": "Jersey",
- "Acronym": "JEY"
- },
- {
- "FlagName": "BY",
- "FullName": "Belarus",
- "Acronym": "BLR"
- },
- {
- "FlagName": "BZ",
- "FullName": "Belize",
- "Acronym": "BLZ"
- },
- {
- "FlagName": "RU",
- "FullName": "Russia",
- "Acronym": "RUS"
- },
- {
- "FlagName": "RW",
- "FullName": "Rwanda",
- "Acronym": "RWA"
- },
- {
- "FlagName": "RS",
- "FullName": "Serbia",
- "Acronym": "SRB"
- },
- {
- "FlagName": "TL",
- "FullName": "East Timor",
- "Acronym": "TLS"
- },
- {
- "FlagName": "RE",
- "FullName": "Reunion",
- "Acronym": "REU"
- },
- {
- "FlagName": "TM",
- "FullName": "Turkmenistan",
- "Acronym": "TKM"
- },
- {
- "FlagName": "TJ",
- "FullName": "Tajikistan",
- "Acronym": "TJK"
- },
- {
- "FlagName": "RO",
- "FullName": "Romania",
- "Acronym": "ROU"
- },
- {
- "FlagName": "TK",
- "FullName": "Tokelau",
- "Acronym": "TKL"
- },
- {
- "FlagName": "GW",
- "FullName": "Guinea-Bissau",
- "Acronym": "GNB"
- },
- {
- "FlagName": "GU",
- "FullName": "Guam",
- "Acronym": "GUM"
- },
- {
- "FlagName": "GT",
- "FullName": "Guatemala",
- "Acronym": "GTM"
- },
- {
- "FlagName": "GS",
- "FullName": "South Georgia and the South Sandwich Islands",
- "Acronym": "SGS"
- },
- {
- "FlagName": "GR",
- "FullName": "Greece",
- "Acronym": "GRC"
- },
- {
- "FlagName": "GQ",
- "FullName": "Equatorial Guinea",
- "Acronym": "GNQ"
- },
- {
- "FlagName": "GP",
- "FullName": "Guadeloupe",
- "Acronym": "GLP"
- },
- {
- "FlagName": "JP",
- "FullName": "Japan",
- "Acronym": "JPN"
- },
- {
- "FlagName": "GY",
- "FullName": "Guyana",
- "Acronym": "GUY"
- },
- {
- "FlagName": "GG",
- "FullName": "Guernsey",
- "Acronym": "GGY"
- },
- {
- "FlagName": "GF",
- "FullName": "French Guiana",
- "Acronym": "GUF"
- },
- {
- "FlagName": "GE",
- "FullName": "Georgia",
- "Acronym": "GEO"
- },
- {
- "FlagName": "GD",
- "FullName": "Grenada",
- "Acronym": "GRD"
- },
- {
- "FlagName": "GB",
- "FullName": "United Kingdom",
- "Acronym": "GBR"
- },
- {
- "FlagName": "GA",
- "FullName": "Gabon",
- "Acronym": "GAB"
- },
- {
- "FlagName": "SV",
- "FullName": "El Salvador",
- "Acronym": "SLV"
- },
- {
- "FlagName": "GN",
- "FullName": "Guinea",
- "Acronym": "GIN"
- },
- {
- "FlagName": "GM",
- "FullName": "Gambia",
- "Acronym": "GMB"
- },
- {
- "FlagName": "GL",
- "FullName": "Greenland",
- "Acronym": "GRL"
- },
- {
- "FlagName": "GI",
- "FullName": "Gibraltar",
- "Acronym": "GIB"
- },
- {
- "FlagName": "GH",
- "FullName": "Ghana",
- "Acronym": "GHA"
- },
- {
- "FlagName": "OM",
- "FullName": "Oman",
- "Acronym": "OMN"
- },
- {
- "FlagName": "TN",
- "FullName": "Tunisia",
- "Acronym": "TUN"
- },
- {
- "FlagName": "JO",
- "FullName": "Jordan",
- "Acronym": "JOR"
- },
- {
- "FlagName": "HR",
- "FullName": "Croatia",
- "Acronym": "HRV"
- },
- {
- "FlagName": "HT",
- "FullName": "Haiti",
- "Acronym": "HTI"
- },
- {
- "FlagName": "HU",
- "FullName": "Hungary",
- "Acronym": "HUN"
- },
- {
- "FlagName": "HK",
- "FullName": "Hong Kong",
- "Acronym": "HKG"
- },
- {
- "FlagName": "HN",
- "FullName": "Honduras",
- "Acronym": "HND"
- },
- {
- "FlagName": "HM",
- "FullName": "Heard Island and McDonald Islands",
- "Acronym": "HMD"
- },
- {
- "FlagName": "VE",
- "FullName": "Venezuela",
- "Acronym": "VEN"
- },
- {
- "FlagName": "PR",
- "FullName": "Puerto Rico",
- "Acronym": "PRI"
- },
- {
- "FlagName": "PS",
- "FullName": "Palestinian Territory",
- "Acronym": "PSE"
- },
- {
- "FlagName": "PW",
- "FullName": "Palau",
- "Acronym": "PLW"
- },
- {
- "FlagName": "PT",
- "FullName": "Portugal",
- "Acronym": "PRT"
- },
- {
- "FlagName": "SJ",
- "FullName": "Svalbard and Jan Mayen",
- "Acronym": "SJM"
- },
- {
- "FlagName": "PY",
- "FullName": "Paraguay",
- "Acronym": "PRY"
- },
- {
- "FlagName": "IQ",
- "FullName": "Iraq",
- "Acronym": "IRQ"
- },
- {
- "FlagName": "PA",
- "FullName": "Panama",
- "Acronym": "PAN"
- },
- {
- "FlagName": "PF",
- "FullName": "French Polynesia",
- "Acronym": "PYF"
- },
- {
- "FlagName": "PG",
- "FullName": "Papua New Guinea",
- "Acronym": "PNG"
- },
- {
- "FlagName": "PE",
- "FullName": "Peru",
- "Acronym": "PER"
- },
- {
- "FlagName": "PK",
- "FullName": "Pakistan",
- "Acronym": "PAK"
- },
- {
- "FlagName": "PH",
- "FullName": "Philippines",
- "Acronym": "PHL"
- },
- {
- "FlagName": "PN",
- "FullName": "Pitcairn",
- "Acronym": "PCN"
- },
- {
- "FlagName": "PL",
- "FullName": "Poland",
- "Acronym": "POL"
- },
- {
- "FlagName": "PM",
- "FullName": "Saint Pierre and Miquelon",
- "Acronym": "SPM"
- },
- {
- "FlagName": "ZM",
- "FullName": "Zambia",
- "Acronym": "ZMB"
- },
- {
- "FlagName": "EH",
- "FullName": "Western Sahara",
- "Acronym": "ESH"
- },
- {
- "FlagName": "EE",
- "FullName": "Estonia",
- "Acronym": "EST"
- },
- {
- "FlagName": "EG",
- "FullName": "Egypt",
- "Acronym": "EGY"
- },
- {
- "FlagName": "ZA",
- "FullName": "South Africa",
- "Acronym": "ZAF"
- },
- {
- "FlagName": "EC",
- "FullName": "Ecuador",
- "Acronym": "ECU"
- },
- {
- "FlagName": "IT",
- "FullName": "Italy",
- "Acronym": "ITA"
- },
- {
- "FlagName": "VN",
- "FullName": "Vietnam",
- "Acronym": "VNM"
- },
- {
- "FlagName": "SB",
- "FullName": "Solomon Islands",
- "Acronym": "SLB"
- },
- {
- "FlagName": "ET",
- "FullName": "Ethiopia",
- "Acronym": "ETH"
- },
- {
- "FlagName": "SO",
- "FullName": "Somalia",
- "Acronym": "SOM"
- },
- {
- "FlagName": "ZW",
- "FullName": "Zimbabwe",
- "Acronym": "ZWE"
- },
- {
- "FlagName": "SA",
- "FullName": "Saudi Arabia",
- "Acronym": "SAU"
- },
- {
- "FlagName": "ES",
- "FullName": "Spain",
- "Acronym": "ESP"
- },
- {
- "FlagName": "ER",
- "FullName": "Eritrea",
- "Acronym": "ERI"
- },
- {
- "FlagName": "ME",
- "FullName": "Montenegro",
- "Acronym": "MNE"
- },
- {
- "FlagName": "MD",
- "FullName": "Moldova",
- "Acronym": "MDA"
- },
- {
- "FlagName": "MG",
- "FullName": "Madagascar",
- "Acronym": "MDG"
- },
- {
- "FlagName": "MF",
- "FullName": "Saint Martin",
- "Acronym": "MAF"
- },
- {
- "FlagName": "MA",
- "FullName": "Morocco",
- "Acronym": "MAR"
- },
- {
- "FlagName": "MC",
- "FullName": "Monaco",
- "Acronym": "MCO"
- },
- {
- "FlagName": "UZ",
- "FullName": "Uzbekistan",
- "Acronym": "UZB"
- },
- {
- "FlagName": "MM",
- "FullName": "Myanmar",
- "Acronym": "MMR"
- },
- {
- "FlagName": "ML",
- "FullName": "Mali",
- "Acronym": "MLI"
- },
- {
- "FlagName": "MO",
- "FullName": "Macao",
- "Acronym": "MAC"
- },
- {
- "FlagName": "MN",
- "FullName": "Mongolia",
- "Acronym": "MNG"
- },
- {
- "FlagName": "MH",
- "FullName": "Marshall Islands",
- "Acronym": "MHL"
- },
- {
- "FlagName": "MK",
- "FullName": "North Macedonia",
- "Acronym": "MKD"
- },
- {
- "FlagName": "MU",
- "FullName": "Mauritius",
- "Acronym": "MUS"
- },
- {
- "FlagName": "MT",
- "FullName": "Malta",
- "Acronym": "MLT"
- },
- {
- "FlagName": "MW",
- "FullName": "Malawi",
- "Acronym": "MWI"
- },
- {
- "FlagName": "MV",
- "FullName": "Maldives",
- "Acronym": "MDV"
- },
- {
- "FlagName": "MQ",
- "FullName": "Martinique",
- "Acronym": "MTQ"
- },
- {
- "FlagName": "MP",
- "FullName": "Northern Mariana Islands",
- "Acronym": "MNP"
- },
- {
- "FlagName": "MS",
- "FullName": "Montserrat",
- "Acronym": "MSR"
- },
- {
- "FlagName": "MR",
- "FullName": "Mauritania",
- "Acronym": "MRT"
- },
- {
- "FlagName": "IM",
- "FullName": "Isle of Man",
- "Acronym": "IMN"
- },
- {
- "FlagName": "UG",
- "FullName": "Uganda",
- "Acronym": "UGA"
- },
- {
- "FlagName": "TZ",
- "FullName": "Tanzania",
- "Acronym": "TZA"
- },
- {
- "FlagName": "MY",
- "FullName": "Malaysia",
- "Acronym": "MYS"
- },
- {
- "FlagName": "MX",
- "FullName": "Mexico",
- "Acronym": "MEX"
- },
- {
- "FlagName": "IL",
- "FullName": "Israel",
- "Acronym": "ISR"
- },
- {
- "FlagName": "FR",
- "FullName": "France",
- "Acronym": "FRA"
- },
- {
- "FlagName": "IO",
- "FullName": "British Indian Ocean Territory",
- "Acronym": "IOT"
- },
- {
- "FlagName": "SH",
- "FullName": "Saint Helena",
- "Acronym": "SHN"
- },
- {
- "FlagName": "FI",
- "FullName": "Finland",
- "Acronym": "FIN"
- },
- {
- "FlagName": "FJ",
- "FullName": "Fiji",
- "Acronym": "FJI"
- },
- {
- "FlagName": "FK",
- "FullName": "Falkland Islands",
- "Acronym": "FLK"
- },
- {
- "FlagName": "FM",
- "FullName": "Micronesia",
- "Acronym": "FSM"
- },
- {
- "FlagName": "FO",
- "FullName": "Faroe Islands",
- "Acronym": "FRO"
- },
- {
- "FlagName": "NI",
- "FullName": "Nicaragua",
- "Acronym": "NIC"
- },
- {
- "FlagName": "NL",
- "FullName": "Netherlands",
- "Acronym": "NLD"
- },
- {
- "FlagName": "NO",
- "FullName": "Norway",
- "Acronym": "NOR"
- },
- {
- "FlagName": "NA",
- "FullName": "Namibia",
- "Acronym": "NAM"
- },
- {
- "FlagName": "VU",
- "FullName": "Vanuatu",
- "Acronym": "VUT"
- },
- {
- "FlagName": "NC",
- "FullName": "New Caledonia",
- "Acronym": "NCL"
- },
- {
- "FlagName": "NE",
- "FullName": "Niger",
- "Acronym": "NER"
- },
- {
- "FlagName": "NF",
- "FullName": "Norfolk Island",
- "Acronym": "NFK"
- },
- {
- "FlagName": "NG",
- "FullName": "Nigeria",
- "Acronym": "NGA"
- },
- {
- "FlagName": "NZ",
- "FullName": "New Zealand",
- "Acronym": "NZL"
- },
- {
- "FlagName": "NP",
- "FullName": "Nepal",
- "Acronym": "NPL"
- },
- {
- "FlagName": "NR",
- "FullName": "Nauru",
- "Acronym": "NRU"
- },
- {
- "FlagName": "NU",
- "FullName": "Niue",
- "Acronym": "NIU"
- },
- {
- "FlagName": "CK",
- "FullName": "Cook Islands",
- "Acronym": "COK"
- },
- {
- "FlagName": "XK",
- "FullName": "Kosovo",
- "Acronym": "XKX"
- },
- {
- "FlagName": "CI",
- "FullName": "Ivory Coast",
- "Acronym": "CIV"
- },
- {
- "FlagName": "CH",
- "FullName": "Switzerland",
- "Acronym": "CHE"
- },
- {
- "FlagName": "CO",
- "FullName": "Colombia",
- "Acronym": "COL"
- },
- {
- "FlagName": "CN",
- "FullName": "China",
- "Acronym": "CHN"
- },
- {
- "FlagName": "CM",
- "FullName": "Cameroon",
- "Acronym": "CMR"
- },
- {
- "FlagName": "CL",
- "FullName": "Chile",
- "Acronym": "CHL"
- },
- {
- "FlagName": "CC",
- "FullName": "Cocos Islands",
- "Acronym": "CCK"
- },
- {
- "FlagName": "CA",
- "FullName": "Canada",
- "Acronym": "CAN"
- },
- {
- "FlagName": "CG",
- "FullName": "Republic of the Congo",
- "Acronym": "COG"
- },
- {
- "FlagName": "CF",
- "FullName": "Central African Republic",
- "Acronym": "CAF"
- },
- {
- "FlagName": "CD",
- "FullName": "Democratic Republic of the Congo",
- "Acronym": "COD"
- },
- {
- "FlagName": "CZ",
- "FullName": "Czech Republic",
- "Acronym": "CZE"
- },
- {
- "FlagName": "CY",
- "FullName": "Cyprus",
- "Acronym": "CYP"
- },
- {
- "FlagName": "CX",
- "FullName": "Christmas Island",
- "Acronym": "CXR"
- },
- {
- "FlagName": "CR",
- "FullName": "Costa Rica",
- "Acronym": "CRI"
- },
- {
- "FlagName": "CW",
- "FullName": "Curacao",
- "Acronym": "CUW"
- },
- {
- "FlagName": "CV",
- "FullName": "Cabo Verde",
- "Acronym": "CPV"
- },
- {
- "FlagName": "CU",
- "FullName": "Cuba",
- "Acronym": "CUB"
- },
- {
- "FlagName": "SZ",
- "FullName": "Eswatini",
- "Acronym": "SWZ"
- },
- {
- "FlagName": "SY",
- "FullName": "Syria",
- "Acronym": "SYR"
- },
- {
- "FlagName": "SX",
- "FullName": "Sint Maarten",
- "Acronym": "SXM"
- },
- {
- "FlagName": "KG",
- "FullName": "Kyrgyzstan",
- "Acronym": "KGZ"
- },
- {
- "FlagName": "KE",
- "FullName": "Kenya",
- "Acronym": "KEN"
- },
- {
- "FlagName": "SS",
- "FullName": "South Sudan",
- "Acronym": "SSD"
- },
- {
- "FlagName": "SR",
- "FullName": "Suriname",
- "Acronym": "SUR"
- },
- {
- "FlagName": "KI",
- "FullName": "Kiribati",
- "Acronym": "KIR"
- },
- {
- "FlagName": "KH",
- "FullName": "Cambodia",
- "Acronym": "KHM"
- },
- {
- "FlagName": "KN",
- "FullName": "Saint Kitts and Nevis",
- "Acronym": "KNA"
- },
- {
- "FlagName": "KM",
- "FullName": "Comoros",
- "Acronym": "COM"
- },
- {
- "FlagName": "ST",
- "FullName": "Sao Tome and Principe",
- "Acronym": "STP"
- },
- {
- "FlagName": "SK",
- "FullName": "Slovakia",
- "Acronym": "SVK"
- },
- {
- "FlagName": "KR",
- "FullName": "South Korea",
- "Acronym": "KOR"
- },
- {
- "FlagName": "SI",
- "FullName": "Slovenia",
- "Acronym": "SVN"
- },
- {
- "FlagName": "KP",
- "FullName": "North Korea",
- "Acronym": "PRK"
- },
- {
- "FlagName": "KW",
- "FullName": "Kuwait",
- "Acronym": "KWT"
- },
- {
- "FlagName": "SN",
- "FullName": "Senegal",
- "Acronym": "SEN"
- },
- {
- "FlagName": "SM",
- "FullName": "San Marino",
- "Acronym": "SMR"
- },
- {
- "FlagName": "SL",
- "FullName": "Sierra Leone",
- "Acronym": "SLE"
- },
- {
- "FlagName": "SC",
- "FullName": "Seychelles",
- "Acronym": "SYC"
- },
- {
- "FlagName": "KZ",
- "FullName": "Kazakhstan",
- "Acronym": "KAZ"
- },
- {
- "FlagName": "KY",
- "FullName": "Cayman Islands",
- "Acronym": "CYM"
- },
- {
- "FlagName": "SG",
- "FullName": "Singapore",
- "Acronym": "SGP"
- },
- {
- "FlagName": "SE",
- "FullName": "Sweden",
- "Acronym": "SWE"
- },
- {
- "FlagName": "SD",
- "FullName": "Sudan",
- "Acronym": "SDN"
- },
- {
- "FlagName": "DO",
- "FullName": "Dominican Republic",
- "Acronym": "DOM"
- },
- {
- "FlagName": "DM",
- "FullName": "Dominica",
- "Acronym": "DMA"
- },
- {
- "FlagName": "DJ",
- "FullName": "Djibouti",
- "Acronym": "DJI"
- },
- {
- "FlagName": "DK",
- "FullName": "Denmark",
- "Acronym": "DNK"
- },
- {
- "FlagName": "VG",
- "FullName": "British Virgin Islands",
- "Acronym": "VGB"
- },
- {
- "FlagName": "DE",
- "FullName": "Germany",
- "Acronym": "DEU"
- },
- {
- "FlagName": "YE",
- "FullName": "Yemen",
- "Acronym": "YEM"
- },
- {
- "FlagName": "DZ",
- "FullName": "Algeria",
- "Acronym": "DZA"
- },
- {
- "FlagName": "US",
- "FullName": "United States",
- "Acronym": "USA"
- },
- {
- "FlagName": "UY",
- "FullName": "Uruguay",
- "Acronym": "URY"
- },
- {
- "FlagName": "YT",
- "FullName": "Mayotte",
- "Acronym": "MYT"
- },
- {
- "FlagName": "UM",
- "FullName": "United States Minor Outlying Islands",
- "Acronym": "UMI"
- },
- {
- "FlagName": "LB",
- "FullName": "Lebanon",
- "Acronym": "LBN"
- },
- {
- "FlagName": "LC",
- "FullName": "Saint Lucia",
- "Acronym": "LCA"
- },
- {
- "FlagName": "LA",
- "FullName": "Laos",
- "Acronym": "LAO"
- },
- {
- "FlagName": "TV",
- "FullName": "Tuvalu",
- "Acronym": "TUV"
- },
- {
- "FlagName": "TW",
- "FullName": "Taiwan",
- "Acronym": "TWN"
- },
- {
- "FlagName": "TT",
- "FullName": "Trinidad and Tobago",
- "Acronym": "TTO"
- },
- {
- "FlagName": "TR",
- "FullName": "Turkey",
- "Acronym": "TUR"
- },
- {
- "FlagName": "LK",
- "FullName": "Sri Lanka",
- "Acronym": "LKA"
- },
- {
- "FlagName": "LI",
- "FullName": "Liechtenstein",
- "Acronym": "LIE"
- },
- {
- "FlagName": "LV",
- "FullName": "Latvia",
- "Acronym": "LVA"
- },
- {
- "FlagName": "TO",
- "FullName": "Tonga",
- "Acronym": "TON"
- },
- {
- "FlagName": "LT",
- "FullName": "Lithuania",
- "Acronym": "LTU"
- },
- {
- "FlagName": "LU",
- "FullName": "Luxembourg",
- "Acronym": "LUX"
- },
- {
- "FlagName": "LR",
- "FullName": "Liberia",
- "Acronym": "LBR"
- },
- {
- "FlagName": "LS",
- "FullName": "Lesotho",
- "Acronym": "LSO"
- },
- {
- "FlagName": "TH",
- "FullName": "Thailand",
- "Acronym": "THA"
- },
- {
- "FlagName": "TF",
- "FullName": "French Southern Territories",
- "Acronym": "ATF"
- },
- {
- "FlagName": "TG",
- "FullName": "Togo",
- "Acronym": "TGO"
- },
- {
- "FlagName": "TD",
- "FullName": "Chad",
- "Acronym": "TCD"
- },
- {
- "FlagName": "TC",
- "FullName": "Turks and Caicos Islands",
- "Acronym": "TCA"
- },
- {
- "FlagName": "LY",
- "FullName": "Libya",
- "Acronym": "LBY"
- },
- {
- "FlagName": "VA",
- "FullName": "Vatican",
- "Acronym": "VAT"
- },
- {
- "FlagName": "VC",
- "FullName": "Saint Vincent and the Grenadines",
- "Acronym": "VCT"
- },
- {
- "FlagName": "AE",
- "FullName": "United Arab Emirates",
- "Acronym": "ARE"
- },
- {
- "FlagName": "AD",
- "FullName": "Andorra",
- "Acronym": "AND"
- },
- {
- "FlagName": "AG",
- "FullName": "Antigua and Barbuda",
- "Acronym": "ATG"
- },
- {
- "FlagName": "AF",
- "FullName": "Afghanistan",
- "Acronym": "AFG"
- },
- {
- "FlagName": "AI",
- "FullName": "Anguilla",
- "Acronym": "AIA"
- },
- {
- "FlagName": "VI",
- "FullName": "U.S. Virgin Islands",
- "Acronym": "VIR"
- },
- {
- "FlagName": "IS",
- "FullName": "Iceland",
- "Acronym": "ISL"
- },
- {
- "FlagName": "IR",
- "FullName": "Iran",
- "Acronym": "IRN"
- },
- {
- "FlagName": "AM",
- "FullName": "Armenia",
- "Acronym": "ARM"
- },
- {
- "FlagName": "AL",
- "FullName": "Albania",
- "Acronym": "ALB"
- },
- {
- "FlagName": "AO",
- "FullName": "Angola",
- "Acronym": "AGO"
- },
- {
- "FlagName": "AQ",
- "FullName": "Antarctica",
- "Acronym": "ATA"
- },
- {
- "FlagName": "AS",
- "FullName": "American Samoa",
- "Acronym": "ASM"
- },
- {
- "FlagName": "AR",
- "FullName": "Argentina",
- "Acronym": "ARG"
- },
- {
- "FlagName": "AU",
- "FullName": "Australia",
- "Acronym": "AUS"
- },
- {
- "FlagName": "AT",
- "FullName": "Austria",
- "Acronym": "AUT"
- },
- {
- "FlagName": "AW",
- "FullName": "Aruba",
- "Acronym": "ABW"
- },
- {
- "FlagName": "IN",
- "FullName": "India",
- "Acronym": "IND"
- },
- {
- "FlagName": "AX",
- "FullName": "Aland Islands",
- "Acronym": "ALA"
- },
- {
- "FlagName": "AZ",
- "FullName": "Azerbaijan",
- "Acronym": "AZE"
- },
- {
- "FlagName": "IE",
- "FullName": "Ireland",
- "Acronym": "IRL"
- },
- {
- "FlagName": "ID",
- "FullName": "Indonesia",
- "Acronym": "IDN"
- },
- {
- "FlagName": "UA",
- "FullName": "Ukraine",
- "Acronym": "UKR"
- },
- {
- "FlagName": "QA",
- "FullName": "Qatar",
- "Acronym": "QAT"
- },
- {
- "FlagName": "MZ",
- "FullName": "Mozambique",
- "Acronym": "MOZ"
- }
-]
\ No newline at end of file
diff --git a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs
index 111893d18c..da27c09e01 100644
--- a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs
+++ b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs
@@ -3,13 +3,13 @@
#nullable disable
+using System;
using System.Collections.Generic;
using System.Diagnostics;
-using System.IO;
using System.Linq;
-using Newtonsoft.Json;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -25,9 +25,6 @@ namespace osu.Game.Tournament.Screens.Editors
{
public class TeamEditorScreen : TournamentEditorScreen
{
- [Resolved]
- private TournamentGameBase game { get; set; }
-
protected override BindableList Storage => LadderInfo.Teams;
[BackgroundDependencyLoader]
@@ -45,11 +42,17 @@ namespace osu.Game.Tournament.Screens.Editors
private void addAllCountries()
{
- List countries;
+ var countries = new List();
- using (Stream stream = game.Resources.GetStream("Resources/countries.json"))
- using (var sr = new StreamReader(stream))
- countries = JsonConvert.DeserializeObject>(sr.ReadToEnd());
+ foreach (var country in Enum.GetValues(typeof(CountryCode)).Cast().Skip(1))
+ {
+ countries.Add(new TournamentTeam
+ {
+ FlagName = { Value = country.ToString() },
+ FullName = { Value = country.GetDescription() },
+ Acronym = { Value = country.GetAcronym() },
+ });
+ }
Debug.Assert(countries != null);
diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs
index f2a35ea5b3..063b62bf08 100644
--- a/osu.Game.Tournament/TournamentGameBase.cs
+++ b/osu.Game.Tournament/TournamentGameBase.cs
@@ -21,6 +21,7 @@ using osu.Game.Online.API.Requests.Responses;
using osu.Game.Tournament.IO;
using osu.Game.Tournament.IPC;
using osu.Game.Tournament.Models;
+using osu.Game.Users;
using osuTK.Input;
namespace osu.Game.Tournament
@@ -186,7 +187,9 @@ namespace osu.Game.Tournament
{
var playersRequiringPopulation = ladder.Teams
.SelectMany(t => t.Players)
- .Where(p => string.IsNullOrEmpty(p.Username) || p.Rank == null).ToList();
+ .Where(p => string.IsNullOrEmpty(p.Username)
+ || p.CountryCode == CountryCode.Unknown
+ || p.Rank == null).ToList();
if (playersRequiringPopulation.Count == 0)
return false;
@@ -288,7 +291,7 @@ namespace osu.Game.Tournament
user.Username = res.Username;
user.CoverUrl = res.CoverUrl;
- user.Country = res.Country;
+ user.CountryCode = res.CountryCode;
user.Rank = res.Statistics?.GlobalRank;
success?.Invoke();
diff --git a/osu.Game/Audio/HitSampleInfo.cs b/osu.Game/Audio/HitSampleInfo.cs
index 6aaf3d5cc2..efa5562cb8 100644
--- a/osu.Game/Audio/HitSampleInfo.cs
+++ b/osu.Game/Audio/HitSampleInfo.cs
@@ -14,15 +14,15 @@ namespace osu.Game.Audio
[Serializable]
public class HitSampleInfo : ISampleInfo, IEquatable
{
+ public const string HIT_NORMAL = @"hitnormal";
public const string HIT_WHISTLE = @"hitwhistle";
public const string HIT_FINISH = @"hitfinish";
- public const string HIT_NORMAL = @"hitnormal";
public const string HIT_CLAP = @"hitclap";
///
/// All valid sample addition constants.
///
- public static IEnumerable AllAdditions => new[] { HIT_WHISTLE, HIT_CLAP, HIT_FINISH };
+ public static IEnumerable AllAdditions => new[] { HIT_WHISTLE, HIT_FINISH, HIT_CLAP };
///
/// The name of the sample to load.
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 45d76259fc..41e89d864e 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -169,8 +169,8 @@ namespace osu.Game.Beatmaps
Debug.Assert(x.BeatmapSet != null);
Debug.Assert(y.BeatmapSet != null);
- string? fileHashX = x.BeatmapSet.Files.FirstOrDefault(f => f.Filename == getFilename(x.BeatmapSet.Metadata))?.File.Hash;
- string? fileHashY = y.BeatmapSet.Files.FirstOrDefault(f => f.Filename == getFilename(y.BeatmapSet.Metadata))?.File.Hash;
+ string? fileHashX = x.BeatmapSet.Files.FirstOrDefault(f => f.Filename == getFilename(x.Metadata))?.File.Hash;
+ string? fileHashY = y.BeatmapSet.Files.FirstOrDefault(f => f.Filename == getFilename(y.Metadata))?.File.Hash;
return fileHashX == fileHashY;
}
diff --git a/osu.Game/Beatmaps/BeatmapStatisticIcon.cs b/osu.Game/Beatmaps/BeatmapStatisticIcon.cs
index 58d13a3172..8002910b52 100644
--- a/osu.Game/Beatmaps/BeatmapStatisticIcon.cs
+++ b/osu.Game/Beatmaps/BeatmapStatisticIcon.cs
@@ -3,10 +3,10 @@
#nullable disable
-using Humanizer;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
+using osu.Game.Extensions;
namespace osu.Game.Beatmaps
{
@@ -25,7 +25,7 @@ namespace osu.Game.Beatmaps
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
- Texture = textures.Get($"Icons/BeatmapDetails/{iconType.ToString().Kebaberize()}");
+ Texture = textures.Get($"Icons/BeatmapDetails/{iconType.ToString().ToKebabCase()}");
}
}
diff --git a/osu.Game/Beatmaps/WorkingBeatmapCache.cs b/osu.Game/Beatmaps/WorkingBeatmapCache.cs
index ce883a7092..df44f01629 100644
--- a/osu.Game/Beatmaps/WorkingBeatmapCache.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmapCache.cs
@@ -168,7 +168,7 @@ namespace osu.Game.Beatmaps
if (texture == null)
{
- Logger.Log($"Beatmap background failed to load (file {Metadata.BackgroundFile} not found on disk at expected location {fileStorePath}).", level: LogLevel.Error);
+ Logger.Log($"Beatmap background failed to load (file {Metadata.BackgroundFile} not found on disk at expected location {fileStorePath}).");
return null;
}
diff --git a/osu.Game/Database/EFToRealmMigrator.cs b/osu.Game/Database/EFToRealmMigrator.cs
index 3b5424b3fb..294a8cd3ed 100644
--- a/osu.Game/Database/EFToRealmMigrator.cs
+++ b/osu.Game/Database/EFToRealmMigrator.cs
@@ -443,7 +443,6 @@ namespace osu.Game.Database
TotalScore = score.TotalScore,
MaxCombo = score.MaxCombo,
Accuracy = score.Accuracy,
- HasReplay = ((IScoreInfo)score).HasReplay,
Date = score.Date,
PP = score.PP,
Rank = score.Rank,
diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs
index 02b5a51f1f..c4d65f4f10 100644
--- a/osu.Game/Database/RealmAccess.cs
+++ b/osu.Game/Database/RealmAccess.cs
@@ -59,8 +59,10 @@ namespace osu.Game.Database
/// 13 2022-01-13 Final migration of beatmaps and scores to realm (multiple new storage fields).
/// 14 2022-03-01 Added BeatmapUserSettings to BeatmapInfo.
/// 15 2022-07-13 Added LastPlayed to BeatmapInfo.
+ /// 16 2022-07-15 Removed HasReplay from ScoreInfo.
+ /// 17 2022-07-16 Added CountryCode to RealmUser.
///
- private const int schema_version = 15;
+ private const int schema_version = 17;
///
/// Lock object which is held during sections, blocking realm retrieval during blocking periods.
diff --git a/osu.Game/Extensions/DrawableExtensions.cs b/osu.Game/Extensions/DrawableExtensions.cs
index d1aba2bfe3..35f2d61437 100644
--- a/osu.Game/Extensions/DrawableExtensions.cs
+++ b/osu.Game/Extensions/DrawableExtensions.cs
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using Humanizer;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -67,7 +66,7 @@ namespace osu.Game.Extensions
foreach (var (_, property) in component.GetSettingsSourceProperties())
{
- if (!info.Settings.TryGetValue(property.Name.Underscore(), out object settingValue))
+ if (!info.Settings.TryGetValue(property.Name.ToSnakeCase(), out object settingValue))
continue;
skinnable.CopyAdjustedSetting((IBindable)property.GetValue(component), settingValue);
diff --git a/osu.Game/Extensions/StringDehumanizeExtensions.cs b/osu.Game/Extensions/StringDehumanizeExtensions.cs
new file mode 100644
index 0000000000..6f0d7622d3
--- /dev/null
+++ b/osu.Game/Extensions/StringDehumanizeExtensions.cs
@@ -0,0 +1,94 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+// Based on code from the Humanizer library (https://github.com/Humanizr/Humanizer/blob/606e958cb83afc9be5b36716ac40d4daa9fa73a7/src/Humanizer/InflectorExtensions.cs)
+//
+// Humanizer is licenced under the MIT License (MIT)
+//
+// Copyright (c) .NET Foundation and Contributors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System.Text.RegularExpressions;
+
+namespace osu.Game.Extensions
+{
+ ///
+ /// Class with extension methods used to turn human-readable strings to casing conventions frequently used in code.
+ /// Often used for communicating with other systems (web API, spectator server).
+ /// All of the operations in this class are intentionally culture-invariant.
+ ///
+ public static class StringDehumanizeExtensions
+ {
+ ///
+ /// Converts the string to "Pascal case" (also known as "upper camel case").
+ ///
+ ///
+ ///
+ /// "this is a test string".ToPascalCase() == "ThisIsATestString"
+ ///
+ ///
+ public static string ToPascalCase(this string input)
+ {
+ return Regex.Replace(input, "(?:^|_|-| +)(.)", match => match.Groups[1].Value.ToUpperInvariant());
+ }
+
+ ///
+ /// Converts the string to (lower) "camel case".
+ ///
+ ///
+ ///
+ /// "this is a test string".ToCamelCase() == "thisIsATestString"
+ ///
+ ///
+ public static string ToCamelCase(this string input)
+ {
+ string word = input.ToPascalCase();
+ return word.Length > 0 ? word.Substring(0, 1).ToLowerInvariant() + word.Substring(1) : word;
+ }
+
+ ///
+ /// Converts the string to "snake case".
+ ///
+ ///
+ ///
+ /// "this is a test string".ToSnakeCase() == "this_is_a_test_string"
+ ///
+ ///
+ public static string ToSnakeCase(this string input)
+ {
+ return Regex.Replace(
+ Regex.Replace(
+ Regex.Replace(input, @"([\p{Lu}]+)([\p{Lu}][\p{Ll}])", "$1_$2"), @"([\p{Ll}\d])([\p{Lu}])", "$1_$2"), @"[-\s]", "_").ToLowerInvariant();
+ }
+
+ ///
+ /// Converts the string to "kebab case".
+ ///
+ ///
+ ///
+ /// "this is a test string".ToKebabCase() == "this-is-a-test-string"
+ ///
+ ///
+ public static string ToKebabCase(this string input)
+ {
+ return ToSnakeCase(input).Replace('_', '-');
+ }
+ }
+}
diff --git a/osu.Game/IO/Serialization/SnakeCaseKeyContractResolver.cs b/osu.Game/IO/Serialization/SnakeCaseKeyContractResolver.cs
index 4808ac1384..b51a8473ca 100644
--- a/osu.Game/IO/Serialization/SnakeCaseKeyContractResolver.cs
+++ b/osu.Game/IO/Serialization/SnakeCaseKeyContractResolver.cs
@@ -3,8 +3,8 @@
#nullable disable
-using Humanizer;
using Newtonsoft.Json.Serialization;
+using osu.Game.Extensions;
namespace osu.Game.IO.Serialization
{
@@ -12,7 +12,7 @@ namespace osu.Game.IO.Serialization
{
protected override string ResolvePropertyName(string propertyName)
{
- return propertyName.Underscore();
+ return propertyName.ToSnakeCase();
}
}
}
diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs
index c13a1a10cb..6a4e5110e6 100644
--- a/osu.Game/Localisation/Language.cs
+++ b/osu.Game/Localisation/Language.cs
@@ -2,9 +2,11 @@
// See the LICENCE file in the repository root for full licence text.
using System.ComponentModel;
+using JetBrains.Annotations;
namespace osu.Game.Localisation
{
+ [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
public enum Language
{
[Description(@"English")]
diff --git a/osu.Game/Models/RealmUser.cs b/osu.Game/Models/RealmUser.cs
index 58fd7ff2a3..e20ffc0808 100644
--- a/osu.Game/Models/RealmUser.cs
+++ b/osu.Game/Models/RealmUser.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System;
using osu.Game.Database;
using osu.Game.Users;
@@ -17,6 +15,16 @@ namespace osu.Game.Models
public string Username { get; set; } = string.Empty;
+ [Ignored]
+ public CountryCode CountryCode
+ {
+ get => Enum.TryParse(CountryString, out CountryCode country) ? country : CountryCode.Unknown;
+ set => CountryString = value.ToString();
+ }
+
+ [MapTo(nameof(CountryCode))]
+ public string CountryString { get; set; } = default(CountryCode).ToString();
+
public bool IsBot => false;
public bool Equals(RealmUser other)
diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs
index 43cea7fb97..7af19f6dd1 100644
--- a/osu.Game/Online/API/APIAccess.cs
+++ b/osu.Game/Online/API/APIAccess.cs
@@ -163,7 +163,13 @@ namespace osu.Game.Online.API
userReq.Failure += ex =>
{
- if (ex is WebException webException && webException.Message == @"Unauthorized")
+ if (ex is APIException)
+ {
+ LastLoginError = ex;
+ log.Add("Login failed on local user retrieval!");
+ Logout();
+ }
+ else if (ex is WebException webException && webException.Message == @"Unauthorized")
{
log.Add(@"Login no longer valid");
Logout();
diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs
index dc1db08174..900f59290c 100644
--- a/osu.Game/Online/API/APIMod.cs
+++ b/osu.Game/Online/API/APIMod.cs
@@ -5,13 +5,14 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
-using Humanizer;
using MessagePack;
using Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Framework.Logging;
using osu.Game.Configuration;
+using osu.Game.Extensions;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
@@ -44,11 +45,11 @@ namespace osu.Game.Online.API
var bindable = (IBindable)property.GetValue(mod);
if (!bindable.IsDefault)
- Settings.Add(property.Name.Underscore(), bindable.GetUnderlyingSettingValue());
+ Settings.Add(property.Name.ToSnakeCase(), bindable.GetUnderlyingSettingValue());
}
}
- public Mod ToMod(Ruleset ruleset)
+ public Mod ToMod([NotNull] Ruleset ruleset)
{
Mod resultMod = ruleset.CreateModFromAcronym(Acronym);
@@ -62,10 +63,17 @@ namespace osu.Game.Online.API
{
foreach (var (_, property) in resultMod.GetSettingsSourceProperties())
{
- if (!Settings.TryGetValue(property.Name.Underscore(), out object settingValue))
+ if (!Settings.TryGetValue(property.Name.ToSnakeCase(), out object settingValue))
continue;
- resultMod.CopyAdjustedSetting((IBindable)property.GetValue(resultMod), settingValue);
+ try
+ {
+ resultMod.CopyAdjustedSetting((IBindable)property.GetValue(resultMod), settingValue);
+ }
+ catch (Exception ex)
+ {
+ Logger.Log($"Failed to copy mod setting value '{settingValue ?? "null"}' to \"{property.Name}\": {ex.Message}");
+ }
}
}
diff --git a/osu.Game/Online/API/Requests/GetCommentsRequest.cs b/osu.Game/Online/API/Requests/GetCommentsRequest.cs
index c63c574124..1aa08f2ed8 100644
--- a/osu.Game/Online/API/Requests/GetCommentsRequest.cs
+++ b/osu.Game/Online/API/Requests/GetCommentsRequest.cs
@@ -4,7 +4,7 @@
#nullable disable
using osu.Framework.IO.Network;
-using Humanizer;
+using osu.Game.Extensions;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.Comments;
@@ -32,7 +32,7 @@ namespace osu.Game.Online.API.Requests
var req = base.CreateWebRequest();
req.AddParameter("commentable_id", commentableId.ToString());
- req.AddParameter("commentable_type", type.ToString().Underscore().ToLowerInvariant());
+ req.AddParameter("commentable_type", type.ToString().ToSnakeCase().ToLowerInvariant());
req.AddParameter("page", page.ToString());
req.AddParameter("sort", sort.ToString().ToLowerInvariant());
diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs
index a6cd9a52c7..966e69938c 100644
--- a/osu.Game/Online/API/Requests/GetScoresRequest.cs
+++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs
@@ -35,7 +35,7 @@ namespace osu.Game.Online.API.Requests
this.mods = mods ?? Array.Empty();
}
- protected override string Target => $@"beatmaps/{beatmapInfo.OnlineID}/scores{createQueryParameters()}";
+ protected override string Target => $@"beatmaps/{beatmapInfo.OnlineID}/solo-scores{createQueryParameters()}";
private string createQueryParameters()
{
diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs
index 3ec60cd06c..d723786f23 100644
--- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs
+++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs
@@ -3,8 +3,8 @@
#nullable disable
-using Humanizer;
using System.Collections.Generic;
+using osu.Game.Extensions;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests
@@ -22,7 +22,7 @@ namespace osu.Game.Online.API.Requests
this.type = type;
}
- protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().Underscore()}";
+ protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().ToSnakeCase()}";
}
public enum BeatmapSetType
diff --git a/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs b/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs
index ab0cc3a56d..c27a83b695 100644
--- a/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs
+++ b/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs
@@ -5,6 +5,7 @@
using osu.Framework.IO.Network;
using osu.Game.Rulesets;
+using osu.Game.Users;
namespace osu.Game.Online.API.Requests
{
@@ -12,21 +13,21 @@ namespace osu.Game.Online.API.Requests
{
public readonly UserRankingsType Type;
- private readonly string country;
+ private readonly CountryCode countryCode;
- public GetUserRankingsRequest(RulesetInfo ruleset, UserRankingsType type = UserRankingsType.Performance, int page = 1, string country = null)
+ public GetUserRankingsRequest(RulesetInfo ruleset, UserRankingsType type = UserRankingsType.Performance, int page = 1, CountryCode countryCode = CountryCode.Unknown)
: base(ruleset, page)
{
Type = type;
- this.country = country;
+ this.countryCode = countryCode;
}
protected override WebRequest CreateWebRequest()
{
var req = base.CreateWebRequest();
- if (country != null)
- req.AddParameter("country", country);
+ if (countryCode != CountryCode.Unknown)
+ req.AddParameter("country", countryCode.ToString());
return req;
}
diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs
index 9bd78b7be1..8ef797f799 100644
--- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs
+++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs
@@ -10,7 +10,7 @@ using osu.Game.Rulesets;
namespace osu.Game.Online.API.Requests
{
- public class GetUserScoresRequest : PaginatedAPIRequest>
+ public class GetUserScoresRequest : PaginatedAPIRequest>
{
private readonly long userId;
private readonly ScoreType type;
diff --git a/osu.Game/Online/API/Requests/Responses/APIRecentActivity.cs b/osu.Game/Online/API/Requests/Responses/APIRecentActivity.cs
index 8fefe4d9c2..2def18926f 100644
--- a/osu.Game/Online/API/Requests/Responses/APIRecentActivity.cs
+++ b/osu.Game/Online/API/Requests/Responses/APIRecentActivity.cs
@@ -4,8 +4,8 @@
#nullable disable
using System;
-using Humanizer;
using Newtonsoft.Json;
+using osu.Game.Extensions;
using osu.Game.Scoring;
namespace osu.Game.Online.API.Requests.Responses
@@ -21,7 +21,7 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty]
private string type
{
- set => Type = (RecentActivityType)Enum.Parse(typeof(RecentActivityType), value.Pascalize());
+ set => Type = (RecentActivityType)Enum.Parse(typeof(RecentActivityType), value.ToPascalCase());
}
public RecentActivityType Type;
diff --git a/osu.Game/Online/API/Requests/Responses/APIScoresCollection.cs b/osu.Game/Online/API/Requests/Responses/APIScoresCollection.cs
index 38c67d92f4..4ef39be5e5 100644
--- a/osu.Game/Online/API/Requests/Responses/APIScoresCollection.cs
+++ b/osu.Game/Online/API/Requests/Responses/APIScoresCollection.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"scores")]
public List Scores;
- [JsonProperty(@"userScore")]
+ [JsonProperty(@"user_score")]
public APIScoreWithPosition UserScore;
}
}
diff --git a/osu.Game/Online/API/Requests/Responses/APIUser.cs b/osu.Game/Online/API/Requests/Responses/APIUser.cs
index 63aaa9b90e..5f843e9a7b 100644
--- a/osu.Game/Online/API/Requests/Responses/APIUser.cs
+++ b/osu.Game/Online/API/Requests/Responses/APIUser.cs
@@ -34,8 +34,19 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"previous_usernames")]
public string[] PreviousUsernames;
+ private CountryCode? countryCode;
+
+ public CountryCode CountryCode
+ {
+ get => countryCode ??= (Enum.TryParse(country?.Code, out CountryCode result) ? result : default);
+ set => countryCode = value;
+ }
+
+#pragma warning disable 649
+ [CanBeNull]
[JsonProperty(@"country")]
- public Country Country;
+ private Country country;
+#pragma warning restore 649
public readonly Bindable Status = new Bindable();
@@ -256,5 +267,13 @@ namespace osu.Game.Online.API.Requests.Responses
public int OnlineID => Id;
public bool Equals(APIUser other) => this.MatchesOnlineID(other);
+
+#pragma warning disable 649
+ private class Country
+ {
+ [JsonProperty(@"code")]
+ public string Code;
+ }
+#pragma warning restore 649
}
}
diff --git a/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs
index b70da194a5..bfc8b4102a 100644
--- a/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs
+++ b/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs
@@ -54,7 +54,7 @@ namespace osu.Game.Online.API.Requests.Responses
public DateTimeOffset? StartedAt { get; set; }
[JsonProperty("ended_at")]
- public DateTimeOffset? EndedAt { get; set; }
+ public DateTimeOffset EndedAt { get; set; }
[JsonProperty("mods")]
public APIMod[] Mods { get; set; } = Array.Empty();
@@ -82,6 +82,23 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty("user")]
public APIUser? User { get; set; }
+ [JsonProperty("beatmap")]
+ public APIBeatmap? Beatmap { get; set; }
+
+ [JsonProperty("beatmapset")]
+ public APIBeatmapSet? BeatmapSet
+ {
+ set
+ {
+ // in the deserialisation case we need to ferry this data across.
+ // the order of properties returned by the API guarantees that the beatmap is populated by this point.
+ if (!(Beatmap is APIBeatmap apiBeatmap))
+ throw new InvalidOperationException("Beatmap set metadata arrived before beatmap metadata in response");
+
+ apiBeatmap.BeatmapSet = value;
+ }
+ }
+
[JsonProperty("pp")]
public double? PP { get; set; }
@@ -101,10 +118,7 @@ namespace osu.Game.Online.API.Requests.Responses
var rulesetInstance = ruleset.CreateInstance();
- var mods = Mods.Select(apiMod => rulesetInstance.CreateModFromAcronym(apiMod.Acronym)).Where(m => m != null).ToArray();
-
- // all API scores provided by this class are considered to be legacy.
- mods = mods.Append(rulesetInstance.CreateMod()).ToArray();
+ var mods = Mods.Select(apiMod => apiMod.ToMod(rulesetInstance)).ToArray();
var scoreInfo = ToScoreInfo(mods);
@@ -131,9 +145,8 @@ namespace osu.Game.Online.API.Requests.Responses
MaxCombo = MaxCombo,
Rank = Rank,
Statistics = Statistics,
- Date = EndedAt ?? DateTimeOffset.Now,
- Hash = "online", // TODO: temporary?
- HasReplay = HasReplay,
+ Date = EndedAt,
+ Hash = HasReplay ? "online" : string.Empty, // TODO: temporary?
Mods = mods,
PP = PP,
};
diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs
index 082f9bb371..c303c410ec 100644
--- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs
+++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs
@@ -5,7 +5,6 @@
using System.Collections.Generic;
using System.Linq;
-using Humanizer;
using JetBrains.Annotations;
using osu.Framework.IO.Network;
using osu.Game.Extensions;
@@ -86,7 +85,7 @@ namespace osu.Game.Online.API.Requests
req.AddParameter("q", query);
if (General != null && General.Any())
- req.AddParameter("c", string.Join('.', General.Select(e => e.ToString().Underscore())));
+ req.AddParameter("c", string.Join('.', General.Select(e => e.ToString().ToSnakeCase())));
if (ruleset.OnlineID >= 0)
req.AddParameter("m", ruleset.OnlineID.ToString());
diff --git a/osu.Game/Online/DownloadState.cs b/osu.Game/Online/DownloadState.cs
index 3d389d45f9..a58c40d16a 100644
--- a/osu.Game/Online/DownloadState.cs
+++ b/osu.Game/Online/DownloadState.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
namespace osu.Game.Online
{
public enum DownloadState
diff --git a/osu.Game/Online/HubClientConnector.cs b/osu.Game/Online/HubClientConnector.cs
index 01f0f3a902..6bfe09e911 100644
--- a/osu.Game/Online/HubClientConnector.cs
+++ b/osu.Game/Online/HubClientConnector.cs
@@ -64,26 +64,26 @@ namespace osu.Game.Online
this.preferMessagePack = preferMessagePack;
apiState.BindTo(api.State);
- apiState.BindValueChanged(_ => connectIfPossible(), true);
+ apiState.BindValueChanged(_ => Task.Run(connectIfPossible), true);
}
- public void Reconnect()
+ public Task Reconnect()
{
Logger.Log($"{clientName} reconnecting...", LoggingTarget.Network);
- Task.Run(connectIfPossible);
+ return Task.Run(connectIfPossible);
}
- private void connectIfPossible()
+ private async Task connectIfPossible()
{
switch (apiState.Value)
{
case APIState.Failing:
case APIState.Offline:
- Task.Run(() => disconnect(true));
+ await disconnect(true);
break;
case APIState.Online:
- Task.Run(connect);
+ await connect();
break;
}
}
diff --git a/osu.Game/Online/IHubClientConnector.cs b/osu.Game/Online/IHubClientConnector.cs
index 2afab9091b..53c4897e73 100644
--- a/osu.Game/Online/IHubClientConnector.cs
+++ b/osu.Game/Online/IHubClientConnector.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
using osu.Framework.Bindables;
using osu.Game.Online.API;
@@ -32,6 +33,6 @@ namespace osu.Game.Online
///
/// Reconnect if already connected.
///
- void Reconnect();
+ Task Reconnect();
}
}
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index 62827f50aa..a7b6bd044d 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
@@ -181,7 +181,7 @@ namespace osu.Game.Online.Leaderboards
Masking = true,
Children = new Drawable[]
{
- new UpdateableFlag(user.Country)
+ new UpdateableFlag(user.CountryCode)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
index 9832acb140..603bd10c38 100644
--- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs
@@ -21,7 +21,6 @@ using osu.Game.Online.Rooms.RoomStatuses;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Utils;
-using APIUser = osu.Game.Online.API.Requests.Responses.APIUser;
namespace osu.Game.Online.Multiplayer
{
@@ -91,7 +90,7 @@ namespace osu.Game.Online.Multiplayer
///
/// The joined .
///
- public virtual MultiplayerRoom? Room
+ public virtual MultiplayerRoom? Room // virtual for moq
{
get
{
@@ -150,7 +149,7 @@ namespace osu.Game.Online.Multiplayer
// clean up local room state on server disconnect.
if (!connected.NewValue && Room != null)
{
- Logger.Log("Connection to multiplayer server was lost.", LoggingTarget.Runtime, LogLevel.Important);
+ Logger.Log("Clearing room due to multiplayer server connection loss.", LoggingTarget.Runtime, LogLevel.Important);
LeaveRoom();
}
}));
diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
index c061398209..190d150502 100644
--- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
+++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs
@@ -85,7 +85,13 @@ namespace osu.Game.Online.Multiplayer
catch (HubException exception)
{
if (exception.GetHubExceptionMessage() == HubClientConnector.SERVER_SHUTDOWN_MESSAGE)
- connector?.Reconnect();
+ {
+ Debug.Assert(connector != null);
+
+ await connector.Reconnect();
+ return await JoinRoom(roomId, password);
+ }
+
throw;
}
}
diff --git a/osu.Game/Online/Rooms/GetRoomsRequest.cs b/osu.Game/Online/Rooms/GetRoomsRequest.cs
index 5ae9d58189..afab83b5be 100644
--- a/osu.Game/Online/Rooms/GetRoomsRequest.cs
+++ b/osu.Game/Online/Rooms/GetRoomsRequest.cs
@@ -4,8 +4,8 @@
#nullable disable
using System.Collections.Generic;
-using Humanizer;
using osu.Framework.IO.Network;
+using osu.Game.Extensions;
using osu.Game.Online.API;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
@@ -27,7 +27,7 @@ namespace osu.Game.Online.Rooms
var req = base.CreateWebRequest();
if (status != RoomStatusFilter.Open)
- req.AddParameter("mode", status.ToString().Underscore().ToLowerInvariant());
+ req.AddParameter("mode", status.ToString().ToSnakeCase().ToLowerInvariant());
if (!string.IsNullOrEmpty(category))
req.AddParameter("category", category);
diff --git a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs
index cd9f5233a2..030ca724c4 100644
--- a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs
+++ b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs
@@ -56,13 +56,20 @@ namespace osu.Game.Online.Spectator
try
{
- await connection.SendAsync(nameof(ISpectatorServer.BeginPlaySession), state);
+ await connection.InvokeAsync(nameof(ISpectatorServer.BeginPlaySession), state);
}
catch (HubException exception)
{
if (exception.GetHubExceptionMessage() == HubClientConnector.SERVER_SHUTDOWN_MESSAGE)
- connector?.Reconnect();
- throw;
+ {
+ Debug.Assert(connector != null);
+
+ await connector.Reconnect();
+ await BeginPlayingInternal(state);
+ }
+
+ // Exceptions can occur if, for instance, the locally played beatmap doesn't have a server-side counterpart.
+ // For now, let's ignore these so they don't cause unobserved exceptions to appear to the user (and sentry).
}
}
@@ -73,7 +80,7 @@ namespace osu.Game.Online.Spectator
Debug.Assert(connection != null);
- return connection.SendAsync(nameof(ISpectatorServer.SendFrameData), bundle);
+ return connection.InvokeAsync(nameof(ISpectatorServer.SendFrameData), bundle);
}
protected override Task EndPlayingInternal(SpectatorState state)
@@ -83,7 +90,7 @@ namespace osu.Game.Online.Spectator
Debug.Assert(connection != null);
- return connection.SendAsync(nameof(ISpectatorServer.EndPlaySession), state);
+ return connection.InvokeAsync(nameof(ISpectatorServer.EndPlaySession), state);
}
protected override Task WatchUserInternal(int userId)
@@ -93,7 +100,7 @@ namespace osu.Game.Online.Spectator
Debug.Assert(connection != null);
- return connection.SendAsync(nameof(ISpectatorServer.StartWatchingUser), userId);
+ return connection.InvokeAsync(nameof(ISpectatorServer.StartWatchingUser), userId);
}
protected override Task StopWatchingUserInternal(int userId)
@@ -103,7 +110,7 @@ namespace osu.Game.Online.Spectator
Debug.Assert(connection != null);
- return connection.SendAsync(nameof(ISpectatorServer.EndWatchingUser), userId);
+ return connection.InvokeAsync(nameof(ISpectatorServer.EndWatchingUser), userId);
}
}
}
diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs
index 767b8646e3..2ca369d459 100644
--- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs
+++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs
@@ -143,7 +143,7 @@ namespace osu.Game.Overlays.BeatmapListing
}
public void Search(string query)
- => searchControl.Query.Value = query;
+ => Schedule(() => searchControl.Query.Value = query);
protected override void LoadComplete()
{
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs
index b2e5be6601..6acc9bf002 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs
@@ -165,10 +165,10 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Font = OsuFont.GetFont(size: text_size),
Colour = score.Accuracy == 1 ? highAccuracyColour : Color4.White
},
- new UpdateableFlag(score.User.Country)
+ new UpdateableFlag(score.User.CountryCode)
{
Size = new Vector2(19, 14),
- ShowPlaceholderOnNull = false,
+ ShowPlaceholderOnUnknown = false,
},
username,
new OsuSpriteText
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs
index f093c6b53f..2eaa03a05d 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs
@@ -120,7 +120,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Origin = Anchor.CentreLeft,
Size = new Vector2(19, 14),
Margin = new MarginPadding { Top = 3 }, // makes spacing look more even
- ShowPlaceholderOnNull = false,
+ ShowPlaceholderOnUnknown = false,
},
}
}
@@ -141,7 +141,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
set
{
avatar.User = value.User;
- flag.Country = value.User.Country;
+ flag.CountryCode = value.User.CountryCode;
achievedOn.Date = value.Date;
usernameText.Clear();
diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs
index 5961298485..4c425d3d4c 100644
--- a/osu.Game/Overlays/Chat/ChatLine.cs
+++ b/osu.Game/Overlays/Chat/ChatLine.cs
@@ -257,10 +257,14 @@ namespace osu.Game.Overlays.Chat
}
[BackgroundDependencyLoader]
- private void load(UserProfileOverlay? profile, ChannelManager? chatManager)
+ private void load(UserProfileOverlay? profile, ChannelManager? chatManager, ChatOverlay? chatOverlay)
{
Action = () => profile?.ShowUser(sender);
- startChatAction = () => chatManager?.OpenPrivateChannel(sender);
+ startChatAction = () =>
+ {
+ chatManager?.OpenPrivateChannel(sender);
+ chatOverlay?.Show();
+ };
}
public MenuItem[] ContextMenuItems
diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs
index 9c957e387a..7e079c8341 100644
--- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs
+++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs
@@ -5,6 +5,7 @@
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
@@ -136,7 +137,7 @@ namespace osu.Game.Overlays.Profile.Header
userFlag = new UpdateableFlag
{
Size = new Vector2(28, 20),
- ShowPlaceholderOnNull = false,
+ ShowPlaceholderOnUnknown = false,
},
userCountryText = new OsuSpriteText
{
@@ -174,8 +175,8 @@ namespace osu.Game.Overlays.Profile.Header
avatar.User = user;
usernameText.Text = user?.Username ?? string.Empty;
openUserExternally.Link = $@"{api.WebsiteRootUrl}/users/{user?.Id ?? 0}";
- userFlag.Country = user?.Country;
- userCountryText.Text = user?.Country?.FullName ?? "Alien";
+ userFlag.CountryCode = user?.CountryCode ?? default;
+ userCountryText.Text = (user?.CountryCode ?? default).GetDescription();
supporterTag.SupportLevel = user?.SupportLevel ?? 0;
titleText.Text = user?.Title ?? string.Empty;
titleText.Colour = Color4Extensions.FromHex(user?.Colour ?? "fff");
diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs
index 90a357a281..5d8f8c8326 100644
--- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs
+++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs
@@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
private const float performance_background_shear = 0.45f;
- protected readonly APIScore Score;
+ protected readonly SoloScoreInfo Score;
[Resolved]
private OsuColour colours { get; set; }
@@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
[Resolved]
private OverlayColourProvider colourProvider { get; set; }
- public DrawableProfileScore(APIScore score)
+ public DrawableProfileScore(SoloScoreInfo score)
{
Score = score;
@@ -98,7 +98,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular),
Colour = colours.Yellow
},
- new DrawableDate(Score.Date, 12)
+ new DrawableDate(Score.EndedAt, 12)
{
Colour = colourProvider.Foreground1
}
@@ -138,7 +138,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
{
var ruleset = rulesets.GetRuleset(Score.RulesetID) ?? throw new InvalidOperationException($"Ruleset with ID of {Score.RulesetID} not found locally");
- return new ModIcon(ruleset.CreateInstance().CreateModFromAcronym(mod.Acronym))
+ return new ModIcon(mod.ToMod(ruleset.CreateInstance()))
{
Scale = new Vector2(0.35f)
};
diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs
index f8f83883fc..94d95dc27e 100644
--- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs
+++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
{
private readonly double weight;
- public DrawableProfileWeightedScore(APIScore score, double weight)
+ public DrawableProfileWeightedScore(SoloScoreInfo score, double weight)
: base(score)
{
this.weight = weight;
diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs
index 15c7b8f042..2564692c87 100644
--- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs
+++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs
@@ -17,7 +17,7 @@ using APIUser = osu.Game.Online.API.Requests.Responses.APIUser;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
- public class PaginatedScoreContainer : PaginatedProfileSubsection
+ public class PaginatedScoreContainer : PaginatedProfileSubsection
{
private readonly ScoreType type;
@@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
}
}
- protected override void OnItemsReceived(List items)
+ protected override void OnItemsReceived(List items)
{
if (CurrentPage == null || CurrentPage?.Offset == 0)
drawableItemIndex = 0;
@@ -62,12 +62,12 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
base.OnItemsReceived(items);
}
- protected override APIRequest> CreateRequest(PaginationParameters pagination) =>
+ protected override APIRequest> CreateRequest(PaginationParameters pagination) =>
new GetUserScoresRequest(User.Value.Id, type, pagination);
private int drawableItemIndex;
- protected override Drawable CreateDrawableItem(APIScore model)
+ protected override Drawable CreateDrawableItem(SoloScoreInfo model)
{
switch (type)
{
diff --git a/osu.Game/Overlays/Rankings/CountryFilter.cs b/osu.Game/Overlays/Rankings/CountryFilter.cs
index 9ba2018522..9376bafdff 100644
--- a/osu.Game/Overlays/Rankings/CountryFilter.cs
+++ b/osu.Game/Overlays/Rankings/CountryFilter.cs
@@ -16,14 +16,14 @@ using osuTK;
namespace osu.Game.Overlays.Rankings
{
- public class CountryFilter : CompositeDrawable, IHasCurrentValue
+ public class CountryFilter : CompositeDrawable, IHasCurrentValue
{
private const int duration = 200;
private const int height = 70;
- private readonly BindableWithCurrent current = new BindableWithCurrent();
+ private readonly BindableWithCurrent current = new BindableWithCurrent();
- public Bindable Current
+ public Bindable Current
{
get => current.Current;
set => current.Current = value;
@@ -89,9 +89,9 @@ namespace osu.Game.Overlays.Rankings
Current.BindValueChanged(onCountryChanged, true);
}
- private void onCountryChanged(ValueChangedEvent country)
+ private void onCountryChanged(ValueChangedEvent country)
{
- if (country.NewValue == null)
+ if (Current.Value == CountryCode.Unknown)
{
countryPill.Collapse();
this.ResizeHeightTo(0, duration, Easing.OutQuint);
diff --git a/osu.Game/Overlays/Rankings/CountryPill.cs b/osu.Game/Overlays/Rankings/CountryPill.cs
index 90f8c85557..ee4c4f08af 100644
--- a/osu.Game/Overlays/Rankings/CountryPill.cs
+++ b/osu.Game/Overlays/Rankings/CountryPill.cs
@@ -6,6 +6,7 @@
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -21,13 +22,13 @@ using osuTK.Graphics;
namespace osu.Game.Overlays.Rankings
{
- public class CountryPill : CompositeDrawable, IHasCurrentValue
+ public class CountryPill : CompositeDrawable, IHasCurrentValue
{
private const int duration = 200;
- private readonly BindableWithCurrent current = new BindableWithCurrent();
+ private readonly BindableWithCurrent current = new BindableWithCurrent();
- public Bindable Current
+ public Bindable Current
{
get => current.Current;
set => current.Current = value;
@@ -93,7 +94,7 @@ namespace osu.Game.Overlays.Rankings
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Action = () => Current.Value = null
+ Action = Current.SetDefault,
}
}
}
@@ -130,13 +131,13 @@ namespace osu.Game.Overlays.Rankings
this.FadeOut(duration, Easing.OutQuint);
}
- private void onCountryChanged(ValueChangedEvent country)
+ private void onCountryChanged(ValueChangedEvent country)
{
- if (country.NewValue == null)
+ if (Current.Value == CountryCode.Unknown)
return;
- flag.Country = country.NewValue;
- countryName.Text = country.NewValue.FullName;
+ flag.CountryCode = country.NewValue;
+ countryName.Text = country.NewValue.GetDescription();
}
private class CloseButton : OsuHoverContainer
diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs
index 9b0b75f663..936545bd49 100644
--- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs
+++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Rankings
{
public Bindable Ruleset => rulesetSelector.Current;
- public Bindable Country => countryFilter.Current;
+ public Bindable Country => countryFilter.Current;
private OverlayRulesetSelector rulesetSelector;
private CountryFilter countryFilter;
diff --git a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs
index 53d10b3e53..a6f0c7a123 100644
--- a/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs
+++ b/osu.Game/Overlays/Rankings/Tables/CountriesTable.cs
@@ -11,6 +11,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using System.Collections.Generic;
using osu.Framework.Allocation;
+using osu.Framework.Extensions;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Game.Resources.Localisation.Web;
@@ -33,9 +34,9 @@ namespace osu.Game.Overlays.Rankings.Tables
new RankingsTableColumn(RankingsStrings.StatAveragePerformance, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
};
- protected override Country GetCountry(CountryStatistics item) => item.Country;
+ protected override CountryCode GetCountryCode(CountryStatistics item) => item.Code;
- protected override Drawable CreateFlagContent(CountryStatistics item) => new CountryName(item.Country);
+ protected override Drawable CreateFlagContent(CountryStatistics item) => new CountryName(item.Code);
protected override Drawable[] CreateAdditionalContent(CountryStatistics item) => new Drawable[]
{
@@ -70,15 +71,15 @@ namespace osu.Game.Overlays.Rankings.Tables
[Resolved(canBeNull: true)]
private RankingsOverlay rankings { get; set; }
- public CountryName(Country country)
+ public CountryName(CountryCode countryCode)
: base(t => t.Font = OsuFont.GetFont(size: 12))
{
AutoSizeAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
TextAnchor = Anchor.CentreLeft;
- if (!string.IsNullOrEmpty(country.FullName))
- AddLink(country.FullName, () => rankings?.ShowCountry(country));
+ if (countryCode != CountryCode.Unknown)
+ AddLink(countryCode.GetDescription(), () => rankings?.ShowCountry(countryCode));
}
}
}
diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs
index 56e318637a..073bf86a7a 100644
--- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs
+++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs
@@ -79,7 +79,7 @@ namespace osu.Game.Overlays.Rankings.Tables
protected sealed override Drawable CreateHeader(int index, TableColumn column)
=> (column as RankingsTableColumn)?.CreateHeaderText() ?? new HeaderText(column?.Header ?? default, false);
- protected abstract Country GetCountry(TModel item);
+ protected abstract CountryCode GetCountryCode(TModel item);
protected abstract Drawable CreateFlagContent(TModel item);
@@ -97,10 +97,10 @@ namespace osu.Game.Overlays.Rankings.Tables
Margin = new MarginPadding { Bottom = row_spacing },
Children = new[]
{
- new UpdateableFlag(GetCountry(item))
+ new UpdateableFlag(GetCountryCode(item))
{
Size = new Vector2(28, 20),
- ShowPlaceholderOnNull = false,
+ ShowPlaceholderOnUnknown = false,
},
CreateFlagContent(item)
}
diff --git a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs
index 2edd1b21ba..48185e6083 100644
--- a/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs
+++ b/osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs
@@ -59,7 +59,7 @@ namespace osu.Game.Overlays.Rankings.Tables
.Concat(GradeColumns.Select(grade => new GradeTableColumn(grade, Anchor.Centre, new Dimension(GridSizeMode.AutoSize))))
.ToArray();
- protected sealed override Country GetCountry(UserStatistics item) => item.User.Country;
+ protected sealed override CountryCode GetCountryCode(UserStatistics item) => item.User.CountryCode;
protected sealed override Drawable CreateFlagContent(UserStatistics item)
{
diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs
index a66b8cf2ba..586b883604 100644
--- a/osu.Game/Overlays/RankingsOverlay.cs
+++ b/osu.Game/Overlays/RankingsOverlay.cs
@@ -17,7 +17,7 @@ namespace osu.Game.Overlays
{
public class RankingsOverlay : TabbableOnlineOverlay
{
- protected Bindable Country => Header.Country;
+ protected Bindable Country => Header.Country;
private APIRequest lastRequest;
@@ -44,7 +44,7 @@ namespace osu.Game.Overlays
Country.BindValueChanged(_ =>
{
// if a country is requested, force performance scope.
- if (Country.Value != null)
+ if (!Country.IsDefault)
Header.Current.Value = RankingsScope.Performance;
Scheduler.AddOnce(triggerTabChanged);
@@ -76,7 +76,7 @@ namespace osu.Game.Overlays
{
// country filtering is only valid for performance scope.
if (Header.Current.Value != RankingsScope.Performance)
- Country.Value = null;
+ Country.SetDefault();
Scheduler.AddOnce(triggerTabChanged);
}
@@ -85,9 +85,9 @@ namespace osu.Game.Overlays
protected override RankingsOverlayHeader CreateHeader() => new RankingsOverlayHeader();
- public void ShowCountry(Country requested)
+ public void ShowCountry(CountryCode requested)
{
- if (requested == null)
+ if (requested == default)
return;
Show();
@@ -128,7 +128,7 @@ namespace osu.Game.Overlays
switch (Header.Current.Value)
{
case RankingsScope.Performance:
- return new GetUserRankingsRequest(ruleset.Value, country: Country.Value?.FlagName);
+ return new GetUserRankingsRequest(ruleset.Value, countryCode: Country.Value);
case RankingsScope.Country:
return new GetCountryRankingsRequest(ruleset.Value);
diff --git a/osu.Game/Overlays/Settings/Sections/RulesetSection.cs b/osu.Game/Overlays/Settings/Sections/RulesetSection.cs
index 2368459b4e..a5f5810214 100644
--- a/osu.Game/Overlays/Settings/Sections/RulesetSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/RulesetSection.cs
@@ -1,17 +1,14 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
-using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Framework.Logging;
-using osu.Game.Rulesets;
using osu.Game.Localisation;
+using osu.Game.Rulesets;
namespace osu.Game.Overlays.Settings.Sections
{
@@ -36,9 +33,9 @@ namespace osu.Game.Overlays.Settings.Sections
if (section != null)
Add(section);
}
- catch (Exception e)
+ catch
{
- Logger.Error(e, "Failed to load ruleset settings");
+ Logger.Log($"Failed to load ruleset settings for {ruleset.RulesetInfo.Name}. Please check for an update from the developer.", level: LogLevel.Error);
}
}
}
diff --git a/osu.Game/Rulesets/Mods/ICreateReplayData.cs b/osu.Game/Rulesets/Mods/ICreateReplayData.cs
index d4587b673c..c13e65c7b8 100644
--- a/osu.Game/Rulesets/Mods/ICreateReplayData.cs
+++ b/osu.Game/Rulesets/Mods/ICreateReplayData.cs
@@ -56,6 +56,7 @@ namespace osu.Game.Rulesets.Mods
public class ModCreatedUser : IUser
{
public int OnlineID => APIUser.SYSTEM_USER_ID;
+ public CountryCode CountryCode => default;
public bool IsBot => true;
public string Username { get; set; } = string.Empty;
diff --git a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs
index 54ee4554b1..38d26ed05a 100644
--- a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs
+++ b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs
@@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Mods
public override bool ValidForMultiplayer => false;
public override bool ValidForMultiplayerAsFreeMod => false;
- public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust), typeof(ModTimeRamp) };
+ public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust), typeof(ModTimeRamp), typeof(ModAutoplay) };
[SettingSource("Initial rate", "The starting speed of the track")]
public BindableNumber InitialRate { get; } = new BindableDouble
diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs
index 0ebe11b393..ab49dd5575 100644
--- a/osu.Game/Rulesets/Mods/ModAutoplay.cs
+++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mods
public override bool ValidForMultiplayer => false;
public override bool ValidForMultiplayerAsFreeMod => false;
- public override Type[] IncompatibleMods => new[] { typeof(ModCinema), typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail) };
+ public override Type[] IncompatibleMods => new[] { typeof(ModCinema), typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail), typeof(ModAdaptiveSpeed) };
public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0;
diff --git a/osu.Game/Rulesets/Objects/HitObjectProperty.cs b/osu.Game/Rulesets/Objects/HitObjectProperty.cs
new file mode 100644
index 0000000000..f1df83f80c
--- /dev/null
+++ b/osu.Game/Rulesets/Objects/HitObjectProperty.cs
@@ -0,0 +1,52 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+#nullable disable
+
+using JetBrains.Annotations;
+using osu.Framework.Bindables;
+
+namespace osu.Game.Rulesets.Objects
+{
+ ///
+ /// Represents a wrapper containing a lazily-initialised , backed by a temporary field used for storage until initialisation.
+ ///
+ public struct HitObjectProperty
+ {
+ [CanBeNull]
+ private Bindable backingBindable;
+
+ ///
+ /// A temporary field to store the current value to, prior to 's initialisation.
+ ///
+ private T backingValue;
+
+ ///
+ /// The underlying , only initialised on first access.
+ ///
+ public Bindable Bindable => backingBindable ??= new Bindable(defaultValue) { Value = backingValue };
+
+ ///
+ /// The current value, derived from and delegated to if initialised, or a temporary field otherwise.
+ ///
+ public T Value
+ {
+ get => backingBindable != null ? backingBindable.Value : backingValue;
+ set
+ {
+ if (backingBindable != null)
+ backingBindable.Value = value;
+ else
+ backingValue = value;
+ }
+ }
+
+ private readonly T defaultValue;
+
+ public HitObjectProperty(T value = default)
+ {
+ backingValue = defaultValue = value;
+ backingBindable = null;
+ }
+ }
+}
diff --git a/osu.Game/Scoring/ScoreImporter.cs b/osu.Game/Scoring/ScoreImporter.cs
index 53dd511d57..4107c66dfe 100644
--- a/osu.Game/Scoring/ScoreImporter.cs
+++ b/osu.Game/Scoring/ScoreImporter.cs
@@ -84,7 +84,7 @@ namespace osu.Game.Scoring
api.Perform(userRequest);
if (userRequest.Response is APIUser user)
- model.RealmUser.OnlineID = user.Id;
+ model.User = user;
}
}
}
diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs
index cbe9615380..d32d611a27 100644
--- a/osu.Game/Scoring/ScoreInfo.cs
+++ b/osu.Game/Scoring/ScoreInfo.cs
@@ -45,7 +45,7 @@ namespace osu.Game.Scoring
public double Accuracy { get; set; }
- public bool HasReplay { get; set; }
+ public bool HasReplay => !string.IsNullOrEmpty(Hash);
public DateTimeOffset Date { get; set; }
@@ -85,8 +85,9 @@ namespace osu.Game.Scoring
{
get => user ??= new APIUser
{
- Username = RealmUser.Username,
Id = RealmUser.OnlineID,
+ Username = RealmUser.Username,
+ CountryCode = RealmUser.CountryCode,
};
set
{
@@ -95,7 +96,8 @@ namespace osu.Game.Scoring
RealmUser = new RealmUser
{
OnlineID = user.OnlineID,
- Username = user.Username
+ Username = user.Username,
+ CountryCode = user.CountryCode,
};
}
}
@@ -135,6 +137,7 @@ namespace osu.Game.Scoring
{
OnlineID = RealmUser.OnlineID,
Username = RealmUser.Username,
+ CountryCode = RealmUser.CountryCode,
};
return clone;
diff --git a/osu.Game/Scoring/ScoreRank.cs b/osu.Game/Scoring/ScoreRank.cs
index 9de8561b4a..a1916953c4 100644
--- a/osu.Game/Scoring/ScoreRank.cs
+++ b/osu.Game/Scoring/ScoreRank.cs
@@ -11,6 +11,10 @@ namespace osu.Game.Scoring
{
public enum ScoreRank
{
+ // TODO: Localisable?
+ [Description(@"F")]
+ F = -1,
+
[LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.RankD))]
[Description(@"D")]
D,
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
index 4eb16a854b..4bbeb39063 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs
@@ -70,12 +70,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
client.LoadRequested += onLoadRequested;
client.RoomUpdated += onRoomUpdated;
- isConnected.BindTo(client.IsConnected);
- isConnected.BindValueChanged(connected =>
- {
- if (!connected.NewValue)
- handleRoomLost();
- }, true);
+ if (!client.IsConnected.Value)
+ handleRoomLost();
}
protected override Drawable CreateMainContent() => new Container
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs
index f632951f41..3ef2f033b0 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs
@@ -1,8 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
+using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
@@ -22,6 +21,7 @@ using osu.Game.Online.API;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play.HUD;
using osu.Game.Users;
using osu.Game.Users.Drawables;
@@ -35,18 +35,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
public readonly MultiplayerRoomUser User;
[Resolved]
- private IAPIProvider api { get; set; }
+ private IAPIProvider api { get; set; } = null!;
[Resolved]
- private IRulesetStore rulesets { get; set; }
+ private IRulesetStore rulesets { get; set; } = null!;
- private SpriteIcon crown;
+ private SpriteIcon crown = null!;
- private OsuSpriteText userRankText;
- private ModDisplay userModsDisplay;
- private StateDisplay userStateDisplay;
+ private OsuSpriteText userRankText = null!;
+ private ModDisplay userModsDisplay = null!;
+ private StateDisplay userStateDisplay = null!;
- private IconButton kickButton;
+ private IconButton kickButton = null!;
public ParticipantPanel(MultiplayerRoomUser user)
{
@@ -128,14 +128,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(28, 20),
- Country = user?.Country
+ CountryCode = user?.CountryCode ?? default
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 18),
- Text = user?.Username
+ Text = user?.Username ?? string.Empty
},
userRankText = new OsuSpriteText
{
@@ -188,7 +188,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
const double fade_time = 50;
var currentItem = Playlist.GetCurrentItem();
- var ruleset = currentItem != null ? rulesets.GetRuleset(currentItem.RulesetID)?.CreateInstance() : null;
+ Ruleset? ruleset = currentItem != null ? rulesets.GetRuleset(currentItem.RulesetID)?.CreateInstance() : null;
int? currentModeRank = ruleset != null ? User.User?.RulesetsStatistics?.GetValueOrDefault(ruleset.ShortName)?.GlobalRank : null;
userRankText.Text = currentModeRank != null ? $"#{currentModeRank.Value:N0}" : string.Empty;
@@ -205,10 +205,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
// If the mods are updated at the end of the frame, the flow container will skip a reflow cycle: https://github.com/ppy/osu-framework/issues/4187
// This looks particularly jarring here, so re-schedule the update to that start of our frame as a fix.
- Schedule(() => userModsDisplay.Current.Value = User.Mods.Select(m => m.ToMod(ruleset)).ToList());
+ Schedule(() =>
+ {
+ userModsDisplay.Current.Value = ruleset != null ? User.Mods.Select(m => m.ToMod(ruleset)).ToList() : Array.Empty();
+ });
}
- public MenuItem[] ContextMenuItems
+ public MenuItem[]? ContextMenuItems
{
get
{
diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs
index 1a31b0f462..2af5102369 100644
--- a/osu.Game/Screens/Play/FailOverlay.cs
+++ b/osu.Game/Screens/Play/FailOverlay.cs
@@ -3,14 +3,25 @@
#nullable disable
+using System;
+using System.Threading.Tasks;
+using osu.Game.Scoring;
using osu.Game.Graphics;
+using osu.Game.Graphics.UserInterface;
+using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
namespace osu.Game.Screens.Play
{
public class FailOverlay : GameplayMenuOverlay
{
+ public Func> SaveReplay;
+
public override string Header => "failed";
public override string Description => "you're dead, try again?";
@@ -19,6 +30,39 @@ namespace osu.Game.Screens.Play
{
AddButton("Retry", colours.YellowDark, () => OnRetry?.Invoke());
AddButton("Quit", new Color4(170, 27, 39, 255), () => OnQuit?.Invoke());
+ // from #10339 maybe this is a better visual effect
+ Add(new Container
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ RelativeSizeAxes = Axes.X,
+ Height = TwoLayerButton.SIZE_EXTENDED.Y,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4Extensions.FromHex("#333")
+ },
+ new FillFlowContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ AutoSizeAxes = Axes.X,
+ RelativeSizeAxes = Axes.Y,
+ Spacing = new Vector2(5),
+ Padding = new MarginPadding(10),
+ Direction = FillDirection.Horizontal,
+ Children = new Drawable[]
+ {
+ new SaveFailedScoreButton(SaveReplay)
+ {
+ Width = 300
+ },
+ }
+ }
+ }
+ });
}
}
}
diff --git a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs
index f045ab661e..ee29241321 100644
--- a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs
+++ b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs
@@ -6,7 +6,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using Humanizer;
using Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@@ -71,7 +70,7 @@ namespace osu.Game.Screens.Play.HUD
var bindable = (IBindable)property.GetValue(component);
if (!bindable.IsDefault)
- Settings.Add(property.Name.Underscore(), bindable.GetUnderlyingSettingValue());
+ Settings.Add(property.Name.ToSnakeCase(), bindable.GetUnderlyingSettingValue());
}
if (component is Container container)
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index b4016fc1cf..516364e13a 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -267,6 +267,12 @@ namespace osu.Game.Screens.Play
},
FailOverlay = new FailOverlay
{
+ SaveReplay = () =>
+ {
+ Score.ScoreInfo.Passed = false;
+ Score.ScoreInfo.Rank = ScoreRank.F;
+ return prepareAndImportScore();
+ },
OnRetry = Restart,
OnQuit = () => PerformExit(true),
},
@@ -720,7 +726,7 @@ namespace osu.Game.Screens.Play
if (!Configuration.ShowResults)
return;
- prepareScoreForDisplayTask ??= Task.Run(prepareScoreForResults);
+ prepareScoreForDisplayTask ??= Task.Run(prepareAndImportScore);
bool storyboardHasOutro = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value;
@@ -739,7 +745,7 @@ namespace osu.Game.Screens.Play
/// Asynchronously run score preparation operations (database import, online submission etc.).
///
/// The final score.
- private async Task prepareScoreForResults()
+ private async Task prepareAndImportScore()
{
var scoreCopy = Score.DeepClone();
@@ -1024,8 +1030,7 @@ namespace osu.Game.Screens.Play
if (prepareScoreForDisplayTask == null)
{
Score.ScoreInfo.Passed = false;
- // potentially should be ScoreRank.F instead? this is the best alternative for now.
- Score.ScoreInfo.Rank = ScoreRank.D;
+ Score.ScoreInfo.Rank = ScoreRank.F;
}
// EndPlaying() is typically called from ReplayRecorder.Dispose(). Disposal is currently asynchronous.
diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs
index 64b4853a67..e82238945b 100644
--- a/osu.Game/Screens/Play/ReplayPlayer.cs
+++ b/osu.Game/Screens/Play/ReplayPlayer.cs
@@ -22,12 +22,21 @@ namespace osu.Game.Screens.Play
{
private readonly Func, Score> createScore;
+ private readonly bool replayIsFailedScore;
+
// Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108)
- protected override bool CheckModsAllowFailure() => false;
+ protected override bool CheckModsAllowFailure()
+ {
+ if (!replayIsFailedScore)
+ return false;
+
+ return base.CheckModsAllowFailure();
+ }
public ReplayPlayer(Score score, PlayerConfiguration configuration = null)
: this((_, _) => score, configuration)
{
+ replayIsFailedScore = score.ScoreInfo.Rank == ScoreRank.F;
}
public ReplayPlayer(Func, Score> createScore, PlayerConfiguration configuration = null)
diff --git a/osu.Game/Screens/Play/SaveFailedScoreButton.cs b/osu.Game/Screens/Play/SaveFailedScoreButton.cs
new file mode 100644
index 0000000000..3f6e741dff
--- /dev/null
+++ b/osu.Game/Screens/Play/SaveFailedScoreButton.cs
@@ -0,0 +1,92 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Threading.Tasks;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Database;
+using osu.Game.Scoring;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Online;
+using osuTK;
+
+namespace osu.Game.Screens.Play
+{
+ public class SaveFailedScoreButton : CompositeDrawable
+ {
+ private readonly Bindable state = new Bindable();
+
+ private readonly Func> importFailedScore;
+
+ private ScoreInfo? importedScore;
+
+ private DownloadButton button = null!;
+
+ public SaveFailedScoreButton(Func> importFailedScore)
+ {
+ Size = new Vector2(50, 30);
+
+ this.importFailedScore = importFailedScore;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuGame? game, Player? player, RealmAccess realm)
+ {
+ InternalChild = button = new DownloadButton
+ {
+ RelativeSizeAxes = Axes.Both,
+ State = { BindTarget = state },
+ Action = () =>
+ {
+ switch (state.Value)
+ {
+ case DownloadState.LocallyAvailable:
+ game?.PresentScore(importedScore, ScorePresentType.Gameplay);
+ break;
+
+ case DownloadState.NotDownloaded:
+ state.Value = DownloadState.Importing;
+ Task.Run(importFailedScore).ContinueWith(t =>
+ {
+ importedScore = realm.Run(r => r.Find(t.GetResultSafely().ID)?.Detach());
+ Schedule(() => state.Value = importedScore != null ? DownloadState.LocallyAvailable : DownloadState.NotDownloaded);
+ });
+ break;
+ }
+ }
+ };
+
+ if (player != null)
+ {
+ importedScore = realm.Run(r => r.Find(player.Score.ScoreInfo.ID)?.Detach());
+ if (importedScore != null)
+ state.Value = DownloadState.LocallyAvailable;
+ }
+
+ state.BindValueChanged(state =>
+ {
+ switch (state.NewValue)
+ {
+ case DownloadState.LocallyAvailable:
+ button.TooltipText = @"watch replay";
+ button.Enabled.Value = true;
+ break;
+
+ case DownloadState.Importing:
+ button.TooltipText = @"importing score";
+ button.Enabled.Value = false;
+ break;
+
+ default:
+ button.TooltipText = @"save score";
+ button.Enabled.Value = true;
+ break;
+ }
+ }, true);
+ }
+ }
+}
diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs
index 7b8b4ce608..3e67868f34 100644
--- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs
+++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs
@@ -6,6 +6,7 @@
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
@@ -214,7 +215,7 @@ namespace osu.Game.Screens.Ranking.Contracted
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
- Text = key,
+ Text = key.ToTitle(),
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold)
},
new OsuSpriteText
diff --git a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs
index 954a5159b9..7081a0156e 100644
--- a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs
+++ b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs
@@ -33,7 +33,7 @@ namespace osu.Game.Screens.Ranking
if (State.Value == DownloadState.LocallyAvailable)
return ReplayAvailability.Local;
- if (!string.IsNullOrEmpty(Score.Value?.Hash))
+ if (Score.Value?.HasReplay == true)
return ReplayAvailability.Online;
return ReplayAvailability.NotAvailable;
diff --git a/osu.Game/Screens/Ranking/RetryButton.cs b/osu.Game/Screens/Ranking/RetryButton.cs
index a0ddc98943..c56f364ae8 100644
--- a/osu.Game/Screens/Ranking/RetryButton.cs
+++ b/osu.Game/Screens/Ranking/RetryButton.cs
@@ -37,7 +37,7 @@ namespace osu.Game.Screens.Ranking
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(13),
- Icon = FontAwesome.Solid.ArrowCircleLeft,
+ Icon = FontAwesome.Solid.Redo,
},
};
diff --git a/osu.Game/Screens/Select/NoResultsPlaceholder.cs b/osu.Game/Screens/Select/NoResultsPlaceholder.cs
index b8b589ff99..f3c3fb4d87 100644
--- a/osu.Game/Screens/Select/NoResultsPlaceholder.cs
+++ b/osu.Game/Screens/Select/NoResultsPlaceholder.cs
@@ -109,7 +109,7 @@ namespace osu.Game.Screens.Select
textFlow.AddParagraph("No beatmaps found!");
textFlow.AddParagraph(string.Empty);
- textFlow.AddParagraph("Consider using the \"");
+ textFlow.AddParagraph("- Consider running the \"");
textFlow.AddLink(FirstRunSetupOverlayStrings.FirstRunSetupTitle, () => firstRunSetupOverlay?.Show());
textFlow.AddText("\" to download or import some beatmaps!");
}
@@ -141,15 +141,14 @@ namespace osu.Game.Screens.Select
textFlow.AddLink(" enabling ", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, true));
textFlow.AddText("automatic conversion!");
}
-
- if (!string.IsNullOrEmpty(filter?.SearchText))
- {
- textFlow.AddParagraph("- Try ");
- textFlow.AddLink("searching online", LinkAction.SearchBeatmapSet, filter.SearchText);
- textFlow.AddText($" for \"{filter.SearchText}\".");
- }
}
+ if (!string.IsNullOrEmpty(filter?.SearchText))
+ {
+ textFlow.AddParagraph("- Try ");
+ textFlow.AddLink("searching online", LinkAction.SearchBeatmapSet, filter.SearchText);
+ textFlow.AddText($" for \"{filter.SearchText}\".");
+ }
// TODO: add clickable link to reset criteria.
}
}
diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs
index 43ae0a2252..e8414b4c11 100644
--- a/osu.Game/Skinning/LegacyBeatmapSkin.cs
+++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs
@@ -96,7 +96,7 @@ namespace osu.Game.Skinning
new SkinInfo
{
Name = beatmapInfo.ToString(),
- Creator = beatmapInfo.Metadata.Author.Username ?? string.Empty
+ Creator = beatmapInfo.Metadata.Author.Username
};
}
}
diff --git a/osu.Game/Users/Country.cs b/osu.Game/Users/Country.cs
deleted file mode 100644
index b38600fa1b..0000000000
--- a/osu.Game/Users/Country.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-#nullable disable
-
-using System;
-using Newtonsoft.Json;
-
-namespace osu.Game.Users
-{
- public class Country : IEquatable
- {
- ///
- /// The name of this country.
- ///
- [JsonProperty(@"name")]
- public string FullName;
-
- ///
- /// Two-letter flag acronym (ISO 3166 standard)
- ///
- [JsonProperty(@"code")]
- public string FlagName;
-
- public bool Equals(Country other) => FlagName == other?.FlagName;
- }
-}
diff --git a/osu.Game/Users/CountryCode.cs b/osu.Game/Users/CountryCode.cs
new file mode 100644
index 0000000000..775de2bcf5
--- /dev/null
+++ b/osu.Game/Users/CountryCode.cs
@@ -0,0 +1,768 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.ComponentModel;
+using JetBrains.Annotations;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+
+namespace osu.Game.Users
+{
+ [JsonConverter(typeof(StringEnumConverter))]
+ [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
+ public enum CountryCode
+ {
+ [Description("Unknown")]
+ Unknown = 0,
+
+ [Description("Bangladesh")]
+ BD,
+
+ [Description("Belgium")]
+ BE,
+
+ [Description("Burkina Faso")]
+ BF,
+
+ [Description("Bulgaria")]
+ BG,
+
+ [Description("Bosnia and Herzegovina")]
+ BA,
+
+ [Description("Barbados")]
+ BB,
+
+ [Description("Wallis and Futuna")]
+ WF,
+
+ [Description("Saint Barthelemy")]
+ BL,
+
+ [Description("Bermuda")]
+ BM,
+
+ [Description("Brunei")]
+ BN,
+
+ [Description("Bolivia")]
+ BO,
+
+ [Description("Bahrain")]
+ BH,
+
+ [Description("Burundi")]
+ BI,
+
+ [Description("Benin")]
+ BJ,
+
+ [Description("Bhutan")]
+ BT,
+
+ [Description("Jamaica")]
+ JM,
+
+ [Description("Bouvet Island")]
+ BV,
+
+ [Description("Botswana")]
+ BW,
+
+ [Description("Samoa")]
+ WS,
+
+ [Description("Bonaire, Saint Eustatius and Saba")]
+ BQ,
+
+ [Description("Brazil")]
+ BR,
+
+ [Description("Bahamas")]
+ BS,
+
+ [Description("Jersey")]
+ JE,
+
+ [Description("Belarus")]
+ BY,
+
+ [Description("Belize")]
+ BZ,
+
+ [Description("Russia")]
+ RU,
+
+ [Description("Rwanda")]
+ RW,
+
+ [Description("Serbia")]
+ RS,
+
+ [Description("East Timor")]
+ TL,
+
+ [Description("Reunion")]
+ RE,
+
+ [Description("Turkmenistan")]
+ TM,
+
+ [Description("Tajikistan")]
+ TJ,
+
+ [Description("Romania")]
+ RO,
+
+ [Description("Tokelau")]
+ TK,
+
+ [Description("Guinea-Bissau")]
+ GW,
+
+ [Description("Guam")]
+ GU,
+
+ [Description("Guatemala")]
+ GT,
+
+ [Description("South Georgia and the South Sandwich Islands")]
+ GS,
+
+ [Description("Greece")]
+ GR,
+
+ [Description("Equatorial Guinea")]
+ GQ,
+
+ [Description("Guadeloupe")]
+ GP,
+
+ [Description("Japan")]
+ JP,
+
+ [Description("Guyana")]
+ GY,
+
+ [Description("Guernsey")]
+ GG,
+
+ [Description("French Guiana")]
+ GF,
+
+ [Description("Georgia")]
+ GE,
+
+ [Description("Grenada")]
+ GD,
+
+ [Description("United Kingdom")]
+ GB,
+
+ [Description("Gabon")]
+ GA,
+
+ [Description("El Salvador")]
+ SV,
+
+ [Description("Guinea")]
+ GN,
+
+ [Description("Gambia")]
+ GM,
+
+ [Description("Greenland")]
+ GL,
+
+ [Description("Gibraltar")]
+ GI,
+
+ [Description("Ghana")]
+ GH,
+
+ [Description("Oman")]
+ OM,
+
+ [Description("Tunisia")]
+ TN,
+
+ [Description("Jordan")]
+ JO,
+
+ [Description("Croatia")]
+ HR,
+
+ [Description("Haiti")]
+ HT,
+
+ [Description("Hungary")]
+ HU,
+
+ [Description("Hong Kong")]
+ HK,
+
+ [Description("Honduras")]
+ HN,
+
+ [Description("Heard Island and McDonald Islands")]
+ HM,
+
+ [Description("Venezuela")]
+ VE,
+
+ [Description("Puerto Rico")]
+ PR,
+
+ [Description("Palestinian Territory")]
+ PS,
+
+ [Description("Palau")]
+ PW,
+
+ [Description("Portugal")]
+ PT,
+
+ [Description("Svalbard and Jan Mayen")]
+ SJ,
+
+ [Description("Paraguay")]
+ PY,
+
+ [Description("Iraq")]
+ IQ,
+
+ [Description("Panama")]
+ PA,
+
+ [Description("French Polynesia")]
+ PF,
+
+ [Description("Papua New Guinea")]
+ PG,
+
+ [Description("Peru")]
+ PE,
+
+ [Description("Pakistan")]
+ PK,
+
+ [Description("Philippines")]
+ PH,
+
+ [Description("Pitcairn")]
+ PN,
+
+ [Description("Poland")]
+ PL,
+
+ [Description("Saint Pierre and Miquelon")]
+ PM,
+
+ [Description("Zambia")]
+ ZM,
+
+ [Description("Western Sahara")]
+ EH,
+
+ [Description("Estonia")]
+ EE,
+
+ [Description("Egypt")]
+ EG,
+
+ [Description("South Africa")]
+ ZA,
+
+ [Description("Ecuador")]
+ EC,
+
+ [Description("Italy")]
+ IT,
+
+ [Description("Vietnam")]
+ VN,
+
+ [Description("Solomon Islands")]
+ SB,
+
+ [Description("Ethiopia")]
+ ET,
+
+ [Description("Somalia")]
+ SO,
+
+ [Description("Zimbabwe")]
+ ZW,
+
+ [Description("Saudi Arabia")]
+ SA,
+
+ [Description("Spain")]
+ ES,
+
+ [Description("Eritrea")]
+ ER,
+
+ [Description("Montenegro")]
+ ME,
+
+ [Description("Moldova")]
+ MD,
+
+ [Description("Madagascar")]
+ MG,
+
+ [Description("Saint Martin")]
+ MF,
+
+ [Description("Morocco")]
+ MA,
+
+ [Description("Monaco")]
+ MC,
+
+ [Description("Uzbekistan")]
+ UZ,
+
+ [Description("Myanmar")]
+ MM,
+
+ [Description("Mali")]
+ ML,
+
+ [Description("Macao")]
+ MO,
+
+ [Description("Mongolia")]
+ MN,
+
+ [Description("Marshall Islands")]
+ MH,
+
+ [Description("North Macedonia")]
+ MK,
+
+ [Description("Mauritius")]
+ MU,
+
+ [Description("Malta")]
+ MT,
+
+ [Description("Malawi")]
+ MW,
+
+ [Description("Maldives")]
+ MV,
+
+ [Description("Martinique")]
+ MQ,
+
+ [Description("Northern Mariana Islands")]
+ MP,
+
+ [Description("Montserrat")]
+ MS,
+
+ [Description("Mauritania")]
+ MR,
+
+ [Description("Isle of Man")]
+ IM,
+
+ [Description("Uganda")]
+ UG,
+
+ [Description("Tanzania")]
+ TZ,
+
+ [Description("Malaysia")]
+ MY,
+
+ [Description("Mexico")]
+ MX,
+
+ [Description("Israel")]
+ IL,
+
+ [Description("France")]
+ FR,
+
+ [Description("British Indian Ocean Territory")]
+ IO,
+
+ [Description("Saint Helena")]
+ SH,
+
+ [Description("Finland")]
+ FI,
+
+ [Description("Fiji")]
+ FJ,
+
+ [Description("Falkland Islands")]
+ FK,
+
+ [Description("Micronesia")]
+ FM,
+
+ [Description("Faroe Islands")]
+ FO,
+
+ [Description("Nicaragua")]
+ NI,
+
+ [Description("Netherlands")]
+ NL,
+
+ [Description("Norway")]
+ NO,
+
+ [Description("Namibia")]
+ NA,
+
+ [Description("Vanuatu")]
+ VU,
+
+ [Description("New Caledonia")]
+ NC,
+
+ [Description("Niger")]
+ NE,
+
+ [Description("Norfolk Island")]
+ NF,
+
+ [Description("Nigeria")]
+ NG,
+
+ [Description("New Zealand")]
+ NZ,
+
+ [Description("Nepal")]
+ NP,
+
+ [Description("Nauru")]
+ NR,
+
+ [Description("Niue")]
+ NU,
+
+ [Description("Cook Islands")]
+ CK,
+
+ [Description("Kosovo")]
+ XK,
+
+ [Description("Ivory Coast")]
+ CI,
+
+ [Description("Switzerland")]
+ CH,
+
+ [Description("Colombia")]
+ CO,
+
+ [Description("China")]
+ CN,
+
+ [Description("Cameroon")]
+ CM,
+
+ [Description("Chile")]
+ CL,
+
+ [Description("Cocos Islands")]
+ CC,
+
+ [Description("Canada")]
+ CA,
+
+ [Description("Republic of the Congo")]
+ CG,
+
+ [Description("Central African Republic")]
+ CF,
+
+ [Description("Democratic Republic of the Congo")]
+ CD,
+
+ [Description("Czech Republic")]
+ CZ,
+
+ [Description("Cyprus")]
+ CY,
+
+ [Description("Christmas Island")]
+ CX,
+
+ [Description("Costa Rica")]
+ CR,
+
+ [Description("Curacao")]
+ CW,
+
+ [Description("Cabo Verde")]
+ CV,
+
+ [Description("Cuba")]
+ CU,
+
+ [Description("Eswatini")]
+ SZ,
+
+ [Description("Syria")]
+ SY,
+
+ [Description("Sint Maarten")]
+ SX,
+
+ [Description("Kyrgyzstan")]
+ KG,
+
+ [Description("Kenya")]
+ KE,
+
+ [Description("South Sudan")]
+ SS,
+
+ [Description("Suriname")]
+ SR,
+
+ [Description("Kiribati")]
+ KI,
+
+ [Description("Cambodia")]
+ KH,
+
+ [Description("Saint Kitts and Nevis")]
+ KN,
+
+ [Description("Comoros")]
+ KM,
+
+ [Description("Sao Tome and Principe")]
+ ST,
+
+ [Description("Slovakia")]
+ SK,
+
+ [Description("South Korea")]
+ KR,
+
+ [Description("Slovenia")]
+ SI,
+
+ [Description("North Korea")]
+ KP,
+
+ [Description("Kuwait")]
+ KW,
+
+ [Description("Senegal")]
+ SN,
+
+ [Description("San Marino")]
+ SM,
+
+ [Description("Sierra Leone")]
+ SL,
+
+ [Description("Seychelles")]
+ SC,
+
+ [Description("Kazakhstan")]
+ KZ,
+
+ [Description("Cayman Islands")]
+ KY,
+
+ [Description("Singapore")]
+ SG,
+
+ [Description("Sweden")]
+ SE,
+
+ [Description("Sudan")]
+ SD,
+
+ [Description("Dominican Republic")]
+ DO,
+
+ [Description("Dominica")]
+ DM,
+
+ [Description("Djibouti")]
+ DJ,
+
+ [Description("Denmark")]
+ DK,
+
+ [Description("British Virgin Islands")]
+ VG,
+
+ [Description("Germany")]
+ DE,
+
+ [Description("Yemen")]
+ YE,
+
+ [Description("Algeria")]
+ DZ,
+
+ [Description("United States")]
+ US,
+
+ [Description("Uruguay")]
+ UY,
+
+ [Description("Mayotte")]
+ YT,
+
+ [Description("United States Minor Outlying Islands")]
+ UM,
+
+ [Description("Lebanon")]
+ LB,
+
+ [Description("Saint Lucia")]
+ LC,
+
+ [Description("Laos")]
+ LA,
+
+ [Description("Tuvalu")]
+ TV,
+
+ [Description("Taiwan")]
+ TW,
+
+ [Description("Trinidad and Tobago")]
+ TT,
+
+ [Description("Turkey")]
+ TR,
+
+ [Description("Sri Lanka")]
+ LK,
+
+ [Description("Liechtenstein")]
+ LI,
+
+ [Description("Latvia")]
+ LV,
+
+ [Description("Tonga")]
+ TO,
+
+ [Description("Lithuania")]
+ LT,
+
+ [Description("Luxembourg")]
+ LU,
+
+ [Description("Liberia")]
+ LR,
+
+ [Description("Lesotho")]
+ LS,
+
+ [Description("Thailand")]
+ TH,
+
+ [Description("French Southern Territories")]
+ TF,
+
+ [Description("Togo")]
+ TG,
+
+ [Description("Chad")]
+ TD,
+
+ [Description("Turks and Caicos Islands")]
+ TC,
+
+ [Description("Libya")]
+ LY,
+
+ [Description("Vatican")]
+ VA,
+
+ [Description("Saint Vincent and the Grenadines")]
+ VC,
+
+ [Description("United Arab Emirates")]
+ AE,
+
+ [Description("Andorra")]
+ AD,
+
+ [Description("Antigua and Barbuda")]
+ AG,
+
+ [Description("Afghanistan")]
+ AF,
+
+ [Description("Anguilla")]
+ AI,
+
+ [Description("U.S. Virgin Islands")]
+ VI,
+
+ [Description("Iceland")]
+ IS,
+
+ [Description("Iran")]
+ IR,
+
+ [Description("Armenia")]
+ AM,
+
+ [Description("Albania")]
+ AL,
+
+ [Description("Angola")]
+ AO,
+
+ [Description("Antarctica")]
+ AQ,
+
+ [Description("American Samoa")]
+ AS,
+
+ [Description("Argentina")]
+ AR,
+
+ [Description("Australia")]
+ AU,
+
+ [Description("Austria")]
+ AT,
+
+ [Description("Aruba")]
+ AW,
+
+ [Description("India")]
+ IN,
+
+ [Description("Aland Islands")]
+ AX,
+
+ [Description("Azerbaijan")]
+ AZ,
+
+ [Description("Ireland")]
+ IE,
+
+ [Description("Indonesia")]
+ ID,
+
+ [Description("Ukraine")]
+ UA,
+
+ [Description("Qatar")]
+ QA,
+
+ [Description("Mozambique")]
+ MZ,
+ }
+}
diff --git a/osu.Game/Users/CountryStatistics.cs b/osu.Game/Users/CountryStatistics.cs
index e784630d56..03d455bc04 100644
--- a/osu.Game/Users/CountryStatistics.cs
+++ b/osu.Game/Users/CountryStatistics.cs
@@ -9,11 +9,8 @@ namespace osu.Game.Users
{
public class CountryStatistics
{
- [JsonProperty]
- public Country Country;
-
[JsonProperty(@"code")]
- public string FlagName;
+ public CountryCode Code;
[JsonProperty(@"active_users")]
public long ActiveUsers;
diff --git a/osu.Game/Users/Drawables/DrawableFlag.cs b/osu.Game/Users/Drawables/DrawableFlag.cs
index e5ac8fa257..253835b415 100644
--- a/osu.Game/Users/Drawables/DrawableFlag.cs
+++ b/osu.Game/Users/Drawables/DrawableFlag.cs
@@ -5,6 +5,7 @@
using System;
using osu.Framework.Allocation;
+using osu.Framework.Extensions;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
@@ -14,13 +15,13 @@ namespace osu.Game.Users.Drawables
{
public class DrawableFlag : Sprite, IHasTooltip
{
- private readonly Country country;
+ private readonly CountryCode countryCode;
- public LocalisableString TooltipText => country?.FullName;
+ public LocalisableString TooltipText => countryCode == CountryCode.Unknown ? string.Empty : countryCode.GetDescription();
- public DrawableFlag(Country country)
+ public DrawableFlag(CountryCode countryCode)
{
- this.country = country;
+ this.countryCode = countryCode;
}
[BackgroundDependencyLoader]
@@ -29,7 +30,8 @@ namespace osu.Game.Users.Drawables
if (ts == null)
throw new ArgumentNullException(nameof(ts));
- Texture = ts.Get($@"Flags/{country?.FlagName ?? @"__"}") ?? ts.Get(@"Flags/__");
+ string textureName = countryCode == CountryCode.Unknown ? "__" : countryCode.ToString();
+ Texture = ts.Get($@"Flags/{textureName}") ?? ts.Get(@"Flags/__");
}
}
}
diff --git a/osu.Game/Users/Drawables/UpdateableFlag.cs b/osu.Game/Users/Drawables/UpdateableFlag.cs
index fd59bf305d..9b101131b3 100644
--- a/osu.Game/Users/Drawables/UpdateableFlag.cs
+++ b/osu.Game/Users/Drawables/UpdateableFlag.cs
@@ -13,18 +13,18 @@ using osu.Game.Overlays;
namespace osu.Game.Users.Drawables
{
- public class UpdateableFlag : ModelBackedDrawable
+ public class UpdateableFlag : ModelBackedDrawable
{
- public Country Country
+ public CountryCode CountryCode
{
get => Model;
set => Model = value;
}
///
- /// Whether to show a place holder on null country.
+ /// Whether to show a place holder on unknown country.
///
- public bool ShowPlaceholderOnNull = true;
+ public bool ShowPlaceholderOnUnknown = true;
///
/// Perform an action in addition to showing the country ranking.
@@ -32,14 +32,14 @@ namespace osu.Game.Users.Drawables
///
public Action Action;
- public UpdateableFlag(Country country = null)
+ public UpdateableFlag(CountryCode countryCode = CountryCode.Unknown)
{
- Country = country;
+ CountryCode = countryCode;
}
- protected override Drawable CreateDrawable(Country country)
+ protected override Drawable CreateDrawable(CountryCode countryCode)
{
- if (country == null && !ShowPlaceholderOnNull)
+ if (countryCode == CountryCode.Unknown && !ShowPlaceholderOnUnknown)
return null;
return new Container
@@ -47,7 +47,7 @@ namespace osu.Game.Users.Drawables
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
- new DrawableFlag(country)
+ new DrawableFlag(countryCode)
{
RelativeSizeAxes = Axes.Both
},
@@ -62,7 +62,7 @@ namespace osu.Game.Users.Drawables
protected override bool OnClick(ClickEvent e)
{
Action?.Invoke();
- rankingsOverlay?.ShowCountry(Country);
+ rankingsOverlay?.ShowCountry(CountryCode);
return true;
}
}
diff --git a/osu.Game/Users/ExtendedUserPanel.cs b/osu.Game/Users/ExtendedUserPanel.cs
index 7d475c545a..a4cba8b920 100644
--- a/osu.Game/Users/ExtendedUserPanel.cs
+++ b/osu.Game/Users/ExtendedUserPanel.cs
@@ -53,7 +53,7 @@ namespace osu.Game.Users
protected UpdateableAvatar CreateAvatar() => new UpdateableAvatar(User, false);
- protected UpdateableFlag CreateFlag() => new UpdateableFlag(User.Country)
+ protected UpdateableFlag CreateFlag() => new UpdateableFlag(User.CountryCode)
{
Size = new Vector2(36, 26),
Action = Action,
diff --git a/osu.Game/Users/IUser.cs b/osu.Game/Users/IUser.cs
index 7a233b5d8b..b7f545f68b 100644
--- a/osu.Game/Users/IUser.cs
+++ b/osu.Game/Users/IUser.cs
@@ -10,6 +10,8 @@ namespace osu.Game.Users
{
string Username { get; }
+ CountryCode CountryCode { get; }
+
bool IsBot { get; }
bool IEquatable.Equals(IUser? other)
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 61fcf2e375..40e01d5f2e 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -36,8 +36,8 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 8843b5c831..0e2e7d57b7 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -61,8 +61,8 @@
-
-
+
+
@@ -84,7 +84,7 @@
-
+