1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-18 02:49:53 +08:00

Merge branch 'master' into taiko-target-classic-position

This commit is contained in:
Dean Herbert
2022-07-20 19:09:57 +09:00
committed by GitHub
Unverified
135 changed files with 1200 additions and 470 deletions
+4
View File
@@ -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.
+1 -1
View File
@@ -52,7 +52,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.716.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.719.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.720.0" />
</ItemGroup>
<ItemGroup Label="Transitive Dependencies">
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
+7 -1
View File
@@ -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;
}
+166
View File
@@ -0,0 +1,166 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using BenchmarkDotNet.Attributes;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Taiko.Objects;
namespace osu.Game.Benchmarks
{
public class BenchmarkHitObject : BenchmarkTest
{
[Params(1, 100, 1000)]
public int Count { get; set; }
[Params(false, true)]
public bool WithBindableAccess { get; set; }
[Benchmark]
public HitCircle[] OsuCircle()
{
var circles = new HitCircle[Count];
for (int i = 0; i < Count; i++)
{
circles[i] = new HitCircle();
if (WithBindableAccess)
{
_ = circles[i].PositionBindable;
_ = circles[i].ScaleBindable;
_ = circles[i].ComboIndexBindable;
_ = circles[i].ComboOffsetBindable;
_ = circles[i].StackHeightBindable;
_ = circles[i].LastInComboBindable;
_ = circles[i].ComboIndexWithOffsetsBindable;
_ = circles[i].IndexInCurrentComboBindable;
_ = circles[i].SamplesBindable;
_ = circles[i].StartTimeBindable;
}
else
{
_ = circles[i].Position;
_ = circles[i].Scale;
_ = circles[i].ComboIndex;
_ = circles[i].ComboOffset;
_ = circles[i].StackHeight;
_ = circles[i].LastInCombo;
_ = circles[i].ComboIndexWithOffsets;
_ = circles[i].IndexInCurrentCombo;
_ = circles[i].Samples;
_ = circles[i].StartTime;
_ = circles[i].Position;
_ = circles[i].Scale;
_ = circles[i].ComboIndex;
_ = circles[i].ComboOffset;
_ = circles[i].StackHeight;
_ = circles[i].LastInCombo;
_ = circles[i].ComboIndexWithOffsets;
_ = circles[i].IndexInCurrentCombo;
_ = circles[i].Samples;
_ = circles[i].StartTime;
}
}
return circles;
}
[Benchmark]
public Hit[] TaikoHit()
{
var hits = new Hit[Count];
for (int i = 0; i < Count; i++)
{
hits[i] = new Hit();
if (WithBindableAccess)
{
_ = hits[i].TypeBindable;
_ = hits[i].IsStrongBindable;
_ = hits[i].SamplesBindable;
_ = hits[i].StartTimeBindable;
}
else
{
_ = hits[i].Type;
_ = hits[i].IsStrong;
_ = hits[i].Samples;
_ = hits[i].StartTime;
}
}
return hits;
}
[Benchmark]
public Fruit[] CatchFruit()
{
var fruit = new Fruit[Count];
for (int i = 0; i < Count; i++)
{
fruit[i] = new Fruit();
if (WithBindableAccess)
{
_ = fruit[i].OriginalXBindable;
_ = fruit[i].XOffsetBindable;
_ = fruit[i].ScaleBindable;
_ = fruit[i].ComboIndexBindable;
_ = fruit[i].HyperDashBindable;
_ = fruit[i].LastInComboBindable;
_ = fruit[i].ComboIndexWithOffsetsBindable;
_ = fruit[i].IndexInCurrentComboBindable;
_ = fruit[i].IndexInBeatmapBindable;
_ = fruit[i].SamplesBindable;
_ = fruit[i].StartTimeBindable;
}
else
{
_ = fruit[i].OriginalX;
_ = fruit[i].XOffset;
_ = fruit[i].Scale;
_ = fruit[i].ComboIndex;
_ = fruit[i].HyperDash;
_ = fruit[i].LastInCombo;
_ = fruit[i].ComboIndexWithOffsets;
_ = fruit[i].IndexInCurrentCombo;
_ = fruit[i].IndexInBeatmap;
_ = fruit[i].Samples;
_ = fruit[i].StartTime;
}
}
return fruit;
}
[Benchmark]
public Note[] ManiaNote()
{
var notes = new Note[Count];
for (int i = 0; i < Count; i++)
{
notes[i] = new Note();
if (WithBindableAccess)
{
_ = notes[i].ColumnBindable;
_ = notes[i].SamplesBindable;
_ = notes[i].StartTimeBindable;
}
else
{
_ = notes[i].Column;
_ = notes[i].Samples;
_ = notes[i].StartTime;
}
}
return notes;
}
}
}
@@ -19,7 +19,9 @@ namespace osu.Game.Rulesets.Catch.Objects
{
public const float OBJECT_RADIUS = 64;
public readonly Bindable<float> OriginalXBindable = new Bindable<float>();
private HitObjectProperty<float> originalX;
public Bindable<float> OriginalXBindable => originalX.Bindable;
/// <summary>
/// The horizontal position of the hit object between 0 and <see cref="CatchPlayfield.WIDTH"/>.
@@ -31,18 +33,20 @@ namespace osu.Game.Rulesets.Catch.Objects
[JsonIgnore]
public float X
{
set => OriginalXBindable.Value = value;
set => originalX.Value = value;
}
public readonly Bindable<float> XOffsetBindable = new Bindable<float>();
private HitObjectProperty<float> xOffset;
public Bindable<float> XOffsetBindable => xOffset.Bindable;
/// <summary>
/// A random offset applied to the horizontal position, set by the beatmap processing.
/// </summary>
public float XOffset
{
get => XOffsetBindable.Value;
set => XOffsetBindable.Value = value;
get => xOffset.Value;
set => xOffset.Value = value;
}
/// <summary>
@@ -54,8 +58,8 @@ namespace osu.Game.Rulesets.Catch.Objects
/// </remarks>
public float OriginalX
{
get => OriginalXBindable.Value;
set => OriginalXBindable.Value = value;
get => originalX.Value;
set => originalX.Value = value;
}
/// <summary>
@@ -69,59 +73,71 @@ namespace osu.Game.Rulesets.Catch.Objects
public double TimePreempt { get; set; } = 1000;
public readonly Bindable<int> IndexInBeatmapBindable = new Bindable<int>();
private HitObjectProperty<int> indexInBeatmap;
public Bindable<int> IndexInBeatmapBindable => indexInBeatmap.Bindable;
public int IndexInBeatmap
{
get => IndexInBeatmapBindable.Value;
set => IndexInBeatmapBindable.Value = value;
get => indexInBeatmap.Value;
set => indexInBeatmap.Value = value;
}
public virtual bool NewCombo { get; set; }
public int ComboOffset { get; set; }
public Bindable<int> IndexInCurrentComboBindable { get; } = new Bindable<int>();
private HitObjectProperty<int> indexInCurrentCombo;
public Bindable<int> IndexInCurrentComboBindable => indexInCurrentCombo.Bindable;
public int IndexInCurrentCombo
{
get => IndexInCurrentComboBindable.Value;
set => IndexInCurrentComboBindable.Value = value;
get => indexInCurrentCombo.Value;
set => indexInCurrentCombo.Value = value;
}
public Bindable<int> ComboIndexBindable { get; } = new Bindable<int>();
private HitObjectProperty<int> comboIndex;
public Bindable<int> ComboIndexBindable => comboIndex.Bindable;
public int ComboIndex
{
get => ComboIndexBindable.Value;
set => ComboIndexBindable.Value = value;
get => comboIndex.Value;
set => comboIndex.Value = value;
}
public Bindable<int> ComboIndexWithOffsetsBindable { get; } = new Bindable<int>();
private HitObjectProperty<int> comboIndexWithOffsets;
public Bindable<int> ComboIndexWithOffsetsBindable => comboIndexWithOffsets.Bindable;
public int ComboIndexWithOffsets
{
get => ComboIndexWithOffsetsBindable.Value;
set => ComboIndexWithOffsetsBindable.Value = value;
get => comboIndexWithOffsets.Value;
set => comboIndexWithOffsets.Value = value;
}
public Bindable<bool> LastInComboBindable { get; } = new Bindable<bool>();
private HitObjectProperty<bool> lastInCombo;
public Bindable<bool> LastInComboBindable => lastInCombo.Bindable;
/// <summary>
/// The next fruit starts a new combo. Used for explodey.
/// </summary>
public virtual bool LastInCombo
{
get => LastInComboBindable.Value;
set => LastInComboBindable.Value = value;
get => lastInCombo.Value;
set => lastInCombo.Value = value;
}
public readonly Bindable<float> ScaleBindable = new Bindable<float>(1);
private HitObjectProperty<float> scale = new HitObjectProperty<float>(1);
public Bindable<float> ScaleBindable => scale.Bindable;
public float Scale
{
get => ScaleBindable.Value;
set => ScaleBindable.Value = value;
get => scale.Value;
set => scale.Value = value;
}
/// <summary>
@@ -5,6 +5,7 @@
using Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Skinning;
using osuTK.Graphics;
@@ -24,12 +25,14 @@ namespace osu.Game.Rulesets.Catch.Objects
/// </summary>
public float DistanceToHyperDash { get; set; }
public readonly Bindable<bool> HyperDashBindable = new Bindable<bool>();
private HitObjectProperty<bool> hyperDash;
public Bindable<bool> HyperDashBindable => hyperDash.Bindable;
/// <summary>
/// Whether this fruit can initiate a hyperdash.
/// </summary>
public bool HyperDash => HyperDashBindable.Value;
public bool HyperDash => hyperDash.Value;
private CatchHitObject hyperDashTarget;
@@ -13,12 +13,14 @@ namespace osu.Game.Rulesets.Mania.Objects
{
public abstract class ManiaHitObject : HitObject, IHasColumn, IHasXPosition
{
public readonly Bindable<int> ColumnBindable = new Bindable<int>();
private HitObjectProperty<int> column;
public Bindable<int> 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();
@@ -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());
@@ -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);
@@ -16,6 +16,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit;
using osuTK;
using osuTK.Input;
@@ -24,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
{
public class SliderPlacementBlueprint : PlacementBlueprint
{
public new Objects.Slider HitObject => (Objects.Slider)base.HitObject;
public new Slider HitObject => (Slider)base.HitObject;
private SliderBodyPiece bodyPiece;
private HitCirclePiece headCirclePiece;
@@ -42,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
private IDistanceSnapProvider snapProvider { get; set; }
public SliderPlacementBlueprint()
: base(new Objects.Slider())
: base(new Slider())
{
RelativeSizeAxes = Axes.Both;
@@ -82,7 +83,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
case SliderPlacementState.Initial:
BeginPlacement();
var nearestDifficultyPoint = editorBeatmap.HitObjects.LastOrDefault(h => h.GetEndTime() < HitObject.StartTime)?.DifficultyControlPoint?.DeepClone() as DifficultyControlPoint;
var nearestDifficultyPoint = editorBeatmap.HitObjects
.LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime)?
.DifficultyControlPoint?.DeepClone() as DifficultyControlPoint;
HitObject.DifficultyControlPoint = nearestDifficultyPoint ?? new DifficultyControlPoint();
HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
+43 -27
View File
@@ -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<Vector2> PositionBindable = new Bindable<Vector2>();
private HitObjectProperty<Vector2> position;
public Bindable<Vector2> 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<int> StackHeightBindable = new Bindable<int>();
private HitObjectProperty<int> stackHeight;
public Bindable<int> 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<float> ScaleBindable = new BindableFloat(1);
private HitObjectProperty<float> scale = new HitObjectProperty<float>(1);
public Bindable<float> ScaleBindable => scale.Bindable;
public float Scale
{
get => ScaleBindable.Value;
set => ScaleBindable.Value = value;
get => scale.Value;
set => scale.Value = value;
}
public virtual bool NewCombo { get; set; }
public readonly Bindable<int> ComboOffsetBindable = new Bindable<int>();
private HitObjectProperty<int> comboOffset;
public Bindable<int> ComboOffsetBindable => comboOffset.Bindable;
public int ComboOffset
{
get => ComboOffsetBindable.Value;
set => ComboOffsetBindable.Value = value;
get => comboOffset.Value;
set => comboOffset.Value = value;
}
public Bindable<int> IndexInCurrentComboBindable { get; } = new Bindable<int>();
private HitObjectProperty<int> indexInCurrentCombo;
public Bindable<int> IndexInCurrentComboBindable => indexInCurrentCombo.Bindable;
public virtual int IndexInCurrentCombo
{
get => IndexInCurrentComboBindable.Value;
set => IndexInCurrentComboBindable.Value = value;
get => indexInCurrentCombo.Value;
set => indexInCurrentCombo.Value = value;
}
public Bindable<int> ComboIndexBindable { get; } = new Bindable<int>();
private HitObjectProperty<int> comboIndex;
public Bindable<int> ComboIndexBindable => comboIndex.Bindable;
public virtual int ComboIndex
{
get => ComboIndexBindable.Value;
set => ComboIndexBindable.Value = value;
get => comboIndex.Value;
set => comboIndex.Value = value;
}
public Bindable<int> ComboIndexWithOffsetsBindable { get; } = new Bindable<int>();
private HitObjectProperty<int> comboIndexWithOffsets;
public Bindable<int> ComboIndexWithOffsetsBindable => comboIndexWithOffsets.Bindable;
public int ComboIndexWithOffsets
{
get => ComboIndexWithOffsetsBindable.Value;
set => ComboIndexWithOffsetsBindable.Value = value;
get => comboIndexWithOffsets.Value;
set => comboIndexWithOffsets.Value = value;
}
public Bindable<bool> LastInComboBindable { get; } = new Bindable<bool>();
private HitObjectProperty<bool> lastInCombo;
public Bindable<bool> LastInComboBindable => lastInCombo.Bindable;
public bool LastInCombo
{
get => LastInComboBindable.Value;
set => LastInComboBindable.Value = value;
get => lastInCombo.Value;
set => lastInCombo.Value = value;
}
protected OsuHitObject()
@@ -1,10 +1,12 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Diagnostics;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Utils;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osuTK.Graphics;
@@ -32,19 +34,35 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
protected override void OnTrackingChanged(ValueChangedEvent<bool> tracking)
{
const float scale_duration = 300f;
const float fade_duration = 300f;
Debug.Assert(ParentObject != null);
this.ScaleTo(tracking.NewValue ? DrawableSliderBall.FOLLOW_AREA : 1f, scale_duration, Easing.OutQuint)
.FadeTo(tracking.NewValue ? 1f : 0, fade_duration, Easing.OutQuint);
const float duration = 300f;
if (ParentObject.Judged)
return;
if (tracking.NewValue)
{
if (Precision.AlmostEquals(0, Alpha))
this.ScaleTo(1);
this.ScaleTo(DrawableSliderBall.FOLLOW_AREA, duration, Easing.OutQuint)
.FadeTo(1f, duration, Easing.OutQuint);
}
else
{
this.ScaleTo(DrawableSliderBall.FOLLOW_AREA * 1.2f, duration / 2, Easing.OutQuint)
.FadeTo(0, duration / 2, Easing.OutQuint);
}
}
protected override void OnSliderEnd()
{
const float fade_duration = 450f;
const float fade_duration = 300;
// intentionally pile on an extra FadeOut to make it happen much faster
this.FadeOut(fade_duration / 4, Easing.Out);
this.ScaleTo(1, fade_duration, Easing.OutQuint);
this.FadeOut(fade_duration / 2, Easing.OutQuint);
}
}
}
@@ -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<TaikoHitObject>))
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<TaikoHitObject>) && score.Mods.Any(m => m is ModHidden))
accuracyValue *= 1.10 * lengthBonus;
return accuracyValue;
}
private int totalHits => countGreat + countOk + countMeh + countMiss;
+6 -4
View File
@@ -11,14 +11,16 @@ namespace osu.Game.Rulesets.Taiko.Objects
{
public class BarLine : TaikoHitObject, IBarLine
{
private HitObjectProperty<bool> major;
public Bindable<bool> MajorBindable => major.Bindable;
public bool Major
{
get => MajorBindable.Value;
set => MajorBindable.Value = value;
get => major.Value;
set => major.Value = value;
}
public readonly Bindable<bool> MajorBindable = new BindableBool();
public override Judgement CreateJudgement() => new IgnoreJudgement();
}
}
+7 -4
View File
@@ -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<HitType> TypeBindable = new Bindable<HitType>();
private HitObjectProperty<HitType> type;
public Bindable<Color4> DisplayColour { get; } = new Bindable<Color4>(COLOUR_CENTRE);
public Bindable<HitType> TypeBindable => type.Bindable;
/// <summary>
/// The <see cref="HitType"/> that actuates this <see cref="Hit"/>.
/// </summary>
public HitType Type
{
get => TypeBindable.Value;
set => TypeBindable.Value = value;
get => type.Value;
set => type.Value = value;
}
public Bindable<Color4> DisplayColour { get; } = new Bindable<Color4>(COLOUR_CENTRE);
public static readonly Color4 COLOUR_CENTRE = Color4Extensions.FromHex(@"bb1177");
public static readonly Color4 COLOUR_RIM = Color4Extensions.FromHex(@"2299bb");
@@ -0,0 +1,85 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.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);
}
}
}
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Collections.Generic;
using NUnit.Framework;
using osu.Game.Beatmaps;
@@ -17,7 +15,7 @@ namespace osu.Game.Tests.Mods
[TestFixture]
public class ModDifficultyAdjustTest
{
private TestModDifficultyAdjust testMod;
private TestModDifficultyAdjust testMod = null!;
[SetUp]
public void Setup()
@@ -148,7 +146,7 @@ namespace osu.Game.Tests.Mods
yield return new TestModDifficultyAdjust();
}
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod>? mods = null)
{
throw new System.NotImplementedException();
}
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using NUnit.Framework;
using osu.Game.Online.API;
using osu.Game.Rulesets.Osu;
-2
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using NUnit.Framework;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
+14 -16
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Linq;
using Moq;
@@ -164,19 +162,19 @@ namespace osu.Game.Tests.Mods
new object[]
{
new Mod[] { new OsuModHidden(), new InvalidMultiplayerMod() },
null
Array.Empty<Type>()
},
// invalid free mod is valid for local.
new object[]
{
new Mod[] { new OsuModHidden(), new InvalidMultiplayerFreeMod() },
null
Array.Empty<Type>()
},
// valid pair.
new object[]
{
new Mod[] { new OsuModHidden(), new OsuModHardRock() },
null
Array.Empty<Type>()
},
};
@@ -216,13 +214,13 @@ namespace osu.Game.Tests.Mods
new object[]
{
new Mod[] { new OsuModHidden(), new InvalidMultiplayerFreeMod() },
null
Array.Empty<Type>()
},
// valid pair.
new object[]
{
new Mod[] { new OsuModHidden(), new OsuModHardRock() },
null
Array.Empty<Type>()
},
};
@@ -256,19 +254,19 @@ namespace osu.Game.Tests.Mods
new object[]
{
new Mod[] { new OsuModHidden(), new OsuModApproachDifferent() },
null,
Array.Empty<Type>(),
},
// incompatible pair with derived class is valid for free mods.
new object[]
{
new Mod[] { new OsuModDeflate(), new OsuModSpinIn() },
null,
Array.Empty<Type>(),
},
// valid pair.
new object[]
{
new Mod[] { new OsuModHidden(), new OsuModHardRock() },
null
Array.Empty<Type>()
},
};
@@ -277,12 +275,12 @@ namespace osu.Game.Tests.Mods
{
bool isValid = ModUtils.CheckValidForGameplay(inputMods, out var invalid);
Assert.That(isValid, Is.EqualTo(expectedInvalid == null));
Assert.That(isValid, Is.EqualTo(expectedInvalid.Length == 0));
if (isValid)
Assert.IsNull(invalid);
else
Assert.That(invalid.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
Assert.That(invalid?.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
}
[TestCaseSource(nameof(invalid_multiplayer_mod_test_scenarios))]
@@ -290,12 +288,12 @@ namespace osu.Game.Tests.Mods
{
bool isValid = ModUtils.CheckValidRequiredModsForMultiplayer(inputMods, out var invalid);
Assert.That(isValid, Is.EqualTo(expectedInvalid == null));
Assert.That(isValid, Is.EqualTo(expectedInvalid.Length == 0));
if (isValid)
Assert.IsNull(invalid);
else
Assert.That(invalid.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
Assert.That(invalid?.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
}
[TestCaseSource(nameof(invalid_free_mod_test_scenarios))]
@@ -303,12 +301,12 @@ namespace osu.Game.Tests.Mods
{
bool isValid = ModUtils.CheckValidFreeModsForMultiplayer(inputMods, out var invalid);
Assert.That(isValid, Is.EqualTo(expectedInvalid == null));
Assert.That(isValid, Is.EqualTo(expectedInvalid.Length == 0));
if (isValid)
Assert.IsNull(invalid);
else
Assert.That(invalid.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
Assert.That(invalid?.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
}
public abstract class CustomMod1 : Mod, IModCompatibilitySpecification
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
@@ -29,10 +27,10 @@ namespace osu.Game.Tests.Mods
[TestCase(typeof(ManiaRuleset))]
public void TestAllMultiModsFromRulesetAreIncompatible(Type rulesetType)
{
var ruleset = (Ruleset)Activator.CreateInstance(rulesetType);
var ruleset = Activator.CreateInstance(rulesetType) as Ruleset;
Assert.That(ruleset, Is.Not.Null);
var allMultiMods = getMultiMods(ruleset);
var allMultiMods = getMultiMods(ruleset!);
Assert.Multiple(() =>
{
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Linq;
using NUnit.Framework;
using osu.Framework.Bindables;
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Collections.Generic;
using osu.Framework.Bindables;
@@ -33,7 +31,7 @@ namespace osu.Game.Tests.Mods
return Array.Empty<Mod>();
}
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => throw new NotImplementedException();
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod>? mods = null) => throw new NotImplementedException();
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException();
@@ -80,7 +80,7 @@ namespace osu.Game.Tests.Online
{
AddStep("download beatmap", () => beatmaps.Download(test_db_model));
AddStep("cancel download from request", () => beatmaps.GetExistingDownload(test_db_model).Cancel());
AddStep("cancel download from request", () => beatmaps.GetExistingDownload(test_db_model)!.Cancel());
AddUntilStep("is removed from download list", () => beatmaps.GetExistingDownload(test_db_model) == null);
AddAssert("is notification cancelled", () => recentNotification.State == ProgressNotificationState.Cancelled);
@@ -126,10 +126,10 @@ namespace osu.Game.Tests.Online
AddStep("start downloading", () => beatmapDownloader.Download(testBeatmapSet));
addAvailabilityCheckStep("state downloading 0%", () => BeatmapAvailability.Downloading(0.0f));
AddStep("set progress 40%", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet)).SetProgress(0.4f));
AddStep("set progress 40%", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet))!.SetProgress(0.4f));
addAvailabilityCheckStep("state downloading 40%", () => BeatmapAvailability.Downloading(0.4f));
AddStep("finish download", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet)).TriggerSuccess(testBeatmapFile));
AddStep("finish download", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet))!.TriggerSuccess(testBeatmapFile));
addAvailabilityCheckStep("state importing", BeatmapAvailability.Importing);
AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true));
@@ -246,7 +246,7 @@ namespace osu.Game.Tests.Online
=> new TestDownloadRequest(set);
}
private class TestDownloadRequest : ArchiveDownloadRequest<IBeatmapSetInfo>
internal class TestDownloadRequest : ArchiveDownloadRequest<IBeatmapSetInfo>
{
public new void SetProgress(float progress) => base.SetProgress(progress);
public new void TriggerSuccess(string filename) => base.TriggerSuccess(filename);
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using NUnit.Framework;
using osu.Framework.Audio.Track;
using osu.Framework.Timing;
@@ -19,8 +17,8 @@ namespace osu.Game.Tests.Rulesets.Mods
private const double start_time = 1000;
private const double duration = 9000;
private TrackVirtual track;
private OsuPlayfield playfield;
private TrackVirtual track = null!;
private OsuPlayfield playfield = null!;
[SetUp]
public void SetUp()
@@ -402,16 +402,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestPlayStartsWithCorrectBeatmapWhileAtSongSelect()
{
createRoom(() => new Room
PlaylistItem? item = null;
createRoom(() =>
{
Name = { Value = "Test Room" },
Playlist =
item = new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
};
return new Room
{
Name = { Value = "Test Room" },
Playlist = { item }
};
});
pressReadyButton();
@@ -419,7 +421,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("Enter song select", () =>
{
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen;
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.ClientRoom?.Settings.PlaylistItemId);
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(item);
});
AddUntilStep("wait for song select", () => this.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault()?.BeatmapSetsLoaded == true);
@@ -440,16 +442,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestPlayStartsWithCorrectRulesetWhileAtSongSelect()
{
createRoom(() => new Room
PlaylistItem? item = null;
createRoom(() =>
{
Name = { Value = "Test Room" },
Playlist =
item = new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
};
return new Room
{
Name = { Value = "Test Room" },
Playlist = { item }
};
});
pressReadyButton();
@@ -457,7 +461,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("Enter song select", () =>
{
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen;
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.ClientRoom?.Settings.PlaylistItemId);
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(item);
});
AddUntilStep("wait for song select", () => this.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault()?.BeatmapSetsLoaded == true);
@@ -478,16 +482,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestPlayStartsWithCorrectModsWhileAtSongSelect()
{
createRoom(() => new Room
PlaylistItem? item = null;
createRoom(() =>
{
Name = { Value = "Test Room" },
Playlist =
item = new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
{
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
};
return new Room
{
Name = { Value = "Test Room" },
Playlist = { item }
};
});
pressReadyButton();
@@ -495,7 +501,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("Enter song select", () =>
{
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen;
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.ClientRoom?.Settings.PlaylistItemId);
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(item);
});
AddUntilStep("wait for song select", () => this.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault()?.BeatmapSetsLoaded == true);
@@ -255,18 +255,25 @@ namespace osu.Game.Tests.Visual.Online
};
const int initial_great_count = 2000;
const int initial_tick_count = 100;
int greatCount = initial_great_count;
int tickCount = initial_tick_count;
foreach (var s in scores.Scores)
{
s.Statistics = new Dictionary<HitResult, int>
{
{ HitResult.Great, greatCount -= 100 },
{ HitResult.Great, greatCount },
{ HitResult.LargeTickHit, tickCount },
{ HitResult.Ok, RNG.Next(100) },
{ HitResult.Meh, RNG.Next(100) },
{ HitResult.Miss, initial_great_count - greatCount }
{ HitResult.Miss, initial_great_count - greatCount },
{ HitResult.LargeTickMiss, initial_tick_count - tickCount },
};
greatCount -= 100;
tickCount -= RNG.Next(1, 5);
}
return scores;
@@ -825,7 +825,8 @@ namespace osu.Game.Tests.Visual.SongSelect
checkVisibleItemCount(true, 15);
}
private void loadBeatmaps(List<BeatmapSetInfo> beatmapSets = null, Func<FilterCriteria> initialCriteria = null, Action<BeatmapCarousel> carouselAdjust = null, int? count = null, bool randomDifficulties = false)
private void loadBeatmaps(List<BeatmapSetInfo> beatmapSets = null, Func<FilterCriteria> initialCriteria = null, Action<BeatmapCarousel> carouselAdjust = null, int? count = null,
bool randomDifficulties = false)
{
bool changed = false;
@@ -0,0 +1,154 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Tests.Online;
using osu.Game.Tests.Resources;
namespace osu.Game.Tests.Visual.SongSelect
{
[TestFixture]
public class TestSceneUpdateBeatmapSetButton : OsuManualInputManagerTestScene
{
private BeatmapCarousel carousel = null!;
private TestSceneOnlinePlayBeatmapAvailabilityTracker.TestBeatmapModelDownloader beatmapDownloader = null!;
private BeatmapSetInfo testBeatmapSetInfo = null!;
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
var importer = parent.Get<BeatmapManager>();
dependencies.CacheAs<BeatmapModelDownloader>(beatmapDownloader = new TestSceneOnlinePlayBeatmapAvailabilityTracker.TestBeatmapModelDownloader(importer, API));
return dependencies;
}
private UpdateBeatmapSetButton? getUpdateButton() => carousel.ChildrenOfType<UpdateBeatmapSetButton>().SingleOrDefault();
[SetUpSteps]
public void SetUpSteps()
{
AddStep("create carousel", () =>
{
Child = carousel = new BeatmapCarousel
{
RelativeSizeAxes = Axes.Both,
BeatmapSets = new List<BeatmapSetInfo>
{
(testBeatmapSetInfo = TestResources.CreateTestBeatmapSetInfo()),
}
};
});
AddUntilStep("wait for load", () => carousel.BeatmapSetsLoaded);
AddAssert("update button not visible", () => getUpdateButton() == null);
}
[Test]
public void TestDownloadToCompletion()
{
ArchiveDownloadRequest<IBeatmapSetInfo>? downloadRequest = null;
AddStep("update online hash", () =>
{
testBeatmapSetInfo.Beatmaps.First().OnlineMD5Hash = "different hash";
testBeatmapSetInfo.Beatmaps.First().LastOnlineUpdate = DateTimeOffset.Now;
carousel.UpdateBeatmapSet(testBeatmapSetInfo);
});
AddUntilStep("update button visible", () => getUpdateButton() != null);
AddStep("click button", () => getUpdateButton()?.TriggerClick());
AddUntilStep("wait for download started", () =>
{
downloadRequest = beatmapDownloader.GetExistingDownload(testBeatmapSetInfo);
return downloadRequest != null;
});
AddUntilStep("wait for button disabled", () => getUpdateButton()?.Enabled.Value == false);
AddUntilStep("progress download to completion", () =>
{
if (downloadRequest is TestSceneOnlinePlayBeatmapAvailabilityTracker.TestDownloadRequest testRequest)
{
testRequest.SetProgress(testRequest.Progress + 0.1f);
if (testRequest.Progress >= 1)
{
testRequest.TriggerSuccess();
// usually this would be done by the import process.
testBeatmapSetInfo.Beatmaps.First().MD5Hash = "different hash";
testBeatmapSetInfo.Beatmaps.First().LastOnlineUpdate = DateTimeOffset.Now;
// usually this would be done by a realm subscription.
carousel.UpdateBeatmapSet(testBeatmapSetInfo);
return true;
}
}
return false;
});
}
[Test]
public void TestDownloadFailed()
{
ArchiveDownloadRequest<IBeatmapSetInfo>? downloadRequest = null;
AddStep("update online hash", () =>
{
testBeatmapSetInfo.Beatmaps.First().OnlineMD5Hash = "different hash";
testBeatmapSetInfo.Beatmaps.First().LastOnlineUpdate = DateTimeOffset.Now;
carousel.UpdateBeatmapSet(testBeatmapSetInfo);
});
AddUntilStep("update button visible", () => getUpdateButton() != null);
AddStep("click button", () => getUpdateButton()?.TriggerClick());
AddUntilStep("wait for download started", () =>
{
downloadRequest = beatmapDownloader.GetExistingDownload(testBeatmapSetInfo);
return downloadRequest != null;
});
AddUntilStep("wait for button disabled", () => getUpdateButton()?.Enabled.Value == false);
AddUntilStep("progress download to failure", () =>
{
if (downloadRequest is TestSceneOnlinePlayBeatmapAvailabilityTracker.TestDownloadRequest testRequest)
{
testRequest.SetProgress(testRequest.Progress + 0.1f);
if (testRequest.Progress >= 0.5f)
{
testRequest.TriggerFailure(new Exception());
return true;
}
}
return false;
});
AddUntilStep("wait for button enabled", () => getUpdateButton()?.Enabled.Value == true);
}
}
}
@@ -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);
+2 -2
View File
@@ -14,15 +14,15 @@ namespace osu.Game.Audio
[Serializable]
public class HitSampleInfo : ISampleInfo, IEquatable<HitSampleInfo>
{
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";
/// <summary>
/// All valid sample addition constants.
/// </summary>
public static IEnumerable<string> AllAdditions => new[] { HIT_WHISTLE, HIT_CLAP, HIT_FINISH };
public static IEnumerable<string> AllAdditions => new[] { HIT_WHISTLE, HIT_FINISH, HIT_CLAP };
/// <summary>
/// The name of the sample to load.
+10
View File
@@ -92,6 +92,16 @@ namespace osu.Game.Beatmaps
[Indexed]
public string MD5Hash { get; set; } = string.Empty;
public string OnlineMD5Hash { get; set; } = string.Empty;
public DateTimeOffset? LastOnlineUpdate { get; set; }
/// <summary>
/// Whether this beatmap matches the online version, based on fetched online metadata.
/// Will return <c>true</c> if no online metadata is available.
/// </summary>
public bool MatchesOnlineVersion => LastOnlineUpdate == null || MD5Hash == OnlineMD5Hash;
[JsonIgnore]
public bool Hidden { get; set; }
+1 -3
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Database;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
@@ -14,7 +12,7 @@ namespace osu.Game.Beatmaps
protected override ArchiveDownloadRequest<IBeatmapSetInfo> CreateDownloadRequest(IBeatmapSetInfo set, bool minimiseDownloadSize) =>
new DownloadBeatmapSetRequest(set, minimiseDownloadSize);
public override ArchiveDownloadRequest<IBeatmapSetInfo> GetExistingDownload(IBeatmapSetInfo model)
public override ArchiveDownloadRequest<IBeatmapSetInfo>? GetExistingDownload(IBeatmapSetInfo model)
=> CurrentDownloads.Find(r => r.Model.OnlineID == model.OnlineID);
public BeatmapModelDownloader(IModelImporter<BeatmapSetInfo> beatmapImporter, IAPIProvider api)
@@ -0,0 +1,50 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Graphics;
using osu.Game.Database;
using osu.Game.Online.Metadata;
namespace osu.Game.Beatmaps
{
/// <summary>
/// Ingests any changes that happen externally to the client, reprocessing as required.
/// </summary>
public class BeatmapOnlineChangeIngest : Component
{
private readonly BeatmapUpdater beatmapUpdater;
private readonly RealmAccess realm;
private readonly MetadataClient metadataClient;
public BeatmapOnlineChangeIngest(BeatmapUpdater beatmapUpdater, RealmAccess realm, MetadataClient metadataClient)
{
this.beatmapUpdater = beatmapUpdater;
this.realm = realm;
this.metadataClient = metadataClient;
metadataClient.ChangedBeatmapSetsArrived += changesDetected;
}
private void changesDetected(int[] beatmapSetIds)
{
// May want to batch incoming updates further if the background realm operations ever becomes a concern.
realm.Run(r =>
{
foreach (int id in beatmapSetIds)
{
var matchingSet = r.All<BeatmapSetInfo>().FirstOrDefault(s => s.OnlineID == id);
if (matchingSet != null)
beatmapUpdater.Queue(matchingSet.ToLive(realm));
}
});
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
metadataClient.ChangedBeatmapSetsArrived -= changesDetected;
}
}
}
@@ -102,6 +102,10 @@ namespace osu.Game.Beatmaps
beatmapInfo.BeatmapSet.Status = res.BeatmapSet?.Status ?? BeatmapOnlineStatus.None;
beatmapInfo.BeatmapSet.OnlineID = res.OnlineBeatmapSetID;
beatmapInfo.OnlineMD5Hash = res.MD5Hash;
beatmapInfo.LastOnlineUpdate = res.LastUpdated;
beatmapInfo.OnlineID = res.OnlineID;
beatmapInfo.Metadata.Author.OnlineID = res.AuthorID;
@@ -190,7 +194,7 @@ namespace osu.Game.Beatmaps
using (var cmd = db.CreateCommand())
{
cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved, user_id FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineID OR filename = @Path";
cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved, user_id, checksum, last_update FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineID OR filename = @Path";
cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmapInfo.MD5Hash));
cmd.Parameters.Add(new SqliteParameter("@OnlineID", beatmapInfo.OnlineID));
@@ -209,9 +213,11 @@ namespace osu.Game.Beatmaps
beatmapInfo.BeatmapSet.Status = status;
beatmapInfo.BeatmapSet.OnlineID = reader.GetInt32(0);
beatmapInfo.OnlineID = reader.GetInt32(1);
beatmapInfo.Metadata.Author.OnlineID = reader.GetInt32(3);
beatmapInfo.OnlineMD5Hash = reader.GetString(4);
beatmapInfo.LastOnlineUpdate = reader.GetDateTimeOffset(5);
logForModel(set, $"Cached local retrieval for {beatmapInfo}.");
return true;
}
+2
View File
@@ -93,5 +93,7 @@ namespace osu.Game.Beatmaps
IEnumerable<IBeatmapInfo> IBeatmapSetInfo.Beatmaps => Beatmaps;
IEnumerable<INamedFileUsage> IHasNamedFiles.Files => Files;
public bool AllBeatmapsUpToDate => Beatmaps.All(b => b.MatchesOnlineVersion);
}
}
+2 -2
View File
@@ -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()}");
}
}
+4 -10
View File
@@ -6,6 +6,7 @@ using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.Online.API;
@@ -30,21 +31,12 @@ namespace osu.Game.Beatmaps
onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage);
}
/// <summary>
/// Queue a beatmap for background processing.
/// </summary>
public void Queue(int beatmapSetId)
{
// TODO: implement
}
/// <summary>
/// Queue a beatmap for background processing.
/// </summary>
public void Queue(Live<BeatmapSetInfo> beatmap)
{
// For now, just fire off a task.
// TODO: Add actual queueing probably.
Logger.Log($"Queueing change for local beatmap {beatmap}");
Task.Factory.StartNew(() => beatmap.PerformRead(Process));
}
@@ -56,6 +48,8 @@ namespace osu.Game.Beatmaps
// Before we use below, we want to invalidate.
workingBeatmapCache.Invalidate(beatmapSet);
// TODO: this call currently uses the local `online.db` lookup.
// We probably don't want this to happen after initial import (as the data may be stale).
onlineLookupQueue.Update(beatmapSet);
foreach (var beatmap in beatmapSet.Beatmaps)
+7 -9
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
@@ -19,18 +17,18 @@ namespace osu.Game.Database
where TModel : class, IHasGuidPrimaryKey, ISoftDelete, IEquatable<TModel>, T
where T : class
{
public Action<Notification> PostNotification { protected get; set; }
public Action<Notification>? PostNotification { protected get; set; }
public event Action<ArchiveDownloadRequest<T>> DownloadBegan;
public event Action<ArchiveDownloadRequest<T>>? DownloadBegan;
public event Action<ArchiveDownloadRequest<T>> DownloadFailed;
public event Action<ArchiveDownloadRequest<T>>? DownloadFailed;
private readonly IModelImporter<TModel> importer;
private readonly IAPIProvider api;
private readonly IAPIProvider? api;
protected readonly List<ArchiveDownloadRequest<T>> CurrentDownloads = new List<ArchiveDownloadRequest<T>>();
protected ModelDownloader(IModelImporter<TModel> importer, IAPIProvider api)
protected ModelDownloader(IModelImporter<TModel> importer, IAPIProvider? api)
{
this.importer = importer;
this.api = api;
@@ -87,7 +85,7 @@ namespace osu.Game.Database
CurrentDownloads.Add(request);
PostNotification?.Invoke(notification);
api.PerformAsync(request);
api?.PerformAsync(request);
DownloadBegan?.Invoke(request);
return true;
@@ -105,7 +103,7 @@ namespace osu.Game.Database
}
}
public abstract ArchiveDownloadRequest<T> GetExistingDownload(T model);
public abstract ArchiveDownloadRequest<T>? GetExistingDownload(T model);
private bool canDownload(T model) => GetExistingDownload(model) == null && api != null;
+2 -1
View File
@@ -61,8 +61,9 @@ namespace osu.Game.Database
/// 15 2022-07-13 Added LastPlayed to BeatmapInfo.
/// 16 2022-07-15 Removed HasReplay from ScoreInfo.
/// 17 2022-07-16 Added CountryCode to RealmUser.
/// 18 2022-07-19 Added OnlineMD5Hash and LastOnlineUpdate to BeatmapInfo.
/// </summary>
private const int schema_version = 17;
private const int schema_version = 18;
/// <summary>
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
+1 -2
View File
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
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);
@@ -0,0 +1,94 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
// 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
{
/// <summary>
/// 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.
/// </summary>
public static class StringDehumanizeExtensions
{
/// <summary>
/// Converts the string to "Pascal case" (also known as "upper camel case").
/// </summary>
/// <example>
/// <code>
/// "this is a test string".ToPascalCase() == "ThisIsATestString"
/// </code>
/// </example>
public static string ToPascalCase(this string input)
{
return Regex.Replace(input, "(?:^|_|-| +)(.)", match => match.Groups[1].Value.ToUpperInvariant());
}
/// <summary>
/// Converts the string to (lower) "camel case".
/// </summary>
/// <example>
/// <code>
/// "this is a test string".ToCamelCase() == "thisIsATestString"
/// </code>
/// </example>
public static string ToCamelCase(this string input)
{
string word = input.ToPascalCase();
return word.Length > 0 ? word.Substring(0, 1).ToLowerInvariant() + word.Substring(1) : word;
}
/// <summary>
/// Converts the string to "snake case".
/// </summary>
/// <example>
/// <code>
/// "this is a test string".ToSnakeCase() == "this_is_a_test_string"
/// </code>
/// </example>
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();
}
/// <summary>
/// Converts the string to "kebab case".
/// </summary>
/// <example>
/// <code>
/// "this is a test string".ToKebabCase() == "this-is-a-test-string"
/// </code>
/// </example>
public static string ToKebabCase(this string input)
{
return ToSnakeCase(input).Replace('_', '-');
}
}
}
+22 -12
View File
@@ -3,21 +3,21 @@
#nullable disable
using osuTK;
using System;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Game.Configuration;
using System;
using JetBrains.Annotations;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Events;
using osu.Framework.Utils;
using osu.Game.Configuration;
using osuTK;
namespace osu.Game.Graphics.Cursor
{
@@ -35,6 +35,7 @@ namespace osu.Game.Graphics.Cursor
private Vector2 positionMouseDown;
private Sample tapSample;
private Vector2 lastMovePosition;
[BackgroundDependencyLoader(true)]
private void load([NotNull] OsuConfigManager config, [CanBeNull] ScreenshotManager screenshotManager, AudioManager audio)
@@ -47,16 +48,25 @@ namespace osu.Game.Graphics.Cursor
tapSample = audio.Samples.Get(@"UI/cursor-tap");
}
protected override void Update()
{
base.Update();
if (dragRotationState != DragRotationState.NotDragging
&& Vector2.Distance(positionMouseDown, lastMovePosition) > 60)
{
// make the rotation centre point floating.
positionMouseDown = Interpolation.ValueAt(0.04f, positionMouseDown, lastMovePosition, 0, Clock.ElapsedFrameTime);
}
}
protected override bool OnMouseMove(MouseMoveEvent e)
{
if (dragRotationState != DragRotationState.NotDragging)
{
// make the rotation centre point floating.
if (Vector2.Distance(positionMouseDown, e.MousePosition) > 60)
positionMouseDown = Interpolation.ValueAt(0.005f, positionMouseDown, e.MousePosition, 0, Clock.ElapsedFrameTime);
lastMovePosition = e.MousePosition;
var position = e.MousePosition;
float distance = Vector2Extensions.Distance(position, positionMouseDown);
float distance = Vector2Extensions.Distance(lastMovePosition, positionMouseDown);
// don't start rotating until we're moved a minimum distance away from the mouse down location,
// else it can have an annoying effect.
@@ -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();
}
}
}
+3 -3
View File
@@ -7,12 +7,12 @@ 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;
@@ -45,7 +45,7 @@ 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());
}
}
@@ -63,7 +63,7 @@ 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;
try
@@ -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());
@@ -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
@@ -81,6 +81,9 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"max_combo")]
public int? MaxCombo { get; set; }
[JsonProperty(@"last_updated")]
public DateTimeOffset LastUpdated { get; set; }
public double BPM { get; set; }
#region Implementation of IBeatmapInfo
@@ -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;
@@ -13,7 +13,7 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"scores")]
public List<SoloScoreInfo> Scores;
[JsonProperty(@"userScore")]
[JsonProperty(@"user_score")]
public APIScoreWithPosition UserScore;
}
}
@@ -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());
+6 -6
View File
@@ -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;
}
}
+2 -1
View File
@@ -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
/// <summary>
/// Reconnect if already connected.
/// </summary>
void Reconnect();
Task Reconnect();
}
}
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Graphics;
@@ -11,5 +13,13 @@ namespace osu.Game.Online.Metadata
public abstract Task BeatmapSetsUpdated(BeatmapUpdates updates);
public abstract Task<BeatmapUpdates> GetChangesSince(int queueId);
public Action<int[]>? ChangedBeatmapSetsArrived;
protected Task ProcessChanges(int[] beatmapSetIDs)
{
ChangedBeatmapSetsArrived?.Invoke(beatmapSetIDs.Distinct().ToArray());
return Task.CompletedTask;
}
}
}
@@ -7,7 +7,6 @@ using Microsoft.AspNetCore.SignalR.Client;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Online.API;
@@ -15,7 +14,6 @@ namespace osu.Game.Online.Metadata
{
public class OnlineMetadataClient : MetadataClient
{
private readonly BeatmapUpdater beatmapUpdater;
private readonly string endpoint;
private IHubClientConnector? connector;
@@ -24,9 +22,8 @@ namespace osu.Game.Online.Metadata
private HubConnection? connection => connector?.CurrentConnection;
public OnlineMetadataClient(EndpointConfiguration endpoints, BeatmapUpdater beatmapUpdater)
public OnlineMetadataClient(EndpointConfiguration endpoints)
{
this.beatmapUpdater = beatmapUpdater;
endpoint = endpoints.MetadataEndpointUrl;
}
@@ -102,17 +99,6 @@ namespace osu.Game.Online.Metadata
await ProcessChanges(updates.BeatmapSetIDs);
}
protected Task ProcessChanges(int[] beatmapSetIDs)
{
foreach (int id in beatmapSetIDs)
{
Logger.Log($"Processing {id}...");
beatmapUpdater.Queue(id);
}
return Task.CompletedTask;
}
public override Task<BeatmapUpdates> GetChangesSince(int queueId)
{
if (connector?.IsConnected.Value != true)
@@ -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
/// <summary>
/// The joined <see cref="MultiplayerRoom"/>.
/// </summary>
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();
}
}));
@@ -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;
}
}
+2 -2
View File
@@ -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);
@@ -61,8 +61,15 @@ namespace osu.Game.Online.Spectator
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).
}
}
+3 -1
View File
@@ -287,7 +287,9 @@ namespace osu.Game
dependencies.CacheAs(spectatorClient = new OnlineSpectatorClient(endpoints));
dependencies.CacheAs(multiplayerClient = new OnlineMultiplayerClient(endpoints));
dependencies.CacheAs(metadataClient = new OnlineMetadataClient(endpoints, beatmapUpdater));
dependencies.CacheAs(metadataClient = new OnlineMetadataClient(endpoints));
AddInternal(new BeatmapOnlineChangeIngest(beatmapUpdater, realm, metadataClient));
BeatmapManager.ProcessBeatmap = set => beatmapUpdater.Process(set);
@@ -23,6 +23,7 @@ using osuTK;
using osuTK.Graphics;
using osu.Framework.Localisation;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics.Cursor;
using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Overlays.BeatmapSet.Scores
@@ -38,8 +39,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
private readonly FillFlowContainer backgroundFlow;
private Color4 highAccuracyColour;
public ScoreTable()
{
RelativeSizeAxes = Axes.X;
@@ -57,12 +56,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
highAccuracyColour = colours.GreenLight;
}
/// <summary>
/// The statistics that appear in the table, in order of appearance.
/// </summary>
@@ -158,12 +151,10 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Current = scoreManager.GetBindableTotalScoreString(score),
Font = OsuFont.GetFont(size: text_size, weight: index == 0 ? FontWeight.Bold : FontWeight.Medium)
},
new OsuSpriteText
new StatisticText(score.Accuracy, 1, showTooltip: false)
{
Margin = new MarginPadding { Right = horizontal_inset },
Text = score.DisplayAccuracy,
Font = OsuFont.GetFont(size: text_size),
Colour = score.Accuracy == 1 ? highAccuracyColour : Color4.White
},
new UpdateableFlag(score.User.CountryCode)
{
@@ -171,14 +162,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
ShowPlaceholderOnUnknown = false,
},
username,
new OsuSpriteText
{
Text = score.MaxCombo.ToLocalisableString(@"0\x"),
Font = OsuFont.GetFont(size: text_size),
#pragma warning disable 618
Colour = score.MaxCombo == score.BeatmapInfo.MaxCombo ? highAccuracyColour : Color4.White
new StatisticText(score.MaxCombo, score.BeatmapInfo.MaxCombo, @"0\x"),
#pragma warning restore 618
}
};
var availableStatistics = score.GetStatisticsForDisplay().ToDictionary(tuple => tuple.Result);
@@ -188,23 +174,13 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
if (!availableStatistics.TryGetValue(result.result, out var stat))
stat = new HitResultDisplayStatistic(result.result, 0, null, result.displayName);
content.Add(new OsuSpriteText
{
Text = stat.MaxCount == null ? stat.Count.ToLocalisableString(@"N0") : (LocalisableString)$"{stat.Count}/{stat.MaxCount}",
Font = OsuFont.GetFont(size: text_size),
Colour = stat.Count == 0 ? Color4.Gray : Color4.White
});
content.Add(new StatisticText(stat.Count, stat.MaxCount, @"N0") { Colour = stat.Count == 0 ? Color4.Gray : Color4.White });
}
if (showPerformancePoints)
{
Debug.Assert(score.PP != null);
content.Add(new OsuSpriteText
{
Text = score.PP.ToLocalisableString(@"N0"),
Font = OsuFont.GetFont(size: text_size)
});
content.Add(new StatisticText(score.PP.Value, format: @"N0"));
}
content.Add(new ScoreboardTime(score.Date, text_size)
@@ -243,5 +219,31 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Colour = colourProvider.Foreground1;
}
}
private class StatisticText : OsuSpriteText, IHasTooltip
{
private readonly double count;
private readonly double? maxCount;
private readonly bool showTooltip;
public LocalisableString TooltipText => maxCount == null || !showTooltip ? string.Empty : $"{count}/{maxCount}";
public StatisticText(double count, double? maxCount = null, string format = null, bool showTooltip = true)
{
this.count = count;
this.maxCount = maxCount;
this.showTooltip = showTooltip;
Text = count.ToLocalisableString(format);
Font = OsuFont.GetFont(size: text_size);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
if (count == maxCount)
Colour = colours.GreenLight;
}
}
}
}
+1 -3
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using osu.Framework.Bindables;
using osu.Game.Beatmaps;
@@ -29,7 +27,7 @@ namespace osu.Game.Rulesets.Mods
/// <summary>
/// A function that can extract the current value of this setting from a beatmap difficulty for display purposes.
/// </summary>
public Func<IBeatmapDifficultyInfo, float> ReadCurrentFromDifficulty;
public Func<IBeatmapDifficultyInfo, float>? ReadCurrentFromDifficulty;
public float Precision
{
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Beatmaps;
namespace osu.Game.Rulesets.Mods
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
namespace osu.Game.Rulesets.Mods
{
/// <summary>
-2
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
namespace osu.Game.Rulesets.Mods
{
/// <summary>
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
namespace osu.Game.Rulesets.Mods
{
public interface IApplicableToAudio : IApplicableToTrack, IApplicableToSample
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Beatmaps;
namespace osu.Game.Rulesets.Mods
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Beatmaps;
namespace osu.Game.Rulesets.Mods
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Beatmaps;
namespace osu.Game.Rulesets.Mods
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Beatmaps;
namespace osu.Game.Rulesets.Mods
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mods
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Collections.Generic;
using osu.Framework.Extensions.IEnumerableExtensions;
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI;
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Screens.Play;
namespace osu.Game.Rulesets.Mods
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mods
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mods
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Screens.Play;
namespace osu.Game.Rulesets.Mods
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
namespace osu.Game.Rulesets.Mods
{
/// <summary>
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Audio;
namespace osu.Game.Rulesets.Mods
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Audio;
namespace osu.Game.Rulesets.Mods
-2
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Collections.Generic;
using osu.Game.Beatmaps;
+1 -3
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
@@ -45,7 +43,7 @@ namespace osu.Game.Rulesets.Mods
/// </summary>
public readonly ModCreatedUser User;
public ModReplayData(Replay replay, ModCreatedUser user = null)
public ModReplayData(Replay replay, ModCreatedUser? user = null)
{
Replay = replay;
User = user ?? new ModCreatedUser();
-2
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Bindables;
namespace osu.Game.Rulesets.Mods
-2
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using osu.Framework.Graphics.Sprites;
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Configuration;
namespace osu.Game.Rulesets.Mods
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mods
-2
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
+3 -5
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
@@ -117,7 +115,7 @@ namespace osu.Game.Rulesets.Mods
[JsonIgnore]
public virtual Type[] IncompatibleMods => Array.Empty<Type>();
private IReadOnlyList<IBindable> settingsBacking;
private IReadOnlyList<IBindable>? settingsBacking;
/// <summary>
/// A list of the all <see cref="IBindable"/> settings within this mod.
@@ -216,8 +214,8 @@ namespace osu.Game.Rulesets.Mods
public bool Equals(IBindable x, IBindable y)
{
object xValue = x?.GetUnderlyingSettingValue();
object yValue = y?.GetUnderlyingSettingValue();
object xValue = x.GetUnderlyingSettingValue();
object yValue = y.GetUnderlyingSettingValue();
return EqualityComparer<object>.Default.Equals(xValue, yValue);
}
+1 -3
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
@@ -79,7 +77,7 @@ namespace osu.Game.Rulesets.Mods
// Apply a fixed rate change when missing, allowing the player to catch up when the rate is too fast.
private const double rate_change_on_miss = 0.95d;
private IAdjustableAudioComponent track;
private IAdjustableAudioComponent? track;
private double targetRate = 1d;
/// <summary>
-2
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Collections.Generic;
using osu.Framework.Graphics.Sprites;
-2
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
+2 -4
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Bindables;
using osu.Game.Configuration;
using osu.Game.Screens.Play;
@@ -11,7 +9,7 @@ namespace osu.Game.Rulesets.Mods
{
public abstract class ModBlockFail : Mod, IApplicableFailOverride, IApplicableToHUD, IReadFromConfig
{
private Bindable<bool> showHealthBar;
private readonly Bindable<bool> showHealthBar = new Bindable<bool>();
/// <summary>
/// We never fail, 'yo.
@@ -22,7 +20,7 @@ namespace osu.Game.Rulesets.Mods
public void ReadFromConfig(OsuConfigManager config)
{
showHealthBar = config.GetBindable<bool>(OsuSetting.ShowHealthDisplayWhenCantFail);
config.BindWith(OsuSetting.ShowHealthDisplayWhenCantFail, showHealthBar);
}
public void ApplyToHUD(HUDOverlay overlay)
-2
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Linq;
using osu.Framework.Graphics.Sprites;
-2
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Graphics.Sprites;
namespace osu.Game.Rulesets.Mods
-2
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Linq;
using osu.Framework.Bindables;
-2
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
using osu.Game.Configuration;
-2
View File
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;

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