Merge branch 'master' into websocket-chat-2

This commit is contained in:
Dean Herbert 2022-11-02 14:56:13 +09:00 committed by GitHub
commit 2b04e852be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 124 additions and 70 deletions

View File

@ -52,7 +52,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1021.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.1028.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.1101.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. -->

View File

@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Acronym => "HO";
public override double ScoreMultiplier => 1;
public override double ScoreMultiplier => 0.9;
public override LocalisableString Description => @"Replaces all hold notes with normal notes.";

View File

@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Tests
new object[] { LegacyMods.Autoplay, new[] { typeof(OsuModAutoplay) } },
new object[] { LegacyMods.SpunOut, new[] { typeof(OsuModSpunOut) } },
new object[] { LegacyMods.Autopilot, new[] { typeof(OsuModAutopilot) } },
new object[] { LegacyMods.Target, new[] { typeof(OsuModTarget) } },
new object[] { LegacyMods.Target, new[] { typeof(OsuModTargetPractice) } },
new object[] { LegacyMods.HardRock | LegacyMods.DoubleTime, new[] { typeof(OsuModHardRock), typeof(OsuModDoubleTime) } }
};

View File

@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override LocalisableString Description => "It never gets boring!";
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModTarget)).ToArray();
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModTargetPractice)).ToArray();
[SettingSource("Angle sharpness", "How sharp angles should be", SettingControlType = typeof(SettingsSlider<float>))]
public BindableFloat AngleSharpness { get; } = new BindableFloat(7)

View File

@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override ModType Type => ModType.Automation;
public override LocalisableString Description => @"Spinners will be automatically completed.";
public override double ScoreMultiplier => 0.9;
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot), typeof(OsuModTarget) };
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot), typeof(OsuModTargetPractice) };
public void ApplyToDrawableHitObject(DrawableHitObject hitObject)
{

View File

@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override ModType Type => ModType.DifficultyIncrease;
public override LocalisableString Description => @"Once you start a slider, follow precisely or get a miss.";
public override double ScoreMultiplier => 1.0;
public override Type[] IncompatibleMods => new[] { typeof(ModClassic), typeof(OsuModTarget) };
public override Type[] IncompatibleMods => new[] { typeof(ModClassic), typeof(OsuModTargetPractice) };
public void ApplyToDrawableHitObject(DrawableHitObject drawable)
{

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[]
{
typeof(OsuModAutopilot),
typeof(OsuModTarget),
typeof(OsuModTargetPractice),
}).ToArray();
}
}

View File

@ -32,16 +32,15 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModTarget : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset<OsuHitObject>,
IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride,
IHasSeed, IHidesApproachCircles
public class OsuModTargetPractice : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset<OsuHitObject>,
IApplicableToHealthProcessor, IApplicableToDifficulty, IApplicableFailOverride, IHasSeed, IHidesApproachCircles
{
public override string Name => "Target";
public override string Name => "Target Practice";
public override string Acronym => "TP";
public override ModType Type => ModType.Conversion;
public override IconUsage? Icon => OsuIcon.ModTarget;
public override LocalisableString Description => @"Practice keeping up with the beat of the song.";
public override double ScoreMultiplier => 1;
public override double ScoreMultiplier => 0.1;
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[]
{

View File

@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Osu
yield return new OsuModSpunOut();
if (mods.HasFlagFast(LegacyMods.Target))
yield return new OsuModTarget();
yield return new OsuModTargetPractice();
if (mods.HasFlagFast(LegacyMods.TouchDevice))
yield return new OsuModTouchDevice();
@ -131,7 +131,7 @@ namespace osu.Game.Rulesets.Osu
value |= LegacyMods.SpunOut;
break;
case OsuModTarget:
case OsuModTargetPractice:
value |= LegacyMods.Target;
break;
@ -170,7 +170,7 @@ namespace osu.Game.Rulesets.Osu
case ModType.Conversion:
return new Mod[]
{
new OsuModTarget(),
new OsuModTargetPractice(),
new OsuModDifficultyAdjust(),
new OsuModClassic(),
new OsuModRandom(),

View File

@ -121,12 +121,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
innerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue.Darken(0.5f), colour.NewValue.Darken(0.6f));
flash.Colour = colour.NewValue;
updateStateTransforms(drawableObject, drawableObject.State.Value);
// Accent colour may be changed many times during a paused gameplay state.
// Schedule the change to avoid transforms piling up.
Scheduler.AddOnce(updateStateTransforms);
}, true);
drawableObject.ApplyCustomUpdateState += updateStateTransforms;
}
private void updateStateTransforms() => updateStateTransforms(drawableObject, drawableObject.State.Value);
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
{
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
@ -171,7 +175,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
// This is to give it a bomb-like effect, with the border "triggering" its animation when getting close.
using (BeginDelayedSequence(flash_in_duration / 12))
{
outerGradient.ResizeTo(outerGradient.Size * shrink_size, resize_duration, Easing.OutElasticHalf);
outerGradient.ResizeTo(OUTER_GRADIENT_SIZE * shrink_size, resize_duration, Easing.OutElasticHalf);
outerGradient
.FadeColour(Color4.White, 80)
.Then()

View File

@ -7,6 +7,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
@ -66,7 +67,7 @@ namespace osu.Game.Tests.Editing
{
AddStep($"set slider multiplier = {multiplier}", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = multiplier);
assertSnapDistance(100 * multiplier);
assertSnapDistance(100 * multiplier, null, true);
}
[TestCase(1)]
@ -79,7 +80,20 @@ namespace osu.Game.Tests.Editing
{
SliderVelocity = multiplier
}
});
}, false);
}
[TestCase(1)]
[TestCase(2)]
public void TestSpeedMultiplierDoesChangeDistanceSnap(float multiplier)
{
assertSnapDistance(100 * multiplier, new HitObject
{
DifficultyControlPoint = new DifficultyControlPoint
{
SliderVelocity = multiplier
}
}, true);
}
[TestCase(1)]
@ -88,7 +102,32 @@ namespace osu.Game.Tests.Editing
{
AddStep($"set divisor = {divisor}", () => BeatDivisor.Value = divisor);
assertSnapDistance(100f / divisor);
assertSnapDistance(100f / divisor, null, true);
}
/// <summary>
/// The basic distance-duration functions should always include slider velocity of the reference object.
/// </summary>
[Test]
public void TestConversionsWithSliderVelocity()
{
const float base_distance = 100;
const float slider_velocity = 1.2f;
var referenceObject = new HitObject
{
DifficultyControlPoint = new DifficultyControlPoint
{
SliderVelocity = slider_velocity
}
};
assertSnapDistance(base_distance * slider_velocity, referenceObject, true);
assertSnappedDistance(base_distance * slider_velocity + 10, base_distance * slider_velocity, referenceObject);
assertSnappedDuration(base_distance * slider_velocity + 10, 1000, referenceObject);
assertDistanceToDuration(base_distance * slider_velocity, 1000, referenceObject);
assertDurationToDistance(1000, base_distance * slider_velocity, referenceObject);
}
[Test]
@ -197,20 +236,20 @@ namespace osu.Game.Tests.Editing
assertSnappedDistance(400, 400);
}
private void assertSnapDistance(float expectedDistance, HitObject? hitObject = null)
=> AddAssert($"distance is {expectedDistance}", () => composer.GetBeatSnapDistanceAt(hitObject ?? new HitObject()), () => Is.EqualTo(expectedDistance));
private void assertSnapDistance(float expectedDistance, HitObject? referenceObject, bool includeSliderVelocity)
=> AddAssert($"distance is {expectedDistance}", () => composer.GetBeatSnapDistanceAt(referenceObject ?? new HitObject(), includeSliderVelocity), () => Is.EqualTo(expectedDistance).Within(Precision.FLOAT_EPSILON));
private void assertDurationToDistance(double duration, float expectedDistance)
=> AddAssert($"duration = {duration} -> distance = {expectedDistance}", () => composer.DurationToDistance(new HitObject(), duration) == expectedDistance);
private void assertDurationToDistance(double duration, float expectedDistance, HitObject? referenceObject = null)
=> AddAssert($"duration = {duration} -> distance = {expectedDistance}", () => composer.DurationToDistance(referenceObject ?? new HitObject(), duration), () => Is.EqualTo(expectedDistance).Within(Precision.FLOAT_EPSILON));
private void assertDistanceToDuration(float distance, double expectedDuration)
=> AddAssert($"distance = {distance} -> duration = {expectedDuration}", () => composer.DistanceToDuration(new HitObject(), distance) == expectedDuration);
private void assertDistanceToDuration(float distance, double expectedDuration, HitObject? referenceObject = null)
=> AddAssert($"distance = {distance} -> duration = {expectedDuration}", () => composer.DistanceToDuration(referenceObject ?? new HitObject(), distance), () => Is.EqualTo(expectedDuration).Within(Precision.FLOAT_EPSILON));
private void assertSnappedDuration(float distance, double expectedDuration)
=> AddAssert($"distance = {distance} -> duration = {expectedDuration} (snapped)", () => composer.FindSnappedDuration(new HitObject(), distance) == expectedDuration);
private void assertSnappedDuration(float distance, double expectedDuration, HitObject? referenceObject = null)
=> AddAssert($"distance = {distance} -> duration = {expectedDuration} (snapped)", () => composer.FindSnappedDuration(referenceObject ?? new HitObject(), distance), () => Is.EqualTo(expectedDuration).Within(Precision.FLOAT_EPSILON));
private void assertSnappedDistance(float distance, float expectedDistance)
=> AddAssert($"distance = {distance} -> distance = {expectedDistance} (snapped)", () => composer.FindSnappedDistance(new HitObject(), distance) == expectedDistance);
private void assertSnappedDistance(float distance, float expectedDistance, HitObject? referenceObject = null)
=> AddAssert($"distance = {distance} -> distance = {expectedDistance} (snapped)", () => composer.FindSnappedDistance(referenceObject ?? new HitObject(), distance), () => Is.EqualTo(expectedDistance).Within(Precision.FLOAT_EPSILON));
private class TestHitObjectComposer : OsuHitObjectComposer
{

View File

@ -193,7 +193,7 @@ namespace osu.Game.Tests.Visual.Editing
IBindable<double> IDistanceSnapProvider.DistanceSpacingMultiplier => DistanceSpacingMultiplier;
public float GetBeatSnapDistanceAt(HitObject referenceObject) => beat_snap_distance;
public float GetBeatSnapDistanceAt(HitObject referenceObject, bool useReferenceSliderVelocity = true) => beat_snap_distance;
public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration;

View File

@ -47,6 +47,7 @@ namespace osu.Game.Beatmaps
// Shallow clone isn't enough to ensure we don't mutate beatmap info unexpectedly.
// Can potentially be removed after `Beatmap.Difficulty` doesn't save back to `Beatmap.BeatmapInfo`.
original.BeatmapInfo = original.BeatmapInfo.Clone();
original.ControlPointInfo = original.ControlPointInfo.DeepClone();
return ConvertBeatmap(original, cancellationToken);
}

View File

@ -25,10 +25,9 @@ namespace osu.Game.Graphics.UserInterfaceV2
protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuBreadcrumbDisplayDirectory(directory, displayName);
[BackgroundDependencyLoader]
private void load()
public OsuDirectorySelectorBreadcrumbDisplay()
{
Height = 50;
Padding = new MarginPadding(15);
}
private class OsuBreadcrumbDisplayComputer : OsuBreadcrumbDisplayDirectory

View File

@ -9,10 +9,21 @@ namespace osu.Game.Online
{
public abstract class PersistentEndpointClient : IAsyncDisposable
{
/// <summary>
/// An event notifying the <see cref="PersistentEndpointClientConnector"/> that the connection has been closed
/// </summary>
public event Func<Exception?, Task>? Closed;
/// <summary>
/// Notifies the <see cref="PersistentEndpointClientConnector"/> that the connection has been closed.
/// </summary>
/// <param name="exception">The exception that the connection closed with.</param>
protected Task InvokeClosed(Exception? exception) => Closed?.Invoke(exception) ?? Task.CompletedTask;
/// <summary>
/// Connects the client to the remote service to begin processing messages.
/// </summary>
/// <param name="cancellationToken">A cancellation token to stop processing messages.</param>
public abstract Task ConnectAsync(CancellationToken cancellationToken);
public virtual ValueTask DisposeAsync()

View File

@ -14,7 +14,7 @@ namespace osu.Game.Online
public abstract class PersistentEndpointClientConnector : IDisposable
{
/// <summary>
/// Whether this is connected to the hub, use <see cref="CurrentConnection"/> to access the connection, if this is <c>true</c>.
/// Whether the managed connection is currently connected. When <c>true</c> use <see cref="CurrentConnection"/> to access the connection.
/// </summary>
public IBindable<bool> IsConnected => isConnected;
@ -31,7 +31,7 @@ namespace osu.Game.Online
private CancellationTokenSource connectCancelSource = new CancellationTokenSource();
/// <summary>
/// Constructs a new <see cref="HubClientConnector"/>.
/// Constructs a new <see cref="PersistentEndpointClientConnector"/>.
/// </summary>
/// <param name="api"> An API provider used to react to connection state changes.</param>
protected PersistentEndpointClientConnector(IAPIProvider api)
@ -126,6 +126,10 @@ namespace osu.Game.Online
await Task.Delay(5000, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Creates a new <see cref="PersistentEndpointClient"/>.
/// </summary>
/// <param name="cancellationToken">A cancellation token to stop the process.</param>
protected abstract Task<PersistentEndpointClient> BuildConnectionAsync(CancellationToken cancellationToken);
private async Task onConnectionClosed(Exception? ex, CancellationToken cancellationToken)

View File

@ -315,7 +315,7 @@ namespace osu.Game
Beatmap.BindValueChanged(beatmapChanged, true);
applySafeAreaConsiderations = LocalConfig.GetBindable<bool>(OsuSetting.SafeAreaConsiderations);
applySafeAreaConsiderations.BindValueChanged(apply => SafeAreaContainer.SafeAreaOverrideEdges = apply.NewValue ? SafeAreaOverrideEdges : Edges.All);
applySafeAreaConsiderations.BindValueChanged(apply => SafeAreaContainer.SafeAreaOverrideEdges = apply.NewValue ? SafeAreaOverrideEdges : Edges.All, true);
}
private ExternalLinkOpener externalLinkOpener;

View File

@ -259,9 +259,9 @@ namespace osu.Game.Rulesets.Edit
return true;
}
public virtual float GetBeatSnapDistanceAt(HitObject referenceObject)
public virtual float GetBeatSnapDistanceAt(HitObject referenceObject, bool useReferenceSliderVelocity = true)
{
return (float)(100 * EditorBeatmap.Difficulty.SliderMultiplier * 1 / BeatSnapProvider.BeatDivisor);
return (float)(100 * (useReferenceSliderVelocity ? referenceObject.DifficultyControlPoint.SliderVelocity : 1) * EditorBeatmap.Difficulty.SliderMultiplier * 1 / BeatSnapProvider.BeatDivisor);
}
public virtual float DurationToDistance(HitObject referenceObject, double duration)

View File

@ -27,8 +27,9 @@ namespace osu.Game.Rulesets.Edit
/// Retrieves the distance between two points within a timing point that are one beat length apart.
/// </summary>
/// <param name="referenceObject">An object to be used as a reference point for this operation.</param>
/// <param name="useReferenceSliderVelocity">Whether the <paramref name="referenceObject"/>'s slider velocity should be factored into the returned distance.</param>
/// <returns>The distance between two points residing in the timing point that are one beat length apart.</returns>
float GetBeatSnapDistanceAt(HitObject referenceObject);
float GetBeatSnapDistanceAt(HitObject referenceObject, bool useReferenceSliderVelocity = true);
/// <summary>
/// Converts a duration to a distance without applying any snapping.

View File

@ -266,6 +266,10 @@ namespace osu.Game.Rulesets.Objects.Drawables
updateState(ArmedState.Miss, true);
else
updateState(ArmedState.Idle, true);
// Combo colour may have been applied via a bindable flow while no object entry was attached.
// Update here to ensure we're in a good state.
UpdateComboColour();
}
}

View File

@ -158,7 +158,7 @@ namespace osu.Game.Rulesets
}
catch (Exception e)
{
LogFailedLoad(assembly.FullName, e);
LogFailedLoad(assembly.GetName().Name.Split('.').Last(), e);
}
}
@ -168,14 +168,14 @@ namespace osu.Game.Rulesets
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
protected void Dispose(bool disposing)
{
AppDomain.CurrentDomain.AssemblyResolve -= resolveRulesetDependencyAssembly;
}
protected void LogFailedLoad(string name, Exception exception)
{
Logger.Log($"Could not load ruleset {name}. Please check for an update from the developer.", level: LogLevel.Error);
Logger.Log($"Could not load ruleset \"{name}\". Please check for an update from the developer.", level: LogLevel.Error);
Logger.Log($"Ruleset load failed: {exception}");
}

View File

@ -97,7 +97,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
private void updateSpacing()
{
float distanceSpacingMultiplier = (float)DistanceSpacingMultiplier.Value;
float beatSnapDistance = SnapProvider.GetBeatSnapDistanceAt(ReferenceObject);
float beatSnapDistance = SnapProvider.GetBeatSnapDistanceAt(ReferenceObject, false);
DistanceBetweenTicks = beatSnapDistance * distanceSpacingMultiplier;

View File

@ -1,22 +1,27 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Settings;
namespace osu.Game.Screens.Play.PlayerSettings
{
public class PlayerCheckbox : OsuCheckbox
public class PlayerCheckbox : SettingsCheckbox
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
protected override Drawable CreateControl() => new PlayerCheckboxControl();
public class PlayerCheckboxControl : OsuCheckbox
{
Nub.AccentColour = colours.Yellow;
Nub.GlowingAccentColour = colours.YellowLighter;
Nub.GlowColour = colours.YellowDark;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Nub.AccentColour = colours.Yellow;
Nub.GlowingAccentColour = colours.YellowLighter;
Nub.GlowColour = colours.YellowDark;
}
}
}
}

View File

@ -1,12 +1,9 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osu.Game.Graphics.Sprites;
using osu.Game.Localisation;
namespace osu.Game.Screens.Play.PlayerSettings
@ -24,26 +21,16 @@ namespace osu.Game.Screens.Play.PlayerSettings
{
Children = new Drawable[]
{
new OsuSpriteText
{
Text = GameplaySettingsStrings.BackgroundDim
},
dimSliderBar = new PlayerSliderBar<double>
{
LabelText = GameplaySettingsStrings.BackgroundDim,
DisplayAsPercentage = true
},
new OsuSpriteText
{
Text = GameplaySettingsStrings.BackgroundBlur
},
blurSliderBar = new PlayerSliderBar<double>
{
LabelText = GameplaySettingsStrings.BackgroundBlur,
DisplayAsPercentage = true
},
new OsuSpriteText
{
Text = "Toggles:"
},
showStoryboardToggle = new PlayerCheckbox { LabelText = GraphicsSettingsStrings.StoryboardVideo },
beatmapSkinsToggle = new PlayerCheckbox { LabelText = SkinSettingsStrings.BeatmapSkins },
beatmapColorsToggle = new PlayerCheckbox { LabelText = SkinSettingsStrings.BeatmapColours },

View File

@ -35,7 +35,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Realm" Version="10.17.0" />
<PackageReference Include="ppy.osu.Framework" Version="2022.1028.0" />
<PackageReference Include="ppy.osu.Framework" Version="2022.1101.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1021.0" />
<PackageReference Include="Sentry" Version="3.22.0" />
<PackageReference Include="SharpCompress" Version="0.32.2" />

View File

@ -62,7 +62,7 @@
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1021.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.1028.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.1101.0" />
</ItemGroup>
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net6.0) -->
<PropertyGroup>
@ -82,7 +82,7 @@
<PackageReference Include="DiffPlex" Version="1.7.1" />
<PackageReference Include="Humanizer" Version="2.14.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="ppy.osu.Framework" Version="2022.1028.0" />
<PackageReference Include="ppy.osu.Framework" Version="2022.1101.0" />
<PackageReference Include="SharpCompress" Version="0.32.2" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />