mirror of
https://github.com/ppy/osu.git
synced 2026-05-18 02:09:52 +08:00
Compare commits
357 Commits
2025.227.0
...
2025.316.0
@@ -18,3 +18,6 @@ M:Humanizer.InflectorExtensions.Pascalize(System.String);Humanizer's .Pascalize(
|
||||
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.
|
||||
M:osuTK.MathHelper.Clamp(System.Int32,System.Int32,System.Int32)~System.Int32;Use Math.Clamp() instead.
|
||||
M:osuTK.MathHelper.Clamp(System.Single,System.Single,System.Single)~System.Single;This osuTK helper has unsafe semantics when one of the bounds provided is NaN. Use Math.Clamp() instead.
|
||||
M:osuTK.MathHelper.Clamp(System.Double,System.Double,System.Double)~System.Double;This osuTK helper has unsafe semantics when one of the bounds provided is NaN. Use Math.Clamp() instead.
|
||||
|
||||
+2
-2
@@ -1,6 +1,7 @@
|
||||
// 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 System.Threading;
|
||||
@@ -9,7 +10,6 @@ using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Pippidon.Objects;
|
||||
using osu.Game.Rulesets.Pippidon.UI;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Pippidon.Beatmaps
|
||||
{
|
||||
@@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Pippidon.Beatmaps
|
||||
};
|
||||
}
|
||||
|
||||
private int getLane(HitObject hitObject) => (int)MathHelper.Clamp(
|
||||
private int getLane(HitObject hitObject) => (int)Math.Clamp(
|
||||
(getUsablePosition(hitObject) - minPosition) / (maxPosition - minPosition) * PippidonPlayfield.LANE_COUNT, 0, PippidonPlayfield.LANE_COUNT - 1);
|
||||
|
||||
private float getUsablePosition(HitObject h) => (h as IHasYPosition)?.Y ?? ((IHasXPosition)h).X;
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2025.225.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2025.313.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
||||
{
|
||||
public abstract partial class CatchPlacementBlueprintTestScene : PlacementBlueprintTestScene
|
||||
{
|
||||
protected sealed override Ruleset CreateRuleset() => new CatchRuleset();
|
||||
|
||||
protected const double TIME_SNAP = 100;
|
||||
|
||||
protected DrawableCatchHitObject LastObject;
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
|
||||
private float halfCatcherWidth;
|
||||
|
||||
public override int Version => 20220701;
|
||||
public override int Version => 20250306;
|
||||
|
||||
public CatchDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
|
||||
@@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
||||
{
|
||||
base.UpdateHitObjectFromPath(hitObject);
|
||||
|
||||
if (hitObject.Path.ControlPoints.Count <= 1 || !hitObject.Path.HasValidLength)
|
||||
if (hitObject.Path.ControlPoints.Count <= 1 || !hitObject.Path.HasValidLengthForPlacement)
|
||||
EditorBeatmap?.Remove(hitObject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 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 osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -110,7 +111,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Argon
|
||||
|
||||
double duration = ObjectState.HitObject.StartTime - ObjectState.DisplayStartTime;
|
||||
|
||||
fadeContent.Alpha = MathHelper.Clamp(
|
||||
fadeContent.Alpha = Math.Clamp(
|
||||
Interpolation.ValueAt(
|
||||
Time.Current, 1f, 0f,
|
||||
ObjectState.DisplayStartTime + duration * lens_flare_start,
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
{
|
||||
public abstract partial class ManiaPlacementBlueprintTestScene : PlacementBlueprintTestScene
|
||||
{
|
||||
protected sealed override Ruleset CreateRuleset() => new ManiaRuleset();
|
||||
|
||||
private readonly Column column;
|
||||
|
||||
[Cached(typeof(IReadOnlyList<Mod>))]
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
{
|
||||
keyCount.Current.Value = 8;
|
||||
});
|
||||
AddUntilStep("dialog visible", () => Game.ChildrenOfType<IDialogOverlay>().SingleOrDefault()?.CurrentDialog, Is.InstanceOf<ReloadEditorDialog>);
|
||||
AddUntilStep("dialog visible", () => Game.ChildrenOfType<IDialogOverlay>().SingleOrDefault()?.CurrentDialog, Is.InstanceOf<SaveAndReloadEditorDialog>);
|
||||
AddStep("refuse", () => InputManager.Key(Key.Number2));
|
||||
AddAssert("key count is 5", () => keyCount.Current.Value, () => Is.EqualTo(5));
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
{
|
||||
keyCount.Current.Value = 8;
|
||||
});
|
||||
AddUntilStep("dialog visible", () => Game.ChildrenOfType<IDialogOverlay>().Single().CurrentDialog, Is.InstanceOf<ReloadEditorDialog>);
|
||||
AddUntilStep("dialog visible", () => Game.ChildrenOfType<IDialogOverlay>().Single().CurrentDialog, Is.InstanceOf<SaveAndReloadEditorDialog>);
|
||||
AddStep("acquiesce", () => InputManager.Key(Key.Number1));
|
||||
AddUntilStep("beatmap became 8K", () => Game.Beatmap.Value.BeatmapInfo.Difficulty.CircleSize, () => Is.EqualTo(8));
|
||||
}
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
@@ -14,6 +17,11 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() => toggleTouchControls(false));
|
||||
|
||||
#region Without touch controls
|
||||
|
||||
[Test]
|
||||
public void TestTouchInput()
|
||||
{
|
||||
@@ -63,6 +71,79 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
() => Does.Not.Contain(getColumn(0).Action.Value));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region With touch controls
|
||||
|
||||
[Test]
|
||||
public void TestTouchAreaNotInitiallyVisible()
|
||||
{
|
||||
AddStep("enable touch controls", () => toggleTouchControls(true));
|
||||
AddAssert("touch area not visible", () => getTouchOverlay()?.State.Value == Visibility.Hidden);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPressReceptors()
|
||||
{
|
||||
AddStep("enable touch controls", () => toggleTouchControls(true));
|
||||
AddAssert("touch area not visible", () => getTouchOverlay()?.State.Value == Visibility.Hidden);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
int index = i;
|
||||
|
||||
AddStep($"touch receptor {index}", () => InputManager.BeginTouch(new Touch(TouchSource.Touch1, getReceptor(index).ScreenSpaceDrawQuad.Centre)));
|
||||
|
||||
AddAssert("action sent",
|
||||
() => this.ChildrenOfType<ManiaInputManager>().SelectMany(m => m.KeyBindingContainer.PressedActions),
|
||||
() => Does.Contain(getReceptor(index).Action.Value));
|
||||
|
||||
AddStep($"release receptor {index}", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, getReceptor(index).ScreenSpaceDrawQuad.Centre)));
|
||||
|
||||
AddAssert("touch area visible", () => getTouchOverlay()?.State.Value == Visibility.Visible);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestColumnsNotTouchableWithTouchControls()
|
||||
{
|
||||
AddStep("enable touch controls", () => toggleTouchControls(true));
|
||||
|
||||
AddStep("touch receptor 0", () => InputManager.BeginTouch(new Touch(TouchSource.Touch1, getReceptor(0).ScreenSpaceDrawQuad.Centre)));
|
||||
|
||||
AddAssert("action sent",
|
||||
() => this.ChildrenOfType<ManiaInputManager>().SelectMany(m => m.KeyBindingContainer.PressedActions),
|
||||
() => Does.Contain(getReceptor(0).Action.Value));
|
||||
|
||||
AddStep("release receptor 0", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, getReceptor(0).ScreenSpaceDrawQuad.Centre)));
|
||||
|
||||
AddAssert("touch area visible", () => getTouchOverlay()?.State.Value == Visibility.Visible);
|
||||
|
||||
AddStep("touch column 0", () => InputManager.BeginTouch(new Touch(TouchSource.Touch1, getColumn(0).ScreenSpaceDrawQuad.Centre + new Vector2(0f, -50f))));
|
||||
|
||||
AddAssert("action not sent",
|
||||
() => this.ChildrenOfType<ManiaInputManager>().SelectMany(m => m.KeyBindingContainer.PressedActions),
|
||||
() => Does.Not.Contain(getColumn(0).Action.Value));
|
||||
|
||||
AddStep("release column 0", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, getColumn(0).ScreenSpaceDrawQuad.Centre + new Vector2(0f, -50f))));
|
||||
|
||||
AddAssert("action not sent",
|
||||
() => this.ChildrenOfType<ManiaInputManager>().SelectMany(m => m.KeyBindingContainer.PressedActions),
|
||||
() => Does.Not.Contain(getColumn(0).Action.Value));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void toggleTouchControls(bool enabled)
|
||||
{
|
||||
var maniaConfig = (ManiaRulesetConfigManager)RulesetConfigs.GetConfigFor(CreatePlayerRuleset())!;
|
||||
maniaConfig.SetValue(ManiaRulesetSetting.MobileLayout, enabled ? ManiaMobileLayout.LandscapeWithOverlay : ManiaMobileLayout.Portrait);
|
||||
}
|
||||
|
||||
private ManiaTouchInputArea? getTouchOverlay() => this.ChildrenOfType<ManiaTouchInputArea>().SingleOrDefault();
|
||||
|
||||
private ManiaTouchInputArea.ColumnInputReceptor getReceptor(int index) => this.ChildrenOfType<ManiaTouchInputArea.ColumnInputReceptor>().ElementAt(index);
|
||||
|
||||
private Column getColumn(int index) => this.ChildrenOfType<Column>().ElementAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
||||
SetDefault(ManiaRulesetSetting.ScrollSpeed, 8.0, 1.0, 40.0, 0.1);
|
||||
SetDefault(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
||||
SetDefault(ManiaRulesetSetting.TimingBasedNoteColouring, false);
|
||||
SetDefault(ManiaRulesetSetting.MobileLayout, ManiaMobileLayout.Portrait);
|
||||
|
||||
#pragma warning disable CS0618
|
||||
// Although obsolete, this is still required to populate the bindable from the database in case migration is required.
|
||||
@@ -55,6 +56,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
||||
ScrollTime,
|
||||
ScrollSpeed,
|
||||
ScrollDirection,
|
||||
TimingBasedNoteColouring
|
||||
TimingBasedNoteColouring,
|
||||
MobileLayout,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup
|
||||
|
||||
updatingKeyCount = true;
|
||||
|
||||
editor.Reload().ContinueWith(t =>
|
||||
editor.SaveAndReload().ContinueWith(t =>
|
||||
{
|
||||
if (!t.GetResultSafely())
|
||||
{
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// 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.Localisation;
|
||||
using osu.Game.Localisation;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania
|
||||
{
|
||||
public enum ManiaMobileLayout
|
||||
{
|
||||
[LocalisableDescription(typeof(RulesetSettingsStrings), nameof(RulesetSettingsStrings.PortraitExpandedColumns))]
|
||||
Portrait,
|
||||
|
||||
[LocalisableDescription(typeof(RulesetSettingsStrings), nameof(RulesetSettingsStrings.LandscapeExpandedColumns))]
|
||||
Landscape,
|
||||
|
||||
[LocalisableDescription(typeof(RulesetSettingsStrings), nameof(RulesetSettingsStrings.LandscapeTouchOverlay))]
|
||||
LandscapeWithOverlay,
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// 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;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Localisation;
|
||||
@@ -44,8 +45,17 @@ namespace osu.Game.Rulesets.Mania
|
||||
Keywords = new[] { "color" },
|
||||
LabelText = RulesetSettingsStrings.TimingBasedColouring,
|
||||
Current = config.GetBindable<bool>(ManiaRulesetSetting.TimingBasedNoteColouring),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if (RuntimeInfo.IsMobile)
|
||||
{
|
||||
Add(new SettingsEnumDropdown<ManiaMobileLayout>
|
||||
{
|
||||
LabelText = RulesetSettingsStrings.MobileLayout,
|
||||
Current = config.GetBindable<ManiaMobileLayout>(ManiaRulesetSetting.MobileLayout),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private partial class ManiaScrollSlider : RoundedSliderBar<double>
|
||||
|
||||
@@ -42,8 +42,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
var locations = column.OfType<Note>().Select(n => (startTime: n.StartTime, samples: n.Samples))
|
||||
.Concat(column.OfType<HoldNote>().SelectMany(h => new[]
|
||||
{
|
||||
(startTime: h.StartTime, samples: h.GetNodeSamples(0)),
|
||||
(startTime: h.EndTime, samples: h.GetNodeSamples(1))
|
||||
(startTime: h.StartTime, samples: h.GetNodeSamples(0))
|
||||
}))
|
||||
.OrderBy(h => h.startTime).ToList();
|
||||
|
||||
|
||||
@@ -26,9 +26,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
: base(barLine)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 1;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load()
|
||||
{
|
||||
AddInternal(new SkinnableDrawable(new ManiaSkinComponentLookup(ManiaSkinComponents.BarLine), _ => new DefaultBarLine())
|
||||
@@ -36,8 +37,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
|
||||
Major.BindValueChanged(major => Height = major.NewValue ? 1.7f : 1.2f, true);
|
||||
}
|
||||
|
||||
protected override void OnApply()
|
||||
|
||||
@@ -53,7 +53,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
}
|
||||
}
|
||||
|
||||
private void onDirectionChanged() => Y = direction.Value == ScrollingDirection.Up ? -judgement_y_position : judgement_y_position;
|
||||
private void onDirectionChanged()
|
||||
{
|
||||
Anchor = direction.Value == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||
Y = direction.Value == ScrollingDirection.Up ? -judgement_y_position : judgement_y_position;
|
||||
}
|
||||
|
||||
protected override SpriteText CreateJudgementText() =>
|
||||
new OsuSpriteText
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Default
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(DrawableHitObject drawableHitObject)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
// Avoid flickering due to no anti-aliasing of boxes by default.
|
||||
var edgeSmoothness = new Vector2(0.3f);
|
||||
@@ -75,6 +75,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Default
|
||||
|
||||
private void updateMajor(ValueChangedEvent<bool> major)
|
||||
{
|
||||
Height = major.NewValue ? 1.7f : 1.2f;
|
||||
|
||||
mainLine.Alpha = major.NewValue ? 0.5f : 0.2f;
|
||||
leftAnchor.Alpha = rightAnchor.Alpha = major.NewValue ? mainLine.Alpha * 0.3f : 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
{
|
||||
public partial class LegacyBarLine : CompositeDrawable
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
{
|
||||
float skinHeight = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.BarLineHeight)?.Value ?? 1;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 1.2f * skinHeight;
|
||||
Colour = skin.GetManiaSkinConfig<Color4>(LegacyManiaSkinConfigurationLookups.BarLineColour)?.Value ?? Color4.White;
|
||||
|
||||
// Avoid flickering due to no anti-aliasing of boxes by default.
|
||||
var edgeSmoothness = new Vector2(0.3f);
|
||||
|
||||
AddInternal(new Box
|
||||
{
|
||||
Name = "Bar line",
|
||||
EdgeSmoothness = edgeSmoothness,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
this.result = result;
|
||||
this.animation = animation;
|
||||
|
||||
Anchor = Anchor.BottomCentre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
AutoSizeAxes = Axes.Both;
|
||||
@@ -53,10 +52,18 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
float hitPosition = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.HitPosition)?.Value ?? 0;
|
||||
float scorePosition = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.ScorePosition)?.Value ?? 0;
|
||||
|
||||
float absoluteHitPosition = 480f * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR - hitPosition;
|
||||
float finalPosition = scorePosition - absoluteHitPosition;
|
||||
float hitPositionFromTop = 480f * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR - hitPosition;
|
||||
|
||||
Y = direction.Value == ScrollingDirection.Up ? -finalPosition : finalPosition;
|
||||
if (scorePosition > hitPositionFromTop / 2f)
|
||||
{
|
||||
Anchor = direction.Value == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||
Y = direction.Value == ScrollingDirection.Up ? hitPositionFromTop - scorePosition : scorePosition - hitPositionFromTop;
|
||||
}
|
||||
else
|
||||
{
|
||||
Anchor = direction.Value == ScrollingDirection.Up ? Anchor.BottomCentre : Anchor.TopCentre;
|
||||
Y = direction.Value == ScrollingDirection.Up ? -scorePosition : scorePosition;
|
||||
}
|
||||
}
|
||||
|
||||
public void PlayAnimation()
|
||||
|
||||
@@ -163,7 +163,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
return new LegacyStageForeground();
|
||||
|
||||
case ManiaSkinComponents.BarLine:
|
||||
return null; // Not yet implemented.
|
||||
return new LegacyBarLine();
|
||||
|
||||
default:
|
||||
throw new UnsupportedSkinComponentException(lookup);
|
||||
|
||||
@@ -12,6 +12,7 @@ using osu.Framework.Input.Events;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Skinning;
|
||||
@@ -57,6 +58,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
public readonly Bindable<Color4> AccentColour = new Bindable<Color4>(Color4.Black);
|
||||
|
||||
private IBindable<ManiaMobileLayout> mobilePlayStyle = null!;
|
||||
|
||||
public Column(int index, bool isSpecial)
|
||||
{
|
||||
Index = index;
|
||||
@@ -77,7 +80,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
private ISkinSource skin { get; set; } = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host)
|
||||
private void load(GameHost host, ManiaRulesetConfigManager? rulesetConfig)
|
||||
{
|
||||
SkinnableDrawable keyArea;
|
||||
|
||||
@@ -115,6 +118,9 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
RegisterPool<HeadNote, DrawableHoldNoteHead>(10, 50);
|
||||
RegisterPool<TailNote, DrawableHoldNoteTail>(10, 50);
|
||||
RegisterPool<HoldNoteBody, DrawableHoldNoteBody>(10, 50);
|
||||
|
||||
if (rulesetConfig != null)
|
||||
mobilePlayStyle = rulesetConfig.GetBindable<ManiaMobileLayout>(ManiaRulesetSetting.MobileLayout);
|
||||
}
|
||||
|
||||
private void onSourceChanged()
|
||||
@@ -193,6 +199,10 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
protected override bool OnTouchDown(TouchDownEvent e)
|
||||
{
|
||||
// if touch overlay is visible, disallow columns from handling touch directly.
|
||||
if (mobilePlayStyle.Value == ManiaMobileLayout.LandscapeWithOverlay)
|
||||
return false;
|
||||
|
||||
maniaInputManager?.KeyBindingContainer.TriggerPressed(Action.Value);
|
||||
touchActivationCount++;
|
||||
return true;
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
// 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;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Layout;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Skinning;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
@@ -34,6 +39,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
set => base.Masking = value;
|
||||
}
|
||||
|
||||
private readonly LayoutValue layout = new LayoutValue(Invalidation.DrawSize);
|
||||
|
||||
public ColumnFlow(StageDefinition stageDefinition)
|
||||
{
|
||||
this.stageDefinition = stageDefinition;
|
||||
@@ -52,42 +59,32 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
for (int i = 0; i < stageDefinition.Columns; i++)
|
||||
columns.Add(new Container<TContent> { RelativeSizeAxes = Axes.Y });
|
||||
|
||||
AddLayout(layout);
|
||||
}
|
||||
|
||||
private ISkinSource currentSkin;
|
||||
[Resolved]
|
||||
private ISkinSource skin { get; set; } = null!;
|
||||
|
||||
private readonly Bindable<ManiaMobileLayout> mobileLayout = new Bindable<ManiaMobileLayout>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
private void load(ManiaRulesetConfigManager? rulesetConfig)
|
||||
{
|
||||
currentSkin = skin;
|
||||
rulesetConfig?.BindWith(ManiaRulesetSetting.MobileLayout, mobileLayout);
|
||||
|
||||
skin.SourceChanged += onSkinChanged;
|
||||
onSkinChanged();
|
||||
mobileLayout.BindValueChanged(_ => invalidateLayout());
|
||||
skin.SourceChanged += invalidateLayout;
|
||||
}
|
||||
|
||||
private void onSkinChanged()
|
||||
protected override void Update()
|
||||
{
|
||||
for (int i = 0; i < stageDefinition.Columns; i++)
|
||||
base.Update();
|
||||
|
||||
if (!layout.IsValid)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
float spacing = currentSkin.GetConfig<ManiaSkinConfigurationLookup, float>(
|
||||
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnSpacing, i - 1))
|
||||
?.Value ?? Stage.COLUMN_SPACING;
|
||||
|
||||
columns[i].Margin = new MarginPadding { Left = spacing };
|
||||
}
|
||||
|
||||
float? width = currentSkin.GetConfig<ManiaSkinConfigurationLookup, float>(
|
||||
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnWidth, i))
|
||||
?.Value;
|
||||
|
||||
bool isSpecialColumn = stageDefinition.IsSpecialColumn(i);
|
||||
|
||||
// only used by default skin (legacy skins get defaults set in LegacyManiaSkinConfiguration)
|
||||
width ??= isSpecialColumn ? Column.SPECIAL_COLUMN_WIDTH : Column.COLUMN_WIDTH;
|
||||
|
||||
columns[i].Width = width.Value;
|
||||
updateColumnSize();
|
||||
layout.Validate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,12 +98,60 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
Content[column] = columns[column].Child = content;
|
||||
}
|
||||
|
||||
private void invalidateLayout() => layout.Invalidate();
|
||||
|
||||
private void updateColumnSize()
|
||||
{
|
||||
float mobileAdjust = 1f;
|
||||
|
||||
if (RuntimeInfo.IsMobile && mobileLayout.Value == ManiaMobileLayout.Landscape)
|
||||
{
|
||||
// GridContainer+CellContainer containing this stage (gets split up for dual stages).
|
||||
Vector2? containingCell = this.FindClosestParent<Stage>()?.Parent?.DrawSize;
|
||||
|
||||
// Will be null in tests.
|
||||
if (containingCell != null && containingCell.Value.X >= containingCell.Value.Y)
|
||||
{
|
||||
float aspectRatio = containingCell.Value.X / containingCell.Value.Y;
|
||||
|
||||
// 2.83 is a mostly arbitrary scale-up (170 / 60, based on original implementation for argon)
|
||||
mobileAdjust = 2.83f * Math.Min(1, 7f / stageDefinition.Columns);
|
||||
// 1.92 is a "reference" mobile screen aspect ratio for phones.
|
||||
// We should scale it back for cases like tablets which aren't so extreme.
|
||||
mobileAdjust *= aspectRatio / 1.92f;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < stageDefinition.Columns; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
float spacing = skin.GetConfig<ManiaSkinConfigurationLookup, float>(
|
||||
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnSpacing, i - 1))
|
||||
?.Value ?? Stage.COLUMN_SPACING;
|
||||
|
||||
columns[i].Margin = new MarginPadding { Left = spacing };
|
||||
}
|
||||
|
||||
float? width = skin.GetConfig<ManiaSkinConfigurationLookup, float>(
|
||||
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnWidth, i))
|
||||
?.Value;
|
||||
|
||||
bool isSpecialColumn = stageDefinition.IsSpecialColumn(i);
|
||||
|
||||
// only used by default skin (legacy skins get defaults set in LegacyManiaSkinConfiguration)
|
||||
width ??= isSpecialColumn ? Column.SPECIAL_COLUMN_WIDTH : Column.COLUMN_WIDTH;
|
||||
|
||||
columns[i].Width = width.Value * mobileAdjust;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (currentSkin != null)
|
||||
currentSkin.SourceChanged -= onSkinChanged;
|
||||
if (skin.IsNotNull())
|
||||
skin.SourceChanged -= invalidateLayout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,11 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
direction.BindValueChanged(_ => onDirectionChanged(), true);
|
||||
}
|
||||
|
||||
private void onDirectionChanged() => Y = direction.Value == ScrollingDirection.Up ? -judgement_y_position : judgement_y_position;
|
||||
private void onDirectionChanged()
|
||||
{
|
||||
Anchor = direction.Value == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||
Y = direction.Value == ScrollingDirection.Up ? -judgement_y_position : judgement_y_position;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
|
||||
@@ -3,30 +3,23 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
public partial class DrawableManiaJudgement : DrawableJudgement
|
||||
{
|
||||
private IBindable<ScrollingDirection> direction;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IScrollingInfo scrollingInfo)
|
||||
public DrawableManiaJudgement()
|
||||
{
|
||||
direction = scrollingInfo.Direction.GetBoundCopy();
|
||||
direction.BindValueChanged(_ => onDirectionChanged(), true);
|
||||
}
|
||||
|
||||
private void onDirectionChanged()
|
||||
{
|
||||
Anchor = direction.Value == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||
Origin = Anchor.Centre;
|
||||
// Extend the dimensions of this drawable to the entire parenting container.
|
||||
// This allows skin implementations (i.e. LegacyManiaJudgementPiece) to freely choose the anchor based on skin settings.
|
||||
Anchor = Anchor.TopLeft;
|
||||
Origin = Anchor.TopLeft;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Size = new Vector2(1f);
|
||||
}
|
||||
|
||||
protected override Drawable CreateDefaultJudgement(HitResult result) => new DefaultManiaJudgementPiece(result);
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
public IEnumerable<BarLine> BarLines;
|
||||
|
||||
public override bool RequiresPortraitOrientation => Beatmap.Stages.Count == 1;
|
||||
public override bool RequiresPortraitOrientation => Beatmap.Stages.Count == 1 && mobileLayout.Value == ManiaMobileLayout.Portrait;
|
||||
|
||||
protected override bool RelativeScaleBeatLengths => true;
|
||||
|
||||
@@ -58,6 +58,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
||||
private readonly BindableDouble configScrollSpeed = new BindableDouble();
|
||||
private readonly Bindable<ManiaMobileLayout> mobileLayout = new Bindable<ManiaMobileLayout>();
|
||||
|
||||
private double currentTimeRange;
|
||||
protected double TargetTimeRange;
|
||||
@@ -111,6 +112,28 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
configScrollSpeed.BindValueChanged(speed => TargetTimeRange = ComputeScrollTime(speed.NewValue));
|
||||
|
||||
TimeRange.Value = TargetTimeRange = currentTimeRange = ComputeScrollTime(configScrollSpeed.Value);
|
||||
|
||||
Config.BindWith(ManiaRulesetSetting.MobileLayout, mobileLayout);
|
||||
mobileLayout.BindValueChanged(_ => updateMobileLayout(), true);
|
||||
}
|
||||
|
||||
private ManiaTouchInputArea? touchInputArea;
|
||||
|
||||
private void updateMobileLayout()
|
||||
{
|
||||
switch (mobileLayout.Value)
|
||||
{
|
||||
case ManiaMobileLayout.LandscapeWithOverlay:
|
||||
KeyBindingInputManager.Add(touchInputArea = new ManiaTouchInputArea(this));
|
||||
break;
|
||||
|
||||
default:
|
||||
if (touchInputArea != null)
|
||||
KeyBindingInputManager.Remove(touchInputArea, true);
|
||||
|
||||
touchInputArea = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void AdjustScrollSpeed(int amount) => configScrollSpeed.Value += amount;
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
// 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 osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Configuration;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// An overlay that captures and displays osu!mania mouse and touch input.
|
||||
/// </summary>
|
||||
public partial class ManiaTouchInputArea : VisibilityContainer
|
||||
{
|
||||
private readonly DrawableManiaRuleset drawableRuleset;
|
||||
|
||||
// visibility state affects our child. we always want to handle input.
|
||||
public override bool PropagatePositionalInputSubTree => true;
|
||||
public override bool PropagateNonPositionalInputSubTree => true;
|
||||
|
||||
[SettingSource("Spacing", "The spacing between receptors.")]
|
||||
public BindableFloat Spacing { get; } = new BindableFloat(10)
|
||||
{
|
||||
Precision = 1,
|
||||
MinValue = 0,
|
||||
MaxValue = 100,
|
||||
};
|
||||
|
||||
[SettingSource("Opacity", "The receptor opacity.")]
|
||||
public BindableFloat Opacity { get; } = new BindableFloat(1)
|
||||
{
|
||||
Precision = 0.1f,
|
||||
MinValue = 0,
|
||||
MaxValue = 1
|
||||
};
|
||||
|
||||
private GridContainer gridContainer = null!;
|
||||
|
||||
public ManiaTouchInputArea(DrawableManiaRuleset drawableRuleset)
|
||||
{
|
||||
this.drawableRuleset = drawableRuleset;
|
||||
|
||||
Anchor = Anchor.BottomCentre;
|
||||
Origin = Anchor.BottomCentre;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Height = 0.5f;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
List<Drawable> receptorGridContent = new List<Drawable>();
|
||||
List<Dimension> receptorGridDimensions = new List<Dimension>();
|
||||
|
||||
bool first = true;
|
||||
|
||||
foreach (var stage in drawableRuleset.Playfield.Stages)
|
||||
{
|
||||
foreach (var column in stage.Columns)
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
receptorGridContent.Add(new Gutter { Spacing = { BindTarget = Spacing } });
|
||||
receptorGridDimensions.Add(new Dimension(GridSizeMode.AutoSize));
|
||||
}
|
||||
|
||||
receptorGridContent.Add(new ColumnInputReceptor
|
||||
{
|
||||
Action = { BindTarget = column.Action },
|
||||
});
|
||||
receptorGridDimensions.Add(new Dimension());
|
||||
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
InternalChild = gridContainer = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
AlwaysPresent = true,
|
||||
Content = new[] { receptorGridContent.ToArray() },
|
||||
ColumnDimensions = receptorGridDimensions.ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
Opacity.BindValueChanged(o => Alpha = o.NewValue, true);
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
// Hide whenever the keyboard is used.
|
||||
Hide();
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool OnTouchDown(TouchDownEvent e)
|
||||
{
|
||||
Show();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
gridContainer.FadeIn(500, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
gridContainer.FadeOut(300);
|
||||
}
|
||||
|
||||
public partial class ColumnInputReceptor : CompositeDrawable
|
||||
{
|
||||
public readonly IBindable<ManiaAction> Action = new Bindable<ManiaAction>();
|
||||
|
||||
private readonly Box highlightOverlay;
|
||||
|
||||
[Resolved]
|
||||
private ManiaInputManager? inputManager { get; set; }
|
||||
|
||||
private bool isPressed;
|
||||
|
||||
public ColumnInputReceptor()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
CornerRadius = 10,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.15f,
|
||||
},
|
||||
highlightOverlay = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
Blending = BlendingParameters.Additive,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnTouchDown(TouchDownEvent e)
|
||||
{
|
||||
updateButton(true);
|
||||
return false; // handled by parent container to show overlay.
|
||||
}
|
||||
|
||||
protected override void OnTouchUp(TouchUpEvent e)
|
||||
{
|
||||
updateButton(false);
|
||||
}
|
||||
|
||||
private void updateButton(bool press)
|
||||
{
|
||||
if (press == isPressed)
|
||||
return;
|
||||
|
||||
isPressed = press;
|
||||
|
||||
if (press)
|
||||
{
|
||||
inputManager?.KeyBindingContainer.TriggerPressed(Action.Value);
|
||||
highlightOverlay.FadeTo(0.1f, 80, Easing.OutQuint);
|
||||
}
|
||||
else
|
||||
{
|
||||
inputManager?.KeyBindingContainer.TriggerReleased(Action.Value);
|
||||
highlightOverlay.FadeTo(0, 400, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private partial class Gutter : Drawable
|
||||
{
|
||||
public readonly IBindable<float> Spacing = new Bindable<float>();
|
||||
|
||||
public Gutter()
|
||||
{
|
||||
Spacing.BindValueChanged(s => Size = new Vector2(s.NewValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
public partial class TestSceneHitCirclePlacementBlueprint : PlacementBlueprintTestScene
|
||||
{
|
||||
protected sealed override Ruleset CreateRuleset() => new OsuRuleset();
|
||||
protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableHitCircle((HitCircle)hitObject);
|
||||
protected override HitObjectPlacementBlueprint CreateBlueprint() => new HitCirclePlacementBlueprint();
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
public partial class TestSceneSliderPlacementBlueprint : PlacementBlueprintTestScene
|
||||
{
|
||||
protected sealed override Ruleset CreateRuleset() => new OsuRuleset();
|
||||
|
||||
[SetUp]
|
||||
public void Setup() => Schedule(() =>
|
||||
{
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
public partial class TestSceneSpinnerPlacementBlueprint : PlacementBlueprintTestScene
|
||||
{
|
||||
protected sealed override Ruleset CreateRuleset() => new OsuRuleset();
|
||||
|
||||
protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableSpinner((Spinner)hitObject);
|
||||
|
||||
protected override HitObjectPlacementBlueprint CreateBlueprint() => new SpinnerPlacementBlueprint();
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
{
|
||||
private const double difficulty_multiplier = 0.0675;
|
||||
|
||||
public override int Version => 20241007;
|
||||
public override int Version => 20250306;
|
||||
|
||||
public OsuDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -76,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components
|
||||
if (hasReachedObject && showHitMarkers.Value)
|
||||
{
|
||||
float alpha = Interpolation.ValueAt(editorTime, 0, 1f, hitObjectTime, hitObjectTime + FADE_OUT_EXTENSION, Easing.In);
|
||||
float ringScale = MathHelper.Clamp(Interpolation.ValueAt(editorTime, 0, 1f, hitObjectTime, hitObjectTime + FADE_OUT_EXTENSION / 2, Easing.OutQuint), 0, 1);
|
||||
float ringScale = Math.Clamp(Interpolation.ValueAt(editorTime, 0, 1f, hitObjectTime, hitObjectTime + FADE_OUT_EXTENSION / 2, Easing.OutQuint), 0, 1);
|
||||
|
||||
ring.Scale = new Vector2(1 + 0.1f * ringScale);
|
||||
content.Alpha = 0.9f * (1 - alpha);
|
||||
|
||||
+1
-1
@@ -484,7 +484,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
// Snap the path to the current beat divisor before checking length validity.
|
||||
hitObject.SnapTo(distanceSnapProvider);
|
||||
|
||||
if (!hitObject.Path.HasValidLength)
|
||||
if (!hitObject.Path.HasValidLengthForPlacement)
|
||||
{
|
||||
for (int i = 0; i < hitObject.Path.ControlPoints.Count; i++)
|
||||
hitObject.Path.ControlPoints[i].Position = oldControlPoints[i];
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
|
||||
private readonly IncrementalBSplineBuilder bSplineBuilder = new IncrementalBSplineBuilder { Degree = 4 };
|
||||
|
||||
protected override bool IsValidForPlacement => HitObject.Path.HasValidLength;
|
||||
protected override bool IsValidForPlacement => HitObject.Path.HasValidLengthForPlacement;
|
||||
|
||||
public SliderPlacementBlueprint()
|
||||
: base(new Slider())
|
||||
|
||||
@@ -270,14 +270,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
if (adjustVelocity)
|
||||
{
|
||||
proposedVelocity = proposedDistance / oldDuration;
|
||||
proposedDistance = MathHelper.Clamp(proposedDistance, 0.1 * oldDuration, 10 * oldDuration);
|
||||
proposedDistance = Math.Clamp(proposedDistance, 0.1 * oldDuration, 10 * oldDuration);
|
||||
}
|
||||
else
|
||||
{
|
||||
double minDistance = distanceSnapProvider?.GetBeatSnapDistance() * oldVelocityMultiplier ?? 1;
|
||||
// Add a small amount to the proposed distance to make it easier to snap to the full length of the slider.
|
||||
proposedDistance = distanceSnapProvider?.FindSnappedDistance((float)proposedDistance + 1, HitObject.StartTime, HitObject) ?? proposedDistance;
|
||||
proposedDistance = MathHelper.Clamp(proposedDistance, minDistance, HitObject.Path.CalculatedDistance);
|
||||
proposedDistance = Math.Clamp(proposedDistance, minDistance, HitObject.Path.CalculatedDistance);
|
||||
}
|
||||
|
||||
if (Precision.AlmostEquals(proposedDistance, HitObject.Path.Distance) && Precision.AlmostEquals(proposedVelocity, HitObject.SliderVelocityMultiplier))
|
||||
@@ -476,7 +476,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
HitObject.SnapTo(distanceSnapProvider);
|
||||
|
||||
// If there are 0 or 1 remaining control points, or the slider has an invalid length, it is in a degenerate form and should be deleted
|
||||
if (controlPoints.Count <= 1 || !HitObject.Path.HasValidLength)
|
||||
if (controlPoints.Count <= 1 || !HitObject.Path.HasValidLengthForPlacement)
|
||||
{
|
||||
placementHandler?.Delete(HitObject);
|
||||
return;
|
||||
@@ -626,7 +626,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos)
|
||||
{
|
||||
if (BodyPiece.ReceivePositionalInputAt(screenSpacePos) && (IsSelected || DrawableObject.Body.Alpha > 0))
|
||||
if (BodyPiece.ReceivePositionalInputAt(screenSpacePos) && (IsSelected || DrawableObject.Body.Alpha > 0 || DrawableObject.HeadCircle.Alpha > 0))
|
||||
return true;
|
||||
|
||||
if (ControlPointVisualiser == null)
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
public BindableFloat Spacing { get; } = new BindableFloat(4f)
|
||||
{
|
||||
MinValue = 4f,
|
||||
MaxValue = 128f,
|
||||
MaxValue = 256f,
|
||||
Precision = 0.01f,
|
||||
};
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Edit.Components.TernaryButtons;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
@@ -351,6 +352,35 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
// Why is this logic here and not in `OsuSelectionHandler`?
|
||||
// Because we only want to handle this toggle after all other right-click handling completes.
|
||||
//
|
||||
// Consider that input is handled from the most nested child first:
|
||||
//
|
||||
// ComposeScreen
|
||||
// |- OsuContextMenuContainer // right click for context
|
||||
// |- TimelineBlueprintContainer
|
||||
// |- TimelineSelectionHandler
|
||||
// |- (Osu)HitObjectComposer // right click for toggle new combo
|
||||
// |- (Osu)EditorBlueprintContainer // right click for select
|
||||
// |- (Osu)EditorSelectionHandler // right click for delete
|
||||
if (e.Button == MouseButton.Right)
|
||||
{
|
||||
var osuSelectionHandler = (OsuSelectionHandler)BlueprintContainer.SelectionHandler;
|
||||
|
||||
if (!osuSelectionHandler.SelectedItems.Any())
|
||||
{
|
||||
osuSelectionHandler.SelectionNewComboState.Value =
|
||||
osuSelectionHandler.SelectionNewComboState.Value == TernaryState.False ? TernaryState.True : TernaryState.False;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnMouseDown(e);
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
|
||||
@@ -180,7 +180,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
Quad scaledQuad = GeometryUtils.GetSurroundingQuad(new OsuHitObject[] { slider });
|
||||
(bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad);
|
||||
|
||||
if (xInBounds && yInBounds && slider.Path.HasValidLength)
|
||||
if (xInBounds && yInBounds && slider.Path.HasValidLengthForPlacement)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < slider.Path.ControlPoints.Count; i++)
|
||||
@@ -263,12 +263,12 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
case Axes.X:
|
||||
(sLowerBound, sUpperBound) = computeBounds(lowerBounds - b, upperBounds - b, a);
|
||||
s.X = MathHelper.Clamp(s.X, sLowerBound, sUpperBound);
|
||||
s.X = Math.Clamp(s.X, sLowerBound, sUpperBound);
|
||||
break;
|
||||
|
||||
case Axes.Y:
|
||||
(sLowerBound, sUpperBound) = computeBounds(lowerBounds - a, upperBounds - a, b);
|
||||
s.Y = MathHelper.Clamp(s.Y, sLowerBound, sUpperBound);
|
||||
s.Y = Math.Clamp(s.Y, sLowerBound, sUpperBound);
|
||||
break;
|
||||
|
||||
case Axes.Both:
|
||||
@@ -276,11 +276,11 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
// Therefore the ratio s.X / s.Y will be maintained
|
||||
(sLowerBound, sUpperBound) = computeBounds(lowerBounds, upperBounds, a * s.X + b * s.Y);
|
||||
s.X = s.X < 0
|
||||
? MathHelper.Clamp(s.X, s.X * sUpperBound, s.X * sLowerBound)
|
||||
: MathHelper.Clamp(s.X, s.X * sLowerBound, s.X * sUpperBound);
|
||||
? Math.Clamp(s.X, s.X * sUpperBound, s.X * sLowerBound)
|
||||
: Math.Clamp(s.X, s.X * sLowerBound, s.X * sUpperBound);
|
||||
s.Y = s.Y < 0
|
||||
? MathHelper.Clamp(s.Y, s.Y * sUpperBound, s.Y * sLowerBound)
|
||||
: MathHelper.Clamp(s.Y, s.Y * sLowerBound, s.Y * sUpperBound);
|
||||
? Math.Clamp(s.Y, s.Y * sUpperBound, s.Y * sLowerBound)
|
||||
: Math.Clamp(s.Y, s.Y * sLowerBound, s.Y * sUpperBound);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 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.Diagnostics;
|
||||
using System.Linq;
|
||||
@@ -127,8 +128,11 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
|
||||
if (relativeCheckbox.Current.Value)
|
||||
{
|
||||
(xBindable.MinValue, xBindable.MaxValue) = (0 - initialSurroundingQuad.TopLeft.X, OsuPlayfield.BASE_SIZE.X - initialSurroundingQuad.BottomRight.X);
|
||||
(yBindable.MinValue, yBindable.MaxValue) = (0 - initialSurroundingQuad.TopLeft.Y, OsuPlayfield.BASE_SIZE.Y - initialSurroundingQuad.BottomRight.Y);
|
||||
xBindable.MinValue = 0 - Math.Max(initialSurroundingQuad.TopLeft.X, 0);
|
||||
xBindable.MaxValue = OsuPlayfield.BASE_SIZE.X - Math.Min(initialSurroundingQuad.BottomRight.X, OsuPlayfield.BASE_SIZE.X);
|
||||
|
||||
yBindable.MinValue = 0 - Math.Max(initialSurroundingQuad.TopLeft.Y, 0);
|
||||
yBindable.MaxValue = OsuPlayfield.BASE_SIZE.Y - Math.Min(initialSurroundingQuad.BottomRight.Y, OsuPlayfield.BASE_SIZE.Y);
|
||||
|
||||
xBindable.Default = yBindable.Default = 0;
|
||||
|
||||
@@ -146,8 +150,21 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
|
||||
var quadRelativeToPosition = new RectangleF(initialSurroundingQuad.Location - initialPosition, initialSurroundingQuad.Size);
|
||||
|
||||
(xBindable.MinValue, xBindable.MaxValue) = (0 - quadRelativeToPosition.TopLeft.X, OsuPlayfield.BASE_SIZE.X - quadRelativeToPosition.BottomRight.X);
|
||||
(yBindable.MinValue, yBindable.MaxValue) = (0 - quadRelativeToPosition.TopLeft.Y, OsuPlayfield.BASE_SIZE.Y - quadRelativeToPosition.BottomRight.Y);
|
||||
if (initialSurroundingQuad.Width < OsuPlayfield.BASE_SIZE.X)
|
||||
{
|
||||
xBindable.MinValue = 0 - quadRelativeToPosition.TopLeft.X;
|
||||
xBindable.MaxValue = OsuPlayfield.BASE_SIZE.X - quadRelativeToPosition.BottomRight.X;
|
||||
}
|
||||
else
|
||||
xBindable.MinValue = xBindable.MaxValue = initialPosition.X;
|
||||
|
||||
if (initialSurroundingQuad.Height < OsuPlayfield.BASE_SIZE.Y)
|
||||
{
|
||||
yBindable.MinValue = 0 - quadRelativeToPosition.TopLeft.Y;
|
||||
yBindable.MaxValue = OsuPlayfield.BASE_SIZE.Y - quadRelativeToPosition.BottomRight.Y;
|
||||
}
|
||||
else
|
||||
yBindable.MinValue = yBindable.MaxValue = initialPosition.Y;
|
||||
|
||||
xBindable.Default = initialPosition.X;
|
||||
yBindable.Default = initialPosition.Y;
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||
using osu.Game.Tests.Visual;
|
||||
@@ -31,7 +29,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Editor
|
||||
AddStep("hover over first hit", () => InputManager.MoveMouseTo(Editor.ChildrenOfType<DrawableHit>().ElementAt(1)));
|
||||
AddStep("hover over second hit", () => InputManager.MoveMouseTo(Editor.ChildrenOfType<DrawableHit>().ElementAt(0)));
|
||||
AddStep("right click", () => InputManager.Click(MouseButton.Right));
|
||||
AddUntilStep("context menu open", () => Editor.ChildrenOfType<OsuContextMenu>().Any(menu => menu.State == MenuState.Open));
|
||||
AddUntilStep("second hit deleted", () => Editor.ChildrenOfType<DrawableHit>().Count(), () => Is.EqualTo(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,25 +13,21 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
/// <summary>
|
||||
/// The difficulty corresponding to the rhythm skill.
|
||||
/// </summary>
|
||||
[JsonProperty("rhythm_difficulty")]
|
||||
public double RhythmDifficulty { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The difficulty corresponding to the reading skill.
|
||||
/// </summary>
|
||||
[JsonProperty("reading_difficulty")]
|
||||
public double ReadingDifficulty { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The difficulty corresponding to the colour skill.
|
||||
/// </summary>
|
||||
[JsonProperty("colour_difficulty")]
|
||||
public double ColourDifficulty { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The difficulty corresponding to the stamina skill.
|
||||
/// </summary>
|
||||
[JsonProperty("stamina_difficulty")]
|
||||
public double StaminaDifficulty { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -40,13 +36,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
[JsonProperty("mono_stamina_factor")]
|
||||
public double MonoStaminaFactor { get; set; }
|
||||
|
||||
[JsonProperty("rhythm_difficult_strains")]
|
||||
public double RhythmTopStrains { get; set; }
|
||||
|
||||
[JsonProperty("colour_difficult_strains")]
|
||||
public double ColourTopStrains { get; set; }
|
||||
|
||||
[JsonProperty("stamina_difficult_strains")]
|
||||
public double StaminaTopStrains { get; set; }
|
||||
|
||||
public override IEnumerable<(int attributeId, object value)> ToDatabaseAttributes()
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
|
||||
private bool isConvert;
|
||||
|
||||
public override int Version => 20241007;
|
||||
public override int Version => 20250306;
|
||||
|
||||
public TaikoDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Taiko.Beatmaps;
|
||||
@@ -21,9 +19,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
|
||||
public readonly IBindable<bool> LockPlayfieldAspectRange = new BindableBool(true);
|
||||
|
||||
[Resolved]
|
||||
private OsuGame? osuGame { get; set; }
|
||||
|
||||
public TaikoPlayfieldAdjustmentContainer()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
@@ -60,19 +55,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
// Limit the maximum relative height of the playfield to one-third of available area to avoid it masking out on extreme resolutions.
|
||||
relativeHeight = Math.Min(relativeHeight, 1f / 3f);
|
||||
|
||||
Scale = new Vector2(Math.Max((Parent!.ChildSize.Y / 768f) * (relativeHeight / base_relative_height), 1f));
|
||||
|
||||
// on mobile platforms where the base aspect ratio is wider, the taiko playfield
|
||||
// needs to be scaled down to remain playable.
|
||||
if (RuntimeInfo.IsMobile && osuGame != null)
|
||||
{
|
||||
const float base_aspect_ratio = 1024f / 768f;
|
||||
float gameAspectRatio = osuGame.ScalingContainerTargetDrawSize.X / osuGame.ScalingContainerTargetDrawSize.Y;
|
||||
// this magic scale is unexplainable, but required so the playfield doesn't become too zoomed out as the aspect ratio increases.
|
||||
const float magic_scale = 1.25f;
|
||||
Scale *= magic_scale * new Vector2(base_aspect_ratio / gameAspectRatio);
|
||||
}
|
||||
|
||||
Scale = new Vector2(Parent!.ChildSize.Y / 768f * (relativeHeight / base_relative_height));
|
||||
Width = 1 / Scale.X;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps
|
||||
{
|
||||
public class BeatmapExtensionsTest
|
||||
{
|
||||
[Test]
|
||||
public void TestLengthCalculations()
|
||||
{
|
||||
var beatmap = new Beatmap
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new HitCircle { StartTime = 5_000 },
|
||||
new HitCircle { StartTime = 300_000 },
|
||||
new Spinner { StartTime = 280_000, Duration = 40_000 }
|
||||
},
|
||||
Breaks =
|
||||
{
|
||||
new BreakPeriod(50_000, 75_000),
|
||||
new BreakPeriod(100_000, 150_000),
|
||||
}
|
||||
};
|
||||
|
||||
Assert.That(beatmap.CalculatePlayableBounds(), Is.EqualTo((5_000, 320_000)));
|
||||
Assert.That(beatmap.CalculatePlayableLength(), Is.EqualTo(315_000)); // 320_000 - 5_000
|
||||
Assert.That(beatmap.CalculateDrainLength(), Is.EqualTo(240_000)); // 315_000 - (25_000 + 50_000) = 315_000 - 75_000
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDrainLengthCannotGoNegative()
|
||||
{
|
||||
var beatmap = new Beatmap
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new HitCircle { StartTime = 5_000 },
|
||||
new HitCircle { StartTime = 300_000 },
|
||||
new Spinner { StartTime = 280_000, Duration = 40_000 }
|
||||
},
|
||||
Breaks =
|
||||
{
|
||||
new BreakPeriod(0, 350_000),
|
||||
}
|
||||
};
|
||||
|
||||
Assert.That(beatmap.CalculatePlayableBounds(), Is.EqualTo((5_000, 320_000)));
|
||||
Assert.That(beatmap.CalculatePlayableLength(), Is.EqualTo(315_000)); // 320_000 - 5_000
|
||||
Assert.That(beatmap.CalculateDrainLength(), Is.EqualTo(0)); // break period encompasses entire beatmap
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -404,6 +404,35 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestComboColourCountIsLimitedToEight()
|
||||
{
|
||||
var decoder = new LegacySkinDecoder();
|
||||
|
||||
using (var resStream = TestResources.OpenResource("too-many-combo-colours.osu"))
|
||||
using (var stream = new LineBufferedReader(resStream))
|
||||
{
|
||||
var comboColors = decoder.Decode(stream).ComboColours;
|
||||
|
||||
Debug.Assert(comboColors != null);
|
||||
|
||||
Color4[] expectedColors =
|
||||
{
|
||||
new Color4(142, 199, 255, 255),
|
||||
new Color4(255, 128, 128, 255),
|
||||
new Color4(128, 255, 255, 255),
|
||||
new Color4(128, 255, 128, 255),
|
||||
new Color4(255, 187, 255, 255),
|
||||
new Color4(255, 177, 140, 255),
|
||||
new Color4(100, 100, 100, 255),
|
||||
new Color4(142, 199, 255, 255),
|
||||
};
|
||||
Assert.AreEqual(expectedColors.Length, comboColors.Count);
|
||||
for (int i = 0; i < expectedColors.Length; i++)
|
||||
Assert.AreEqual(expectedColors[i], comboColors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGetLastObjectTime()
|
||||
{
|
||||
|
||||
@@ -28,6 +28,7 @@ using osu.Game.Skinning;
|
||||
using osu.Game.Storyboards;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps.Formats
|
||||
{
|
||||
@@ -184,6 +185,32 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Assert.That(decodedSlider.Path.ControlPoints.Count, Is.EqualTo(5));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOnlyEightComboColoursEncoded()
|
||||
{
|
||||
var beatmapSkin = new LegacyBeatmapSkin(new BeatmapInfo(), null)
|
||||
{
|
||||
Configuration =
|
||||
{
|
||||
CustomComboColours =
|
||||
{
|
||||
new Color4(1, 1, 1, 255),
|
||||
new Color4(2, 2, 2, 255),
|
||||
new Color4(3, 3, 3, 255),
|
||||
new Color4(4, 4, 4, 255),
|
||||
new Color4(5, 5, 5, 255),
|
||||
new Color4(6, 6, 6, 255),
|
||||
new Color4(7, 7, 7, 255),
|
||||
new Color4(8, 8, 8, 255),
|
||||
new Color4(9, 9, 9, 255),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var decodedAfterEncode = decodeFromLegacy(encodeToLegacy((new Beatmap(), beatmapSkin)), string.Empty);
|
||||
Assert.That(decodedAfterEncode.skin.Configuration.CustomComboColours, Has.Count.EqualTo(8));
|
||||
}
|
||||
|
||||
private bool areComboColoursEqual(IHasComboColours a, IHasComboColours b)
|
||||
{
|
||||
// equal to null, no need to SequenceEqual
|
||||
@@ -212,6 +239,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
{
|
||||
var beatmap = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(reader);
|
||||
var beatmapSkin = new TestLegacySkin(beatmaps_resource_store, name);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
beatmapSkin.Configuration = new LegacySkinDecoder().Decode(reader);
|
||||
return (convert(beatmap), beatmapSkin);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ using Humanizer;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Tests.Visual.Multiplayer;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual.Multiplayer
|
||||
@@ -16,6 +16,13 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
|
||||
[HeadlessTest]
|
||||
public partial class StatefulMultiplayerClientTest : MultiplayerTestScene
|
||||
{
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
AddStep("join room", () => JoinRoom(CreateDefaultRoom()));
|
||||
WaitForJoined();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUserAddedOnJoin()
|
||||
{
|
||||
@@ -72,10 +79,6 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
|
||||
|
||||
AddStep("create room initially in gameplay", () =>
|
||||
{
|
||||
var newRoom = new Room();
|
||||
newRoom.CopyFrom(SelectedRoom.Value!);
|
||||
|
||||
newRoom.RoomID = null;
|
||||
MultiplayerClient.RoomSetupAction = room =>
|
||||
{
|
||||
room.State = MultiplayerRoomState.Playing;
|
||||
@@ -86,13 +89,32 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
|
||||
});
|
||||
};
|
||||
|
||||
RoomManager.CreateRoom(newRoom);
|
||||
MultiplayerClient.JoinRoom(MultiplayerClient.ServerSideRooms.Single()).ConfigureAwait(false);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for room join", () => RoomJoined);
|
||||
checkPlayingUserCount(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestJoinRoomWithManyUsers()
|
||||
{
|
||||
AddStep("leave room", () => MultiplayerClient.LeaveRoom());
|
||||
AddUntilStep("wait for room part", () => !RoomJoined);
|
||||
|
||||
AddStep("create room with many users", () =>
|
||||
{
|
||||
MultiplayerClient.RoomSetupAction = room =>
|
||||
{
|
||||
room.Users.AddRange(Enumerable.Range(PLAYER_1_ID, 100).Select(id => new MultiplayerRoomUser(id)));
|
||||
};
|
||||
|
||||
MultiplayerClient.JoinRoom(MultiplayerClient.ServerSideRooms.Single()).ConfigureAwait(false);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for room join", () => RoomJoined);
|
||||
}
|
||||
|
||||
private void checkPlayingUserCount(int expectedCount)
|
||||
=> AddAssert($"{"user".ToQuantity(expectedCount)} playing", () => MultiplayerClient.CurrentMatchPlayingUserIds.Count == expectedCount);
|
||||
|
||||
|
||||
@@ -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 NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Playlists;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
|
||||
namespace osu.Game.Tests.OnlinePlay
|
||||
{
|
||||
[HeadlessTest]
|
||||
public partial class TestSceneOnlinePlaySubScreenStack : OnlinePlayTestScene
|
||||
{
|
||||
private ScreenStack stack = null!;
|
||||
|
||||
[SetUp]
|
||||
public void Setup() => Schedule(() =>
|
||||
{
|
||||
Child = stack = new OnlinePlaySubScreenStack
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestBindablesDisabledWhenRequested()
|
||||
{
|
||||
AddAssert("bindables not disabled", () => Beatmap.Disabled || Ruleset.Disabled || SelectedMods.Disabled, () => Is.False);
|
||||
|
||||
AddStep("push screen that disables bindables", () => stack.Push(new ScreenWithExternalBindableDisablement(true)));
|
||||
AddAssert("bindables disabled", () => Beatmap.Disabled && Ruleset.Disabled && SelectedMods.Disabled, () => Is.True);
|
||||
|
||||
AddStep("push screen that does not disable bindables", () => stack.Push(new ScreenWithExternalBindableDisablement(false)));
|
||||
AddAssert("bindables not disabled", () => Beatmap.Disabled || Ruleset.Disabled || SelectedMods.Disabled, () => Is.False);
|
||||
|
||||
AddStep("exit one screen", () => stack.Exit());
|
||||
AddAssert("bindables disabled", () => Beatmap.Disabled && Ruleset.Disabled && SelectedMods.Disabled, () => Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestModsResetWhenExitToLounge()
|
||||
{
|
||||
AddStep("push lounge", () => stack.Push(new PlaylistsLoungeSubScreen()));
|
||||
|
||||
AddStep("push screen with mod", () => stack.Push(new ScreenWithMod(new OsuModDoubleTime())));
|
||||
AddUntilStep("wait for screen to load", () => ((OsuScreen)stack.CurrentScreen).IsLoaded);
|
||||
AddAssert("mod set", () => SelectedMods.Value.Count, () => Is.GreaterThan(0));
|
||||
|
||||
AddStep("exit to lounge", () => stack.Exit());
|
||||
AddAssert("mods reset", () => SelectedMods.Value.Count, () => Is.Zero);
|
||||
}
|
||||
|
||||
private partial class ScreenWithExternalBindableDisablement : OsuScreen
|
||||
{
|
||||
public override bool DisallowExternalBeatmapRulesetChanges { get; }
|
||||
|
||||
public ScreenWithExternalBindableDisablement(bool disableBindables)
|
||||
{
|
||||
DisallowExternalBeatmapRulesetChanges = disableBindables;
|
||||
}
|
||||
}
|
||||
|
||||
private partial class ScreenWithMod : OsuScreen
|
||||
{
|
||||
private readonly Mod mod;
|
||||
|
||||
public ScreenWithMod(Mod mod)
|
||||
{
|
||||
this.mod = mod;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
Mods.Value = [mod];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
AudioFilename: 03. Renatus - Soleily 192kbps.mp3
|
||||
AudioLeadIn: 0
|
||||
PreviewTime: 164471
|
||||
Countdown: 0
|
||||
SampleSet: Soft
|
||||
StackLeniency: 0.7
|
||||
Mode: 0
|
||||
LetterboxInBreaks: 0
|
||||
WidescreenStoryboard: 0
|
||||
|
||||
[Editor]
|
||||
Bookmarks: 11505,22054,32604,43153,53703,64252,74802,85351,95901,106450,116999,119637,130186,140735,151285,161834,164471,175020,185570,196119,206669,209306
|
||||
DistanceSpacing: 1.8
|
||||
BeatDivisor: 4
|
||||
GridSize: 4
|
||||
TimelineZoom: 2
|
||||
|
||||
[Metadata]
|
||||
Title:Renatus
|
||||
TitleUnicode:Renatus
|
||||
Artist:Soleily
|
||||
ArtistUnicode:Soleily
|
||||
Creator:Gamu
|
||||
Version:Insane
|
||||
Source:
|
||||
Tags:MBC7 Unisphere 地球ヤバイEP Chikyu Yabai
|
||||
BeatmapID:557821
|
||||
BeatmapSetID:241526
|
||||
|
||||
[Difficulty]
|
||||
HPDrainRate:6.5
|
||||
CircleSize:4
|
||||
OverallDifficulty:8
|
||||
ApproachRate:9
|
||||
SliderMultiplier:1.8
|
||||
SliderTickRate:2
|
||||
|
||||
[Events]
|
||||
//Background and Video events
|
||||
0,0,"machinetop_background.jpg",0,0
|
||||
//Break Periods
|
||||
2,122474,140135
|
||||
//Storyboard Layer 0 (Background)
|
||||
//Storyboard Layer 1 (Fail)
|
||||
//Storyboard Layer 2 (Pass)
|
||||
//Storyboard Layer 3 (Foreground)
|
||||
//Storyboard Sound Samples
|
||||
|
||||
[TimingPoints]
|
||||
956,329.67032967033,4,2,0,60,1,0
|
||||
|
||||
|
||||
[Colours]
|
||||
Combo1:142,199,255
|
||||
Combo2:255,128,128
|
||||
Combo3:128,255,255
|
||||
Combo4:128,255,128
|
||||
Combo5:255,187,255
|
||||
Combo6:255,177,140
|
||||
Combo7:100,100,100
|
||||
Combo8:142,199,255
|
||||
Combo9:255,128,128
|
||||
Combo10:128,255,255
|
||||
Combo11:128,255,128
|
||||
Combo12:255,187,255
|
||||
Combo13:255,177,140
|
||||
Combo14:100,100,100
|
||||
|
||||
[HitObjects]
|
||||
192,168,956,6,0,P|184:128|200:80,1,90,4|0,1:2|0:0,0:0:0:0:
|
||||
@@ -6,6 +6,8 @@ using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Metadata;
|
||||
@@ -13,9 +15,11 @@ using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.SelectV2.Leaderboards;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Tests.Visual.Metadata;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
{
|
||||
@@ -57,6 +61,39 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
AddStep("push screen", () => LoadScreen(new Screens.OnlinePlay.DailyChallenge.DailyChallenge(room)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUseTheseModsUnavailableIfNoFreeMods()
|
||||
{
|
||||
var room = new Room
|
||||
{
|
||||
RoomID = 1234,
|
||||
Name = "Daily Challenge: June 4, 2024",
|
||||
Playlist =
|
||||
[
|
||||
new PlaylistItem(TestResources.CreateTestBeatmapSetInfo().Beatmaps.First())
|
||||
{
|
||||
RequiredMods = [new APIMod(new OsuModTraceable())],
|
||||
AllowedMods = []
|
||||
}
|
||||
],
|
||||
EndDate = DateTimeOffset.Now.AddHours(12),
|
||||
Category = RoomCategory.DailyChallenge
|
||||
};
|
||||
|
||||
AddStep("add room", () => API.Perform(new CreateRoomRequest(room)));
|
||||
Screens.OnlinePlay.DailyChallenge.DailyChallenge screen = null!;
|
||||
AddStep("push screen", () => LoadScreen(screen = new Screens.OnlinePlay.DailyChallenge.DailyChallenge(room)));
|
||||
AddUntilStep("wait for pushed", () => screen.IsCurrentScreen());
|
||||
AddStep("force transforms to finish", () => FinishTransforms(true));
|
||||
AddStep("right click second score", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<LeaderboardScoreV2>().ElementAt(1));
|
||||
InputManager.Click(MouseButton.Right);
|
||||
});
|
||||
AddAssert("use these mods not present",
|
||||
() => this.ChildrenOfType<OsuContextMenu>().All(m => m.Items.All(item => item.Text.Value != "Use these mods")));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNotifications()
|
||||
{
|
||||
|
||||
@@ -94,6 +94,8 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddAssert("new beatmap not persisted", () => beatmapManager.QueryBeatmapSet(s => s.ID == editorBeatmap.BeatmapInfo.BeatmapSet.AsNonNull().ID)?.Value.DeletePending == true);
|
||||
|
||||
AddUntilStep("wait for default beatmap", () => Editor.Beatmap.Value is DummyWorkingBeatmap);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -171,6 +173,8 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
return difficultyName != null && difficultyName != firstDifficultyName;
|
||||
});
|
||||
|
||||
ensureEditorLoaded();
|
||||
|
||||
AddAssert("created difficulty has timing point", () =>
|
||||
{
|
||||
var timingPoint = EditorBeatmap.ControlPointInfo.TimingPoints.Single();
|
||||
@@ -215,6 +219,8 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
return difficultyName != null && difficultyName != previousDifficultyName;
|
||||
});
|
||||
|
||||
ensureEditorLoaded();
|
||||
|
||||
AddStep("set unique difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = previousDifficultyName = Guid.NewGuid().ToString());
|
||||
AddStep("add timing point", () => EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 }));
|
||||
AddStep("add effect points", () =>
|
||||
@@ -239,6 +245,8 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
return difficultyName != null && difficultyName != previousDifficultyName;
|
||||
});
|
||||
|
||||
ensureEditorLoaded();
|
||||
|
||||
AddAssert("created difficulty has timing point", () =>
|
||||
{
|
||||
var timingPoint = EditorBeatmap.ControlPointInfo.TimingPoints.Single();
|
||||
@@ -287,6 +295,8 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
return difficultyName != null && difficultyName != firstDifficultyName;
|
||||
});
|
||||
|
||||
ensureEditorLoaded();
|
||||
|
||||
AddAssert("created difficulty has timing point", () =>
|
||||
{
|
||||
var timingPoint = EditorBeatmap.ControlPointInfo.TimingPoints.Single();
|
||||
@@ -367,6 +377,8 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
return difficultyName != null && difficultyName != originalDifficultyName;
|
||||
});
|
||||
|
||||
ensureEditorLoaded();
|
||||
|
||||
AddAssert("created difficulty has copy suffix in name", () => EditorBeatmap.BeatmapInfo.DifficultyName == copyDifficultyName);
|
||||
AddAssert("created difficulty has timing point", () =>
|
||||
{
|
||||
@@ -377,7 +389,9 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddAssert("approach rate correctly copied", () => EditorBeatmap.Difficulty.ApproachRate == 4);
|
||||
AddAssert("combo colours correctly copied", () => EditorBeatmap.BeatmapSkin.AsNonNull().ComboColours.Count == 2);
|
||||
|
||||
ensureEditorLoaded();
|
||||
AddAssert("status is modified", () => EditorBeatmap.BeatmapInfo.Status == BeatmapOnlineStatus.LocallyModified);
|
||||
|
||||
AddAssert("online ID not copied", () => EditorBeatmap.BeatmapInfo.OnlineID == -1);
|
||||
|
||||
AddStep("save beatmap", () => Editor.Save());
|
||||
@@ -440,6 +454,8 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
return difficultyName != null && difficultyName != originalDifficultyName;
|
||||
});
|
||||
|
||||
ensureEditorLoaded();
|
||||
|
||||
AddStep("save without changes", () => Editor.Save());
|
||||
|
||||
AddAssert("collection still points to old beatmap", () => !collection.BeatmapMD5Hashes.Contains(EditorBeatmap.BeatmapInfo.MD5Hash)
|
||||
@@ -477,6 +493,9 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||
return difficultyName != null && difficultyName != "New Difficulty";
|
||||
});
|
||||
|
||||
ensureEditorLoaded();
|
||||
|
||||
AddAssert("new difficulty has correct name", () => EditorBeatmap.BeatmapInfo.DifficultyName == "New Difficulty (1)");
|
||||
AddAssert("new difficulty persisted", () =>
|
||||
{
|
||||
@@ -514,6 +533,8 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
return difficultyName != null && difficultyName != duplicate_difficulty_name;
|
||||
});
|
||||
|
||||
ensureEditorLoaded();
|
||||
|
||||
AddStep("set difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = duplicate_difficulty_name);
|
||||
AddStep("try to save beatmap", () => Editor.Save());
|
||||
AddAssert("beatmap set not corrupted", () =>
|
||||
@@ -540,6 +561,8 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
return set != null && set.PerformRead(s => s.Beatmaps.Count == 1 && s.Files.Count == 1);
|
||||
});
|
||||
|
||||
ensureEditorLoaded();
|
||||
|
||||
AddStep("create new difficulty", () => Editor.CreateNewDifficulty(new CatchRuleset().RulesetInfo));
|
||||
|
||||
AddUntilStep("wait for created", () =>
|
||||
@@ -547,7 +570,8 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||
return difficultyName != null && difficultyName != duplicate_difficulty_name;
|
||||
});
|
||||
AddUntilStep("wait for editor load", () => Editor.IsLoaded && DialogOverlay.IsLoaded);
|
||||
|
||||
ensureEditorLoaded();
|
||||
|
||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[]
|
||||
{
|
||||
@@ -584,6 +608,9 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||
return difficultyName != null && difficultyName == "New Difficulty";
|
||||
});
|
||||
|
||||
ensureEditorLoaded();
|
||||
|
||||
AddAssert("new difficulty persisted", () =>
|
||||
{
|
||||
var set = beatmapManager.QueryBeatmapSet(s => s.ID == setId);
|
||||
@@ -610,6 +637,9 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||
return difficultyName != null && difficultyName == "New Difficulty (1)";
|
||||
});
|
||||
|
||||
ensureEditorLoaded();
|
||||
|
||||
AddAssert("new difficulty persisted", () =>
|
||||
{
|
||||
var set = beatmapManager.QueryBeatmapSet(s => s.ID == setId);
|
||||
@@ -735,6 +765,8 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddAssert("other audio not removed", () => Beatmap.Value.BeatmapSetInfo.Files.Any(f => f.Filename == "audio (1).mp3"));
|
||||
}
|
||||
|
||||
private void ensureEditorLoaded() => AddUntilStep("wait for editor load", () => Editor.IsLoaded && DialogOverlay.IsLoaded);
|
||||
|
||||
private void createNewDifficulty()
|
||||
{
|
||||
string? currentDifficulty = null;
|
||||
@@ -748,13 +780,14 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
AddUntilStep("wait for dialog", () => DialogOverlay.CurrentDialog is CreateNewDifficultyDialog);
|
||||
AddStep("confirm creation with no objects", () => DialogOverlay.CurrentDialog!.PerformOkAction());
|
||||
|
||||
AddUntilStep("wait for created", () =>
|
||||
{
|
||||
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||
return difficultyName != null && difficultyName != currentDifficulty;
|
||||
});
|
||||
ensureEditorLoaded();
|
||||
|
||||
AddUntilStep("wait for editor load", () => Editor.IsLoaded);
|
||||
AddStep("enter setup mode", () => Editor.Mode.Value = EditorScreenMode.SongSetup);
|
||||
AddUntilStep("wait for load", () => Editor.ChildrenOfType<SetupScreen>().Any());
|
||||
}
|
||||
@@ -765,7 +798,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddStep($"switch to difficulty #{index + 1}", () =>
|
||||
Editor.SwitchToDifficulty(Beatmap.Value.BeatmapSetInfo.Beatmaps.ElementAt(index)));
|
||||
|
||||
AddUntilStep("wait for editor load", () => Editor.IsLoaded);
|
||||
ensureEditorLoaded();
|
||||
AddStep("enter setup mode", () => Editor.Mode.Value = EditorScreenMode.SongSetup);
|
||||
AddUntilStep("wait for load", () => Editor.ChildrenOfType<SetupScreen>().Any());
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
@@ -14,8 +15,10 @@ using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Edit.Components.TernaryButtons;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
@@ -58,19 +61,63 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestContextMenu()
|
||||
public void TestRightClickDuringEmptyPlacementTogglesNewCombo()
|
||||
{
|
||||
AddStep("select circle placement tool", () => InputManager.Key(Key.Number2));
|
||||
AddStep("move mouse to center of playfield", () => InputManager.MoveMouseTo(this.ChildrenOfType<Playfield>().Single()));
|
||||
AddStep("place circle", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("one circle added", () => EditorBeatmap.HitObjects, () => Has.One.Items);
|
||||
|
||||
AddStep("move mouse away from placed circle", () => InputManager.MoveMouseTo(this.ChildrenOfType<Playfield>().Single().ScreenSpaceDrawQuad.TopLeft + Vector2.One));
|
||||
|
||||
AddAssert("new combo false", () => this.ChildrenOfType<NewComboTernaryButton>().Single().Current.Value, () => Is.EqualTo(TernaryState.False));
|
||||
AddStep("click right mouse", () => InputManager.Click(MouseButton.Right));
|
||||
AddAssert("new combo true", () => this.ChildrenOfType<NewComboTernaryButton>().Single().Current.Value, () => Is.EqualTo(TernaryState.True));
|
||||
AddAssert("context menu not visible", () => !Editor.ChildrenOfType<OsuContextMenu>().Any(c => c.IsPresent));
|
||||
|
||||
AddStep("click right mouse", () => InputManager.Click(MouseButton.Right));
|
||||
AddAssert("new combo false", () => this.ChildrenOfType<NewComboTernaryButton>().Single().Current.Value, () => Is.EqualTo(TernaryState.False));
|
||||
AddAssert("context menu not visible", () => !Editor.ChildrenOfType<OsuContextMenu>().Any(c => c.IsPresent));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRightClickDuringPlacementDeletes()
|
||||
{
|
||||
AddStep("select circle placement tool", () => InputManager.Key(Key.Number2));
|
||||
AddStep("move mouse to center of playfield", () => InputManager.MoveMouseTo(this.ChildrenOfType<Playfield>().Single()));
|
||||
AddStep("place circle", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("one circle added", () => EditorBeatmap.HitObjects, () => Has.One.Items);
|
||||
|
||||
AddStep("click right mouse", () => InputManager.Click(MouseButton.Right));
|
||||
|
||||
AddAssert("circle removed", () => EditorBeatmap.HitObjects, () => Has.Exactly(0).Items);
|
||||
AddAssert("circle not selected", () => EditorBeatmap.SelectedHitObjects, () => Has.Exactly(0).Items);
|
||||
AddAssert("context menu not visible", () => !Editor.ChildrenOfType<OsuContextMenu>().Any(c => c.IsPresent));
|
||||
AddAssert("new combo false", () => this.ChildrenOfType<NewComboTernaryButton>().Single().Current.Value, () => Is.EqualTo(TernaryState.False));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRightClickDuringSelectionShowsContextMenu()
|
||||
{
|
||||
AddStep("select circle placement tool", () => InputManager.Key(Key.Number2));
|
||||
AddStep("move mouse to center of playfield", () => InputManager.MoveMouseTo(this.ChildrenOfType<Playfield>().Single()));
|
||||
AddStep("place circle", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
AddAssert("one circle added", () => EditorBeatmap.HitObjects, () => Has.One.Items);
|
||||
AddStep("delete with right mouse", () =>
|
||||
{
|
||||
InputManager.Click(MouseButton.Right);
|
||||
});
|
||||
AddAssert("circle not removed", () => EditorBeatmap.HitObjects, () => Has.One.Items);
|
||||
// ensure the circle we're selecting is not a new combo so we can assert
|
||||
// new combo doesn't happen to get toggled by right click.
|
||||
AddStep("seek forward", () => EditorClock.Seek(1000));
|
||||
AddStep("place second circle", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
AddAssert("two circles added", () => EditorBeatmap.HitObjects, () => Has.Exactly(2).Items);
|
||||
AddAssert("context menu not visible", () => !Editor.ChildrenOfType<OsuContextMenu>().Any(c => c.IsPresent));
|
||||
|
||||
AddStep("select selection tool", () => InputManager.Key(Key.Number1));
|
||||
AddStep("click right mouse", () => InputManager.Click(MouseButton.Right));
|
||||
|
||||
AddAssert("circle not removed", () => EditorBeatmap.HitObjects, () => Has.Exactly(2).Items);
|
||||
AddAssert("circle selected", () => EditorBeatmap.SelectedHitObjects, () => Has.One.Items);
|
||||
AddAssert("context menu visible", () => Editor.ChildrenOfType<OsuContextMenu>().Any(c => c.IsPresent));
|
||||
AddAssert("new combo false", () => this.ChildrenOfType<NewComboTernaryButton>().Single().Current.Value, () => Is.EqualTo(TernaryState.False));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -128,12 +128,12 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddStep("Press alt down", () => InputManager.PressKey(Key.AltLeft));
|
||||
AddStep("Scroll by 3", () => InputManager.ScrollBy(new Vector2(0, 3)));
|
||||
AddAssert("Box not at 0", () => !Precision.AlmostEquals(boxQuad.TopLeft, scrollQuad.TopLeft));
|
||||
AddAssert("Box 1/4 at 1/4", () => Precision.AlmostEquals(boxQuad.TopLeft.X + 0.25f * boxQuad.Size.X, scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X));
|
||||
AddAssert("Box 1/2 at 1/2", () => Precision.AlmostEquals(boxQuad.TopLeft.X + 0.5f * boxQuad.Size.X, scrollQuad.TopLeft.X + 0.5f * scrollQuad.Size.X));
|
||||
|
||||
// Scroll out at 0.25
|
||||
AddStep("Scroll by -3", () => InputManager.ScrollBy(new Vector2(0, -3)));
|
||||
AddAssert("Box at 0", () => Precision.AlmostEquals(boxQuad.TopLeft, scrollQuad.TopLeft));
|
||||
AddAssert("Box 1/4 at 1/4", () => Precision.AlmostEquals(boxQuad.TopLeft.X + 0.25f * boxQuad.Size.X, scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X));
|
||||
AddAssert("Box 1/2 at 1/2", () => Precision.AlmostEquals(boxQuad.TopLeft.X + 0.5f * boxQuad.Size.X, scrollQuad.TopLeft.X + 0.5f * scrollQuad.Size.X));
|
||||
AddStep("Release alt", () => InputManager.ReleaseKey(Key.AltLeft));
|
||||
}
|
||||
|
||||
|
||||
@@ -40,11 +40,16 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
breakTracker = new TestBreakTracker(),
|
||||
breakOverlay = new BreakOverlay(true, new ScoreProcessor(new OsuRuleset()))
|
||||
breakOverlay = new BreakOverlay(new ScoreProcessor(new OsuRuleset()))
|
||||
{
|
||||
ProcessCustomClock = false,
|
||||
BreakTracker = breakTracker,
|
||||
}
|
||||
},
|
||||
new LetterboxOverlay
|
||||
{
|
||||
ProcessCustomClock = false,
|
||||
BreakTracker = breakTracker,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
// 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;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Screens.Play.Break;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public partial class TestSceneLetterboxOverlay : OsuTestScene
|
||||
{
|
||||
public TestSceneLetterboxOverlay()
|
||||
{
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
new LetterboxOverlay()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
Bindable<LocalUserPlayingState> playingState = new Bindable<LocalUserPlayingState>();
|
||||
GameplayState gameplayState = new GameplayState(new Beatmap(), new OsuRuleset(), healthProcessor: new OsuHealthProcessor(0), localUserPlayingState: playingState);
|
||||
TestSpectatorClient spectatorClient = new TestSpectatorClient();
|
||||
TestMultiplayerClient multiplayerClient = new TestMultiplayerClient(new TestMultiplayerRoomManager(new TestRoomRequestsHandler()));
|
||||
TestMultiplayerClient multiplayerClient = new TestMultiplayerClient(new TestRoomRequestsHandler());
|
||||
|
||||
AddStep("create spectator list", () =>
|
||||
{
|
||||
@@ -75,8 +75,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddRepeatStep("remove random user", () => ((ISpectatorClient)spectatorClient).UserEndedWatching(
|
||||
spectatorClient.WatchingUsers[RNG.Next(spectatorClient.WatchingUsers.Count)].OnlineID), 5);
|
||||
|
||||
AddStep("change font to venera", () => list.Font.Value = Typeface.Venera);
|
||||
AddStep("change font to torus", () => list.Font.Value = Typeface.Torus);
|
||||
AddStep("change font to venera", () => list.HeaderFont.Value = Typeface.Venera);
|
||||
AddStep("change font to torus", () => list.HeaderFont.Value = Typeface.Torus);
|
||||
AddStep("change header colour", () => list.HeaderColour.Value = new Colour4(RNG.NextSingle(), RNG.NextSingle(), RNG.NextSingle(), 1));
|
||||
|
||||
AddStep("enter break", () => playingState.Value = LocalUserPlayingState.Break);
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var mockLounge = new Mock<LoungeSubScreen>();
|
||||
var mockLounge = new Mock<IOnlinePlayLounge>();
|
||||
mockLounge
|
||||
.Setup(l => l.Join(It.IsAny<Room>(), It.IsAny<string>(), It.IsAny<Action<Room>>(), It.IsAny<Action<string>>()))
|
||||
.Callback<Room, string, Action<Room>, Action<string>>((_, _, _, d) =>
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public partial class TestSceneDrawableRoomParticipantsList : OnlinePlayTestScene
|
||||
{
|
||||
private Room room = null!;
|
||||
private DrawableRoomParticipantsList list = null!;
|
||||
|
||||
public override void SetUpSteps()
|
||||
@@ -23,7 +24,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("create list", () =>
|
||||
{
|
||||
SelectedRoom.Value = new Room
|
||||
room = new Room
|
||||
{
|
||||
Name = "test room",
|
||||
Host = new APIUser
|
||||
@@ -33,7 +34,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
};
|
||||
|
||||
Child = list = new DrawableRoomParticipantsList(SelectedRoom.Value)
|
||||
Child = list = new DrawableRoomParticipantsList(room)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@@ -119,7 +120,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddAssert("4 circles displayed", () => list.ChildrenOfType<UpdateableAvatar>().Count() == 4);
|
||||
AddAssert("46 hidden users", () => list.ChildrenOfType<DrawableRoomParticipantsList.HiddenUserCount>().Single().Count == 46);
|
||||
|
||||
AddStep("remove from end", () => removeUserAt(SelectedRoom.Value!.RecentParticipants.Count - 1));
|
||||
AddStep("remove from end", () => removeUserAt(room.RecentParticipants.Count - 1));
|
||||
AddAssert("4 circles displayed", () => list.ChildrenOfType<UpdateableAvatar>().Count() == 4);
|
||||
AddAssert("45 hidden users", () => list.ChildrenOfType<DrawableRoomParticipantsList.HiddenUserCount>().Single().Count == 45);
|
||||
|
||||
@@ -138,18 +139,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private void addUser(int id)
|
||||
{
|
||||
SelectedRoom.Value!.RecentParticipants = SelectedRoom.Value!.RecentParticipants.Append(new APIUser
|
||||
room.RecentParticipants = room.RecentParticipants.Append(new APIUser
|
||||
{
|
||||
Id = id,
|
||||
Username = $"User {id}"
|
||||
}).ToArray();
|
||||
SelectedRoom.Value!.ParticipantCount++;
|
||||
room.ParticipantCount++;
|
||||
}
|
||||
|
||||
private void removeUserAt(int index)
|
||||
{
|
||||
SelectedRoom.Value!.RecentParticipants = SelectedRoom.Value!.RecentParticipants.Where(u => !u.Equals(SelectedRoom.Value!.RecentParticipants[index])).ToArray();
|
||||
SelectedRoom.Value!.ParticipantCount--;
|
||||
room.RecentParticipants = room.RecentParticipants.Where(u => !u.Equals(room.RecentParticipants[index])).ToArray();
|
||||
room.ParticipantCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public partial class TestSceneMatchBeatmapDetailArea : OnlinePlayTestScene
|
||||
{
|
||||
private Room room = null!;
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("create area", () =>
|
||||
{
|
||||
SelectedRoom.Value = new Room();
|
||||
|
||||
Child = new MatchBeatmapDetailArea(SelectedRoom.Value)
|
||||
Child = new MatchBeatmapDetailArea(room = new Room())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@@ -36,9 +36,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private void createNewItem()
|
||||
{
|
||||
SelectedRoom.Value!.Playlist = SelectedRoom.Value.Playlist.Append(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
room.Playlist = room.Playlist.Append(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
ID = SelectedRoom.Value.Playlist.Count,
|
||||
ID = room.Playlist.Count,
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
RequiredMods = new[]
|
||||
{
|
||||
|
||||
@@ -61,9 +61,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("create leaderboard", () =>
|
||||
{
|
||||
SelectedRoom.Value = new Room { RoomID = 3 };
|
||||
|
||||
Child = new MatchLeaderboard(SelectedRoom.Value)
|
||||
Child = new MatchLeaderboard(new Room { RoomID = 3 })
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
|
||||
@@ -24,6 +24,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("join room", () => JoinRoom(CreateDefaultRoom()));
|
||||
WaitForJoined();
|
||||
|
||||
AddStep("reset", () =>
|
||||
{
|
||||
leaderboard?.RemoveAndDisposeImmediately();
|
||||
|
||||
@@ -17,6 +17,7 @@ using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
|
||||
@@ -42,6 +43,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
private BeatmapManager beatmapManager { get; set; } = null!;
|
||||
|
||||
private MultiSpectatorScreen spectatorScreen = null!;
|
||||
private Room room = null!;
|
||||
|
||||
private readonly List<MultiplayerRoomUser> playingUsers = new List<MultiplayerRoomUser>();
|
||||
|
||||
@@ -63,6 +65,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("clear playing users", () => playingUsers.Clear());
|
||||
|
||||
AddStep("create room", () => room = CreateDefaultRoom());
|
||||
AddStep("join room", () => JoinRoom(room));
|
||||
WaitForJoined();
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
@@ -456,7 +462,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
applyToBeatmap?.Invoke(Beatmap.Value);
|
||||
|
||||
LoadScreen(spectatorScreen = new MultiSpectatorScreen(SelectedRoom.Value!, playingUsers.ToArray()));
|
||||
LoadScreen(spectatorScreen = new MultiSpectatorScreen(room, playingUsers.ToArray()));
|
||||
});
|
||||
|
||||
AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded && (!waitForPlayerLoad || spectatorScreen.AllPlayersLoaded));
|
||||
|
||||
@@ -33,7 +33,6 @@ using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Taiko;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Match;
|
||||
@@ -58,7 +57,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
private TestMultiplayerComponents multiplayerComponents = null!;
|
||||
|
||||
private TestMultiplayerClient multiplayerClient => multiplayerComponents.MultiplayerClient;
|
||||
private TestMultiplayerRoomManager roomManager => multiplayerComponents.RoomManager;
|
||||
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; } = null!;
|
||||
@@ -257,7 +255,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("create room", () =>
|
||||
{
|
||||
roomManager.AddServerSideRoom(new Room
|
||||
multiplayerClient.AddServerSideRoom(new Room
|
||||
{
|
||||
Name = "Test Room",
|
||||
Playlist =
|
||||
@@ -286,7 +284,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("create room", () =>
|
||||
{
|
||||
roomManager.AddServerSideRoom(new Room
|
||||
multiplayerClient.AddServerSideRoom(new Room
|
||||
{
|
||||
Name = "Test Room",
|
||||
Playlist =
|
||||
@@ -336,7 +334,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("create room", () =>
|
||||
{
|
||||
roomManager.AddServerSideRoom(new Room
|
||||
multiplayerClient.AddServerSideRoom(new Room
|
||||
{
|
||||
Name = "Test Room",
|
||||
Password = "password",
|
||||
@@ -789,7 +787,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("create room", () =>
|
||||
{
|
||||
roomManager.AddServerSideRoom(new Room
|
||||
multiplayerClient.AddServerSideRoom(new Room
|
||||
{
|
||||
Name = "Test Room",
|
||||
QueueMode = QueueMode.AllPlayers,
|
||||
@@ -807,11 +805,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("wait for room", () => this.ChildrenOfType<DrawableRoom>().Any());
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
|
||||
AddStep("disable polling", () => this.ChildrenOfType<ListingPollingComponent>().Single().TimeBetweenPolls.Value = 0);
|
||||
AddStep("disable polling", () => this.ChildrenOfType<LoungeListingPoller>().Single().TimeBetweenPolls.Value = 0);
|
||||
AddStep("change server-side settings", () =>
|
||||
{
|
||||
roomManager.ServerSideRooms[0].Name = "New name";
|
||||
roomManager.ServerSideRooms[0].Playlist =
|
||||
multiplayerClient.ServerSideRooms[0].Name = "New name";
|
||||
multiplayerClient.ServerSideRooms[0].Playlist =
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
@@ -828,7 +826,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddAssert("local room has correct settings", () =>
|
||||
{
|
||||
var localRoom = this.ChildrenOfType<MultiplayerMatchSubScreen>().Single().Room;
|
||||
return localRoom.Name == roomManager.ServerSideRooms[0].Name && localRoom.Playlist.Single().ID == 2;
|
||||
return localRoom.Name == multiplayerClient.ServerSideRooms[0].Name && localRoom.Playlist.Single().ID == 2;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -16,45 +16,32 @@ using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public partial class TestSceneMultiplayerLoungeSubScreen : OnlinePlayTestScene
|
||||
public partial class TestSceneMultiplayerLoungeSubScreen : MultiplayerTestScene
|
||||
{
|
||||
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
|
||||
|
||||
private LoungeSubScreen loungeScreen = null!;
|
||||
private Room? lastJoinedRoom;
|
||||
private string? lastJoinedPassword;
|
||||
private MultiplayerLoungeSubScreen loungeScreen = null!;
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("push screen", () => LoadScreen(loungeScreen = new MultiplayerLoungeSubScreen()));
|
||||
|
||||
AddUntilStep("wait for present", () => loungeScreen.IsCurrentScreen());
|
||||
|
||||
AddStep("bind to event", () =>
|
||||
{
|
||||
lastJoinedRoom = null;
|
||||
lastJoinedPassword = null;
|
||||
RoomManager.JoinRoomRequested = onRoomJoined;
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestJoinRoomWithoutPassword()
|
||||
{
|
||||
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: false));
|
||||
createRooms(GenerateRooms(1, withPassword: false));
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
AddStep("join room", () => InputManager.Key(Key.Enter));
|
||||
|
||||
AddAssert("room join requested", () => lastJoinedRoom == RoomManager.Rooms.First());
|
||||
AddAssert("room join password correct", () => lastJoinedPassword == null);
|
||||
AddAssert("room joined", () => MultiplayerClient.RoomJoined);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPopoverHidesOnBackButton()
|
||||
{
|
||||
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
|
||||
createRooms(GenerateRooms(1, withPassword: true));
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
|
||||
|
||||
@@ -67,18 +54,22 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("hit escape", () => InputManager.Key(Key.Escape));
|
||||
AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().Any());
|
||||
|
||||
AddAssert("room not joined", () => !MultiplayerClient.RoomJoined);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPopoverHidesOnLeavingScreen()
|
||||
{
|
||||
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
|
||||
createRooms(GenerateRooms(1, withPassword: true));
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
|
||||
|
||||
AddUntilStep("password prompt appeared", () => InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().Any());
|
||||
AddStep("exit screen", () => Stack.Exit());
|
||||
AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().Any());
|
||||
|
||||
AddAssert("room not joined", () => !MultiplayerClient.RoomJoined);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -86,16 +77,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
|
||||
|
||||
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
|
||||
createRooms(GenerateRooms(1, withPassword: true));
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
|
||||
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
|
||||
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "wrong");
|
||||
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType<OsuButton>().First().TriggerClick());
|
||||
|
||||
AddAssert("room not joined", () => loungeScreen.IsCurrentScreen());
|
||||
AddAssert("still at lounge", () => loungeScreen.IsCurrentScreen());
|
||||
AddUntilStep("password prompt still visible", () => passwordEntryPopover!.State.Value == Visibility.Visible);
|
||||
AddAssert("textbox still focused", () => InputManager.FocusedDrawable is OsuPasswordTextBox);
|
||||
|
||||
AddAssert("room not joined", () => !MultiplayerClient.RoomJoined);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -103,16 +96,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
|
||||
|
||||
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
|
||||
createRooms(GenerateRooms(1, withPassword: true));
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
|
||||
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
|
||||
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "wrong");
|
||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||
|
||||
AddAssert("room not joined", () => loungeScreen.IsCurrentScreen());
|
||||
AddAssert("still at lounge", () => loungeScreen.IsCurrentScreen());
|
||||
AddUntilStep("password prompt still visible", () => passwordEntryPopover!.State.Value == Visibility.Visible);
|
||||
AddAssert("textbox still focused", () => InputManager.FocusedDrawable is OsuPasswordTextBox);
|
||||
|
||||
AddAssert("room not joined", () => !MultiplayerClient.RoomJoined);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -120,15 +115,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
|
||||
|
||||
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
|
||||
createRooms(GenerateRooms(1, withPassword: true));
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
|
||||
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
|
||||
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "password");
|
||||
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType<OsuButton>().First().TriggerClick());
|
||||
|
||||
AddAssert("room join requested", () => lastJoinedRoom == RoomManager.Rooms.First());
|
||||
AddAssert("room join password correct", () => lastJoinedPassword == "password");
|
||||
AddUntilStep("room joined", () => MultiplayerClient.RoomJoined);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -136,21 +130,27 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
DrawableLoungeRoom.PasswordEntryPopover? passwordEntryPopover = null;
|
||||
|
||||
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
|
||||
createRooms(GenerateRooms(1, withPassword: true));
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
|
||||
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
|
||||
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "password");
|
||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||
|
||||
AddAssert("room join requested", () => lastJoinedRoom == RoomManager.Rooms.First());
|
||||
AddAssert("room join password correct", () => lastJoinedPassword == "password");
|
||||
AddAssert("room joined", () => MultiplayerClient.RoomJoined);
|
||||
}
|
||||
|
||||
private void onRoomJoined(Room room, string? password)
|
||||
private void createRooms(params Room[] rooms)
|
||||
{
|
||||
lastJoinedRoom = room;
|
||||
lastJoinedPassword = password;
|
||||
AddStep("create rooms", () =>
|
||||
{
|
||||
foreach (var room in rooms)
|
||||
API.Queue(new CreateRoomRequest(room));
|
||||
});
|
||||
|
||||
AddStep("refresh lounge", () => loungeScreen.RefreshRooms());
|
||||
}
|
||||
|
||||
protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new MultiplayerTestSceneDependencies();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private TestMultiplayerMatchSongSelect songSelect = null!;
|
||||
private Live<BeatmapSetInfo> importedBeatmapSet = null!;
|
||||
private Room room = null!;
|
||||
|
||||
[Resolved]
|
||||
private OsuConfigManager configManager { get; set; } = null!;
|
||||
@@ -58,6 +59,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
Add(beatmapStore);
|
||||
}
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("create room", () => room = CreateDefaultRoom());
|
||||
AddStep("join room", () => JoinRoom(room));
|
||||
WaitForJoined();
|
||||
}
|
||||
|
||||
private void setUp()
|
||||
{
|
||||
AddStep("create song select", () =>
|
||||
@@ -66,7 +76,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
Beatmap.SetDefault();
|
||||
SelectedMods.SetDefault();
|
||||
|
||||
LoadScreen(songSelect = new TestMultiplayerMatchSongSelect(SelectedRoom.Value!));
|
||||
LoadScreen(songSelect = new TestMultiplayerMatchSongSelect(room));
|
||||
});
|
||||
|
||||
AddUntilStep("wait for present", () => songSelect.IsCurrentScreen() && songSelect.BeatmapSetsLoaded);
|
||||
@@ -138,8 +148,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("create song select", () =>
|
||||
{
|
||||
SelectedRoom.Value!.Playlist.Single().RulesetID = 2;
|
||||
songSelect = new TestMultiplayerMatchSongSelect(SelectedRoom.Value, SelectedRoom.Value.Playlist.Single());
|
||||
room.Playlist.Single().RulesetID = 2;
|
||||
songSelect = new TestMultiplayerMatchSongSelect(room, room.Playlist.Single());
|
||||
songSelect.OnLoadComplete += _ => Ruleset.Value = new TaikoRuleset().RulesetInfo;
|
||||
LoadScreen(songSelect);
|
||||
});
|
||||
|
||||
@@ -43,11 +43,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
private MultiplayerMatchSubScreen screen = null!;
|
||||
private BeatmapManager beatmaps = null!;
|
||||
private BeatmapSetInfo importedSet = null!;
|
||||
|
||||
public TestSceneMultiplayerMatchSubScreen()
|
||||
: base(false)
|
||||
{
|
||||
}
|
||||
private Room room = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, AudioManager audio)
|
||||
@@ -66,8 +62,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("load match", () =>
|
||||
{
|
||||
SelectedRoom.Value = new Room { Name = "Test Room" };
|
||||
LoadScreen(screen = new TestMultiplayerMatchSubScreen(SelectedRoom.Value!));
|
||||
room = new Room { Name = "Test Room" };
|
||||
LoadScreen(screen = new TestMultiplayerMatchSubScreen(room));
|
||||
});
|
||||
|
||||
AddUntilStep("wait for load", () => screen.IsCurrentScreen());
|
||||
@@ -78,7 +74,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("add playlist item", () =>
|
||||
{
|
||||
SelectedRoom.Value!.Playlist =
|
||||
room.Playlist =
|
||||
[
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
@@ -97,7 +93,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("add playlist item", () =>
|
||||
{
|
||||
SelectedRoom.Value!.Playlist =
|
||||
room.Playlist =
|
||||
[
|
||||
new PlaylistItem(new TestBeatmap(new TaikoRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
@@ -122,7 +118,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("set playlist", () =>
|
||||
{
|
||||
SelectedRoom.Value!.Playlist =
|
||||
room.Playlist =
|
||||
[
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
@@ -139,7 +135,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("set playlist", () =>
|
||||
{
|
||||
SelectedRoom.Value!.Playlist =
|
||||
room.Playlist =
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo)
|
||||
{
|
||||
@@ -170,7 +166,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("add playlist item with allowed mod", () =>
|
||||
{
|
||||
SelectedRoom.Value!.Playlist =
|
||||
room.Playlist =
|
||||
[
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
@@ -199,7 +195,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("add playlist item with allowed mod", () =>
|
||||
{
|
||||
SelectedRoom.Value!.Playlist =
|
||||
room.Playlist =
|
||||
[
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
@@ -223,7 +219,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("add playlist item with no allowed mods", () =>
|
||||
{
|
||||
SelectedRoom.Value!.Playlist =
|
||||
room.Playlist =
|
||||
[
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
@@ -246,7 +242,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("add two playlist items", () =>
|
||||
{
|
||||
SelectedRoom.Value!.Playlist =
|
||||
room.Playlist =
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo)
|
||||
{
|
||||
@@ -285,7 +281,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("add playlist item", () =>
|
||||
{
|
||||
SelectedRoom.Value!.Playlist =
|
||||
room.Playlist =
|
||||
[
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
@@ -317,6 +313,29 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddAssert("score multiplier = 1.20", () => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(1.2).Within(0.01));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChangeSettingsButtonVisibleForHost()
|
||||
{
|
||||
AddStep("add playlist item", () =>
|
||||
{
|
||||
room.Playlist =
|
||||
[
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
];
|
||||
});
|
||||
ClickButtonWhenEnabled<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>();
|
||||
|
||||
AddUntilStep("wait for join", () => RoomJoined);
|
||||
|
||||
AddUntilStep("button visible", () => this.ChildrenOfType<DrawableMatchRoom>().Single().ChangeSettingsButton?.Alpha, () => Is.GreaterThan(0));
|
||||
AddStep("join other user", void () => MultiplayerClient.AddUser(new APIUser { Id = PLAYER_1_ID }));
|
||||
AddStep("make other user host", () => MultiplayerClient.TransferHost(PLAYER_1_ID));
|
||||
AddAssert("button hidden", () => this.ChildrenOfType<DrawableMatchRoom>().Single().ChangeSettingsButton?.Alpha, () => Is.EqualTo(0));
|
||||
}
|
||||
|
||||
private partial class TestMultiplayerMatchSubScreen : MultiplayerMatchSubScreen
|
||||
{
|
||||
[Resolved(canBeNull: true)]
|
||||
|
||||
@@ -28,9 +28,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public partial class TestSceneMultiplayerParticipantsList : MultiplayerTestScene
|
||||
{
|
||||
[SetUpSteps]
|
||||
public void SetupSteps()
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("join room", () => JoinRoom(CreateDefaultRoom()));
|
||||
WaitForJoined();
|
||||
createNewParticipantsList();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
private MultiplayerPlayer player = null!;
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
AddStep("join room", () => JoinRoom(CreateDefaultRoom()));
|
||||
WaitForJoined();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGameplay()
|
||||
{
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
private BeatmapManager beatmaps = null!;
|
||||
private BeatmapSetInfo importedSet = null!;
|
||||
private BeatmapInfo importedBeatmap = null!;
|
||||
private Room room = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, AudioManager audio)
|
||||
@@ -46,9 +47,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("create room", () => room = CreateDefaultRoom());
|
||||
AddStep("join room", () => JoinRoom(room));
|
||||
WaitForJoined();
|
||||
|
||||
AddStep("create list", () =>
|
||||
{
|
||||
Child = list = new MultiplayerPlaylist(SelectedRoom.Value!)
|
||||
Child = list = new MultiplayerPlaylist(room)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@@ -127,7 +132,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
addItemStep();
|
||||
AddStep("finish current item", () => MultiplayerClient.FinishCurrentItem().WaitSafely());
|
||||
|
||||
AddStep("leave room", () => RoomManager.PartRoom());
|
||||
AddStep("leave room", () => MultiplayerClient.LeaveRoom());
|
||||
AddUntilStep("wait for room part", () => !RoomJoined);
|
||||
|
||||
AddUntilStep("item 0 not in lists", () => !inHistoryList(0) && !inQueueList(0));
|
||||
@@ -148,7 +153,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("finish current item", () => MultiplayerClient.FinishCurrentItem().WaitSafely());
|
||||
assertQueueTabCount(2);
|
||||
|
||||
AddStep("leave room", () => RoomManager.PartRoom());
|
||||
AddStep("leave room", () => MultiplayerClient.LeaveRoom());
|
||||
AddUntilStep("wait for room part", () => !RoomJoined);
|
||||
assertQueueTabCount(0);
|
||||
}
|
||||
@@ -157,12 +162,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestJoinRoomWithMixedItemsAddedInCorrectLists()
|
||||
{
|
||||
AddStep("leave room", () => RoomManager.PartRoom());
|
||||
AddStep("leave room", () => MultiplayerClient.LeaveRoom());
|
||||
AddUntilStep("wait for room part", () => !RoomJoined);
|
||||
|
||||
AddStep("join room with items", () =>
|
||||
{
|
||||
RoomManager.CreateRoom(new Room
|
||||
API.Queue(new CreateRoomRequest(new Room
|
||||
{
|
||||
Name = "test name",
|
||||
Playlist =
|
||||
@@ -177,7 +182,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
Expired = true
|
||||
}
|
||||
]
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
AddUntilStep("wait for room join", () => RoomJoined);
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
private BeatmapManager beatmaps = null!;
|
||||
private BeatmapSetInfo importedSet = null!;
|
||||
private BeatmapInfo importedBeatmap = null!;
|
||||
private Room room = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, AudioManager audio)
|
||||
@@ -42,9 +43,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("create room", () => room = CreateDefaultRoom());
|
||||
AddStep("join room", () => JoinRoom(room));
|
||||
WaitForJoined();
|
||||
|
||||
AddStep("create playlist", () =>
|
||||
{
|
||||
Child = playlist = new MultiplayerQueueList(SelectedRoom.Value!)
|
||||
Child = playlist = new MultiplayerQueueList(room)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
private MultiplayerSpectateButton spectateButton = null!;
|
||||
private MatchStartControl startControl = null!;
|
||||
private Room room = null!;
|
||||
|
||||
private BeatmapSetInfo importedSet = null!;
|
||||
private BeatmapManager beatmaps = null!;
|
||||
@@ -46,11 +47,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("create room", () => room = CreateDefaultRoom());
|
||||
AddStep("join room", () => JoinRoom(room));
|
||||
WaitForJoined();
|
||||
|
||||
AddStep("create button", () =>
|
||||
{
|
||||
PlaylistItem item = SelectedRoom.Value!.Playlist.First();
|
||||
|
||||
AvailabilityTracker.SelectedItem.Value = item;
|
||||
AvailabilityTracker.SelectedItem.Value = room.Playlist.First();
|
||||
|
||||
importedSet = beatmaps.GetAllUsableBeatmapSets().First();
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First());
|
||||
@@ -69,14 +72,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(200, 50),
|
||||
SelectedItem = new Bindable<PlaylistItem?>(item)
|
||||
SelectedItem = new Bindable<PlaylistItem?>(room.Playlist.First())
|
||||
},
|
||||
startControl = new MatchStartControl
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(200, 50),
|
||||
SelectedItem = new Bindable<PlaylistItem?>(item)
|
||||
SelectedItem = new Bindable<PlaylistItem?>(room.Playlist.First())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
private BeatmapManager manager = null!;
|
||||
private TestPlaylistsSongSelect songSelect = null!;
|
||||
private Room room = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, AudioManager audio)
|
||||
@@ -51,13 +52,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("reset", () =>
|
||||
{
|
||||
SelectedRoom.Value = new Room();
|
||||
room = new Room();
|
||||
Ruleset.Value = new OsuRuleset().RulesetInfo;
|
||||
Beatmap.SetDefault();
|
||||
SelectedMods.Value = Array.Empty<Mod>();
|
||||
});
|
||||
|
||||
AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect(SelectedRoom.Value!)));
|
||||
AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect(room)));
|
||||
AddUntilStep("wait for present", () => songSelect.IsCurrentScreen() && songSelect.BeatmapSetsLoaded);
|
||||
}
|
||||
|
||||
@@ -65,14 +66,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public void TestItemAddedIfEmptyOnStart()
|
||||
{
|
||||
AddStep("finalise selection", () => songSelect.FinaliseSelection());
|
||||
AddAssert("playlist has 1 item", () => SelectedRoom.Value!.Playlist.Count == 1);
|
||||
AddAssert("playlist has 1 item", () => room.Playlist.Count == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestItemAddedWhenCreateNewItemClicked()
|
||||
{
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
|
||||
AddAssert("playlist has 1 item", () => SelectedRoom.Value!.Playlist.Count == 1);
|
||||
AddAssert("playlist has 1 item", () => room.Playlist.Count == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -80,7 +81,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
|
||||
AddStep("finalise selection", () => songSelect.FinaliseSelection());
|
||||
AddAssert("playlist has 1 item", () => SelectedRoom.Value!.Playlist.Count == 1);
|
||||
AddAssert("playlist has 1 item", () => room.Playlist.Count == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -88,7 +89,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
|
||||
AddAssert("playlist has 2 items", () => SelectedRoom.Value!.Playlist.Count == 2);
|
||||
AddAssert("playlist has 2 items", () => room.Playlist.Count == 2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -96,10 +97,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
|
||||
AddStep("rearrange", () => SelectedRoom.Value!.Playlist = SelectedRoom.Value!.Playlist.Skip(1).Append(SelectedRoom.Value!.Playlist[0]).ToArray());
|
||||
AddStep("rearrange", () => room.Playlist = room.Playlist.Skip(1).Append(room.Playlist[0]).ToArray());
|
||||
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
|
||||
AddAssert("new item has id 2", () => SelectedRoom.Value!.Playlist.Last().ID == 2);
|
||||
AddAssert("new item has id 2", () => room.Playlist.Last().ID == 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -115,13 +116,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddAssert("item 1 has rate 1.5", () =>
|
||||
{
|
||||
var mod = (OsuModDoubleTime)SelectedRoom.Value!.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
|
||||
var mod = (OsuModDoubleTime)room.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
|
||||
return Precision.AlmostEquals(1.5, mod.SpeedChange.Value);
|
||||
});
|
||||
|
||||
AddAssert("item 2 has rate 2", () =>
|
||||
{
|
||||
var mod = (OsuModDoubleTime)SelectedRoom.Value!.Playlist.Last().RequiredMods[0].ToMod(new OsuRuleset());
|
||||
var mod = (OsuModDoubleTime)room.Playlist.Last().RequiredMods[0].ToMod(new OsuRuleset());
|
||||
return Precision.AlmostEquals(2, mod.SpeedChange.Value);
|
||||
});
|
||||
}
|
||||
@@ -147,7 +148,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("change stored mod rate", () => mod.SpeedChange.Value = 2);
|
||||
AddAssert("item has rate 1.5", () =>
|
||||
{
|
||||
var m = (OsuModDoubleTime)SelectedRoom.Value!.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
|
||||
var m = (OsuModDoubleTime)room.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
|
||||
return Precision.AlmostEquals(1.5, m.SpeedChange.Value);
|
||||
});
|
||||
}
|
||||
|
||||
+69
-47
@@ -3,6 +3,7 @@
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
@@ -10,6 +11,7 @@ using osu.Framework.Testing;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Taiko;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
@@ -17,11 +19,11 @@ using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public partial class TestSceneLoungeRoomsContainer : OnlinePlayTestScene
|
||||
public partial class TestSceneRoomListing : OnlinePlayTestScene
|
||||
{
|
||||
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
|
||||
|
||||
private RoomsContainer container = null!;
|
||||
private BindableList<Room> rooms = null!;
|
||||
private IBindable<Room?> selectedRoom = null!;
|
||||
private RoomListing container = null!;
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
@@ -29,17 +31,20 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("create container", () =>
|
||||
{
|
||||
rooms = new BindableList<Room>();
|
||||
selectedRoom = new Bindable<Room?>();
|
||||
|
||||
Child = new PopoverContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Width = 0.5f,
|
||||
|
||||
Child = container = new RoomsContainer
|
||||
Child = container = new RoomListing
|
||||
{
|
||||
SelectedRoom = { BindTarget = SelectedRoom }
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Rooms = { BindTarget = rooms },
|
||||
SelectedRoom = { BindTarget = selectedRoom }
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -48,57 +53,58 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestBasicListChanges()
|
||||
{
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(5, withSpotlightRooms: true));
|
||||
AddStep("add rooms", () => rooms.AddRange(GenerateRooms(5, withSpotlightRooms: true)));
|
||||
|
||||
AddAssert("has 5 rooms", () => container.Rooms.Count == 5);
|
||||
AddAssert("has 5 rooms", () => container.DrawableRooms.Count == 5);
|
||||
|
||||
AddAssert("all spotlights at top", () => container.Rooms
|
||||
AddAssert("all spotlights at top", () => container.DrawableRooms
|
||||
.SkipWhile(r => r.Room.Category == RoomCategory.Spotlight)
|
||||
.All(r => r.Room.Category == RoomCategory.Normal));
|
||||
|
||||
AddStep("remove first room", () => RoomManager.RemoveRoom(RoomManager.Rooms.First(r => r.RoomID == 0)));
|
||||
AddAssert("has 4 rooms", () => container.Rooms.Count == 4);
|
||||
AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID != 0));
|
||||
AddStep("remove first room", () => rooms.RemoveAt(0));
|
||||
AddAssert("has 4 rooms", () => container.DrawableRooms.Count == 4);
|
||||
AddAssert("first room removed", () => container.DrawableRooms.All(r => r.Room.RoomID != 0));
|
||||
|
||||
AddStep("select first room", () => container.Rooms.First().TriggerClick());
|
||||
AddAssert("first spotlight selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category == RoomCategory.Spotlight)));
|
||||
AddStep("select first room", () => container.DrawableRooms.First().TriggerClick());
|
||||
AddAssert("first spotlight selected", () => checkRoomSelected(rooms.First(r => r.Category == RoomCategory.Spotlight)));
|
||||
|
||||
AddStep("remove last room", () => RoomManager.RemoveRoom(RoomManager.Rooms.MinBy(r => r.RoomID)!));
|
||||
AddAssert("first spotlight still selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category == RoomCategory.Spotlight)));
|
||||
AddStep("remove last room", () => rooms.RemoveAt(rooms.Count - 1));
|
||||
AddAssert("first spotlight still selected", () => checkRoomSelected(rooms.First(r => r.Category == RoomCategory.Spotlight)));
|
||||
|
||||
AddStep("remove spotlight room", () => RoomManager.RemoveRoom(RoomManager.Rooms.Single(r => r.Category == RoomCategory.Spotlight)));
|
||||
AddStep("remove spotlight room", () => rooms.RemoveAll(r => r.Category == RoomCategory.Spotlight));
|
||||
AddAssert("selection vacated", () => checkRoomSelected(null));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestKeyboardNavigation()
|
||||
{
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(3));
|
||||
AddStep("add rooms", () => rooms.AddRange(GenerateRooms(3)));
|
||||
|
||||
AddAssert("no selection", () => checkRoomSelected(null));
|
||||
|
||||
press(Key.Down);
|
||||
AddAssert("first room selected", () => checkRoomSelected(RoomManager.Rooms.First()));
|
||||
AddAssert("first room selected", () => checkRoomSelected(container.DrawableRooms.First().Room));
|
||||
|
||||
press(Key.Up);
|
||||
AddAssert("first room selected", () => checkRoomSelected(RoomManager.Rooms.First()));
|
||||
AddAssert("first room selected", () => checkRoomSelected(container.DrawableRooms.First().Room));
|
||||
|
||||
press(Key.Down);
|
||||
press(Key.Down);
|
||||
AddAssert("last room selected", () => checkRoomSelected(RoomManager.Rooms.Last()));
|
||||
AddAssert("last room selected", () => checkRoomSelected(container.DrawableRooms.Last().Room));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestKeyboardNavigationAfterOrderChange()
|
||||
{
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(3));
|
||||
AddStep("add rooms", () => rooms.AddRange(GenerateRooms(3)));
|
||||
|
||||
AddStep("reorder rooms", () =>
|
||||
{
|
||||
var room = RoomManager.Rooms[1];
|
||||
var room = rooms[1];
|
||||
rooms.Remove(room);
|
||||
|
||||
RoomManager.RemoveRoom(room);
|
||||
RoomManager.AddOrUpdateRoom(room);
|
||||
room.RoomID += 3;
|
||||
rooms.Add(room);
|
||||
});
|
||||
|
||||
AddAssert("no selection", () => checkRoomSelected(null));
|
||||
@@ -116,12 +122,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestClickDeselection()
|
||||
{
|
||||
AddStep("add room", () => RoomManager.AddRooms(1));
|
||||
AddStep("add room", () => rooms.AddRange(GenerateRooms(1)));
|
||||
|
||||
AddAssert("no selection", () => checkRoomSelected(null));
|
||||
|
||||
press(Key.Down);
|
||||
AddAssert("first room selected", () => checkRoomSelected(RoomManager.Rooms.First()));
|
||||
AddAssert("first room selected", () => checkRoomSelected(container.DrawableRooms.First().Room));
|
||||
|
||||
AddStep("click away", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("no selection", () => checkRoomSelected(null));
|
||||
@@ -135,34 +141,34 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestStringFiltering()
|
||||
{
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(4));
|
||||
AddStep("add rooms", () => rooms.AddRange(GenerateRooms(4)));
|
||||
|
||||
AddUntilStep("4 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 4);
|
||||
AddUntilStep("4 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 4);
|
||||
|
||||
AddStep("filter one room", () => container.Filter.Value = new FilterCriteria { SearchString = "1" });
|
||||
AddStep("filter one room", () => container.Filter.Value = new FilterCriteria { SearchString = rooms.First().Name });
|
||||
|
||||
AddUntilStep("1 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 1);
|
||||
AddUntilStep("1 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 1);
|
||||
|
||||
AddStep("remove filter", () => container.Filter.Value = null);
|
||||
|
||||
AddUntilStep("4 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 4);
|
||||
AddUntilStep("4 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 4);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRulesetFiltering()
|
||||
{
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(2, new OsuRuleset().RulesetInfo));
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(3, new CatchRuleset().RulesetInfo));
|
||||
AddStep("add rooms", () => rooms.AddRange(GenerateRooms(2, new OsuRuleset().RulesetInfo)));
|
||||
AddStep("add rooms", () => rooms.AddRange(GenerateRooms(3, new CatchRuleset().RulesetInfo)));
|
||||
|
||||
// Todo: What even is this case...?
|
||||
AddStep("set empty filter criteria", () => container.Filter.Value = new FilterCriteria());
|
||||
AddUntilStep("5 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 5);
|
||||
AddUntilStep("5 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 5);
|
||||
|
||||
AddStep("filter osu! rooms", () => container.Filter.Value = new FilterCriteria { Ruleset = new OsuRuleset().RulesetInfo });
|
||||
AddUntilStep("2 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 2);
|
||||
AddUntilStep("2 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 2);
|
||||
|
||||
AddStep("filter catch rooms", () => container.Filter.Value = new FilterCriteria { Ruleset = new CatchRuleset().RulesetInfo });
|
||||
AddUntilStep("3 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 3);
|
||||
AddUntilStep("3 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -170,30 +176,46 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("add rooms", () =>
|
||||
{
|
||||
RoomManager.AddRooms(1, withPassword: true);
|
||||
RoomManager.AddRooms(1, withPassword: false);
|
||||
rooms.AddRange(GenerateRooms(1, withPassword: true));
|
||||
rooms.AddRange(GenerateRooms(1, withPassword: false));
|
||||
});
|
||||
|
||||
AddStep("apply default filter", () => container.Filter.SetDefault());
|
||||
|
||||
AddUntilStep("both rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 2);
|
||||
AddUntilStep("both rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 2);
|
||||
|
||||
AddStep("filter public rooms", () => container.Filter.Value = new FilterCriteria { Permissions = RoomPermissionsFilter.Public });
|
||||
|
||||
AddUntilStep("private room hidden", () => container.Rooms.All(r => !r.Room.HasPassword));
|
||||
AddUntilStep("private room hidden", () => container.DrawableRooms.All(r => !r.Room.HasPassword));
|
||||
|
||||
AddStep("filter private rooms", () => container.Filter.Value = new FilterCriteria { Permissions = RoomPermissionsFilter.Private });
|
||||
|
||||
AddUntilStep("public room hidden", () => container.Rooms.All(r => r.Room.HasPassword));
|
||||
AddUntilStep("public room hidden", () => container.DrawableRooms.All(r => r.Room.HasPassword));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPasswordProtectedRooms()
|
||||
{
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(3, withPassword: true));
|
||||
AddStep("add rooms", () => rooms.AddRange(GenerateRooms(3, withPassword: true)));
|
||||
}
|
||||
|
||||
private bool checkRoomSelected(Room? room) => SelectedRoom.Value == room;
|
||||
[Test]
|
||||
public void TestFreestyleBypassesRulesetFilter()
|
||||
{
|
||||
AddStep("apply taiko filter", () => container.Filter.Value = new FilterCriteria { Ruleset = new TaikoRuleset().RulesetInfo });
|
||||
|
||||
AddStep("add osu + freestyle room", () =>
|
||||
{
|
||||
var room = GenerateRooms(1, new OsuRuleset().RulesetInfo)[0];
|
||||
room.Playlist[0].Freestyle = true;
|
||||
room.CurrentPlaylistItem = room.Playlist[0];
|
||||
rooms.Add(room);
|
||||
});
|
||||
|
||||
AddAssert("room visible", () => container.DrawableRooms.Any());
|
||||
}
|
||||
|
||||
private bool checkRoomSelected(Room? room) => selectedRoom.Value == room;
|
||||
|
||||
private Room? getRoomInFlow(int index) =>
|
||||
(container.ChildrenOfType<FillFlowContainer<DrawableLoungeRoom>>().First().FlowingChildren.ElementAt(index) as DrawableRoom)?.Room;
|
||||
@@ -72,10 +72,10 @@ namespace osu.Game.Tests.Visual.Online
|
||||
AddUntilStep("Panel loaded", () => currentlyOnline.ChildrenOfType<UserGridPanel>().FirstOrDefault()?.User.Id == 2);
|
||||
AddAssert("Spectate button disabled", () => currentlyOnline.ChildrenOfType<PurpleRoundedButton>().First().Enabled.Value, () => Is.False);
|
||||
|
||||
AddStep("User began playing", () => spectatorClient.SendStartPlay(streamingUser.Id, 0));
|
||||
AddStep("User began playing", () => metadataClient.UserPresenceUpdated(streamingUser.Id, new UserPresence { Status = UserStatus.Online, Activity = new UserActivity.InSoloGame() }));
|
||||
AddAssert("Spectate button enabled", () => currentlyOnline.ChildrenOfType<PurpleRoundedButton>().First().Enabled.Value, () => Is.True);
|
||||
|
||||
AddStep("User finished playing", () => spectatorClient.SendEndPlay(streamingUser.Id));
|
||||
AddStep("User finished playing", () => metadataClient.UserPresenceUpdated(streamingUser.Id, new UserPresence { Status = UserStatus.Online, Activity = new UserActivity.ChoosingBeatmap() }));
|
||||
AddAssert("Spectate button disabled", () => currentlyOnline.ChildrenOfType<PurpleRoundedButton>().First().Enabled.Value, () => Is.False);
|
||||
|
||||
AddStep("Remove playing user", () => metadataClient.UserPresenceUpdated(streamingUser.Id, null));
|
||||
@@ -88,13 +88,12 @@ namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
IDisposable token = null!;
|
||||
|
||||
AddStep("User began playing", () => spectatorClient.SendStartPlay(streamingUser.Id, 0));
|
||||
AddStep("Begin watching user presence", () => token = metadataClient.BeginWatchingUserPresence());
|
||||
AddStep("Add online user", () => metadataClient.UserPresenceUpdated(streamingUser.Id, new UserPresence { Status = UserStatus.Online, Activity = new UserActivity.ChoosingBeatmap() }));
|
||||
AddUntilStep("Panel loaded", () => currentlyOnline.ChildrenOfType<UserGridPanel>().FirstOrDefault()?.User.Id == 2);
|
||||
AddStep("Add online user", () => metadataClient.UserPresenceUpdated(streamingUser.Id, new UserPresence { Status = UserStatus.Online, Activity = new UserActivity.InSoloGame() }));
|
||||
AddUntilStep("Panel loaded", () => currentlyOnline.ChildrenOfType<UserGridPanel>().FirstOrDefault()?.User.Id == streamingUser.Id);
|
||||
AddAssert("Spectate button enabled", () => currentlyOnline.ChildrenOfType<PurpleRoundedButton>().First().Enabled.Value, () => Is.True);
|
||||
|
||||
AddStep("User finished playing", () => spectatorClient.SendEndPlay(streamingUser.Id));
|
||||
AddStep("User finished playing", () => metadataClient.UserPresenceUpdated(streamingUser.Id, new UserPresence { Status = UserStatus.Online, Activity = new UserActivity.ChoosingBeatmap() }));
|
||||
AddAssert("Spectate button disabled", () => currentlyOnline.ChildrenOfType<PurpleRoundedButton>().First().Enabled.Value, () => Is.False);
|
||||
AddStep("Remove playing user", () => metadataClient.UserPresenceUpdated(streamingUser.Id, null));
|
||||
AddStep("End watching user presence", () => token.Dispose());
|
||||
|
||||
@@ -5,46 +5,227 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.TypeExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Metadata;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Dashboard.Friends;
|
||||
using osu.Game.Tests.Visual.Metadata;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
public partial class TestSceneFriendDisplay : OsuTestScene
|
||||
{
|
||||
protected override bool UseOnlineAPI => true;
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
|
||||
|
||||
private FriendDisplay display;
|
||||
private TestMetadataClient metadataClient;
|
||||
|
||||
[SetUp]
|
||||
public void Setup() => Schedule(() =>
|
||||
{
|
||||
Child = new BasicScrollContainer
|
||||
Child = new DependencyProvidingContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = display = new FriendDisplay()
|
||||
CachedDependencies =
|
||||
[
|
||||
(typeof(MetadataClient), metadataClient = new TestMetadataClient())
|
||||
],
|
||||
Children = new Drawable[]
|
||||
{
|
||||
metadataClient,
|
||||
new BasicScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new FriendDisplay()
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestOffline()
|
||||
public void TestAddAndRemoveFriends()
|
||||
{
|
||||
AddStep("Populate with offline test users", () => display.Users = getUsers());
|
||||
AddStep("set friends", () =>
|
||||
{
|
||||
DummyAPIAccess api = (DummyAPIAccess)API;
|
||||
api.Friends.Clear();
|
||||
api.Friends.AddRange(getUsers().Select(u => new APIRelation
|
||||
{
|
||||
RelationType = RelationType.Friend,
|
||||
TargetID = u.OnlineID,
|
||||
TargetUser = u
|
||||
}));
|
||||
});
|
||||
|
||||
waitForLoad();
|
||||
assertVisiblePanelCount<UserPanel>(3);
|
||||
|
||||
AddStep("remove one friend", () =>
|
||||
{
|
||||
DummyAPIAccess api = (DummyAPIAccess)API;
|
||||
api.Friends.RemoveAt(0);
|
||||
});
|
||||
|
||||
waitForLoad();
|
||||
assertVisiblePanelCount<UserPanel>(2);
|
||||
|
||||
AddStep("add one friend", () =>
|
||||
{
|
||||
DummyAPIAccess api = (DummyAPIAccess)API;
|
||||
api.Friends.AddRange(getUsers().Take(1).Select(u => new APIRelation
|
||||
{
|
||||
RelationType = RelationType.Friend,
|
||||
TargetID = u.OnlineID,
|
||||
TargetUser = u
|
||||
}));
|
||||
});
|
||||
|
||||
waitForLoad();
|
||||
assertVisiblePanelCount<UserPanel>(3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOnline()
|
||||
public void TestChangeDisplayStyle()
|
||||
{
|
||||
// No need to do anything, fetch is performed automatically.
|
||||
AddStep("set friends", () =>
|
||||
{
|
||||
DummyAPIAccess api = (DummyAPIAccess)API;
|
||||
api.Friends.Clear();
|
||||
api.Friends.AddRange(getUsers().Select(u => new APIRelation
|
||||
{
|
||||
RelationType = RelationType.Friend,
|
||||
TargetID = u.OnlineID,
|
||||
TargetUser = u
|
||||
}));
|
||||
});
|
||||
|
||||
waitForLoad();
|
||||
assertVisiblePanelCount<UserGridPanel>(3);
|
||||
|
||||
AddStep("set list style", () => this.ChildrenOfType<UserListToolbar>().Single().DisplayStyle.Value = OverlayPanelDisplayStyle.List);
|
||||
|
||||
waitForLoad();
|
||||
assertVisiblePanelCount<UserListPanel>(3);
|
||||
|
||||
AddStep("set brick style", () => this.ChildrenOfType<UserListToolbar>().Single().DisplayStyle.Value = OverlayPanelDisplayStyle.Brick);
|
||||
|
||||
waitForLoad();
|
||||
assertVisiblePanelCount<UserBrickPanel>(3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOnlinePresence()
|
||||
{
|
||||
AddStep("set friends", () =>
|
||||
{
|
||||
DummyAPIAccess api = (DummyAPIAccess)API;
|
||||
api.Friends.Clear();
|
||||
api.Friends.AddRange(getUsers().Select(u => new APIRelation
|
||||
{
|
||||
RelationType = RelationType.Friend,
|
||||
TargetID = u.OnlineID,
|
||||
TargetUser = u
|
||||
}));
|
||||
});
|
||||
|
||||
waitForLoad();
|
||||
assertVisiblePanelCount<UserPanel>(3);
|
||||
|
||||
AddStep("change to online stream", () => this.ChildrenOfType<FriendOnlineStreamControl>().Single().Current.Value = OnlineStatus.Online);
|
||||
assertVisiblePanelCount<UserPanel>(0);
|
||||
|
||||
AddStep("bring a friend online", () =>
|
||||
{
|
||||
DummyAPIAccess api = (DummyAPIAccess)API;
|
||||
metadataClient.FriendPresenceUpdated(api.Friends[0].TargetID, new UserPresence { Status = UserStatus.Online });
|
||||
});
|
||||
|
||||
assertVisiblePanelCount<UserPanel>(1);
|
||||
|
||||
AddStep("change to offline stream", () => this.ChildrenOfType<FriendOnlineStreamControl>().Single().Current.Value = OnlineStatus.Offline);
|
||||
assertVisiblePanelCount<UserPanel>(2);
|
||||
|
||||
AddStep("bring a friend online", () =>
|
||||
{
|
||||
DummyAPIAccess api = (DummyAPIAccess)API;
|
||||
metadataClient.FriendPresenceUpdated(api.Friends[1].TargetID, new UserPresence { Status = UserStatus.Online });
|
||||
});
|
||||
|
||||
assertVisiblePanelCount<UserPanel>(1);
|
||||
|
||||
AddStep("change to online stream", () => this.ChildrenOfType<FriendOnlineStreamControl>().Single().Current.Value = OnlineStatus.Online);
|
||||
assertVisiblePanelCount<UserPanel>(2);
|
||||
|
||||
AddStep("take friend offline", () =>
|
||||
{
|
||||
DummyAPIAccess api = (DummyAPIAccess)API;
|
||||
metadataClient.FriendPresenceUpdated(api.Friends[1].TargetID, null);
|
||||
});
|
||||
assertVisiblePanelCount<UserPanel>(1);
|
||||
|
||||
AddStep("change to all stream", () => this.ChildrenOfType<FriendOnlineStreamControl>().Single().Current.Value = OnlineStatus.All);
|
||||
assertVisiblePanelCount<UserPanel>(3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLoadFriendsBeforeDisplay()
|
||||
{
|
||||
AddStep("set friends", () =>
|
||||
{
|
||||
DummyAPIAccess api = (DummyAPIAccess)API;
|
||||
api.Friends.Clear();
|
||||
api.Friends.AddRange(getUsers().Select(u => new APIRelation
|
||||
{
|
||||
RelationType = RelationType.Friend,
|
||||
TargetID = u.OnlineID,
|
||||
TargetUser = u
|
||||
}));
|
||||
});
|
||||
|
||||
AddStep("load new display", () =>
|
||||
{
|
||||
Child = new DependencyProvidingContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CachedDependencies =
|
||||
[
|
||||
(typeof(MetadataClient), metadataClient = new TestMetadataClient())
|
||||
],
|
||||
Children = new Drawable[]
|
||||
{
|
||||
metadataClient,
|
||||
new BasicScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new FriendDisplay()
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
waitForLoad();
|
||||
assertVisiblePanelCount<UserPanel>(3);
|
||||
}
|
||||
|
||||
private void waitForLoad()
|
||||
=> AddUntilStep("wait for panels to load", () => this.ChildrenOfType<LoadingSpinner>().Single().State.Value, () => Is.EqualTo(Visibility.Hidden));
|
||||
|
||||
private void assertVisiblePanelCount<T>(int expectedVisible)
|
||||
where T : UserPanel
|
||||
{
|
||||
AddAssert($"{typeof(T).ReadableName()}s in list", () => this.ChildrenOfType<FriendsList>().Last().ChildrenOfType<UserPanel>().All(p => p is T));
|
||||
AddAssert($"{expectedVisible} panels visible", () => this.ChildrenOfType<FriendsList>().Last().ChildrenOfType<FriendsList.FilterableUserPanel>().Count(p => p.IsPresent),
|
||||
() => Is.EqualTo(expectedVisible));
|
||||
}
|
||||
|
||||
private List<APIUser> getUsers() => new List<APIUser>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.Containers;
|
||||
@@ -17,25 +16,22 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
public partial class TestScenePlaylistsLoungeSubScreen : OnlinePlayTestScene
|
||||
{
|
||||
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
|
||||
|
||||
private TestLoungeSubScreen loungeScreen = null!;
|
||||
private PlaylistsLoungeSubScreen loungeScreen = null!;
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("push screen", () => LoadScreen(loungeScreen = new TestLoungeSubScreen()));
|
||||
|
||||
AddStep("push screen", () => LoadScreen(loungeScreen = new PlaylistsLoungeSubScreen()));
|
||||
AddUntilStep("wait for present", () => loungeScreen.IsCurrentScreen());
|
||||
}
|
||||
|
||||
private RoomsContainer roomsContainer => loungeScreen.ChildrenOfType<RoomsContainer>().First();
|
||||
private RoomListing roomListing => loungeScreen.ChildrenOfType<RoomListing>().First();
|
||||
|
||||
[Test]
|
||||
public void TestManyRooms()
|
||||
{
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(500));
|
||||
createRooms(GenerateRooms(500));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -43,59 +39,41 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
AddStep("reset mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(30));
|
||||
AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 30);
|
||||
createRooms(GenerateRooms(30));
|
||||
|
||||
AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0]));
|
||||
|
||||
AddStep("move mouse to third room", () => InputManager.MoveMouseTo(roomsContainer.Rooms[2]));
|
||||
AddStep("move mouse to third room", () => InputManager.MoveMouseTo(roomListing.DrawableRooms[2]));
|
||||
AddStep("hold down", () => InputManager.PressButton(MouseButton.Left));
|
||||
AddStep("drag to top", () => InputManager.MoveMouseTo(roomsContainer.Rooms[0]));
|
||||
AddStep("drag to top", () => InputManager.MoveMouseTo(roomListing.DrawableRooms[0]));
|
||||
|
||||
AddAssert("first and second room masked", ()
|
||||
=> !checkRoomVisible(roomsContainer.Rooms[0]) &&
|
||||
!checkRoomVisible(roomsContainer.Rooms[1]));
|
||||
=> !checkRoomVisible(roomListing.DrawableRooms[0]) &&
|
||||
!checkRoomVisible(roomListing.DrawableRooms[1]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestScrollSelectedIntoView()
|
||||
{
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(30));
|
||||
AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 30);
|
||||
createRooms(GenerateRooms(30));
|
||||
|
||||
AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0]));
|
||||
AddStep("select last room", () => roomListing.DrawableRooms[^1].TriggerClick());
|
||||
|
||||
AddStep("select last room", () => roomsContainer.Rooms[^1].TriggerClick());
|
||||
|
||||
AddUntilStep("first room is masked", () => !checkRoomVisible(roomsContainer.Rooms[0]));
|
||||
AddUntilStep("last room is not masked", () => checkRoomVisible(roomsContainer.Rooms[^1]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEnteringRoomTakesLeaseOnSelection()
|
||||
{
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(1));
|
||||
|
||||
AddAssert("selected room is not disabled", () => !loungeScreen.SelectedRoom.Disabled);
|
||||
|
||||
AddStep("select room", () => roomsContainer.Rooms[0].TriggerClick());
|
||||
AddAssert("selected room is non-null", () => loungeScreen.SelectedRoom.Value != null);
|
||||
|
||||
AddStep("enter room", () => roomsContainer.Rooms[0].TriggerClick());
|
||||
|
||||
AddUntilStep("wait for match load", () => Stack.CurrentScreen is PlaylistsRoomSubScreen);
|
||||
|
||||
AddAssert("selected room is non-null", () => loungeScreen.SelectedRoom.Value != null);
|
||||
AddAssert("selected room is disabled", () => loungeScreen.SelectedRoom.Disabled);
|
||||
AddUntilStep("first room is masked", () => !checkRoomVisible(roomListing.DrawableRooms[0]));
|
||||
AddUntilStep("last room is not masked", () => checkRoomVisible(roomListing.DrawableRooms[^1]));
|
||||
}
|
||||
|
||||
private bool checkRoomVisible(DrawableRoom room) =>
|
||||
loungeScreen.ChildrenOfType<OsuScrollContainer>().First().ScreenSpaceDrawQuad
|
||||
.Contains(room.ScreenSpaceDrawQuad.Centre);
|
||||
|
||||
private partial class TestLoungeSubScreen : PlaylistsLoungeSubScreen
|
||||
private void createRooms(params Room[] rooms)
|
||||
{
|
||||
public new Bindable<Room?> SelectedRoom => base.SelectedRoom;
|
||||
AddStep("create rooms", () =>
|
||||
{
|
||||
foreach (var room in rooms)
|
||||
API.Queue(new CreateRoomRequest(room));
|
||||
});
|
||||
|
||||
AddStep("refresh lounge", () => loungeScreen.RefreshRooms());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Playlists;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
|
||||
@@ -18,21 +17,38 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
public partial class TestScenePlaylistsMatchSettingsOverlay : OnlinePlayTestScene
|
||||
{
|
||||
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
|
||||
|
||||
private TestRoomSettings settings = null!;
|
||||
|
||||
protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies();
|
||||
private Room room = null!;
|
||||
private Func<Room, string?>? handleRequest;
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("setup api", () =>
|
||||
{
|
||||
handleRequest = null;
|
||||
((DummyAPIAccess)API).HandleRequest = req =>
|
||||
{
|
||||
if (req is not CreateRoomRequest createReq || handleRequest == null)
|
||||
return false;
|
||||
|
||||
if (handleRequest(createReq.Room) is string errorText)
|
||||
createReq.TriggerFailure(new APIException(errorText, null));
|
||||
else
|
||||
{
|
||||
var createdRoom = new APICreatedRoom();
|
||||
createdRoom.CopyFrom(createReq.Room);
|
||||
createReq.TriggerSuccess(createdRoom);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("create overlay", () =>
|
||||
{
|
||||
SelectedRoom.Value = new Room();
|
||||
|
||||
Child = settings = new TestRoomSettings(SelectedRoom.Value!)
|
||||
Child = settings = new TestRoomSettings(room = new Room())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
State = { Value = Visibility.Visible }
|
||||
@@ -45,19 +61,19 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
AddStep("clear name and beatmap", () =>
|
||||
{
|
||||
SelectedRoom.Value!.Name = "";
|
||||
SelectedRoom.Value!.Playlist = [];
|
||||
room.Name = "";
|
||||
room.Playlist = [];
|
||||
});
|
||||
|
||||
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
|
||||
|
||||
AddStep("set name", () => SelectedRoom.Value!.Name = "Room name");
|
||||
AddStep("set name", () => room.Name = "Room name");
|
||||
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
|
||||
|
||||
AddStep("set beatmap", () => SelectedRoom.Value!.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)]);
|
||||
AddStep("set beatmap", () => room.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)]);
|
||||
AddAssert("button enabled", () => settings.ApplyButton.Enabled.Value);
|
||||
|
||||
AddStep("clear name", () => SelectedRoom.Value!.Name = "");
|
||||
AddStep("clear name", () => room.Name = "");
|
||||
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
|
||||
}
|
||||
|
||||
@@ -73,12 +89,12 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
settings.NameField.Current.Value = expected_name;
|
||||
settings.DurationField.Current.Value = expectedDuration;
|
||||
SelectedRoom.Value!.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
|
||||
room.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
|
||||
|
||||
RoomManager.CreateRequested = r =>
|
||||
handleRequest = r =>
|
||||
{
|
||||
createdRoom = r;
|
||||
return string.Empty;
|
||||
return null;
|
||||
};
|
||||
});
|
||||
|
||||
@@ -98,22 +114,22 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
var beatmap = CreateBeatmap(Ruleset.Value).BeatmapInfo;
|
||||
|
||||
SelectedRoom.Value!.Name = "Test Room";
|
||||
SelectedRoom.Value!.Playlist = [new PlaylistItem(beatmap)];
|
||||
room.Name = "Test Room";
|
||||
room.Playlist = [new PlaylistItem(beatmap)];
|
||||
|
||||
errorMessage = $"{not_found_prefix} {beatmap.OnlineID}";
|
||||
|
||||
RoomManager.CreateRequested = _ => errorMessage;
|
||||
handleRequest = _ => errorMessage;
|
||||
});
|
||||
|
||||
AddAssert("error not displayed", () => !settings.ErrorText.IsPresent);
|
||||
AddAssert("playlist item valid", () => SelectedRoom.Value!.Playlist[0].Valid.Value);
|
||||
AddAssert("playlist item valid", () => room.Playlist[0].Valid.Value);
|
||||
|
||||
AddStep("create room", () => settings.ApplyButton.Action.Invoke());
|
||||
|
||||
AddAssert("error displayed", () => settings.ErrorText.IsPresent);
|
||||
AddAssert("error has custom text", () => settings.ErrorText.Text != errorMessage);
|
||||
AddAssert("playlist item marked invalid", () => !SelectedRoom.Value!.Playlist[0].Valid.Value);
|
||||
AddAssert("playlist item marked invalid", () => !room.Playlist[0].Valid.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -125,10 +141,10 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
|
||||
AddStep("setup", () =>
|
||||
{
|
||||
SelectedRoom.Value!.Name = "Test Room";
|
||||
SelectedRoom.Value!.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
|
||||
room.Name = "Test Room";
|
||||
room.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
|
||||
|
||||
RoomManager.CreateRequested = _ => failText;
|
||||
handleRequest = _ => failText;
|
||||
});
|
||||
AddAssert("error not displayed", () => !settings.ErrorText.IsPresent);
|
||||
|
||||
@@ -159,48 +175,5 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class TestDependencies : OnlinePlayTestSceneDependencies
|
||||
{
|
||||
protected override IRoomManager CreateRoomManager() => new TestRoomManager();
|
||||
}
|
||||
|
||||
protected class TestRoomManager : IRoomManager
|
||||
{
|
||||
public Func<Room, string>? CreateRequested;
|
||||
|
||||
public event Action RoomsUpdated
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public IBindable<bool> InitialRoomsReceived { get; } = new Bindable<bool>(true);
|
||||
|
||||
public IBindableList<Room> Rooms => null!;
|
||||
|
||||
public void AddOrUpdateRoom(Room room) => throw new NotImplementedException();
|
||||
|
||||
public void RemoveRoom(Room room) => throw new NotImplementedException();
|
||||
|
||||
public void ClearRooms() => throw new NotImplementedException();
|
||||
|
||||
public void CreateRoom(Room room, Action<Room>? onSuccess = null, Action<string>? onError = null)
|
||||
{
|
||||
if (CreateRequested == null)
|
||||
return;
|
||||
|
||||
string error = CreateRequested.Invoke(room);
|
||||
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
onError?.Invoke(error);
|
||||
else
|
||||
onSuccess?.Invoke(room);
|
||||
}
|
||||
|
||||
public void JoinRoom(Room room, string? password, Action<Room>? onSuccess = null, Action<string>? onError = null) => throw new NotImplementedException();
|
||||
|
||||
public void PartRoom() => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,15 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
public partial class TestScenePlaylistsParticipantsList : OnlinePlayTestScene
|
||||
{
|
||||
private Room room = null!;
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("create list", () =>
|
||||
AddStep("create room", () =>
|
||||
{
|
||||
SelectedRoom.Value = new Room
|
||||
room = new Room
|
||||
{
|
||||
RoomID = 7,
|
||||
RecentParticipants = Enumerable.Range(0, 50).Select(_ => new APIUser
|
||||
@@ -38,7 +40,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
AddStep("create component", () =>
|
||||
{
|
||||
Child = new ParticipantsDisplay(SelectedRoom.Value!, Direction.Horizontal)
|
||||
Child = new ParticipantsDisplay(room, Direction.Horizontal)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@@ -52,7 +54,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
AddStep("create component", () =>
|
||||
{
|
||||
Child = new ParticipantsDisplay(SelectedRoom.Value!, Direction.Vertical)
|
||||
Child = new ParticipantsDisplay(room, Direction.Vertical)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
||||
@@ -7,9 +7,12 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
@@ -32,6 +35,9 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
private const int scores_per_result = 10;
|
||||
private const int real_user_position = 200;
|
||||
|
||||
[Cached]
|
||||
private readonly BeatmapLookupCache beatmapLookupCache = new BeatmapLookupCache();
|
||||
|
||||
private ResultsScreen resultsScreen = null!;
|
||||
|
||||
private int lowestScoreId; // Score ID of the lowest score in the list.
|
||||
@@ -41,6 +47,11 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
private int totalCount;
|
||||
private ScoreInfo userScore = null!;
|
||||
|
||||
public TestScenePlaylistsResultsScreen()
|
||||
{
|
||||
Add(beatmapLookupCache);
|
||||
}
|
||||
|
||||
[SetUpSteps]
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
@@ -58,9 +69,11 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
totalCount = 0;
|
||||
|
||||
userScore = TestResources.CreateTestScoreInfo();
|
||||
userScore.OnlineID = 1;
|
||||
userScore.TotalScore = 0;
|
||||
userScore.Statistics = new Dictionary<HitResult, int>();
|
||||
userScore.MaximumStatistics = new Dictionary<HitResult, int>();
|
||||
userScore.Position = real_user_position;
|
||||
|
||||
// Beatmap is required to be an actual beatmap so the scores can get their scores correctly
|
||||
// calculated for standardised scoring, else the tests that rely on ordering will fall over.
|
||||
@@ -143,13 +156,13 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
||||
AddStep("scroll to right", () => resultsScreen.ChildrenOfType<ScorePanelList>().Single().ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
|
||||
|
||||
AddAssert("right loading spinner shown", () =>
|
||||
AddUntilStep("right loading spinner shown", () =>
|
||||
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Visible);
|
||||
|
||||
waitForDisplay();
|
||||
|
||||
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
||||
AddAssert("right loading spinner hidden", () =>
|
||||
AddUntilStep("right loading spinner hidden", () =>
|
||||
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Hidden);
|
||||
}
|
||||
}
|
||||
@@ -167,26 +180,26 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
||||
AddStep("scroll to right", () => resultsScreen.ChildrenOfType<ScorePanelList>().Single().ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
|
||||
|
||||
AddAssert("right loading spinner shown", () =>
|
||||
AddUntilStep("right loading spinner shown", () =>
|
||||
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Visible);
|
||||
|
||||
waitForDisplay();
|
||||
|
||||
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
||||
AddAssert("right loading spinner hidden", () =>
|
||||
AddUntilStep("right loading spinner hidden", () =>
|
||||
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Hidden);
|
||||
|
||||
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
||||
AddStep("bind delayed handler with no scores", () => bindHandler(delayed: true, noScores: true));
|
||||
AddStep("scroll to right", () => resultsScreen.ChildrenOfType<ScorePanelList>().Single().ChildrenOfType<OsuScrollContainer>().Single().ScrollToEnd(false));
|
||||
|
||||
AddAssert("right loading spinner shown", () =>
|
||||
AddUntilStep("right loading spinner shown", () =>
|
||||
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Visible);
|
||||
|
||||
waitForDisplay();
|
||||
|
||||
AddAssert("count not increased", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount);
|
||||
AddAssert("right loading spinner hidden", () =>
|
||||
AddUntilStep("right loading spinner hidden", () =>
|
||||
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreRight).State.Value == Visibility.Hidden);
|
||||
|
||||
AddAssert("no placeholders shown", () => this.ChildrenOfType<MessagePlaceholder>().Count(), () => Is.Zero);
|
||||
@@ -209,13 +222,13 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
||||
AddStep("scroll to left", () => resultsScreen.ChildrenOfType<ScorePanelList>().Single().ChildrenOfType<OsuScrollContainer>().Single().ScrollToStart(false));
|
||||
|
||||
AddAssert("left loading spinner shown", () =>
|
||||
AddUntilStep("left loading spinner shown", () =>
|
||||
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreLeft).State.Value == Visibility.Visible);
|
||||
|
||||
waitForDisplay();
|
||||
|
||||
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
||||
AddAssert("left loading spinner hidden", () =>
|
||||
AddUntilStep("left loading spinner hidden", () =>
|
||||
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreLeft).State.Value == Visibility.Hidden);
|
||||
}
|
||||
}
|
||||
@@ -229,7 +242,36 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
AddStep("bind user score info handler", () => bindHandler(noScores: true));
|
||||
createUserBestResults();
|
||||
AddAssert("no scores visible", () => !resultsScreen.ChildrenOfType<ScorePanelList>().Single().GetScorePanels().Any());
|
||||
AddAssert("placeholder shown", () => this.ChildrenOfType<MessagePlaceholder>().Count(), () => Is.EqualTo(1));
|
||||
AddUntilStep("placeholder shown", () => this.ChildrenOfType<MessagePlaceholder>().Count(), () => Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFetchingAllTheWayToFirstNeverDisplaysNegativePosition()
|
||||
{
|
||||
AddStep("set user position", () => userScore.Position = 20);
|
||||
AddStep("bind user score info handler", () => bindHandler(userScore: userScore));
|
||||
|
||||
createResultsWithScore(() => userScore);
|
||||
waitForDisplay();
|
||||
|
||||
AddStep("bind delayed handler", () => bindHandler(true));
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
AddStep("simulate user falling down ranking", () => userScore.Position += 2);
|
||||
AddStep("scroll to left", () => resultsScreen.ChildrenOfType<ScorePanelList>().Single().ChildrenOfType<OsuScrollContainer>().Single().ScrollToStart(false));
|
||||
|
||||
AddUntilStep("left loading spinner shown", () =>
|
||||
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreLeft).State.Value == Visibility.Visible);
|
||||
|
||||
waitForDisplay();
|
||||
|
||||
AddUntilStep("left loading spinner hidden", () =>
|
||||
resultsScreen.ChildrenOfType<LoadingSpinner>().Single(l => l.Anchor == Anchor.CentreLeft).State.Value == Visibility.Hidden);
|
||||
}
|
||||
|
||||
AddAssert("total count is 34", () => this.ChildrenOfType<ScorePanel>().Count(), () => Is.EqualTo(34));
|
||||
AddUntilStep("all panels have non-negative position", () => this.ChildrenOfType<ScorePanel>().All(p => p.ScorePosition.Value > 0));
|
||||
}
|
||||
|
||||
private void createResultsWithScore(Func<ScoreInfo> getScore)
|
||||
@@ -279,6 +321,25 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
case IndexPlaylistScoresRequest:
|
||||
break;
|
||||
|
||||
case GetBeatmapsRequest getBeatmaps:
|
||||
getBeatmaps.TriggerSuccess(new GetBeatmapsResponse
|
||||
{
|
||||
Beatmaps = getBeatmaps.BeatmapIds.Select(id => new APIBeatmap
|
||||
{
|
||||
OnlineID = id,
|
||||
StarRating = id,
|
||||
DifficultyName = $"Beatmap {id}",
|
||||
BeatmapSet = new APIBeatmapSet
|
||||
{
|
||||
Title = $"Title {id}",
|
||||
Artist = $"Artist {id}",
|
||||
AuthorString = $"Author {id}"
|
||||
}
|
||||
}).ToList()
|
||||
});
|
||||
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -301,7 +362,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
if (userScore == null)
|
||||
triggerFail(s);
|
||||
else
|
||||
triggerSuccess(s, createUserResponse(userScore));
|
||||
triggerSuccess(s, () => createUserResponse(userScore));
|
||||
|
||||
break;
|
||||
|
||||
@@ -309,12 +370,12 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
if (userScore == null)
|
||||
triggerFail(u);
|
||||
else
|
||||
triggerSuccess(u, createUserResponse(userScore));
|
||||
triggerSuccess(u, () => createUserResponse(userScore));
|
||||
|
||||
break;
|
||||
|
||||
case IndexPlaylistScoresRequest i:
|
||||
triggerSuccess(i, createIndexResponse(i, noScores));
|
||||
triggerSuccess(i, () => createIndexResponse(i, noScores));
|
||||
break;
|
||||
}
|
||||
}, delay);
|
||||
@@ -322,11 +383,11 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
return true;
|
||||
};
|
||||
|
||||
private void triggerSuccess<T>(APIRequest<T> req, T result)
|
||||
private void triggerSuccess<T>(APIRequest<T> req, Func<T> result)
|
||||
where T : class
|
||||
{
|
||||
requestComplete = true;
|
||||
req.TriggerSuccess(result);
|
||||
req.TriggerSuccess(result.Invoke());
|
||||
}
|
||||
|
||||
private void triggerFail(APIRequest req)
|
||||
@@ -337,33 +398,20 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
|
||||
private MultiplayerScore createUserResponse(ScoreInfo userScore)
|
||||
{
|
||||
var multiplayerUserScore = new MultiplayerScore
|
||||
{
|
||||
ID = highestScoreId,
|
||||
Accuracy = userScore.Accuracy,
|
||||
Passed = userScore.Passed,
|
||||
Rank = userScore.Rank,
|
||||
Position = real_user_position,
|
||||
MaxCombo = userScore.MaxCombo,
|
||||
User = userScore.User,
|
||||
ScoresAround = new MultiplayerScoresAround
|
||||
{
|
||||
Higher = new MultiplayerScores(),
|
||||
Lower = new MultiplayerScores()
|
||||
}
|
||||
};
|
||||
var multiplayerUserScore = createMultiplayerUserScore(userScore);
|
||||
|
||||
totalCount++;
|
||||
|
||||
for (int i = 1; i <= scores_per_result; i++)
|
||||
{
|
||||
multiplayerUserScore.ScoresAround.Lower.Scores.Add(new MultiplayerScore
|
||||
multiplayerUserScore.ScoresAround!.Lower!.Scores.Add(new MultiplayerScore
|
||||
{
|
||||
ID = getNextLowestScoreId(),
|
||||
Accuracy = userScore.Accuracy,
|
||||
Passed = true,
|
||||
Rank = userScore.Rank,
|
||||
MaxCombo = userScore.MaxCombo,
|
||||
BeatmapId = RNG.Next(0, 7),
|
||||
User = new APIUser
|
||||
{
|
||||
Id = 2 + i,
|
||||
@@ -372,13 +420,14 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
},
|
||||
});
|
||||
|
||||
multiplayerUserScore.ScoresAround.Higher.Scores.Add(new MultiplayerScore
|
||||
multiplayerUserScore.ScoresAround!.Higher!.Scores.Add(new MultiplayerScore
|
||||
{
|
||||
ID = getNextHighestScoreId(),
|
||||
Accuracy = userScore.Accuracy,
|
||||
Passed = true,
|
||||
Rank = userScore.Rank,
|
||||
MaxCombo = userScore.MaxCombo,
|
||||
BeatmapId = RNG.Next(0, 7),
|
||||
User = new APIUser
|
||||
{
|
||||
Id = 2 + i,
|
||||
@@ -390,13 +439,33 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
totalCount += 2;
|
||||
}
|
||||
|
||||
addCursor(multiplayerUserScore.ScoresAround.Lower);
|
||||
addCursor(multiplayerUserScore.ScoresAround.Higher);
|
||||
addCursor(multiplayerUserScore.ScoresAround!.Lower!);
|
||||
addCursor(multiplayerUserScore.ScoresAround!.Higher!);
|
||||
|
||||
return multiplayerUserScore;
|
||||
}
|
||||
|
||||
private IndexedMultiplayerScores createIndexResponse(IndexPlaylistScoresRequest req, bool noScores = false)
|
||||
private MultiplayerScore createMultiplayerUserScore(ScoreInfo userScore)
|
||||
{
|
||||
return new MultiplayerScore
|
||||
{
|
||||
ID = highestScoreId,
|
||||
Accuracy = userScore.Accuracy,
|
||||
Passed = userScore.Passed,
|
||||
Rank = userScore.Rank,
|
||||
Position = userScore.Position,
|
||||
MaxCombo = userScore.MaxCombo,
|
||||
User = userScore.User,
|
||||
BeatmapId = RNG.Next(0, 7),
|
||||
ScoresAround = new MultiplayerScoresAround
|
||||
{
|
||||
Higher = new MultiplayerScores(),
|
||||
Lower = new MultiplayerScores()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private IndexedMultiplayerScores createIndexResponse(IndexPlaylistScoresRequest req, bool noScores)
|
||||
{
|
||||
var result = new IndexedMultiplayerScores();
|
||||
|
||||
@@ -404,15 +473,26 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
|
||||
string sort = req.IndexParams?.Properties["sort"].ToObject<string>() ?? "score_desc";
|
||||
|
||||
bool reachedEnd = false;
|
||||
|
||||
for (int i = 1; i <= scores_per_result; i++)
|
||||
{
|
||||
int nextId = sort == "score_asc" ? getNextHighestScoreId() : getNextLowestScoreId();
|
||||
|
||||
if (userScore.OnlineID - nextId >= userScore.Position)
|
||||
{
|
||||
reachedEnd = true;
|
||||
break;
|
||||
}
|
||||
|
||||
result.Scores.Add(new MultiplayerScore
|
||||
{
|
||||
ID = sort == "score_asc" ? getNextHighestScoreId() : getNextLowestScoreId(),
|
||||
ID = nextId,
|
||||
Accuracy = 1,
|
||||
Passed = true,
|
||||
Rank = ScoreRank.X,
|
||||
MaxCombo = 1000,
|
||||
BeatmapId = RNG.Next(0, 7),
|
||||
User = new APIUser
|
||||
{
|
||||
Id = 2 + i,
|
||||
@@ -424,7 +504,10 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
totalCount++;
|
||||
}
|
||||
|
||||
addCursor(result);
|
||||
if (!reachedEnd)
|
||||
addCursor(result);
|
||||
|
||||
result.UserScore = createMultiplayerUserScore(userScore);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
private BeatmapManager manager = null!;
|
||||
private TestPlaylistsRoomSubScreen match = null!;
|
||||
private BeatmapSetInfo importedBeatmap = null!;
|
||||
private Room room = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, AudioManager audio)
|
||||
@@ -47,11 +48,9 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
[SetUpSteps]
|
||||
public void SetupSteps()
|
||||
{
|
||||
AddStep("set room", () => SelectedRoom.Value = new Room());
|
||||
|
||||
importBeatmap();
|
||||
|
||||
AddStep("load match", () => LoadScreen(match = new TestPlaylistsRoomSubScreen(SelectedRoom.Value!)));
|
||||
AddStep("load match", () => LoadScreen(match = new TestPlaylistsRoomSubScreen(room = new Room())));
|
||||
AddUntilStep("wait for load", () => match.IsCurrentScreen());
|
||||
}
|
||||
|
||||
@@ -119,7 +118,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
];
|
||||
});
|
||||
|
||||
AddAssert("first playlist item selected", () => match.SelectedItem.Value == SelectedRoom.Value!.Playlist[0]);
|
||||
AddAssert("first playlist item selected", () => match.SelectedItem.Value == room.Playlist[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -197,10 +196,9 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
AddUntilStep("match has correct beatmap", () => realHash == match.Beatmap.Value.BeatmapInfo.MD5Hash);
|
||||
}
|
||||
|
||||
private void setupAndCreateRoom(Action<Room> room)
|
||||
private void setupAndCreateRoom(Action<Room> setupFunc)
|
||||
{
|
||||
AddStep("setup room", () => room(SelectedRoom.Value!));
|
||||
|
||||
AddStep("setup room", () => setupFunc(room));
|
||||
AddStep("click create button", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<PlaylistsRoomSettingsOverlay.CreateRoomButton>().Single());
|
||||
|
||||
@@ -0,0 +1,616 @@
|
||||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Catch.Mods;
|
||||
using osu.Game.Rulesets.Mania;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Taiko;
|
||||
using osu.Game.Rulesets.Taiko.Mods;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Playlists;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
public partial class TestScenePlaylistsRoomSubScreen : OnlinePlayTestScene
|
||||
{
|
||||
private BeatmapManager beatmaps = null!;
|
||||
private BeatmapSetInfo importedSet = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, AudioManager audio)
|
||||
{
|
||||
BeatmapStore beatmapStore;
|
||||
|
||||
Dependencies.Cache(new RealmRulesetStore(Realm));
|
||||
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, API, audio, Resources, host, Beatmap.Default));
|
||||
Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore());
|
||||
Dependencies.Cache(Realm);
|
||||
|
||||
Add(beatmapStore);
|
||||
|
||||
importedSet = beatmaps.Import(new BeatmapSetInfo
|
||||
{
|
||||
OnlineID = TestResources.GetNextTestID(),
|
||||
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
|
||||
DateAdded = DateTimeOffset.UtcNow,
|
||||
Beatmaps =
|
||||
{
|
||||
new BeatmapInfo
|
||||
{
|
||||
OnlineID = 1,
|
||||
DifficultyName = "Osu 1",
|
||||
Hash = Guid.NewGuid().ToString().ComputeMD5Hash(),
|
||||
MD5Hash = Guid.NewGuid().ToString().ComputeMD5Hash(),
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
Metadata =
|
||||
{
|
||||
Artist = "Some Artist",
|
||||
Title = "Some Song",
|
||||
Author = { Username = "Some Guy" },
|
||||
},
|
||||
},
|
||||
new BeatmapInfo
|
||||
{
|
||||
OnlineID = 2,
|
||||
DifficultyName = "Osu 2",
|
||||
Hash = Guid.NewGuid().ToString().ComputeMD5Hash(),
|
||||
MD5Hash = Guid.NewGuid().ToString().ComputeMD5Hash(),
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
Metadata =
|
||||
{
|
||||
Artist = "Some Artist",
|
||||
Title = "Some Song",
|
||||
Author = { Username = "Some Guy" },
|
||||
},
|
||||
},
|
||||
new BeatmapInfo
|
||||
{
|
||||
OnlineID = 3,
|
||||
DifficultyName = "Taiko 1",
|
||||
Hash = Guid.NewGuid().ToString().ComputeMD5Hash(),
|
||||
MD5Hash = Guid.NewGuid().ToString().ComputeMD5Hash(),
|
||||
Ruleset = new TaikoRuleset().RulesetInfo,
|
||||
Metadata =
|
||||
{
|
||||
Artist = "Some Artist",
|
||||
Title = "Some Song",
|
||||
Author = { Username = "Some Guy" },
|
||||
},
|
||||
},
|
||||
new BeatmapInfo
|
||||
{
|
||||
OnlineID = 4,
|
||||
DifficultyName = "Taiko 2",
|
||||
Hash = Guid.NewGuid().ToString().ComputeMD5Hash(),
|
||||
MD5Hash = Guid.NewGuid().ToString().ComputeMD5Hash(),
|
||||
Ruleset = new TaikoRuleset().RulesetInfo,
|
||||
Metadata =
|
||||
{
|
||||
Artist = "Some Artist",
|
||||
Title = "Some Song",
|
||||
Author = { Username = "Some Guy" },
|
||||
},
|
||||
}
|
||||
}
|
||||
})!.PerformRead(s => s.Detach());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that the beatmap and ruleset are adjusted to follow the selected item.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestBeatmapAndRuleset_FollowSelection()
|
||||
{
|
||||
Room room = null!;
|
||||
|
||||
AddStep("add room", () =>
|
||||
{
|
||||
room = new Room
|
||||
{
|
||||
RoomID = 1,
|
||||
Playlist =
|
||||
[
|
||||
// osu! beatmap
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
},
|
||||
// osu! beatmap converted played in taiko
|
||||
new PlaylistItem(importedSet.Beatmaps[1])
|
||||
{
|
||||
RulesetID = new TaikoRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
API.Perform(new CreateRoomRequest(room));
|
||||
});
|
||||
|
||||
TestPlaylistsRoomSubScreen screen = null!;
|
||||
AddStep("load screen", () => LoadScreen(new TestPlaylistsScreen(screen = new TestPlaylistsRoomSubScreen(room))));
|
||||
AddUntilStep("wait for load", () => screen.IsLoaded);
|
||||
|
||||
AddStep("select first item", () => screen.SelectedItem.Value = room.Playlist[0]);
|
||||
AddUntilStep("first beatmap selected", () => Beatmap.Value.BeatmapInfo.Equals(importedSet.Beatmaps[0]));
|
||||
AddUntilStep("osu ruleset selected", () => Ruleset.Value.Equals(new OsuRuleset().RulesetInfo));
|
||||
|
||||
AddStep("select second item", () => screen.SelectedItem.Value = room.Playlist[1]);
|
||||
AddUntilStep("second beatmap selected", () => Beatmap.Value.BeatmapInfo.Equals(importedSet.Beatmaps[1]));
|
||||
AddUntilStep("taiko ruleset selected", () => Ruleset.Value.Equals(new TaikoRuleset().RulesetInfo));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that the beatmap style is reset when the selected item is changed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestBeatmapStyle_Reset_OnSelection()
|
||||
{
|
||||
Room room = null!;
|
||||
|
||||
AddStep("add room", () =>
|
||||
{
|
||||
room = new Room
|
||||
{
|
||||
RoomID = 1,
|
||||
Playlist =
|
||||
[
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
},
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
API.Perform(new CreateRoomRequest(room));
|
||||
});
|
||||
|
||||
TestPlaylistsRoomSubScreen screen = null!;
|
||||
AddStep("load screen", () => LoadScreen(new TestPlaylistsScreen(screen = new TestPlaylistsRoomSubScreen(room))));
|
||||
AddUntilStep("wait for load", () => screen.IsLoaded);
|
||||
|
||||
AddStep("set user beatmap style", () => screen.UserBeatmap.Value = importedSet.Beatmaps[1]);
|
||||
AddUntilStep("user beatmap selected", () => Beatmap.Value.BeatmapInfo.Equals(importedSet.Beatmaps[1]));
|
||||
|
||||
AddStep("select second item", () => screen.SelectedItem.Value = room.Playlist[1]);
|
||||
AddUntilStep("user beatmap style reset", () => screen.UserBeatmap.Value == null);
|
||||
AddUntilStep("second beatmap selected", () => Beatmap.Value.BeatmapInfo.Equals(importedSet.Beatmaps[0]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that the ruleset style is reset when the selected item is changed and it's no longer valid.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRulesetStyle_Reset_OnSelection_IfNotValid()
|
||||
{
|
||||
Room room = null!;
|
||||
|
||||
AddStep("add room", () =>
|
||||
{
|
||||
room = new Room
|
||||
{
|
||||
RoomID = 1,
|
||||
Playlist =
|
||||
[
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
},
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new TaikoRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
API.Perform(new CreateRoomRequest(room));
|
||||
});
|
||||
|
||||
TestPlaylistsRoomSubScreen screen = null!;
|
||||
AddStep("load screen", () => LoadScreen(new TestPlaylistsScreen(screen = new TestPlaylistsRoomSubScreen(room))));
|
||||
AddUntilStep("wait for load", () => screen.IsLoaded);
|
||||
|
||||
AddStep("set user ruleset style", () => screen.UserRuleset.Value = new ManiaRuleset().RulesetInfo);
|
||||
AddUntilStep("user ruleset selected", () => Ruleset.Value.Equals(new ManiaRuleset().RulesetInfo));
|
||||
|
||||
AddStep("select second item", () => screen.SelectedItem.Value = room.Playlist[1]);
|
||||
AddUntilStep("user ruleset style reset", () => screen.UserRuleset.Value == null);
|
||||
AddUntilStep("second ruleset selected", () => Ruleset.Value.Equals(new TaikoRuleset().RulesetInfo));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that the ruleset style is preserved when the selected item is changed and the ruleset is still valid.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRulesetStyle_Preserved_OnSelection_IfStillValid()
|
||||
{
|
||||
Room room = null!;
|
||||
|
||||
AddStep("add room", () =>
|
||||
{
|
||||
room = new Room
|
||||
{
|
||||
RoomID = 1,
|
||||
Playlist =
|
||||
[
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
},
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
API.Perform(new CreateRoomRequest(room));
|
||||
});
|
||||
|
||||
TestPlaylistsRoomSubScreen screen = null!;
|
||||
AddStep("load screen", () => LoadScreen(new TestPlaylistsScreen(screen = new TestPlaylistsRoomSubScreen(room))));
|
||||
AddUntilStep("wait for load", () => screen.IsLoaded);
|
||||
|
||||
AddStep("set user ruleset style", () => screen.UserRuleset.Value = new ManiaRuleset().RulesetInfo);
|
||||
AddUntilStep("user ruleset selected", () => Ruleset.Value.Equals(new ManiaRuleset().RulesetInfo));
|
||||
|
||||
AddStep("select second item", () => screen.SelectedItem.Value = room.Playlist[1]);
|
||||
AddUntilStep("user ruleset style preserved", () => screen.UserRuleset.Value!.Equals(new ManiaRuleset().RulesetInfo));
|
||||
AddUntilStep("user ruleset selected", () => Ruleset.Value.Equals(new ManiaRuleset().RulesetInfo));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that mod style is reset when the selected item is changed to another with an inconvertible ruleset.
|
||||
/// No user style is assumed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestModsReset_OnSelection_DifferentRuleset_NoUserStyle()
|
||||
{
|
||||
Room room = null!;
|
||||
|
||||
AddStep("add room", () =>
|
||||
{
|
||||
room = new Room
|
||||
{
|
||||
RoomID = 1,
|
||||
Playlist =
|
||||
[
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
},
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new TaikoRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
API.Perform(new CreateRoomRequest(room));
|
||||
});
|
||||
|
||||
TestPlaylistsRoomSubScreen screen = null!;
|
||||
AddStep("load screen", () => LoadScreen(new TestPlaylistsScreen(screen = new TestPlaylistsRoomSubScreen(room))));
|
||||
AddUntilStep("wait for load", () => screen.IsLoaded);
|
||||
|
||||
AddStep("set user mods", () => screen.UserMods.Value = [new OsuModDoubleTime()]);
|
||||
AddUntilStep("user mods selected", () => SelectedMods.Value.OfType<OsuModDoubleTime>().Any());
|
||||
|
||||
AddStep("select second item", () => screen.SelectedItem.Value = room.Playlist[1]);
|
||||
AddUntilStep("user mod style reset", () => !screen.UserMods.Value.Any());
|
||||
AddUntilStep("mods reset", () => !SelectedMods.Value.Any());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that mod style is preserved when the selected item is changed to another with the same ruleset.
|
||||
/// No user style is assumed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestModsPreserved_OnSelection_SameRuleset_NoUserStyle()
|
||||
{
|
||||
Room room = null!;
|
||||
|
||||
AddStep("add room", () =>
|
||||
{
|
||||
room = new Room
|
||||
{
|
||||
RoomID = 1,
|
||||
Playlist =
|
||||
[
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
},
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
API.Perform(new CreateRoomRequest(room));
|
||||
});
|
||||
|
||||
TestPlaylistsRoomSubScreen screen = null!;
|
||||
AddStep("load screen", () => LoadScreen(new TestPlaylistsScreen(screen = new TestPlaylistsRoomSubScreen(room))));
|
||||
AddUntilStep("wait for load", () => screen.IsLoaded);
|
||||
|
||||
AddStep("set user mods", () => screen.UserMods.Value = [new OsuModDoubleTime()]);
|
||||
AddUntilStep("user mods selected", () => SelectedMods.Value.OfType<OsuModDoubleTime>().Any());
|
||||
|
||||
AddStep("select second item", () => screen.SelectedItem.Value = room.Playlist[1]);
|
||||
AddUntilStep("user mod style preserved", () => screen.UserMods.Value.OfType<OsuModDoubleTime>().Any());
|
||||
AddUntilStep("mods preserved", () => SelectedMods.Value.OfType<OsuModDoubleTime>().Any());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that mod style is reset when the selected item is changed to another with an inconvertible ruleset.
|
||||
/// A user beatmap/ruleset style is assumed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestModsReset_OnSelection_DifferentRuleset_WithUserStyle()
|
||||
{
|
||||
Room room = null!;
|
||||
|
||||
AddStep("add room", () =>
|
||||
{
|
||||
room = new Room
|
||||
{
|
||||
RoomID = 1,
|
||||
Playlist =
|
||||
[
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
},
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new TaikoRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
API.Perform(new CreateRoomRequest(room));
|
||||
});
|
||||
|
||||
TestPlaylistsRoomSubScreen screen = null!;
|
||||
AddStep("load screen", () => LoadScreen(new TestPlaylistsScreen(screen = new TestPlaylistsRoomSubScreen(room))));
|
||||
AddUntilStep("wait for load", () => screen.IsLoaded);
|
||||
|
||||
AddStep("set user ruleset", () => screen.UserRuleset.Value = new CatchRuleset().RulesetInfo);
|
||||
AddUntilStep("user ruleset selected", () => Ruleset.Value.Equals(new CatchRuleset().RulesetInfo));
|
||||
AddStep("set user mods", () => screen.UserMods.Value = [new CatchModDoubleTime()]);
|
||||
AddUntilStep("user mods selected", () => SelectedMods.Value.OfType<CatchModDoubleTime>().Any());
|
||||
|
||||
AddStep("select second item", () => screen.SelectedItem.Value = room.Playlist[1]);
|
||||
AddUntilStep("user mod style reset", () => !screen.UserMods.Value.Any());
|
||||
AddUntilStep("mods reset", () => !SelectedMods.Value.Any());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that mod style is preserved when the selected item is changed to another with the same ruleset.
|
||||
/// A user beatmap/ruleset style is assumed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestModsPreserved_OnSelection_SameRuleset_WithStyle()
|
||||
{
|
||||
Room room = null!;
|
||||
|
||||
AddStep("add room", () =>
|
||||
{
|
||||
room = new Room
|
||||
{
|
||||
RoomID = 1,
|
||||
Playlist =
|
||||
[
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
},
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new TaikoRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
API.Perform(new CreateRoomRequest(room));
|
||||
});
|
||||
|
||||
TestPlaylistsRoomSubScreen screen = null!;
|
||||
AddStep("load screen", () => LoadScreen(new TestPlaylistsScreen(screen = new TestPlaylistsRoomSubScreen(room))));
|
||||
AddUntilStep("wait for load", () => screen.IsLoaded);
|
||||
|
||||
AddStep("set user ruleset", () => screen.UserRuleset.Value = new TaikoRuleset().RulesetInfo);
|
||||
AddUntilStep("user ruleset selected", () => Ruleset.Value.Equals(new TaikoRuleset().RulesetInfo));
|
||||
AddStep("set user mods", () => screen.UserMods.Value = [new TaikoModDoubleTime()]);
|
||||
AddUntilStep("user mods selected", () => SelectedMods.Value.OfType<TaikoModDoubleTime>().Any());
|
||||
|
||||
AddStep("select second item", () => screen.SelectedItem.Value = room.Playlist[1]);
|
||||
AddUntilStep("user mod style preserved", () => screen.UserMods.Value.OfType<TaikoModDoubleTime>().Any());
|
||||
AddUntilStep("mods preserved", () => SelectedMods.Value.OfType<TaikoModDoubleTime>().Any());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that the mod style is revalidated when the ruleset style is changed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestModsValidated_OnRulesetStyleChanged()
|
||||
{
|
||||
Room room = null!;
|
||||
|
||||
AddStep("add room", () =>
|
||||
{
|
||||
room = new Room
|
||||
{
|
||||
RoomID = 1,
|
||||
Playlist =
|
||||
[
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
API.Perform(new CreateRoomRequest(room));
|
||||
});
|
||||
|
||||
TestPlaylistsRoomSubScreen screen = null!;
|
||||
AddStep("load screen", () => LoadScreen(new TestPlaylistsScreen(screen = new TestPlaylistsRoomSubScreen(room))));
|
||||
AddUntilStep("wait for load", () => screen.IsLoaded);
|
||||
|
||||
AddStep("set user mods", () => screen.UserMods.Value = [new OsuModDoubleTime()]);
|
||||
AddUntilStep("user mods selected", () => SelectedMods.Value.OfType<OsuModDoubleTime>().Any());
|
||||
|
||||
AddStep("set user ruleset", () => screen.UserRuleset.Value = new TaikoRuleset().RulesetInfo);
|
||||
AddUntilStep("user ruleset selected", () => Ruleset.Value.Equals(new TaikoRuleset().RulesetInfo));
|
||||
AddUntilStep("user mods reset", () => !screen.UserMods.Value.Any());
|
||||
AddUntilStep("mods reset", () => !SelectedMods.Value.Any());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that the beatmap and ruleset style are reset when the selected item is changed to one without freestyle,
|
||||
/// and that the mod selection is re-validated against the item's allowed mods.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestUserStyle_Reset_OnFreestyleDisabled()
|
||||
{
|
||||
Room room = null!;
|
||||
|
||||
AddStep("add room", () =>
|
||||
{
|
||||
room = new Room
|
||||
{
|
||||
RoomID = 1,
|
||||
Playlist =
|
||||
[
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
Freestyle = true
|
||||
},
|
||||
new PlaylistItem(importedSet.Beatmaps[0])
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
AllowedMods = [new APIMod(new OsuModDoubleTime())]
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
API.Perform(new CreateRoomRequest(room));
|
||||
});
|
||||
|
||||
TestPlaylistsRoomSubScreen screen = null!;
|
||||
AddStep("load screen", () => LoadScreen(new TestPlaylistsScreen(screen = new TestPlaylistsRoomSubScreen(room))));
|
||||
AddUntilStep("wait for load", () => screen.IsLoaded);
|
||||
|
||||
// Set beatmap + ruleset, reset by selecting second playlist item
|
||||
AddStep("set user beatmap/ruleset style", () =>
|
||||
{
|
||||
screen.UserBeatmap.Value = importedSet.Beatmaps[1];
|
||||
screen.UserRuleset.Value = new TaikoRuleset().RulesetInfo;
|
||||
});
|
||||
AddUntilStep("beatmap/ruleset set", () => Beatmap.Value.BeatmapInfo.Equals(importedSet.Beatmaps[1]) && Ruleset.Value.Equals(new TaikoRuleset().RulesetInfo));
|
||||
AddStep("select second playlist item", () => screen.SelectedItem.Value = room.Playlist[1]);
|
||||
AddUntilStep("user style reset", () => screen.UserBeatmap.Value == null && screen.UserRuleset.Value == null);
|
||||
AddUntilStep("beatmap/ruleset set", () => Beatmap.Value.BeatmapInfo.Equals(importedSet.Beatmaps[0]) && Ruleset.Value.Equals(new OsuRuleset().RulesetInfo));
|
||||
|
||||
AddStep("select first playlist item", () => screen.SelectedItem.Value = room.Playlist[0]);
|
||||
|
||||
// Set mods (DT+HR), validate by selecting second playlist item where only DT is allowed.
|
||||
AddStep("set user mods style", () => screen.UserMods.Value = [new OsuModDoubleTime(), new OsuModHardRock()]);
|
||||
AddUntilStep("mods set", () => SelectedMods.Value.OfType<OsuModDoubleTime>().Any() && SelectedMods.Value.OfType<OsuModHardRock>().Any());
|
||||
AddStep("select second playlist item", () => screen.SelectedItem.Value = room.Playlist[1]);
|
||||
AddUntilStep("user mods validated", () => screen.UserMods.Value.Count == 1 && screen.UserMods.Value.OfType<OsuModDoubleTime>().Any());
|
||||
AddUntilStep("mods set", () => SelectedMods.Value.Count == 1 && SelectedMods.Value.OfType<OsuModDoubleTime>().Any());
|
||||
}
|
||||
|
||||
private partial class TestPlaylistsScreen : OsuScreen
|
||||
{
|
||||
public TestPlaylistsScreen(PlaylistsRoomSubScreen screen)
|
||||
{
|
||||
OnlinePlaySubScreenStack stack;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
stack = new OnlinePlaySubScreenStack
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
new BackButton
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
State = { Value = Visibility.Visible },
|
||||
Action = () =>
|
||||
{
|
||||
if (stack.CurrentScreen is not PlaylistsRoomSubScreen)
|
||||
stack.Exit();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
stack.Push(screen);
|
||||
}
|
||||
}
|
||||
|
||||
private partial class TestPlaylistsRoomSubScreen : PlaylistsRoomSubScreen
|
||||
{
|
||||
public new Bindable<PlaylistItem?> SelectedItem => base.SelectedItem;
|
||||
public new Bindable<BeatmapInfo?> UserBeatmap => base.UserBeatmap;
|
||||
public new Bindable<RulesetInfo?> UserRuleset => base.UserRuleset;
|
||||
public new Bindable<IReadOnlyList<Mod>> UserMods => base.UserMods;
|
||||
|
||||
public TestPlaylistsRoomSubScreen(Room room)
|
||||
: base(room)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
showPanel(TestResources.CreateTestScoreInfo(beatmap));
|
||||
});
|
||||
|
||||
AddAssert("pp display faded out", () =>
|
||||
AddUntilStep("pp display faded out", () =>
|
||||
{
|
||||
var ppDisplay = this.ChildrenOfType<PerformanceStatistic>().Single();
|
||||
return ppDisplay.Alpha == 0.5 && ppDisplay.TooltipText == ResultsScreenStrings.NoPPForUnrankedBeatmaps;
|
||||
@@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
showPanel(score);
|
||||
});
|
||||
|
||||
AddAssert("pp display faded out", () =>
|
||||
AddUntilStep("pp display faded out", () =>
|
||||
{
|
||||
var ppDisplay = this.ChildrenOfType<PerformanceStatistic>().Single();
|
||||
return ppDisplay.Alpha == 0.5 && ppDisplay.TooltipText == ResultsScreenStrings.NoPPForUnrankedMods;
|
||||
@@ -116,7 +116,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
showPanel(score);
|
||||
});
|
||||
|
||||
AddAssert("pp display faded out", () => this.ChildrenOfType<PerformanceStatistic>().Single().Alpha == 1);
|
||||
AddUntilStep("pp display faded out", () => this.ChildrenOfType<PerformanceStatistic>().Single().Alpha == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Scoring;
|
||||
@@ -12,7 +13,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
{
|
||||
public partial class TestSceneOverallRanking : OsuTestScene
|
||||
{
|
||||
private OverallRanking overallRanking = null!;
|
||||
private readonly Bindable<ScoreBasedUserStatisticsUpdate?> statisticsUpdate = new Bindable<ScoreBasedUserStatisticsUpdate?>();
|
||||
|
||||
[Test]
|
||||
public void TestUpdatePending()
|
||||
@@ -104,14 +105,19 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
displayUpdate(statistics, statistics);
|
||||
}
|
||||
|
||||
private void createDisplay() => AddStep("create display", () => Child = overallRanking = new OverallRanking
|
||||
private void createDisplay() => AddStep("create display", () =>
|
||||
{
|
||||
Width = 400,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
statisticsUpdate.Value = null;
|
||||
Child = new OverallRanking(new ScoreInfo())
|
||||
{
|
||||
Width = 400,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
DisplayedUpdate = { BindTarget = statisticsUpdate }
|
||||
};
|
||||
});
|
||||
|
||||
private void displayUpdate(UserStatistics before, UserStatistics after) =>
|
||||
AddStep("display update", () => overallRanking.StatisticsUpdate.Value = new ScoreBasedUserStatisticsUpdate(new ScoreInfo(), before, after));
|
||||
AddStep("display update", () => statisticsUpdate.Value = new ScoreBasedUserStatisticsUpdate(new ScoreInfo(), before, after));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
@@ -17,7 +16,6 @@ using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
@@ -406,7 +404,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
: base(score)
|
||||
{
|
||||
AllowRetry = true;
|
||||
ShowUserStatistics = true;
|
||||
IsLocalPlay = true;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@@ -416,21 +414,19 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
RetryOverlay = InternalChildren.OfType<HotkeyRetryOverlay>().SingleOrDefault();
|
||||
}
|
||||
|
||||
protected override APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback)
|
||||
protected override Task<ScoreInfo[]> FetchScores()
|
||||
{
|
||||
var scores = new List<ScoreInfo>();
|
||||
var scores = new ScoreInfo[20];
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
for (int i = 0; i < scores.Length; i++)
|
||||
{
|
||||
var score = TestResources.CreateTestScoreInfo();
|
||||
score.TotalScore += 10 - i;
|
||||
score.HasOnlineReplay = true;
|
||||
scores.Add(score);
|
||||
scores[i] = score;
|
||||
}
|
||||
|
||||
scoresCallback.Invoke(scores);
|
||||
|
||||
return null;
|
||||
return Task.FromResult(scores);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,27 +442,25 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
this.fetchWaitTask = fetchWaitTask ?? Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected override APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback)
|
||||
protected override Task<ScoreInfo[]> FetchScores()
|
||||
{
|
||||
Task.Run(async () =>
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
await fetchWaitTask;
|
||||
|
||||
var scores = new List<ScoreInfo>();
|
||||
var scores = new ScoreInfo[20];
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
for (int i = 0; i < scores.Length; i++)
|
||||
{
|
||||
var score = TestResources.CreateTestScoreInfo();
|
||||
score.TotalScore += 10 - i;
|
||||
scores.Add(score);
|
||||
scores[i] = score;
|
||||
}
|
||||
|
||||
scoresCallback?.Invoke(scores);
|
||||
|
||||
Schedule(() => FetchCompleted = true);
|
||||
});
|
||||
|
||||
return null;
|
||||
return scores;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
@@ -18,6 +19,9 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@@ -36,6 +40,8 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
{
|
||||
public partial class TestSceneStatisticsPanel : OsuTestScene
|
||||
{
|
||||
private DummyAPIAccess dummyAPI => (DummyAPIAccess)API;
|
||||
|
||||
[Test]
|
||||
public void TestScoreWithPositionStatistics()
|
||||
{
|
||||
@@ -137,62 +143,114 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
{
|
||||
CachedDependencies = [(typeof(UserStatisticsWatcher), userStatisticsWatcher)],
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new UserStatisticsPanel(score)
|
||||
Child = new StatisticsPanel
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
State = { Value = Visibility.Visible },
|
||||
Score = { Value = score, }
|
||||
Score = { Value = score, },
|
||||
AchievedScore = score,
|
||||
}
|
||||
});
|
||||
AddUntilStep("overall ranking present", () => this.ChildrenOfType<OverallRanking>().Any());
|
||||
AddUntilStep("loading spinner not visible", () => this.ChildrenOfType<LoadingLayer>().All(l => l.State.Value == Visibility.Hidden));
|
||||
AddUntilStep("loading spinner not visible",
|
||||
() => this.ChildrenOfType<OverallRanking>().Single()
|
||||
.ChildrenOfType<LoadingLayer>().All(l => l.State.Value == Visibility.Hidden));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTagging()
|
||||
{
|
||||
var score = TestResources.CreateTestScoreInfo();
|
||||
|
||||
AddStep("set up network requests", () =>
|
||||
{
|
||||
dummyAPI.HandleRequest = request =>
|
||||
{
|
||||
switch (request)
|
||||
{
|
||||
case ListTagsRequest listTagsRequest:
|
||||
{
|
||||
Scheduler.AddDelayed(() => listTagsRequest.TriggerSuccess(new APITagCollection
|
||||
{
|
||||
Tags =
|
||||
[
|
||||
new APITag { Id = 1, Name = "tech", Description = "Tests uncommon skills.", },
|
||||
new APITag { Id = 2, Name = "alt", Description = "Colloquial term for maps which use rhythms that encourage the player to alternate notes. Typically distinct from burst or stream maps.", },
|
||||
new APITag { Id = 3, Name = "aim", Description = "Category for difficulty relating to cursor movement.", },
|
||||
new APITag { Id = 4, Name = "tap", Description = "Category for difficulty relating to tapping input.", },
|
||||
]
|
||||
}), 500);
|
||||
return true;
|
||||
}
|
||||
|
||||
case GetBeatmapSetRequest getBeatmapSetRequest:
|
||||
{
|
||||
var beatmapSet = CreateAPIBeatmapSet(score.BeatmapInfo);
|
||||
beatmapSet.Beatmaps.Single().TopTags =
|
||||
[
|
||||
new APIBeatmapTag { TagId = 3, VoteCount = 9 },
|
||||
];
|
||||
Scheduler.AddDelayed(() => getBeatmapSetRequest.TriggerSuccess(beatmapSet), 500);
|
||||
return true;
|
||||
}
|
||||
|
||||
case AddBeatmapTagRequest:
|
||||
case RemoveBeatmapTagRequest:
|
||||
{
|
||||
Scheduler.AddDelayed(request.TriggerSuccess, 500);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
});
|
||||
AddStep("load panel", () =>
|
||||
{
|
||||
Child = new PopoverContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new StatisticsPanel
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
State = { Value = Visibility.Visible },
|
||||
Score = { Value = score },
|
||||
AchievedScore = score,
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTaggingWhenRankTooLow()
|
||||
{
|
||||
var score = TestResources.CreateTestScoreInfo();
|
||||
score.Rank = ScoreRank.D;
|
||||
|
||||
AddStep("load panel", () =>
|
||||
{
|
||||
Child = new PopoverContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new StatisticsPanel
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
State = { Value = Visibility.Visible },
|
||||
Score = { Value = score },
|
||||
AchievedScore = score,
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private void loadPanel(ScoreInfo score) => AddStep("load panel", () =>
|
||||
{
|
||||
Child = new UserStatisticsPanel(score)
|
||||
Child = new StatisticsPanel
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
State = { Value = Visibility.Visible },
|
||||
Score = { Value = score },
|
||||
DisplayedUserStatisticsUpdate =
|
||||
{
|
||||
Value = new ScoreBasedUserStatisticsUpdate(score, new UserStatistics
|
||||
{
|
||||
Level = new UserStatistics.LevelInfo
|
||||
{
|
||||
Current = 5,
|
||||
Progress = 20,
|
||||
},
|
||||
GlobalRank = 38000,
|
||||
CountryRank = 12006,
|
||||
PP = 2134,
|
||||
RankedScore = 21123849,
|
||||
Accuracy = 0.985,
|
||||
PlayCount = 13375,
|
||||
PlayTime = 354490,
|
||||
TotalScore = 128749597,
|
||||
TotalHits = 0,
|
||||
MaxCombo = 1233,
|
||||
}, new UserStatistics
|
||||
{
|
||||
Level = new UserStatistics.LevelInfo
|
||||
{
|
||||
Current = 5,
|
||||
Progress = 30,
|
||||
},
|
||||
GlobalRank = 36000,
|
||||
CountryRank = 12000,
|
||||
PP = (decimal)2134.5,
|
||||
RankedScore = 23897015,
|
||||
Accuracy = 0.984,
|
||||
PlayCount = 13376,
|
||||
PlayTime = 35789,
|
||||
TotalScore = 132218497,
|
||||
TotalHits = 0,
|
||||
MaxCombo = 1233,
|
||||
})
|
||||
}
|
||||
AchievedScore = score,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -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.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Screens.Ranking;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Ranking
|
||||
{
|
||||
public partial class TestSceneUserTagControl : OsuTestScene
|
||||
{
|
||||
private DummyAPIAccess dummyAPI => (DummyAPIAccess)API;
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("set up working beatmap", () =>
|
||||
{
|
||||
Beatmap.Value.BeatmapInfo.OnlineID = 42;
|
||||
});
|
||||
AddStep("set up network requests", () =>
|
||||
{
|
||||
dummyAPI.HandleRequest = request =>
|
||||
{
|
||||
switch (request)
|
||||
{
|
||||
case ListTagsRequest listTagsRequest:
|
||||
{
|
||||
Scheduler.AddDelayed(() => listTagsRequest.TriggerSuccess(new APITagCollection
|
||||
{
|
||||
Tags =
|
||||
[
|
||||
new APITag { Id = 1, Name = "tech", Description = "Tests uncommon skills.", },
|
||||
new APITag { Id = 2, Name = "alt", Description = "Colloquial term for maps which use rhythms that encourage the player to alternate notes. Typically distinct from burst or stream maps.", },
|
||||
new APITag { Id = 3, Name = "aim", Description = "Category for difficulty relating to cursor movement.", },
|
||||
new APITag { Id = 4, Name = "tap", Description = "Category for difficulty relating to tapping input.", },
|
||||
]
|
||||
}), 500);
|
||||
return true;
|
||||
}
|
||||
|
||||
case GetBeatmapSetRequest getBeatmapSetRequest:
|
||||
{
|
||||
var beatmapSet = CreateAPIBeatmapSet(Beatmap.Value.BeatmapInfo);
|
||||
beatmapSet.Beatmaps.Single().TopTags =
|
||||
[
|
||||
new APIBeatmapTag { TagId = 3, VoteCount = 9 },
|
||||
];
|
||||
Scheduler.AddDelayed(() => getBeatmapSetRequest.TriggerSuccess(beatmapSet), 500);
|
||||
return true;
|
||||
}
|
||||
|
||||
case AddBeatmapTagRequest:
|
||||
case RemoveBeatmapTagRequest:
|
||||
{
|
||||
Scheduler.AddDelayed(request.TriggerSuccess, 500);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
});
|
||||
AddStep("create control", () =>
|
||||
{
|
||||
Child = new PopoverContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new UserTagControl(Beatmap.Value.BeatmapInfo)
|
||||
{
|
||||
Width = 500,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,6 +136,13 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
|
||||
public Bindable<float> Rotation { get; } = new Bindable<float>();
|
||||
|
||||
public BindableFloat PressureThreshold { get; } = new BindableFloat
|
||||
{
|
||||
MinValue = 0f,
|
||||
MaxValue = 1f,
|
||||
Precision = 0.005f,
|
||||
};
|
||||
|
||||
public IBindable<TabletInfo> Tablet => tablet;
|
||||
|
||||
private readonly Bindable<TabletInfo> tablet = new Bindable<TabletInfo>();
|
||||
|
||||
@@ -12,6 +12,8 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Leaderboards;
|
||||
using osu.Game.Overlays;
|
||||
@@ -20,14 +22,16 @@ using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.Select.Leaderboards;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.SongSelect
|
||||
{
|
||||
public partial class TestSceneBeatmapLeaderboard : OsuTestScene
|
||||
public partial class TestSceneBeatmapLeaderboard : OsuManualInputManagerTestScene
|
||||
{
|
||||
private readonly FailableLeaderboard leaderboard;
|
||||
|
||||
@@ -37,6 +41,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
private ScoreManager scoreManager = null!;
|
||||
private RulesetStore rulesetStore = null!;
|
||||
private BeatmapManager beatmapManager = null!;
|
||||
private PlaySongSelect songSelect = null!;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
@@ -45,25 +50,36 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
dependencies.Cache(rulesetStore = new RealmRulesetStore(Realm));
|
||||
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, null, dependencies.Get<AudioManager>(), Resources, dependencies.Get<GameHost>(), Beatmap.Default));
|
||||
dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, Realm, API));
|
||||
dependencies.CacheAs<Screens.Select.SongSelect>(songSelect = new PlaySongSelect());
|
||||
Dependencies.Cache(Realm);
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
LoadComponent(songSelect);
|
||||
}
|
||||
|
||||
public TestSceneBeatmapLeaderboard()
|
||||
{
|
||||
AddRange(new Drawable[]
|
||||
Add(new OsuContextMenuContainer
|
||||
{
|
||||
dialogOverlay = new DialogOverlay
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Depth = -1
|
||||
},
|
||||
leaderboard = new FailableLeaderboard
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Size = new Vector2(550f, 450f),
|
||||
Scope = BeatmapLeaderboardScope.Global,
|
||||
dialogOverlay = new DialogOverlay
|
||||
{
|
||||
Depth = -1
|
||||
},
|
||||
leaderboard = new FailableLeaderboard
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Size = new Vector2(550f, 450f),
|
||||
Scope = BeatmapLeaderboardScope.Global,
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -165,6 +181,11 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
{
|
||||
AddStep(@"Set scope", () => leaderboard.Scope = BeatmapLeaderboardScope.Global);
|
||||
AddStep(@"New Scores", () => leaderboard.SetScores(generateSampleScores(new BeatmapInfo())));
|
||||
AddStep(@"New Scores with teams", () => leaderboard.SetScores(generateSampleScores(new BeatmapInfo()).Select(s =>
|
||||
{
|
||||
s.User.Team = new APITeam();
|
||||
return s;
|
||||
})));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -180,6 +201,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
AddStep("ensure no scores displayed", () => leaderboard.SetScores(null));
|
||||
|
||||
AddStep(@"Network failure", () => leaderboard.SetErrorState(LeaderboardState.NetworkFailure));
|
||||
AddStep(@"No team", () => leaderboard.SetErrorState(LeaderboardState.NoTeam));
|
||||
AddStep(@"No supporter", () => leaderboard.SetErrorState(LeaderboardState.NotSupporter));
|
||||
AddStep(@"Not logged in", () => leaderboard.SetErrorState(LeaderboardState.NotLoggedIn));
|
||||
AddStep(@"Ruleset unavailable", () => leaderboard.SetErrorState(LeaderboardState.RulesetUnavailable));
|
||||
@@ -187,6 +209,40 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
AddStep(@"None selected", () => leaderboard.SetErrorState(LeaderboardState.NoneSelected));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUseTheseModsDoesNotCopySystemMods()
|
||||
{
|
||||
AddStep(@"set scores", () => leaderboard.SetScores(leaderboard.Scores, new ScoreInfo
|
||||
{
|
||||
Position = 999,
|
||||
Rank = ScoreRank.XH,
|
||||
Accuracy = 1,
|
||||
MaxCombo = 244,
|
||||
TotalScore = 1707827,
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
Mods = new Mod[] { new OsuModHidden(), new ModScoreV2(), },
|
||||
User = new APIUser
|
||||
{
|
||||
Id = 6602580,
|
||||
Username = @"waaiiru",
|
||||
CountryCode = CountryCode.ES,
|
||||
}
|
||||
}));
|
||||
AddUntilStep("wait for scores", () => this.ChildrenOfType<LeaderboardScore>().Count(), () => Is.GreaterThan(0));
|
||||
AddStep("right click panel", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<LeaderboardScore>().Single());
|
||||
InputManager.Click(MouseButton.Right);
|
||||
});
|
||||
AddStep("click use these mods", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<DrawableOsuMenuItem>().Single());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddAssert("song select received HD", () => songSelect.Mods.Value.Any(m => m is OsuModHidden));
|
||||
AddAssert("song select did not receive SV2", () => !songSelect.Mods.Value.Any(m => m is ModScoreV2));
|
||||
}
|
||||
|
||||
private void showPersonalBestWithNullPosition()
|
||||
{
|
||||
leaderboard.SetScores(leaderboard.Scores, new ScoreInfo
|
||||
@@ -423,7 +479,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
Accuracy = 0.5140,
|
||||
MaxCombo = 244,
|
||||
TotalScore = 1707827,
|
||||
Date = DateTime.Now.AddMonths(-3),
|
||||
Date = DateTime.Now.AddMonths(-10),
|
||||
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
BeatmapInfo = beatmapInfo,
|
||||
BeatmapHash = beatmapInfo.Hash,
|
||||
|
||||
@@ -6,16 +6,17 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
{
|
||||
public abstract partial class SongSelectComponentsTestScene : OsuTestScene
|
||||
public abstract partial class SongSelectComponentsTestScene : OsuManualInputManagerTestScene
|
||||
{
|
||||
[Cached]
|
||||
protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
|
||||
|
||||
protected override Container<Drawable> Content { get; } = new Container
|
||||
protected override Container<Drawable> Content { get; } = new OsuContextMenuContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
|
||||
@@ -7,9 +7,11 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets.Mania;
|
||||
@@ -22,6 +24,7 @@ using osu.Game.Screens.SelectV2.Leaderboards;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
{
|
||||
@@ -102,6 +105,69 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUseTheseModsDoesNotCopySystemMods()
|
||||
{
|
||||
LeaderboardScoreV2 score = null!;
|
||||
|
||||
AddStep("create content", () =>
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
fillFlow = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(0f, 2f),
|
||||
Shear = new Vector2(OsuGame.SHEAR, 0)
|
||||
},
|
||||
drawWidthText = new OsuSpriteText(),
|
||||
};
|
||||
|
||||
var scoreInfo = new ScoreInfo
|
||||
{
|
||||
Position = 999,
|
||||
Rank = ScoreRank.X,
|
||||
Accuracy = 1,
|
||||
MaxCombo = 244,
|
||||
TotalScore = RNG.Next(1_800_000, 2_000_000),
|
||||
MaximumStatistics = { { HitResult.Great, 3000 } },
|
||||
Mods = new Mod[] { new OsuModHidden(), new ModScoreV2(), },
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
User = new APIUser
|
||||
{
|
||||
Id = 6602580,
|
||||
Username = @"waaiiru",
|
||||
CountryCode = CountryCode.ES,
|
||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg",
|
||||
},
|
||||
Date = DateTimeOffset.Now.AddYears(-2),
|
||||
};
|
||||
|
||||
fillFlow.Add(score = new LeaderboardScoreV2(scoreInfo)
|
||||
{
|
||||
Rank = scoreInfo.Position,
|
||||
Shear = Vector2.Zero,
|
||||
});
|
||||
|
||||
score.Show();
|
||||
});
|
||||
AddStep("right click panel", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(score);
|
||||
InputManager.Click(MouseButton.Right);
|
||||
});
|
||||
AddStep("click use these mods", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<DrawableOsuMenuItem>().Single());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddAssert("mods received HD", () => score.SelectedMods.Value.Any(m => m is OsuModHidden));
|
||||
AddAssert("mods did not receive SV2", () => !score.SelectedMods.Value.Any(m => m is ModScoreV2));
|
||||
}
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
AddToggleStep("toggle scoring mode", v => config.SetValue(OsuSetting.ScoreDisplayMode, v ? ScoringMode.Classic : ScoringMode.Standardised));
|
||||
|
||||
@@ -9,16 +9,10 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Mania;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@@ -29,7 +23,6 @@ using osu.Game.Screens;
|
||||
using osu.Game.Screens.Footer;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Screens.SelectV2.Footer;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
@@ -42,8 +35,6 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
[Cached]
|
||||
private readonly OsuLogo logo;
|
||||
|
||||
private BeatmapManager beatmapManager = null!;
|
||||
|
||||
protected override bool UseOnlineAPI => true;
|
||||
|
||||
public TestSceneSongSelect()
|
||||
@@ -66,32 +57,12 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, IAPIProvider onlineAPI)
|
||||
private void load()
|
||||
{
|
||||
BeatmapStore beatmapStore;
|
||||
BeatmapUpdater beatmapUpdater;
|
||||
BeatmapDifficultyCache difficultyCache;
|
||||
RealmDetachedBeatmapStore beatmapStore;
|
||||
|
||||
// These DI caches are required to ensure for interactive runs this test scene doesn't nuke all user beatmaps in the local install.
|
||||
// At a point we have isolated interactive test runs enough, this can likely be removed.
|
||||
Dependencies.Cache(new RealmRulesetStore(Realm));
|
||||
Dependencies.Cache(Realm);
|
||||
Dependencies.Cache(difficultyCache = new BeatmapDifficultyCache());
|
||||
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, onlineAPI, Audio, Resources, host, Beatmap.Default, difficultyCache));
|
||||
Dependencies.CacheAs(beatmapUpdater = new BeatmapUpdater(beatmapManager, difficultyCache, onlineAPI, LocalStorage));
|
||||
Dependencies.CacheAs(beatmapStore = new RealmDetachedBeatmapStore());
|
||||
|
||||
beatmapManager.ProcessBeatmap = (set, scope) => beatmapUpdater.Process(set, scope);
|
||||
|
||||
MusicController music;
|
||||
Dependencies.Cache(music = new MusicController());
|
||||
|
||||
// required to get bindables attached
|
||||
Add(difficultyCache);
|
||||
Add(music);
|
||||
Dependencies.CacheAs<BeatmapStore>(beatmapStore = new RealmDetachedBeatmapStore());
|
||||
Add(beatmapStore);
|
||||
|
||||
Dependencies.Cache(new OsuConfigManager(LocalStorage));
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@@ -107,9 +78,8 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("load screen", () => Stack.Push(new Screens.SelectV2.SongSelectV2()));
|
||||
AddUntilStep("wait for load", () => Stack.CurrentScreen is Screens.SelectV2.SongSelectV2 songSelect && songSelect.IsLoaded);
|
||||
AddStep("import test beatmap", () => beatmapManager.Import(TestResources.GetTestBeatmapForImport()));
|
||||
AddStep("load screen", () => Stack.Push(new Screens.SelectV2.SoloSongSelect()));
|
||||
AddUntilStep("wait for load", () => Stack.CurrentScreen is Screens.SelectV2.SongSelect songSelect && songSelect.IsLoaded);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
base.SetUpSteps();
|
||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||
AddWaitStep("wait", 5);
|
||||
PushAndConfirm(() => new Screens.SelectV2.SongSelectV2());
|
||||
PushAndConfirm(() => new Screens.SelectV2.SoloSongSelect());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -11,7 +11,6 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Tests.Visual.Multiplayer;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
|
||||
@@ -26,15 +25,12 @@ namespace osu.Game.Tests.Visual
|
||||
/// <item>Provides a <see cref="TestMultiplayerClient"/> to be resolved as a dependency in the <see cref="Screens.OnlinePlay.Multiplayer.Multiplayer"/> screen,
|
||||
/// which is typically a part of <see cref="OsuGameBase"/>.</item>
|
||||
/// <item>Rebinds the <see cref="DummyAPIAccess"/> to handle requests via a <see cref="TestRoomRequestsHandler"/>.</item>
|
||||
/// <item>Provides a <see cref="TestMultiplayerRoomManager"/> for the <see cref="Screens.OnlinePlay.Multiplayer.Multiplayer"/> screen.</item>
|
||||
/// </list>
|
||||
/// </p>
|
||||
/// </summary>
|
||||
public partial class TestMultiplayerComponents : OsuScreen
|
||||
{
|
||||
public Screens.OnlinePlay.Multiplayer.Multiplayer MultiplayerScreen => multiplayerScreen;
|
||||
|
||||
public TestMultiplayerRoomManager RoomManager => multiplayerScreen.RoomManager;
|
||||
public Screens.OnlinePlay.Multiplayer.Multiplayer MultiplayerScreen { get; }
|
||||
|
||||
public IScreen CurrentScreen => screenStack.CurrentScreen;
|
||||
|
||||
@@ -53,17 +49,17 @@ namespace osu.Game.Tests.Visual
|
||||
private BeatmapManager beatmapManager { get; set; }
|
||||
|
||||
private readonly OsuScreenStack screenStack;
|
||||
private readonly TestMultiplayer multiplayerScreen;
|
||||
private readonly TestRoomRequestsHandler requestsHandler = new TestRoomRequestsHandler();
|
||||
|
||||
public TestMultiplayerComponents()
|
||||
{
|
||||
multiplayerScreen = new TestMultiplayer();
|
||||
MultiplayerScreen = new Screens.OnlinePlay.Multiplayer.Multiplayer();
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
userLookupCache,
|
||||
beatmapLookupCache,
|
||||
MultiplayerClient = new TestMultiplayerClient(RoomManager),
|
||||
MultiplayerClient = new TestMultiplayerClient(requestsHandler),
|
||||
screenStack = new OsuScreenStack
|
||||
{
|
||||
Name = nameof(TestMultiplayerComponents),
|
||||
@@ -71,13 +67,13 @@ namespace osu.Game.Tests.Visual
|
||||
}
|
||||
};
|
||||
|
||||
screenStack.Push(multiplayerScreen);
|
||||
screenStack.Push(MultiplayerScreen);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IAPIProvider api)
|
||||
{
|
||||
((DummyAPIAccess)api).HandleRequest = request => multiplayerScreen.RequestsHandler.HandleRequest(request, api.LocalUser.Value, beatmapManager);
|
||||
((DummyAPIAccess)api).HandleRequest = request => requestsHandler.HandleRequest(request, api.LocalUser.Value, beatmapManager);
|
||||
}
|
||||
|
||||
public override bool OnBackButton() => (screenStack.CurrentScreen as OsuScreen)?.OnBackButton() ?? base.OnBackButton();
|
||||
@@ -90,13 +86,5 @@ namespace osu.Game.Tests.Visual
|
||||
screenStack.Exit();
|
||||
return true;
|
||||
}
|
||||
|
||||
private partial class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer
|
||||
{
|
||||
public new TestMultiplayerRoomManager RoomManager { get; private set; }
|
||||
public TestRoomRequestsHandler RequestsHandler { get; private set; }
|
||||
|
||||
protected override RoomManager CreateRoomManager() => RoomManager = new TestMultiplayerRoomManager(RequestsHandler = new TestRoomRequestsHandler());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
// 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 System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Metadata;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Dashboard.Friends;
|
||||
using osu.Game.Tests.Visual.Metadata;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
@@ -19,37 +20,90 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||
|
||||
private FriendOnlineStreamControl control;
|
||||
private TestMetadataClient metadataClient = null!;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() => Child = control = new FriendOnlineStreamControl
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Child = new DependencyProvidingContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CachedDependencies =
|
||||
[
|
||||
(typeof(MetadataClient), metadataClient = new TestMetadataClient())
|
||||
],
|
||||
Children = new Drawable[]
|
||||
{
|
||||
metadataClient,
|
||||
new FriendOnlineStreamControl
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void Populate()
|
||||
public void TestChangeFriends()
|
||||
{
|
||||
AddStep("Populate", () => control.Populate(new List<APIUser>
|
||||
AddStep("set 10 friends", () =>
|
||||
{
|
||||
new APIUser
|
||||
DummyAPIAccess api = (DummyAPIAccess)API;
|
||||
api.Friends.Clear();
|
||||
api.Friends.AddRange(Enumerable.Range(1, 10).Select(i => new APIRelation
|
||||
{
|
||||
IsOnline = true
|
||||
},
|
||||
new APIUser
|
||||
{
|
||||
IsOnline = false
|
||||
},
|
||||
new APIUser
|
||||
{
|
||||
IsOnline = false
|
||||
}
|
||||
}));
|
||||
RelationType = RelationType.Friend,
|
||||
TargetID = i,
|
||||
TargetUser = new APIUser { Id = i },
|
||||
}));
|
||||
});
|
||||
|
||||
AddAssert("3 users", () => control.Items.FirstOrDefault(item => item.Status == OnlineStatus.All)?.Count == 3);
|
||||
AddAssert("1 online user", () => control.Items.FirstOrDefault(item => item.Status == OnlineStatus.Online)?.Count == 1);
|
||||
AddAssert("2 offline users", () => control.Items.FirstOrDefault(item => item.Status == OnlineStatus.Offline)?.Count == 2);
|
||||
AddStep("set 20 friends", () =>
|
||||
{
|
||||
DummyAPIAccess api = (DummyAPIAccess)API;
|
||||
api.Friends.Clear();
|
||||
api.Friends.AddRange(Enumerable.Range(1, 20).Select(i => new APIRelation
|
||||
{
|
||||
RelationType = RelationType.Friend,
|
||||
TargetID = i,
|
||||
TargetUser = new APIUser { Id = i },
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChangeOnlineStates()
|
||||
{
|
||||
AddStep("set 10 friends", () =>
|
||||
{
|
||||
DummyAPIAccess api = (DummyAPIAccess)API;
|
||||
api.Friends.Clear();
|
||||
api.Friends.AddRange(Enumerable.Range(1, 10).Select(i => new APIRelation
|
||||
{
|
||||
RelationType = RelationType.Friend,
|
||||
TargetID = i,
|
||||
TargetUser = new APIUser { Id = i },
|
||||
}));
|
||||
});
|
||||
|
||||
AddStep("make users 1-5 online", () =>
|
||||
{
|
||||
for (int i = 1; i <= 5; i++)
|
||||
metadataClient.FriendPresenceUpdated(i, new UserPresence { Status = UserStatus.Online });
|
||||
});
|
||||
|
||||
AddStep("make users 1-5 DnD", () =>
|
||||
{
|
||||
for (int i = 1; i <= 5; i++)
|
||||
metadataClient.FriendPresenceUpdated(i, new UserPresence { Status = UserStatus.DoNotDisturb });
|
||||
});
|
||||
|
||||
AddStep("make users 1-5 offline", () =>
|
||||
{
|
||||
for (int i = 1; i <= 5; i++)
|
||||
metadataClient.FriendPresenceUpdated(i, null);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1030,7 +1030,10 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
||||
private partial class TestModSelectOverlay : UserModSelectOverlay
|
||||
{
|
||||
protected override bool ShowPresets => true;
|
||||
public TestModSelectOverlay()
|
||||
{
|
||||
ShowPresets = true;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestUnimplementedMod : Mod
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
private DependencyProvidingContainer contentContainer = null!;
|
||||
private ScreenFooter screenFooter = null!;
|
||||
private TestModSelectOverlay modOverlay = null!;
|
||||
private UserModSelectOverlay modOverlay = null!;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
@@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
modOverlay = new TestModSelectOverlay(),
|
||||
modOverlay = new UserModSelectOverlay { ShowPresets = true },
|
||||
new PopoverContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@@ -196,11 +196,6 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
AddAssert("external overlay content still not shown", () => this.ChildrenOfType<TestShearedOverlayContainer.TestFooterContent>().SingleOrDefault()?.IsPresent, () => Is.Not.True);
|
||||
}
|
||||
|
||||
private partial class TestModSelectOverlay : UserModSelectOverlay
|
||||
{
|
||||
protected override bool ShowPresets => true;
|
||||
}
|
||||
|
||||
private partial class TestShearedOverlayContainer : ShearedOverlayContainer
|
||||
{
|
||||
public TestShearedOverlayContainer()
|
||||
|
||||
@@ -115,11 +115,10 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
||||
private partial class TestModSelectOverlay : UserModSelectOverlay
|
||||
{
|
||||
protected override bool ShowPresets => true;
|
||||
|
||||
public TestModSelectOverlay()
|
||||
: base(OverlayColourScheme.Aquamarine)
|
||||
{
|
||||
ShowPresets = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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 Newtonsoft.Json;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public class APIBeatmapTag
|
||||
{
|
||||
[JsonProperty("tag_id")]
|
||||
public long TagId { get; set; }
|
||||
|
||||
[JsonProperty("count")]
|
||||
public int VoteCount { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,28 @@
|
||||
// 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.Localisation;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Screens.Select;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public static class BeatmapInfoExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Given an <see cref="IBeatmap"/>, update length, BPM and object counts.
|
||||
/// </summary>
|
||||
public static void UpdateStatisticsFromBeatmap(this BeatmapInfo beatmapInfo, IBeatmap beatmap)
|
||||
{
|
||||
beatmapInfo.Length = beatmap.CalculatePlayableLength();
|
||||
beatmapInfo.BPM = 60000 / beatmap.GetMostCommonBeatLength();
|
||||
beatmapInfo.EndTimeObjectCount = beatmap.HitObjects.Count(h => h is IHasDuration);
|
||||
beatmapInfo.TotalObjectCount = beatmap.HitObjects.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A user-presentable display title representing this beatmap.
|
||||
/// </summary>
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace osu.Game.Beatmaps
|
||||
if (lookupScope != MetadataLookupScope.None)
|
||||
metadataLookup.Update(beatmapSet, lookupScope == MetadataLookupScope.OnlineFirst);
|
||||
|
||||
foreach (var beatmap in beatmapSet.Beatmaps)
|
||||
foreach (BeatmapInfo beatmap in beatmapSet.Beatmaps)
|
||||
{
|
||||
difficultyCache.Invalidate(beatmap);
|
||||
|
||||
@@ -63,10 +63,7 @@ namespace osu.Game.Beatmaps
|
||||
var calculator = ruleset.CreateDifficultyCalculator(working);
|
||||
|
||||
beatmap.StarRating = calculator.Calculate().StarRating;
|
||||
beatmap.Length = working.Beatmap.CalculatePlayableLength();
|
||||
beatmap.BPM = 60000 / working.Beatmap.GetMostCommonBeatLength();
|
||||
beatmap.EndTimeObjectCount = working.Beatmap.HitObjects.Count(h => h is IHasDuration);
|
||||
beatmap.TotalObjectCount = working.Beatmap.HitObjects.Count;
|
||||
beatmap.UpdateStatisticsFromBeatmap(working.Beatmap);
|
||||
}
|
||||
|
||||
// And invalidate again afterwards as re-fetching the most up-to-date database metadata will be required.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user