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:
@@ -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
@@ -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. -->
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,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('_', '-');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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).
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user