mirror of
https://github.com/ppy/osu
synced 2024-12-14 19:06:07 +00:00
Merge branch 'master' into freemods
This commit is contained in:
commit
cf5233c6ab
@ -0,0 +1,31 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Rulesets.Mania.Mods;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling.Algorithms;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests.Mods
|
||||||
|
{
|
||||||
|
public class TestSceneManiaModConstantSpeed : ModTestScene
|
||||||
|
{
|
||||||
|
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestConstantScroll() => CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModConstantSpeed(),
|
||||||
|
PassCondition = () =>
|
||||||
|
{
|
||||||
|
var hitObject = Player.ChildrenOfType<DrawableManiaHitObject>().FirstOrDefault();
|
||||||
|
return hitObject?.Dependencies.Get<IScrollingInfo>().Algorithm is ConstantScrollAlgorithm;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -238,6 +238,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
new ManiaModMirror(),
|
new ManiaModMirror(),
|
||||||
new ManiaModDifficultyAdjust(),
|
new ManiaModDifficultyAdjust(),
|
||||||
new ManiaModInvert(),
|
new ManiaModInvert(),
|
||||||
|
new ManiaModConstantSpeed()
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.Automation:
|
case ModType.Automation:
|
||||||
|
35
osu.Game.Rulesets.Mania/Mods/ManiaModConstantSpeed.cs
Normal file
35
osu.Game.Rulesets.Mania/Mods/ManiaModConstantSpeed.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// 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 osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
|
{
|
||||||
|
public class ManiaModConstantSpeed : Mod, IApplicableToDrawableRuleset<ManiaHitObject>
|
||||||
|
{
|
||||||
|
public override string Name => "Constant Speed";
|
||||||
|
|
||||||
|
public override string Acronym => "CS";
|
||||||
|
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
|
public override string Description => "No more tricky speed changes!";
|
||||||
|
|
||||||
|
public override IconUsage? Icon => FontAwesome.Solid.Equals;
|
||||||
|
|
||||||
|
public override ModType Type => ModType.Conversion;
|
||||||
|
|
||||||
|
public override bool Ranked => false;
|
||||||
|
|
||||||
|
public void ApplyToDrawableRuleset(DrawableRuleset<ManiaHitObject> drawableRuleset)
|
||||||
|
{
|
||||||
|
var maniaRuleset = (DrawableManiaRuleset)drawableRuleset;
|
||||||
|
maniaRuleset.ScrollMethod = ScrollVisualisationMethod.Constant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -11,6 +12,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
@ -49,6 +51,22 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
|
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
|
||||||
|
|
||||||
|
public ScrollVisualisationMethod ScrollMethod
|
||||||
|
{
|
||||||
|
get => scrollMethod;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (IsLoaded)
|
||||||
|
throw new InvalidOperationException($"Can't alter {nameof(ScrollMethod)} after ruleset is already loaded");
|
||||||
|
|
||||||
|
scrollMethod = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ScrollVisualisationMethod scrollMethod = ScrollVisualisationMethod.Sequential;
|
||||||
|
|
||||||
|
protected override ScrollVisualisationMethod VisualisationMethod => scrollMethod;
|
||||||
|
|
||||||
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
||||||
private readonly Bindable<double> configTimeRange = new BindableDouble();
|
private readonly Bindable<double> configTimeRange = new BindableDouble();
|
||||||
|
|
||||||
|
@ -3,8 +3,12 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Screens;
|
||||||
using osu.Game.Screens.OnlinePlay;
|
using osu.Game.Screens.OnlinePlay;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
@ -58,5 +62,45 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
AddStep("end operation", () => operation.Dispose());
|
AddStep("end operation", () => operation.Dispose());
|
||||||
AddAssert("operation is ended", () => !operationInProgress.Value);
|
AddAssert("operation is ended", () => !operationInProgress.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestOperationDisposalAfterScreenExit()
|
||||||
|
{
|
||||||
|
TestScreenWithTracker screen = null;
|
||||||
|
OsuScreenStack stack;
|
||||||
|
IDisposable operation = null;
|
||||||
|
|
||||||
|
AddStep("create screen with tracker", () =>
|
||||||
|
{
|
||||||
|
Child = stack = new OsuScreenStack
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
};
|
||||||
|
|
||||||
|
stack.Push(screen = new TestScreenWithTracker());
|
||||||
|
});
|
||||||
|
AddUntilStep("wait for loaded", () => screen.IsLoaded);
|
||||||
|
|
||||||
|
AddStep("begin operation", () => operation = screen.OngoingOperationTracker.BeginOperation());
|
||||||
|
AddAssert("operation in progress", () => screen.OngoingOperationTracker.InProgress.Value);
|
||||||
|
|
||||||
|
AddStep("dispose after screen exit", () =>
|
||||||
|
{
|
||||||
|
screen.Exit();
|
||||||
|
operation.Dispose();
|
||||||
|
});
|
||||||
|
AddAssert("operation ended", () => !screen.OngoingOperationTracker.InProgress.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestScreenWithTracker : OsuScreen
|
||||||
|
{
|
||||||
|
public OngoingOperationTracker OngoingOperationTracker { get; private set; }
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
InternalChild = OngoingOperationTracker = new OngoingOperationTracker();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ using osu.Game.Rulesets.UI;
|
|||||||
namespace osu.Game.Tests.Online
|
namespace osu.Game.Tests.Online
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestAPIModSerialization
|
public class TestAPIModJsonSerialization
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void TestAcronymIsPreserved()
|
public void TestAcronymIsPreserved()
|
139
osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs
Normal file
139
osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using MessagePack;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Online
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestAPIModMessagePackSerialization
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestAcronymIsPreserved()
|
||||||
|
{
|
||||||
|
var apiMod = new APIMod(new TestMod());
|
||||||
|
|
||||||
|
var deserialized = MessagePackSerializer.Deserialize<APIMod>(MessagePackSerializer.Serialize(apiMod));
|
||||||
|
|
||||||
|
Assert.That(deserialized.Acronym, Is.EqualTo(apiMod.Acronym));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRawSettingIsPreserved()
|
||||||
|
{
|
||||||
|
var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } });
|
||||||
|
|
||||||
|
var deserialized = MessagePackSerializer.Deserialize<APIMod>(MessagePackSerializer.Serialize(apiMod));
|
||||||
|
|
||||||
|
Assert.That(deserialized.Settings, Contains.Key("test_setting").With.ContainValue(2.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestConvertedModHasCorrectSetting()
|
||||||
|
{
|
||||||
|
var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } });
|
||||||
|
|
||||||
|
var deserialized = MessagePackSerializer.Deserialize<APIMod>(MessagePackSerializer.Serialize(apiMod));
|
||||||
|
var converted = (TestMod)deserialized.ToMod(new TestRuleset());
|
||||||
|
|
||||||
|
Assert.That(converted.TestSetting.Value, Is.EqualTo(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDeserialiseTimeRampMod()
|
||||||
|
{
|
||||||
|
// Create the mod with values different from default.
|
||||||
|
var apiMod = new APIMod(new TestModTimeRamp
|
||||||
|
{
|
||||||
|
AdjustPitch = { Value = false },
|
||||||
|
InitialRate = { Value = 1.25 },
|
||||||
|
FinalRate = { Value = 0.25 }
|
||||||
|
});
|
||||||
|
|
||||||
|
var deserialised = MessagePackSerializer.Deserialize<APIMod>(MessagePackSerializer.Serialize(apiMod));
|
||||||
|
var converted = (TestModTimeRamp)deserialised.ToMod(new TestRuleset());
|
||||||
|
|
||||||
|
Assert.That(converted.AdjustPitch.Value, Is.EqualTo(false));
|
||||||
|
Assert.That(converted.InitialRate.Value, Is.EqualTo(1.25));
|
||||||
|
Assert.That(converted.FinalRate.Value, Is.EqualTo(0.25));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestRuleset : Ruleset
|
||||||
|
{
|
||||||
|
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[]
|
||||||
|
{
|
||||||
|
new TestMod(),
|
||||||
|
new TestModTimeRamp(),
|
||||||
|
};
|
||||||
|
|
||||||
|
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => throw new System.NotImplementedException();
|
||||||
|
|
||||||
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new System.NotImplementedException();
|
||||||
|
|
||||||
|
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new System.NotImplementedException();
|
||||||
|
|
||||||
|
public override string Description { get; } = string.Empty;
|
||||||
|
public override string ShortName { get; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestMod : Mod
|
||||||
|
{
|
||||||
|
public override string Name => "Test Mod";
|
||||||
|
public override string Acronym => "TM";
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
|
[SettingSource("Test")]
|
||||||
|
public BindableNumber<double> TestSetting { get; } = new BindableDouble
|
||||||
|
{
|
||||||
|
MinValue = 0,
|
||||||
|
MaxValue = 10,
|
||||||
|
Default = 5,
|
||||||
|
Precision = 0.01,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestModTimeRamp : ModTimeRamp
|
||||||
|
{
|
||||||
|
public override string Name => "Test Mod";
|
||||||
|
public override string Acronym => "TMTR";
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
|
[SettingSource("Initial rate", "The starting speed of the track")]
|
||||||
|
public override BindableNumber<double> InitialRate { get; } = new BindableDouble
|
||||||
|
{
|
||||||
|
MinValue = 1,
|
||||||
|
MaxValue = 2,
|
||||||
|
Default = 1.5,
|
||||||
|
Value = 1.5,
|
||||||
|
Precision = 0.01,
|
||||||
|
};
|
||||||
|
|
||||||
|
[SettingSource("Final rate", "The speed increase to ramp towards")]
|
||||||
|
public override BindableNumber<double> FinalRate { get; } = new BindableDouble
|
||||||
|
{
|
||||||
|
MinValue = 0,
|
||||||
|
MaxValue = 1,
|
||||||
|
Default = 0.5,
|
||||||
|
Value = 0.5,
|
||||||
|
Precision = 0.01,
|
||||||
|
};
|
||||||
|
|
||||||
|
[SettingSource("Adjust pitch", "Should pitch be adjusted with speed")]
|
||||||
|
public override BindableBool AdjustPitch { get; } = new BindableBool
|
||||||
|
{
|
||||||
|
Default = true,
|
||||||
|
Value = true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -46,6 +47,32 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddStep("show", () => modSelect.Show());
|
AddStep("show", () => modSelect.Show());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAnimationFlushOnClose()
|
||||||
|
{
|
||||||
|
changeRuleset(0);
|
||||||
|
|
||||||
|
AddStep("Select all fun mods", () =>
|
||||||
|
{
|
||||||
|
modSelect.ModSectionsContainer
|
||||||
|
.Single(c => c.ModType == ModType.DifficultyIncrease)
|
||||||
|
.SelectAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("many mods selected", () => modDisplay.Current.Value.Count >= 5);
|
||||||
|
|
||||||
|
AddStep("trigger deselect and close overlay", () =>
|
||||||
|
{
|
||||||
|
modSelect.ModSectionsContainer
|
||||||
|
.Single(c => c.ModType == ModType.DifficultyIncrease)
|
||||||
|
.DeselectAll();
|
||||||
|
|
||||||
|
modSelect.Hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("all mods deselected", () => modDisplay.Current.Value.Count == 0);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestOsuMods()
|
public void TestOsuMods()
|
||||||
{
|
{
|
||||||
@ -145,11 +172,11 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddAssert("double time visible", () => modSelect.ChildrenOfType<ModButton>().Any(b => b.Mods.Any(m => m is OsuModDoubleTime)));
|
AddAssert("double time visible", () => modSelect.ChildrenOfType<ModButton>().Any(b => b.Mods.Any(m => m is OsuModDoubleTime)));
|
||||||
|
|
||||||
AddStep("make double time invalid", () => modSelect.IsValidMod = m => !(m is OsuModDoubleTime));
|
AddStep("make double time invalid", () => modSelect.IsValidMod = m => !(m is OsuModDoubleTime));
|
||||||
AddAssert("double time not visible", () => modSelect.ChildrenOfType<ModButton>().All(b => !b.Mods.Any(m => m is OsuModDoubleTime)));
|
AddUntilStep("double time not visible", () => modSelect.ChildrenOfType<ModButton>().All(b => !b.Mods.Any(m => m is OsuModDoubleTime)));
|
||||||
AddAssert("nightcore still visible", () => modSelect.ChildrenOfType<ModButton>().Any(b => b.Mods.Any(m => m is OsuModNightcore)));
|
AddAssert("nightcore still visible", () => modSelect.ChildrenOfType<ModButton>().Any(b => b.Mods.Any(m => m is OsuModNightcore)));
|
||||||
|
|
||||||
AddStep("make double time valid again", () => modSelect.IsValidMod = m => true);
|
AddStep("make double time valid again", () => modSelect.IsValidMod = m => true);
|
||||||
AddAssert("double time visible", () => modSelect.ChildrenOfType<ModButton>().Any(b => b.Mods.Any(m => m is OsuModDoubleTime)));
|
AddUntilStep("double time visible", () => modSelect.ChildrenOfType<ModButton>().Any(b => b.Mods.Any(m => m is OsuModDoubleTime)));
|
||||||
AddAssert("nightcore still visible", () => modSelect.ChildrenOfType<ModButton>().Any(b => b.Mods.Any(m => m is OsuModNightcore)));
|
AddAssert("nightcore still visible", () => modSelect.ChildrenOfType<ModButton>().Any(b => b.Mods.Any(m => m is OsuModNightcore)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,6 +339,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
public bool AllLoaded => ModSectionsContainer.Children.All(c => c.ModIconsLoaded);
|
public bool AllLoaded => ModSectionsContainer.Children.All(c => c.ModIconsLoaded);
|
||||||
|
|
||||||
|
public new FillFlowContainer<ModSection> ModSectionsContainer =>
|
||||||
|
base.ModSectionsContainer;
|
||||||
|
|
||||||
public ModButton GetModButton(Mod mod)
|
public ModButton GetModButton(Mod mod)
|
||||||
{
|
{
|
||||||
var section = ModSectionsContainer.Children.Single(s => s.ModType == mod.Type);
|
var section = ModSectionsContainer.Children.Single(s => s.ModType == mod.Type);
|
||||||
|
@ -5,6 +5,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
@ -48,7 +49,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
private SampleChannel sampleChecked;
|
private SampleChannel sampleChecked;
|
||||||
private SampleChannel sampleUnchecked;
|
private SampleChannel sampleUnchecked;
|
||||||
|
|
||||||
public OsuCheckbox()
|
public OsuCheckbox(bool nubOnRight = true)
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
@ -57,26 +58,42 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
labelText = new OsuTextFlowContainer
|
labelText = new OsuTextFlowContainer(ApplyLabelParameters)
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Padding = new MarginPadding { Right = Nub.EXPANDED_SIZE + nub_padding }
|
|
||||||
},
|
|
||||||
Nub = new Nub
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreRight,
|
|
||||||
Origin = Anchor.CentreRight,
|
|
||||||
Margin = new MarginPadding { Right = nub_padding },
|
|
||||||
},
|
},
|
||||||
|
Nub = new Nub(),
|
||||||
new HoverClickSounds()
|
new HoverClickSounds()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (nubOnRight)
|
||||||
|
{
|
||||||
|
Nub.Anchor = Anchor.CentreRight;
|
||||||
|
Nub.Origin = Anchor.CentreRight;
|
||||||
|
Nub.Margin = new MarginPadding { Right = nub_padding };
|
||||||
|
labelText.Padding = new MarginPadding { Right = Nub.EXPANDED_SIZE + nub_padding * 2 };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Nub.Anchor = Anchor.CentreLeft;
|
||||||
|
Nub.Origin = Anchor.CentreLeft;
|
||||||
|
Nub.Margin = new MarginPadding { Left = nub_padding };
|
||||||
|
labelText.Padding = new MarginPadding { Left = Nub.EXPANDED_SIZE + nub_padding * 2 };
|
||||||
|
}
|
||||||
|
|
||||||
Nub.Current.BindTo(Current);
|
Nub.Current.BindTo(Current);
|
||||||
|
|
||||||
Current.DisabledChanged += disabled => labelText.Alpha = Nub.Alpha = disabled ? 0.3f : 1;
|
Current.DisabledChanged += disabled => labelText.Alpha = Nub.Alpha = disabled ? 0.3f : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A function which can be overridden to change the parameters of the label's text.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void ApplyLabelParameters(SpriteText text)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
|
@ -23,6 +23,7 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
[JsonProperty("settings")]
|
[JsonProperty("settings")]
|
||||||
[Key(1)]
|
[Key(1)]
|
||||||
|
[MessagePackFormatter(typeof(ModSettingsDictionaryFormatter))]
|
||||||
public Dictionary<string, object> Settings { get; set; } = new Dictionary<string, object>();
|
public Dictionary<string, object> Settings { get; set; } = new Dictionary<string, object>();
|
||||||
|
|
||||||
[JsonConstructor]
|
[JsonConstructor]
|
||||||
|
67
osu.Game/Online/API/ModSettingsDictionaryFormatter.cs
Normal file
67
osu.Game/Online/API/ModSettingsDictionaryFormatter.cs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// 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.Buffers;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using MessagePack;
|
||||||
|
using MessagePack.Formatters;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.API
|
||||||
|
{
|
||||||
|
public class ModSettingsDictionaryFormatter : IMessagePackFormatter<Dictionary<string, object>>
|
||||||
|
{
|
||||||
|
public void Serialize(ref MessagePackWriter writer, Dictionary<string, object> value, MessagePackSerializerOptions options)
|
||||||
|
{
|
||||||
|
var primitiveFormatter = PrimitiveObjectFormatter.Instance;
|
||||||
|
|
||||||
|
writer.WriteArrayHeader(value.Count);
|
||||||
|
|
||||||
|
foreach (var kvp in value)
|
||||||
|
{
|
||||||
|
var stringBytes = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(kvp.Key));
|
||||||
|
writer.WriteString(in stringBytes);
|
||||||
|
|
||||||
|
switch (kvp.Value)
|
||||||
|
{
|
||||||
|
case Bindable<double> d:
|
||||||
|
primitiveFormatter.Serialize(ref writer, d.Value, options);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Bindable<int> i:
|
||||||
|
primitiveFormatter.Serialize(ref writer, i.Value, options);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Bindable<float> f:
|
||||||
|
primitiveFormatter.Serialize(ref writer, f.Value, options);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Bindable<bool> b:
|
||||||
|
primitiveFormatter.Serialize(ref writer, b.Value, options);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// fall back for non-bindable cases.
|
||||||
|
primitiveFormatter.Serialize(ref writer, kvp.Value, options);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, object> Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
|
||||||
|
{
|
||||||
|
var output = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
int itemCount = reader.ReadArrayHeader();
|
||||||
|
|
||||||
|
for (int i = 0; i < itemCount; i++)
|
||||||
|
{
|
||||||
|
output[reader.ReadString()] =
|
||||||
|
PrimitiveObjectFormatter.Instance.Deserialize(ref reader, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -33,6 +33,8 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private CancellationTokenSource modsLoadCts;
|
private CancellationTokenSource modsLoadCts;
|
||||||
|
|
||||||
|
protected bool SelectionAnimationRunning => pendingSelectionOperations.Count > 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// True when all mod icons have completed loading.
|
/// True when all mod icons have completed loading.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -49,7 +51,11 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
return new ModButton(m)
|
return new ModButton(m)
|
||||||
{
|
{
|
||||||
SelectionChanged = Action,
|
SelectionChanged = mod =>
|
||||||
|
{
|
||||||
|
ModButtonStateChanged(mod);
|
||||||
|
Action?.Invoke(mod);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
|
|
||||||
@ -78,6 +84,10 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void ModButtonStateChanged(Mod mod)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private ModButton[] buttons = Array.Empty<ModButton>();
|
private ModButton[] buttons = Array.Empty<ModButton>();
|
||||||
|
|
||||||
protected override bool OnKeyDown(KeyDownEvent e)
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
@ -94,43 +104,75 @@ namespace osu.Game.Overlays.Mods
|
|||||||
return base.OnKeyDown(e);
|
return base.OnKeyDown(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const double initial_multiple_selection_delay = 120;
|
||||||
|
|
||||||
|
private double selectionDelay = initial_multiple_selection_delay;
|
||||||
|
private double lastSelection;
|
||||||
|
|
||||||
|
private readonly Queue<Action> pendingSelectionOperations = new Queue<Action>();
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (selectionDelay == initial_multiple_selection_delay || Time.Current - lastSelection >= selectionDelay)
|
||||||
|
{
|
||||||
|
if (pendingSelectionOperations.TryDequeue(out var dequeuedAction))
|
||||||
|
{
|
||||||
|
dequeuedAction();
|
||||||
|
|
||||||
|
// each time we play an animation, we decrease the time until the next animation (to ramp the visual and audible elements).
|
||||||
|
selectionDelay = Math.Max(30, selectionDelay * 0.8f);
|
||||||
|
lastSelection = Time.Current;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// reset the selection delay after all animations have been completed.
|
||||||
|
// this will cause the next action to be immediately performed.
|
||||||
|
selectionDelay = initial_multiple_selection_delay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Selects all mods.
|
/// Selects all mods.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SelectAll()
|
public void SelectAll()
|
||||||
{
|
{
|
||||||
|
pendingSelectionOperations.Clear();
|
||||||
|
|
||||||
foreach (var button in buttons.Where(b => !b.Selected))
|
foreach (var button in buttons.Where(b => !b.Selected))
|
||||||
button.SelectAt(0);
|
pendingSelectionOperations.Enqueue(() => button.SelectAt(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deselects all mods.
|
/// Deselects all mods.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="immediate">Set to true to bypass animations and update selections immediately.</param>
|
public void DeselectAll()
|
||||||
public void DeselectAll(bool immediate = false) => DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null), immediate);
|
{
|
||||||
|
pendingSelectionOperations.Clear();
|
||||||
|
DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deselect one or more mods in this section.
|
/// Deselect one or more mods in this section.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="modTypes">The types of <see cref="Mod"/>s which should be deselected.</param>
|
/// <param name="modTypes">The types of <see cref="Mod"/>s which should be deselected.</param>
|
||||||
/// <param name="immediate">Set to true to bypass animations and update selections immediately.</param>
|
/// <param name="immediate">Whether the deselection should happen immediately. Should only be used when required to ensure correct selection flow.</param>
|
||||||
public void DeselectTypes(IEnumerable<Type> modTypes, bool immediate = false)
|
public void DeselectTypes(IEnumerable<Type> modTypes, bool immediate = false)
|
||||||
{
|
{
|
||||||
int delay = 0;
|
|
||||||
|
|
||||||
foreach (var button in buttons)
|
foreach (var button in buttons)
|
||||||
{
|
{
|
||||||
Mod selected = button.SelectedMod;
|
if (button.SelectedMod == null) continue;
|
||||||
if (selected == null) continue;
|
|
||||||
|
|
||||||
foreach (var type in modTypes)
|
foreach (var type in modTypes)
|
||||||
{
|
{
|
||||||
if (type.IsInstanceOfType(selected))
|
if (type.IsInstanceOfType(button.SelectedMod))
|
||||||
{
|
{
|
||||||
if (immediate)
|
if (immediate)
|
||||||
button.Deselect();
|
button.Deselect();
|
||||||
else
|
else
|
||||||
Scheduler.AddDelayed(button.Deselect, delay += 50);
|
pendingSelectionOperations.Enqueue(button.Deselect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -197,5 +239,14 @@ namespace osu.Game.Overlays.Mods
|
|||||||
Font = OsuFont.GetFont(weight: FontWeight.Bold),
|
Font = OsuFont.GetFont(weight: FontWeight.Bold),
|
||||||
Text = text
|
Text = text
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Play out all remaining animations immediately to leave mods in a good (final) state.
|
||||||
|
/// </summary>
|
||||||
|
public void FlushAnimation()
|
||||||
|
{
|
||||||
|
while (pendingSelectionOperations.TryDequeue(out var dequeuedAction))
|
||||||
|
dequeuedAction();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Threading;
|
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Backgrounds;
|
using osu.Game.Graphics.Backgrounds;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
@ -380,6 +379,11 @@ namespace osu.Game.Overlays.Mods
|
|||||||
{
|
{
|
||||||
base.PopOut();
|
base.PopOut();
|
||||||
|
|
||||||
|
foreach (var section in ModSectionsContainer)
|
||||||
|
{
|
||||||
|
section.FlushAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
FooterContainer.MoveToX(content_width, WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
|
FooterContainer.MoveToX(content_width, WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
|
||||||
FooterContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
|
FooterContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
|
||||||
|
|
||||||
@ -496,17 +500,12 @@ namespace osu.Game.Overlays.Mods
|
|||||||
MultiplierLabel.FadeColour(Color4.White, 200);
|
MultiplierLabel.FadeColour(Color4.White, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScheduledDelegate sampleOnDelegate;
|
|
||||||
private ScheduledDelegate sampleOffDelegate;
|
|
||||||
|
|
||||||
private void modButtonPressed(Mod selectedMod)
|
private void modButtonPressed(Mod selectedMod)
|
||||||
{
|
{
|
||||||
if (selectedMod != null)
|
if (selectedMod != null)
|
||||||
{
|
{
|
||||||
// Fixes buzzing when multiple mods are selected in the same frame.
|
|
||||||
sampleOnDelegate?.Cancel();
|
|
||||||
if (State.Value == Visibility.Visible)
|
if (State.Value == Visibility.Visible)
|
||||||
sampleOnDelegate = Scheduler.Add(() => sampleOn?.Play());
|
Scheduler.AddOnce(playSelectedSound);
|
||||||
|
|
||||||
OnModSelected(selectedMod);
|
OnModSelected(selectedMod);
|
||||||
|
|
||||||
@ -514,15 +513,16 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Fixes buzzing when multiple mods are deselected in the same frame.
|
|
||||||
sampleOffDelegate?.Cancel();
|
|
||||||
if (State.Value == Visibility.Visible)
|
if (State.Value == Visibility.Visible)
|
||||||
sampleOffDelegate = Scheduler.Add(() => sampleOff?.Play());
|
Scheduler.AddOnce(playDeselectedSound);
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshSelectedMods();
|
refreshSelectedMods();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void playSelectedSound() => sampleOn?.Play();
|
||||||
|
private void playDeselectedSound() => sampleOff?.Play();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked when a new <see cref="Mod"/> has been selected.
|
/// Invoked when a new <see cref="Mod"/> has been selected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -91,7 +91,11 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
scrollingInfo = new LocalScrollingInfo();
|
scrollingInfo = new LocalScrollingInfo();
|
||||||
scrollingInfo.Direction.BindTo(Direction);
|
scrollingInfo.Direction.BindTo(Direction);
|
||||||
scrollingInfo.TimeRange.BindTo(TimeRange);
|
scrollingInfo.TimeRange.BindTo(TimeRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
switch (VisualisationMethod)
|
switch (VisualisationMethod)
|
||||||
{
|
{
|
||||||
case ScrollVisualisationMethod.Sequential:
|
case ScrollVisualisationMethod.Sequential:
|
||||||
@ -106,11 +110,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
scrollingInfo.Algorithm = new ConstantScrollAlgorithm();
|
scrollingInfo.Algorithm = new ConstantScrollAlgorithm();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
double lastObjectTime = Objects.LastOrDefault()?.GetEndTime() ?? double.MaxValue;
|
double lastObjectTime = Objects.LastOrDefault()?.GetEndTime() ?? double.MaxValue;
|
||||||
double baseBeatLength = TimingControlPoint.DEFAULT_BEAT_LENGTH;
|
double baseBeatLength = TimingControlPoint.DEFAULT_BEAT_LENGTH;
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -69,7 +71,7 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
private void deselectAll()
|
private void deselectAll()
|
||||||
{
|
{
|
||||||
foreach (var section in ModSectionsContainer.Children)
|
foreach (var section in ModSectionsContainer.Children)
|
||||||
section.DeselectAll(true);
|
section.DeselectAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ModSection CreateModSection(ModType type) => new FreeModSection(type);
|
protected override ModSection CreateModSection(ModType type) => new FreeModSection(type);
|
||||||
@ -86,7 +88,7 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
protected override Drawable CreateHeader(string text) => new Container
|
protected override Drawable CreateHeader(string text) => new Container
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Width = 175,
|
RelativeSizeAxes = Axes.X,
|
||||||
Child = checkbox = new HeaderCheckbox
|
Child = checkbox = new HeaderCheckbox
|
||||||
{
|
{
|
||||||
LabelText = text,
|
LabelText = text,
|
||||||
@ -96,21 +98,21 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
|
|
||||||
private void onCheckboxChanged(bool value)
|
private void onCheckboxChanged(bool value)
|
||||||
{
|
{
|
||||||
foreach (var button in ButtonsContainer.OfType<ModButton>())
|
if (value)
|
||||||
{
|
SelectAll();
|
||||||
if (value)
|
else
|
||||||
button.SelectAt(0);
|
DeselectAll();
|
||||||
else
|
|
||||||
button.Deselect();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void ModButtonStateChanged(Mod mod)
|
||||||
{
|
{
|
||||||
base.Update();
|
base.ModButtonStateChanged(mod);
|
||||||
|
|
||||||
var validButtons = ButtonsContainer.OfType<ModButton>().Where(b => b.Mod.HasImplementation);
|
if (!SelectionAnimationRunning)
|
||||||
checkbox.Current.Value = validButtons.All(b => b.Selected);
|
{
|
||||||
|
var validButtons = ButtonsContainer.OfType<ModButton>().Where(b => b.Mod.HasImplementation);
|
||||||
|
checkbox.Current.Value = validButtons.All(b => b.Selected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,6 +122,19 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
|
|
||||||
protected override bool PlaySoundsOnUserChange => false;
|
protected override bool PlaySoundsOnUserChange => false;
|
||||||
|
|
||||||
|
public HeaderCheckbox()
|
||||||
|
: base(false)
|
||||||
|
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ApplyLabelParameters(SpriteText text)
|
||||||
|
{
|
||||||
|
base.ApplyLabelParameters(text);
|
||||||
|
|
||||||
|
text.Font = OsuFont.GetFont(weight: FontWeight.Bold);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnUserChange(bool value)
|
protected override void OnUserChange(bool value)
|
||||||
{
|
{
|
||||||
base.OnUserChange(value);
|
base.OnUserChange(value);
|
||||||
|
@ -47,26 +47,21 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
|
|
||||||
private void endOperationWithKnownLease(LeasedBindable<bool> lease)
|
private void endOperationWithKnownLease(LeasedBindable<bool> lease)
|
||||||
{
|
{
|
||||||
if (lease != leasedInProgress)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// for extra safety, marshal the end of operation back to the update thread if necessary.
|
// for extra safety, marshal the end of operation back to the update thread if necessary.
|
||||||
Scheduler.Add(() =>
|
Scheduler.Add(() =>
|
||||||
{
|
{
|
||||||
leasedInProgress?.Return();
|
if (lease != leasedInProgress)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// UnbindAll() is purposefully used instead of Return() - the two do roughly the same thing, with one difference:
|
||||||
|
// the former won't throw if the lease has already been returned before.
|
||||||
|
// this matters because framework can unbind the lease via the internal UnbindAllBindables(), which is not always detectable
|
||||||
|
// (it is in the case of disposal, but not in the case of screen exit - at least not cleanly).
|
||||||
|
leasedInProgress?.UnbindAll();
|
||||||
leasedInProgress = null;
|
leasedInProgress = null;
|
||||||
}, false);
|
}, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
|
||||||
{
|
|
||||||
base.Dispose(isDisposing);
|
|
||||||
|
|
||||||
// base call does an UnbindAllBindables().
|
|
||||||
// clean up the leased reference here so that it doesn't get returned twice.
|
|
||||||
leasedInProgress = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class OngoingOperation : IDisposable
|
private class OngoingOperation : IDisposable
|
||||||
{
|
{
|
||||||
private readonly OngoingOperationTracker tracker;
|
private readonly OngoingOperationTracker tracker;
|
||||||
|
@ -20,11 +20,13 @@
|
|||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="DiffPlex" Version="1.6.3" />
|
<PackageReference Include="DiffPlex" Version="1.6.3" />
|
||||||
<PackageReference Include="Humanizer" Version="2.8.26" />
|
<PackageReference Include="Humanizer" Version="2.8.26" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.10" />
|
<PackageReference Include="MessagePack" Version="2.2.85" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.1.11" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="5.0.2" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="3.1.10" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="5.0.2" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="5.0.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.NETCore.Targets" Version="3.1.0" />
|
<PackageReference Include="Microsoft.NETCore.Targets" Version="3.1.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2021.128.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2021.128.0" />
|
||||||
|
@ -80,6 +80,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.0.3" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.3" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.3" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.0.3" />
|
||||||
|
<PackageReference Include="MessagePack" Version="1.7.3.7" />
|
||||||
|
<PackageReference Include="MessagePack.Annotations" Version="2.2.85" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
|
Loading…
Reference in New Issue
Block a user