mirror of
https://github.com/osukey/osukey.git
synced 2025-06-04 04:17:21 +09:00
Merge branch 'master' into wiki-image-block
This commit is contained in:
commit
4e52aee5a0
@ -12,7 +12,7 @@
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -12,7 +12,7 @@
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -12,7 +12,7 @@
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -12,7 +12,7 @@
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -52,10 +52,10 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.618.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.628.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.630.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
<PackageReference Include="Realm" Version="10.2.0" />
|
||||
<PackageReference Include="Realm" Version="10.2.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -116,7 +116,7 @@ namespace osu.Desktop.Updater
|
||||
if (scheduleRecheck)
|
||||
{
|
||||
// check again in 30 minutes.
|
||||
Scheduler.AddDelayed(async () => await checkForUpdateAsync().ConfigureAwait(false), 60000 * 30);
|
||||
Scheduler.AddDelayed(() => Task.Run(async () => await checkForUpdateAsync().ConfigureAwait(false)), 60000 * 30);
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,7 +141,7 @@ namespace osu.Desktop.Updater
|
||||
Activated = () =>
|
||||
{
|
||||
updateManager.PrepareUpdateAsync()
|
||||
.ContinueWith(_ => updateManager.Schedule(() => game.GracefullyExit()));
|
||||
.ContinueWith(_ => updateManager.Schedule(() => game?.GracefullyExit()));
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.0" />
|
||||
<PackageReference Include="nunit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
|
@ -4,7 +4,7 @@
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
|
@ -37,11 +37,13 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
private readonly BindableBool snakingIn = new BindableBool();
|
||||
private readonly BindableBool snakingOut = new BindableBool();
|
||||
|
||||
private IBeatmap beatmap;
|
||||
|
||||
private const double duration_of_span = 3605;
|
||||
private const double fade_in_modifier = -1200;
|
||||
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||
=> new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
||||
=> new ClockBackedTestWorkingBeatmap(this.beatmap = beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetConfigCache configCache)
|
||||
@ -51,8 +53,16 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
config.BindWith(OsuRulesetSetting.SnakingOutSliders, snakingOut);
|
||||
}
|
||||
|
||||
private Slider slider;
|
||||
private DrawableSlider drawableSlider;
|
||||
|
||||
[SetUp]
|
||||
public void Setup() => Schedule(() =>
|
||||
{
|
||||
slider = null;
|
||||
drawableSlider = null;
|
||||
});
|
||||
|
||||
[SetUpSteps]
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
@ -67,21 +77,19 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
base.SetUpSteps();
|
||||
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
||||
|
||||
double startTime = hitObjects[sliderIndex].StartTime;
|
||||
addSeekStep(startTime);
|
||||
retrieveDrawableSlider((Slider)hitObjects[sliderIndex]);
|
||||
retrieveSlider(sliderIndex);
|
||||
setSnaking(true);
|
||||
|
||||
ensureSnakingIn(startTime + fade_in_modifier);
|
||||
addEnsureSnakingInSteps(() => slider.StartTime + fade_in_modifier);
|
||||
|
||||
for (int i = 0; i < sliderIndex; i++)
|
||||
{
|
||||
// non-final repeats should not snake out
|
||||
ensureNoSnakingOut(startTime, i);
|
||||
addEnsureNoSnakingOutStep(() => slider.StartTime, i);
|
||||
}
|
||||
|
||||
// final repeat should snake out
|
||||
ensureSnakingOut(startTime, sliderIndex);
|
||||
addEnsureSnakingOutSteps(() => slider.StartTime, sliderIndex);
|
||||
}
|
||||
|
||||
[TestCase(0)]
|
||||
@ -93,17 +101,15 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
base.SetUpSteps();
|
||||
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
||||
|
||||
double startTime = hitObjects[sliderIndex].StartTime;
|
||||
addSeekStep(startTime);
|
||||
retrieveDrawableSlider((Slider)hitObjects[sliderIndex]);
|
||||
retrieveSlider(sliderIndex);
|
||||
setSnaking(false);
|
||||
|
||||
ensureNoSnakingIn(startTime + fade_in_modifier);
|
||||
addEnsureNoSnakingInSteps(() => slider.StartTime + fade_in_modifier);
|
||||
|
||||
for (int i = 0; i <= sliderIndex; i++)
|
||||
{
|
||||
// no snaking out ever, including final repeat
|
||||
ensureNoSnakingOut(startTime, i);
|
||||
addEnsureNoSnakingOutStep(() => slider.StartTime, i);
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,7 +122,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
// repeat might have a chance to update its position depending on where in the frame its hit,
|
||||
// so some leniency is allowed here instead of checking strict equality
|
||||
checkPositionChange(16600, sliderRepeat, positionAlmostSame);
|
||||
addCheckPositionChangeSteps(() => 16600, getSliderRepeat, positionAlmostSame);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -126,38 +132,41 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
setSnaking(true);
|
||||
base.SetUpSteps();
|
||||
|
||||
checkPositionChange(16600, sliderRepeat, positionDecreased);
|
||||
addCheckPositionChangeSteps(() => 16600, getSliderRepeat, positionDecreased);
|
||||
}
|
||||
|
||||
private void retrieveDrawableSlider(Slider slider) => AddUntilStep($"retrieve slider @ {slider.StartTime}", () =>
|
||||
(drawableSlider = (DrawableSlider)Player.DrawableRuleset.Playfield.AllHitObjects.SingleOrDefault(d => d.HitObject == slider)) != null);
|
||||
|
||||
private void ensureSnakingIn(double startTime) => checkPositionChange(startTime, sliderEnd, positionIncreased);
|
||||
private void ensureNoSnakingIn(double startTime) => checkPositionChange(startTime, sliderEnd, positionRemainsSame);
|
||||
|
||||
private void ensureSnakingOut(double startTime, int repeatIndex)
|
||||
private void retrieveSlider(int index)
|
||||
{
|
||||
var repeatTime = timeAtRepeat(startTime, repeatIndex);
|
||||
AddStep("retrieve slider at index", () => slider = (Slider)beatmap.HitObjects[index]);
|
||||
addSeekStep(() => slider);
|
||||
AddUntilStep("retrieve drawable slider", () =>
|
||||
(drawableSlider = (DrawableSlider)Player.DrawableRuleset.Playfield.AllHitObjects.SingleOrDefault(d => d.HitObject == slider)) != null);
|
||||
}
|
||||
|
||||
private void addEnsureSnakingInSteps(Func<double> startTime) => addCheckPositionChangeSteps(startTime, getSliderEnd, positionIncreased);
|
||||
private void addEnsureNoSnakingInSteps(Func<double> startTime) => addCheckPositionChangeSteps(startTime, getSliderEnd, positionRemainsSame);
|
||||
|
||||
private void addEnsureSnakingOutSteps(Func<double> startTime, int repeatIndex)
|
||||
{
|
||||
if (repeatIndex % 2 == 0)
|
||||
checkPositionChange(repeatTime, sliderStart, positionIncreased);
|
||||
addCheckPositionChangeSteps(timeAtRepeat(startTime, repeatIndex), getSliderStart, positionIncreased);
|
||||
else
|
||||
checkPositionChange(repeatTime, sliderEnd, positionDecreased);
|
||||
addCheckPositionChangeSteps(timeAtRepeat(startTime, repeatIndex), getSliderEnd, positionDecreased);
|
||||
}
|
||||
|
||||
private void ensureNoSnakingOut(double startTime, int repeatIndex) =>
|
||||
checkPositionChange(timeAtRepeat(startTime, repeatIndex), positionAtRepeat(repeatIndex), positionRemainsSame);
|
||||
private void addEnsureNoSnakingOutStep(Func<double> startTime, int repeatIndex)
|
||||
=> addCheckPositionChangeSteps(timeAtRepeat(startTime, repeatIndex), positionAtRepeat(repeatIndex), positionRemainsSame);
|
||||
|
||||
private double timeAtRepeat(double startTime, int repeatIndex) => startTime + 100 + duration_of_span * repeatIndex;
|
||||
private Func<Vector2> positionAtRepeat(int repeatIndex) => repeatIndex % 2 == 0 ? (Func<Vector2>)sliderStart : sliderEnd;
|
||||
private Func<double> timeAtRepeat(Func<double> startTime, int repeatIndex) => () => startTime() + 100 + duration_of_span * repeatIndex;
|
||||
private Func<Vector2> positionAtRepeat(int repeatIndex) => repeatIndex % 2 == 0 ? (Func<Vector2>)getSliderStart : getSliderEnd;
|
||||
|
||||
private List<Vector2> sliderCurve => ((PlaySliderBody)drawableSlider.Body.Drawable).CurrentCurve;
|
||||
private Vector2 sliderStart() => sliderCurve.First();
|
||||
private Vector2 sliderEnd() => sliderCurve.Last();
|
||||
private List<Vector2> getSliderCurve() => ((PlaySliderBody)drawableSlider.Body.Drawable).CurrentCurve;
|
||||
private Vector2 getSliderStart() => getSliderCurve().First();
|
||||
private Vector2 getSliderEnd() => getSliderCurve().Last();
|
||||
|
||||
private Vector2 sliderRepeat()
|
||||
private Vector2 getSliderRepeat()
|
||||
{
|
||||
var drawable = Player.DrawableRuleset.Playfield.AllHitObjects.SingleOrDefault(d => d.HitObject == hitObjects[1]);
|
||||
var drawable = Player.DrawableRuleset.Playfield.AllHitObjects.SingleOrDefault(d => d.HitObject == beatmap.HitObjects[1]);
|
||||
var repeat = drawable.ChildrenOfType<Container<DrawableSliderRepeat>>().First().Children.First();
|
||||
return repeat.Position;
|
||||
}
|
||||
@ -167,7 +176,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
private bool positionDecreased(Vector2 previous, Vector2 current) => current.X < previous.X && current.Y < previous.Y;
|
||||
private bool positionAlmostSame(Vector2 previous, Vector2 current) => Precision.AlmostEquals(previous, current, 1);
|
||||
|
||||
private void checkPositionChange(double startTime, Func<Vector2> positionToCheck, Func<Vector2, Vector2, bool> positionAssertion)
|
||||
private void addCheckPositionChangeSteps(Func<double> startTime, Func<Vector2> positionToCheck, Func<Vector2, Vector2, bool> positionAssertion)
|
||||
{
|
||||
Vector2 previousPosition = Vector2.Zero;
|
||||
|
||||
@ -176,7 +185,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
addSeekStep(startTime);
|
||||
AddStep($"save {positionDescription} position", () => previousPosition = positionToCheck.Invoke());
|
||||
addSeekStep(startTime + 100);
|
||||
addSeekStep(() => startTime() + 100);
|
||||
AddAssert($"{positionDescription} {assertionDescription}", () =>
|
||||
{
|
||||
var currentPosition = positionToCheck.Invoke();
|
||||
@ -193,19 +202,21 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
});
|
||||
}
|
||||
|
||||
private void addSeekStep(double time)
|
||||
private void addSeekStep(Func<Slider> slider)
|
||||
{
|
||||
AddStep($"seek to {time}", () => MusicController.SeekTo(time));
|
||||
|
||||
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
|
||||
AddStep("seek to slider", () => Player.GameplayClockContainer.Seek(slider().StartTime));
|
||||
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(slider().StartTime, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
|
||||
}
|
||||
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap
|
||||
private void addSeekStep(Func<double> time)
|
||||
{
|
||||
HitObjects = hitObjects
|
||||
};
|
||||
AddStep("seek to time", () => Player.GameplayClockContainer.Seek(time()));
|
||||
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time(), Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
|
||||
}
|
||||
|
||||
private readonly List<HitObject> hitObjects = new List<HitObject>
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap { HitObjects = createHitObjects() };
|
||||
|
||||
private static List<HitObject> createHitObjects() => new List<HitObject>
|
||||
{
|
||||
new Slider
|
||||
{
|
||||
|
@ -217,7 +217,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
private void addSeekStep(double time)
|
||||
{
|
||||
AddStep($"seek to {time}", () => MusicController.SeekTo(time));
|
||||
AddStep($"seek to {time}", () => Player.GameplayClockContainer.Seek(time));
|
||||
|
||||
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
|
16
osu.Game.Rulesets.Osu/Mods/IHidesApproachCircles.cs
Normal file
16
osu.Game.Rulesets.Osu/Mods/IHidesApproachCircles.cs
Normal file
@ -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.
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
/// <summary>
|
||||
/// Marker interface for any mod which completely hides the approach circles.
|
||||
/// Used for incompatibility with <see cref="IRequiresApproachCircles"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that this is only a marker interface for incompatibility purposes, it does not change any gameplay behaviour.
|
||||
/// </remarks>
|
||||
public interface IHidesApproachCircles
|
||||
{
|
||||
}
|
||||
}
|
@ -1,12 +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.
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
/// <summary>
|
||||
/// Any mod which affects the animation or visibility of approach circles. Should be used for incompatibility purposes.
|
||||
/// </summary>
|
||||
public interface IMutateApproachCircles
|
||||
{
|
||||
}
|
||||
}
|
16
osu.Game.Rulesets.Osu/Mods/IRequiresApproachCircles.cs
Normal file
16
osu.Game.Rulesets.Osu/Mods/IRequiresApproachCircles.cs
Normal file
@ -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.
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
/// <summary>
|
||||
/// Marker interface for any mod which requires the approach circles to be visible.
|
||||
/// Used for incompatibility with <see cref="IHidesApproachCircles"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that this is only a marker interface for incompatibility purposes, it does not change any gameplay behaviour.
|
||||
/// </remarks>
|
||||
public interface IRequiresApproachCircles
|
||||
{
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModApproachDifferent : Mod, IApplicableToDrawableHitObject, IMutateApproachCircles
|
||||
public class OsuModApproachDifferent : Mod, IApplicableToDrawableHitObject, IRequiresApproachCircles
|
||||
{
|
||||
public override string Name => "Approach Different";
|
||||
public override string Acronym => "AD";
|
||||
@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override IconUsage? Icon { get; } = FontAwesome.Regular.Circle;
|
||||
|
||||
public override Type[] IncompatibleMods => new[] { typeof(IMutateApproachCircles) };
|
||||
public override Type[] IncompatibleMods => new[] { typeof(IHidesApproachCircles) };
|
||||
|
||||
[SettingSource("Initial size", "Change the initial size of the approach circle, relative to hit circles.", 0)]
|
||||
public BindableFloat Scale { get; } = new BindableFloat(4)
|
||||
|
@ -15,12 +15,12 @@ using osu.Game.Rulesets.Osu.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModHidden : ModHidden, IMutateApproachCircles
|
||||
public class OsuModHidden : ModHidden, IHidesApproachCircles
|
||||
{
|
||||
public override string Description => @"Play with no approach circles and fading circles/sliders.";
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
|
||||
public override Type[] IncompatibleMods => new[] { typeof(IMutateApproachCircles) };
|
||||
public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn) };
|
||||
|
||||
private const double fade_in_duration_multiplier = 0.4;
|
||||
private const double fade_out_duration_multiplier = 0.3;
|
||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
/// <summary>
|
||||
/// Adjusts the size of hit objects during their fade in animation.
|
||||
/// </summary>
|
||||
public abstract class OsuModObjectScaleTween : ModWithVisibilityAdjustment, IMutateApproachCircles
|
||||
public abstract class OsuModObjectScaleTween : ModWithVisibilityAdjustment, IHidesApproachCircles
|
||||
{
|
||||
public override ModType Type => ModType.Fun;
|
||||
|
||||
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
protected virtual float EndScale => 1;
|
||||
|
||||
public override Type[] IncompatibleMods => new[] { typeof(IMutateApproachCircles) };
|
||||
public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn) };
|
||||
|
||||
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||
{
|
||||
|
@ -12,7 +12,7 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModSpinIn : ModWithVisibilityAdjustment, IMutateApproachCircles
|
||||
public class OsuModSpinIn : ModWithVisibilityAdjustment, IHidesApproachCircles
|
||||
{
|
||||
public override string Name => "Spin In";
|
||||
public override string Acronym => "SI";
|
||||
@ -21,8 +21,9 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override string Description => "Circles spin in. No approach circles.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
// todo: this mod should be able to be compatible with hidden with a bit of further implementation.
|
||||
public override Type[] IncompatibleMods => new[] { typeof(IMutateApproachCircles) };
|
||||
// todo: this mod needs to be incompatible with "hidden" due to forcing the circle to remain opaque,
|
||||
// further implementation will be required for supporting that.
|
||||
public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModObjectScaleTween), typeof(OsuModHidden) };
|
||||
|
||||
private const int rotate_offset = 360;
|
||||
private const float rotate_starting_width = 2;
|
||||
|
@ -11,7 +11,7 @@ using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModTraceable : ModWithVisibilityAdjustment, IMutateApproachCircles
|
||||
public class OsuModTraceable : ModWithVisibilityAdjustment, IRequiresApproachCircles
|
||||
{
|
||||
public override string Name => "Traceable";
|
||||
public override string Acronym => "TC";
|
||||
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override string Description => "Put your faith in the approach circles...";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
public override Type[] IncompatibleMods => new[] { typeof(IMutateApproachCircles) };
|
||||
public override Type[] IncompatibleMods => new[] { typeof(IHidesApproachCircles) };
|
||||
|
||||
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||
{
|
||||
|
@ -4,7 +4,7 @@
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
|
@ -19,7 +19,9 @@ using osu.Game.Database;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Tests.Scores.IO;
|
||||
using osu.Game.Users;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Zip;
|
||||
@ -185,10 +187,58 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
}
|
||||
}
|
||||
|
||||
private string hashFile(string filename)
|
||||
[Test]
|
||||
public async Task TestImportThenImportWithChangedHashedFile()
|
||||
{
|
||||
using (var s = File.OpenRead(filename))
|
||||
return s.ComputeMD5Hash();
|
||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportBeatmapTest)))
|
||||
{
|
||||
try
|
||||
{
|
||||
var osu = LoadOsuIntoHost(host);
|
||||
|
||||
var temp = TestResources.GetTestBeatmapForImport();
|
||||
|
||||
string extractedFolder = $"{temp}_extracted";
|
||||
Directory.CreateDirectory(extractedFolder);
|
||||
|
||||
try
|
||||
{
|
||||
var imported = await LoadOszIntoOsu(osu);
|
||||
|
||||
await createScoreForBeatmap(osu, imported.Beatmaps.First());
|
||||
|
||||
using (var zip = ZipArchive.Open(temp))
|
||||
zip.WriteToDirectory(extractedFolder);
|
||||
|
||||
// arbitrary write to hashed file
|
||||
// this triggers the special BeatmapManager.PreImport deletion/replacement flow.
|
||||
using (var sw = new FileInfo(Directory.GetFiles(extractedFolder, "*.osu").First()).AppendText())
|
||||
await sw.WriteLineAsync("// changed");
|
||||
|
||||
using (var zip = ZipArchive.Create())
|
||||
{
|
||||
zip.AddAllFromDirectory(extractedFolder);
|
||||
zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate));
|
||||
}
|
||||
|
||||
var importedSecondTime = await osu.Dependencies.Get<BeatmapManager>().Import(new ImportTask(temp));
|
||||
|
||||
ensureLoaded(osu);
|
||||
|
||||
// check the newly "imported" beatmap is not the original.
|
||||
Assert.IsTrue(imported.ID != importedSecondTime.ID);
|
||||
Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Beatmaps.First().ID);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Directory.Delete(extractedFolder, true);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
host.Exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -894,7 +944,17 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
Assert.IsTrue(manager.QueryBeatmapSets(_ => true).First().DeletePending);
|
||||
}
|
||||
|
||||
private void checkBeatmapSetCount(OsuGameBase osu, int expected, bool includeDeletePending = false)
|
||||
private static Task createScoreForBeatmap(OsuGameBase osu, BeatmapInfo beatmap)
|
||||
{
|
||||
return ImportScoreTest.LoadScoreIntoOsu(osu, new ScoreInfo
|
||||
{
|
||||
OnlineScoreID = 2,
|
||||
Beatmap = beatmap,
|
||||
BeatmapInfoID = beatmap.ID
|
||||
}, new ImportScoreTest.TestArchiveReader());
|
||||
}
|
||||
|
||||
private static void checkBeatmapSetCount(OsuGameBase osu, int expected, bool includeDeletePending = false)
|
||||
{
|
||||
var manager = osu.Dependencies.Get<BeatmapManager>();
|
||||
|
||||
@ -903,12 +963,18 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
: manager.GetAllUsableBeatmapSets().Count);
|
||||
}
|
||||
|
||||
private void checkBeatmapCount(OsuGameBase osu, int expected)
|
||||
private static string hashFile(string filename)
|
||||
{
|
||||
using (var s = File.OpenRead(filename))
|
||||
return s.ComputeMD5Hash();
|
||||
}
|
||||
|
||||
private static void checkBeatmapCount(OsuGameBase osu, int expected)
|
||||
{
|
||||
Assert.AreEqual(expected, osu.Dependencies.Get<BeatmapManager>().QueryBeatmaps(_ => true).ToList().Count);
|
||||
}
|
||||
|
||||
private void checkSingleReferencedFileCount(OsuGameBase osu, int expected)
|
||||
private static void checkSingleReferencedFileCount(OsuGameBase osu, int expected)
|
||||
{
|
||||
Assert.AreEqual(expected, osu.Dependencies.Get<FileStore>().QueryFiles(f => f.ReferenceCount == 1).Count());
|
||||
}
|
||||
|
105
osu.Game.Tests/Chat/TestSceneChannelManager.cs
Normal file
105
osu.Game.Tests/Chat/TestSceneChannelManager.cs
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Chat
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class TestSceneChannelManager : OsuTestScene
|
||||
{
|
||||
private ChannelManager channelManager;
|
||||
private int currentMessageId;
|
||||
|
||||
[SetUp]
|
||||
public void Setup() => Schedule(() =>
|
||||
{
|
||||
var container = new ChannelManagerContainer();
|
||||
Child = container;
|
||||
channelManager = container.ChannelManager;
|
||||
});
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("register request handling", () =>
|
||||
{
|
||||
currentMessageId = 0;
|
||||
|
||||
((DummyAPIAccess)API).HandleRequest = req =>
|
||||
{
|
||||
switch (req)
|
||||
{
|
||||
case JoinChannelRequest joinChannel:
|
||||
joinChannel.TriggerSuccess();
|
||||
return true;
|
||||
|
||||
case PostMessageRequest postMessage:
|
||||
postMessage.TriggerSuccess(new Message(++currentMessageId)
|
||||
{
|
||||
IsAction = postMessage.Message.IsAction,
|
||||
ChannelId = postMessage.Message.ChannelId,
|
||||
Content = postMessage.Message.Content,
|
||||
Links = postMessage.Message.Links,
|
||||
Timestamp = postMessage.Message.Timestamp,
|
||||
Sender = postMessage.Message.Sender
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCommandsPostedToCorrectChannelWhenNotCurrent()
|
||||
{
|
||||
Channel channel1 = null;
|
||||
Channel channel2 = null;
|
||||
|
||||
AddStep("join 2 rooms", () =>
|
||||
{
|
||||
channelManager.JoinChannel(channel1 = createChannel(1, ChannelType.Public));
|
||||
channelManager.JoinChannel(channel2 = createChannel(2, ChannelType.Public));
|
||||
});
|
||||
|
||||
AddStep("select channel 1", () => channelManager.CurrentChannel.Value = channel1);
|
||||
|
||||
AddStep("post /me command to channel 2", () => channelManager.PostCommand("me dances", channel2));
|
||||
AddAssert("/me command received by channel 2", () => channel2.Messages.Last().Content == "dances");
|
||||
|
||||
AddStep("post /np command to channel 2", () => channelManager.PostCommand("np", channel2));
|
||||
AddAssert("/np command received by channel 2", () => channel2.Messages.Last().Content.Contains("is listening to"));
|
||||
}
|
||||
|
||||
private Channel createChannel(int id, ChannelType type) => new Channel(new User())
|
||||
{
|
||||
Id = id,
|
||||
Name = $"Channel {id}",
|
||||
Topic = $"Topic of channel {id} with type {type}",
|
||||
Type = type,
|
||||
};
|
||||
|
||||
private class ChannelManagerContainer : CompositeDrawable
|
||||
{
|
||||
[Cached]
|
||||
public ChannelManager ChannelManager { get; } = new ChannelManager();
|
||||
|
||||
public ChannelManagerContainer()
|
||||
{
|
||||
InternalChild = ChannelManager;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,8 @@ namespace osu.Game.Tests
|
||||
protected virtual TestOsuGameBase LoadOsuIntoHost(GameHost host, bool withBeatmap = false)
|
||||
{
|
||||
var osu = new TestOsuGameBase(withBeatmap);
|
||||
Task.Run(() => host.Run(osu));
|
||||
Task.Run(() => host.Run(osu))
|
||||
.ContinueWith(t => Assert.Fail($"Host threw exception {t.Exception}"), TaskContinuationOptions.OnlyOnFaulted);
|
||||
|
||||
waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time");
|
||||
|
||||
|
@ -7,6 +7,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Tests.Visual.Multiplayer;
|
||||
using osu.Game.Users;
|
||||
|
||||
@ -50,7 +51,10 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
|
||||
|
||||
AddStep("create room initially in gameplay", () =>
|
||||
{
|
||||
Room.RoomID.Value = null;
|
||||
var newRoom = new Room();
|
||||
newRoom.CopyFrom(SelectedRoom.Value);
|
||||
|
||||
newRoom.RoomID.Value = null;
|
||||
Client.RoomSetupAction = room =>
|
||||
{
|
||||
room.State = MultiplayerRoomState.Playing;
|
||||
@ -61,7 +65,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
|
||||
});
|
||||
};
|
||||
|
||||
RoomManager.CreateRoom(Room);
|
||||
RoomManager.CreateRoom(newRoom);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for room join", () => Client.Room != null);
|
||||
|
@ -31,32 +31,24 @@ namespace osu.Game.Tests.OnlinePlay
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMasterClockStartsWhenAllPlayerClocksHaveFrames()
|
||||
public void TestPlayerClocksStartWhenAllHaveFrames()
|
||||
{
|
||||
setWaiting(() => player1, false);
|
||||
assertMasterState(false);
|
||||
assertPlayerClockState(() => player1, false);
|
||||
assertPlayerClockState(() => player2, false);
|
||||
|
||||
setWaiting(() => player2, false);
|
||||
assertMasterState(true);
|
||||
assertPlayerClockState(() => player1, true);
|
||||
assertPlayerClockState(() => player2, true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMasterClockDoesNotStartWhenNoneReadyForMaximumDelayTime()
|
||||
{
|
||||
AddWaitStep($"wait {CatchUpSyncManager.MAXIMUM_START_DELAY} milliseconds", (int)Math.Ceiling(CatchUpSyncManager.MAXIMUM_START_DELAY / TimePerAction));
|
||||
assertMasterState(false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMasterClockStartsWhenAnyReadyForMaximumDelayTime()
|
||||
public void TestReadyPlayersStartWhenReadyForMaximumDelayTime()
|
||||
{
|
||||
setWaiting(() => player1, false);
|
||||
AddWaitStep($"wait {CatchUpSyncManager.MAXIMUM_START_DELAY} milliseconds", (int)Math.Ceiling(CatchUpSyncManager.MAXIMUM_START_DELAY / TimePerAction));
|
||||
assertMasterState(true);
|
||||
assertPlayerClockState(() => player1, true);
|
||||
assertPlayerClockState(() => player2, false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -153,9 +145,6 @@ namespace osu.Game.Tests.OnlinePlay
|
||||
private void setPlayerClockTime(Func<TestSpectatorPlayerClock> playerClock, double offsetFromMaster)
|
||||
=> AddStep($"set player clock {playerClock().Id} = master - {offsetFromMaster}", () => playerClock().Seek(master.CurrentTime - offsetFromMaster));
|
||||
|
||||
private void assertMasterState(bool running)
|
||||
=> AddAssert($"master clock {(running ? "is" : "is not")} running", () => master.IsRunning == running);
|
||||
|
||||
private void assertCatchingUp(Func<TestSpectatorPlayerClock> playerClock, bool catchingUp) =>
|
||||
AddAssert($"player clock {playerClock().Id} {(catchingUp ? "is" : "is not")} catching up", () => playerClock().IsCatchingUp == catchingUp);
|
||||
|
||||
@ -201,6 +190,11 @@ namespace osu.Game.Tests.OnlinePlay
|
||||
|
||||
private class TestManualClock : ManualClock, IAdjustableClock
|
||||
{
|
||||
public TestManualClock()
|
||||
{
|
||||
IsRunning = true;
|
||||
}
|
||||
|
||||
public void Start() => IsRunning = true;
|
||||
|
||||
public void Stop() => IsRunning = false;
|
||||
|
@ -2,14 +2,15 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.OpenGL.Textures;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Skinning;
|
||||
@ -18,14 +19,21 @@ using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tests.Rulesets
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class TestSceneRulesetSkinProvidingContainer : OsuTestScene
|
||||
{
|
||||
private SkinRequester requester;
|
||||
|
||||
protected override Ruleset CreateRuleset() => new TestSceneRulesetDependencies.TestRuleset();
|
||||
|
||||
[Cached(typeof(ISkinSource))]
|
||||
private readonly ISkinSource testSource = new TestSkinProvider();
|
||||
[Test]
|
||||
public void TestRulesetResources()
|
||||
{
|
||||
setupProviderStep();
|
||||
|
||||
AddAssert("ruleset texture retrieved via skin", () => requester.GetTexture("test-image") != null);
|
||||
AddAssert("ruleset sample retrieved via skin", () => requester.GetSample(new SampleInfo("test-sample")) != null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEarlyAddedSkinRequester()
|
||||
@ -38,7 +46,7 @@ namespace osu.Game.Tests.Rulesets
|
||||
|
||||
rulesetSkinProvider.Add(requester = new SkinRequester());
|
||||
|
||||
requester.OnLoadAsync += () => textureOnLoad = requester.GetTexture(TestSkinProvider.TEXTURE_NAME);
|
||||
requester.OnLoadAsync += () => textureOnLoad = requester.GetTexture("test-image");
|
||||
|
||||
Child = rulesetSkinProvider;
|
||||
});
|
||||
@ -46,6 +54,15 @@ namespace osu.Game.Tests.Rulesets
|
||||
AddAssert("requester got correct initial texture", () => textureOnLoad != null);
|
||||
}
|
||||
|
||||
private void setupProviderStep()
|
||||
{
|
||||
AddStep("setup provider", () =>
|
||||
{
|
||||
Child = new RulesetSkinProvidingContainer(Ruleset.Value.CreateInstance(), Beatmap.Value.Beatmap, Beatmap.Value.Skin)
|
||||
.WithChild(requester = new SkinRequester());
|
||||
});
|
||||
}
|
||||
|
||||
private class SkinRequester : Drawable, ISkin
|
||||
{
|
||||
private ISkinSource skin;
|
||||
@ -68,28 +85,5 @@ namespace osu.Game.Tests.Rulesets
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => skin.GetConfig<TLookup, TValue>(lookup);
|
||||
}
|
||||
|
||||
private class TestSkinProvider : ISkinSource
|
||||
{
|
||||
public const string TEXTURE_NAME = "some-texture";
|
||||
|
||||
public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotImplementedException();
|
||||
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => componentName == TEXTURE_NAME ? Texture.WhitePixel : null;
|
||||
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||
|
||||
public event Action SourceChanged
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => lookupFunction(this) ? this : null;
|
||||
|
||||
public IEnumerable<ISkin> AllSources => new[] { this };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Tests.Scores.IO
|
||||
OnlineScoreID = 12345,
|
||||
};
|
||||
|
||||
var imported = await loadScoreIntoOsu(osu, toImport);
|
||||
var imported = await LoadScoreIntoOsu(osu, toImport);
|
||||
|
||||
Assert.AreEqual(toImport.Rank, imported.Rank);
|
||||
Assert.AreEqual(toImport.TotalScore, imported.TotalScore);
|
||||
@ -75,7 +75,7 @@ namespace osu.Game.Tests.Scores.IO
|
||||
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
|
||||
};
|
||||
|
||||
var imported = await loadScoreIntoOsu(osu, toImport);
|
||||
var imported = await LoadScoreIntoOsu(osu, toImport);
|
||||
|
||||
Assert.IsTrue(imported.Mods.Any(m => m is OsuModHardRock));
|
||||
Assert.IsTrue(imported.Mods.Any(m => m is OsuModDoubleTime));
|
||||
@ -105,7 +105,7 @@ namespace osu.Game.Tests.Scores.IO
|
||||
}
|
||||
};
|
||||
|
||||
var imported = await loadScoreIntoOsu(osu, toImport);
|
||||
var imported = await LoadScoreIntoOsu(osu, toImport);
|
||||
|
||||
Assert.AreEqual(toImport.Statistics[HitResult.Perfect], imported.Statistics[HitResult.Perfect]);
|
||||
Assert.AreEqual(toImport.Statistics[HitResult.Miss], imported.Statistics[HitResult.Miss]);
|
||||
@ -136,7 +136,7 @@ namespace osu.Game.Tests.Scores.IO
|
||||
}
|
||||
};
|
||||
|
||||
var imported = await loadScoreIntoOsu(osu, toImport);
|
||||
var imported = await LoadScoreIntoOsu(osu, toImport);
|
||||
|
||||
var beatmapManager = osu.Dependencies.Get<BeatmapManager>();
|
||||
var scoreManager = osu.Dependencies.Get<ScoreManager>();
|
||||
@ -144,7 +144,7 @@ namespace osu.Game.Tests.Scores.IO
|
||||
beatmapManager.Delete(beatmapManager.QueryBeatmapSet(s => s.Beatmaps.Any(b => b.ID == imported.Beatmap.ID)));
|
||||
Assert.That(scoreManager.Query(s => s.ID == imported.ID).DeletePending, Is.EqualTo(true));
|
||||
|
||||
var secondImport = await loadScoreIntoOsu(osu, imported);
|
||||
var secondImport = await LoadScoreIntoOsu(osu, imported);
|
||||
Assert.That(secondImport, Is.Null);
|
||||
}
|
||||
finally
|
||||
@ -163,7 +163,7 @@ namespace osu.Game.Tests.Scores.IO
|
||||
{
|
||||
var osu = LoadOsuIntoHost(host, true);
|
||||
|
||||
await loadScoreIntoOsu(osu, new ScoreInfo { OnlineScoreID = 2 }, new TestArchiveReader());
|
||||
await LoadScoreIntoOsu(osu, new ScoreInfo { OnlineScoreID = 2 }, new TestArchiveReader());
|
||||
|
||||
var scoreManager = osu.Dependencies.Get<ScoreManager>();
|
||||
|
||||
@ -177,7 +177,7 @@ namespace osu.Game.Tests.Scores.IO
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ScoreInfo> loadScoreIntoOsu(OsuGameBase osu, ScoreInfo score, ArchiveReader archive = null)
|
||||
public static async Task<ScoreInfo> LoadScoreIntoOsu(OsuGameBase osu, ScoreInfo score, ArchiveReader archive = null)
|
||||
{
|
||||
var beatmapManager = osu.Dependencies.Get<BeatmapManager>();
|
||||
|
||||
@ -190,7 +190,7 @@ namespace osu.Game.Tests.Scores.IO
|
||||
return scoreManager.GetAllUsableScores().FirstOrDefault();
|
||||
}
|
||||
|
||||
private class TestArchiveReader : ArchiveReader
|
||||
internal class TestArchiveReader : ArchiveReader
|
||||
{
|
||||
public TestArchiveReader()
|
||||
: base("test_archive")
|
||||
|
@ -2,8 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
@ -41,8 +39,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Resolved]
|
||||
private OsuGameBase game { get; set; }
|
||||
|
||||
private int nextFrame;
|
||||
|
||||
private BeatmapSetInfo importedBeatmap;
|
||||
|
||||
private int importedBeatmapId;
|
||||
@ -51,8 +47,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("reset sent frames", () => nextFrame = 0);
|
||||
|
||||
AddStep("import beatmap", () =>
|
||||
{
|
||||
importedBeatmap = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Result;
|
||||
@ -105,7 +99,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
waitForPlayer();
|
||||
checkPaused(true);
|
||||
|
||||
sendFrames(1000); // send enough frames to ensure play won't be paused
|
||||
// send enough frames to ensure play won't be paused
|
||||
sendFrames(100);
|
||||
|
||||
checkPaused(false);
|
||||
}
|
||||
@ -114,12 +109,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public void TestSpectatingDuringGameplay()
|
||||
{
|
||||
start();
|
||||
sendFrames(300);
|
||||
|
||||
loadSpectatingScreen();
|
||||
waitForPlayer();
|
||||
|
||||
AddStep("advance frame count", () => nextFrame = 300);
|
||||
sendFrames();
|
||||
sendFrames(300);
|
||||
|
||||
AddUntilStep("playing from correct point in time", () => player.ChildrenOfType<DrawableRuleset>().First().FrameStableClock.CurrentTime > 30000);
|
||||
}
|
||||
@ -220,11 +215,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private void sendFrames(int count = 10)
|
||||
{
|
||||
AddStep("send frames", () =>
|
||||
{
|
||||
testSpectatorClient.SendFrames(streamingUser.Id, nextFrame, count);
|
||||
nextFrame += count;
|
||||
});
|
||||
AddStep("send frames", () => testSpectatorClient.SendFrames(streamingUser.Id, count));
|
||||
}
|
||||
|
||||
private void loadSpectatingScreen()
|
||||
@ -232,14 +223,5 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("load screen", () => LoadScreen(spectatorScreen = new SoloSpectator(streamingUser)));
|
||||
AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded);
|
||||
}
|
||||
|
||||
internal class TestUserLookupCache : UserLookupCache
|
||||
{
|
||||
protected override Task<User> ComputeValueAsync(int lookup, CancellationToken token = default) => Task.FromResult(new User
|
||||
{
|
||||
Id = lookup,
|
||||
Username = $"User {lookup}"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,61 +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 System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public abstract class RoomManagerTestScene : RoomTestScene
|
||||
{
|
||||
[Cached(Type = typeof(IRoomManager))]
|
||||
protected TestRoomManager RoomManager { get; } = new TestRoomManager();
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("clear rooms", () => RoomManager.Rooms.Clear());
|
||||
}
|
||||
|
||||
protected void AddRooms(int count, RulesetInfo ruleset = null)
|
||||
{
|
||||
AddStep("add rooms", () =>
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var room = new Room
|
||||
{
|
||||
RoomID = { Value = i },
|
||||
Name = { Value = $"Room {i}" },
|
||||
Host = { Value = new User { Username = "Host" } },
|
||||
EndDate = { Value = DateTimeOffset.Now + TimeSpan.FromSeconds(10) },
|
||||
Category = { Value = i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal }
|
||||
};
|
||||
|
||||
if (ruleset != null)
|
||||
{
|
||||
room.Playlist.Add(new PlaylistItem
|
||||
{
|
||||
Ruleset = { Value = ruleset },
|
||||
Beatmap =
|
||||
{
|
||||
Value = new BeatmapInfo
|
||||
{
|
||||
Metadata = new BeatmapMetadata()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
RoomManager.Rooms.Add(room);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +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 System;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestRoomManager : IRoomManager
|
||||
{
|
||||
public event Action RoomsUpdated
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public readonly BindableList<Room> Rooms = new BindableList<Room>();
|
||||
|
||||
public IBindable<bool> InitialRoomsReceived { get; } = new Bindable<bool>(true);
|
||||
|
||||
IBindableList<Room> IRoomManager.Rooms => Rooms;
|
||||
|
||||
public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null) => Rooms.Add(room);
|
||||
|
||||
public void JoinRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
|
||||
{
|
||||
}
|
||||
|
||||
public void PartRoom()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -4,17 +4,21 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Online.Rooms.RoomStatuses;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneLoungeRoomInfo : RoomTestScene
|
||||
public class TestSceneLoungeRoomInfo : OnlinePlayTestScene
|
||||
{
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
SelectedRoom.Value = new Room();
|
||||
|
||||
Child = new RoomInfo
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
@ -23,15 +27,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
};
|
||||
});
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
// Todo: Temp
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNonSelectedRoom()
|
||||
{
|
||||
AddStep("set null room", () => Room.RoomID.Value = null);
|
||||
AddStep("set null room", () => SelectedRoom.Value.RoomID.Value = null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -39,11 +38,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("set open room", () =>
|
||||
{
|
||||
Room.RoomID.Value = 0;
|
||||
Room.Name.Value = "Room 0";
|
||||
Room.Host.Value = new User { Username = "peppy", Id = 2 };
|
||||
Room.EndDate.Value = DateTimeOffset.Now.AddMonths(1);
|
||||
Room.Status.Value = new RoomStatusOpen();
|
||||
SelectedRoom.Value.RoomID.Value = 0;
|
||||
SelectedRoom.Value.Name.Value = "Room 0";
|
||||
SelectedRoom.Value.Host.Value = new User { Username = "peppy", Id = 2 };
|
||||
SelectedRoom.Value.EndDate.Value = DateTimeOffset.Now.AddMonths(1);
|
||||
SelectedRoom.Value.Status.Value = new RoomStatusOpen();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -3,24 +3,26 @@
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osuTK.Graphics;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneLoungeRoomsContainer : RoomManagerTestScene
|
||||
public class TestSceneLoungeRoomsContainer : OnlinePlayTestScene
|
||||
{
|
||||
protected new BasicTestRoomManager RoomManager => (BasicTestRoomManager)base.RoomManager;
|
||||
|
||||
private RoomsContainer container;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
Child = container = new RoomsContainer
|
||||
{
|
||||
@ -29,12 +31,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
Width = 0.5f,
|
||||
JoinRequested = joinRequested
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestBasicListChanges()
|
||||
{
|
||||
AddRooms(3);
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(3));
|
||||
|
||||
AddAssert("has 3 rooms", () => container.Rooms.Count == 3);
|
||||
AddStep("remove first room", () => RoomManager.Rooms.Remove(RoomManager.Rooms.FirstOrDefault()));
|
||||
@ -51,7 +53,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestKeyboardNavigation()
|
||||
{
|
||||
AddRooms(3);
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(3));
|
||||
|
||||
AddAssert("no selection", () => checkRoomSelected(null));
|
||||
|
||||
@ -72,7 +74,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestClickDeselection()
|
||||
{
|
||||
AddRooms(1);
|
||||
AddStep("add room", () => RoomManager.AddRooms(1));
|
||||
|
||||
AddAssert("no selection", () => checkRoomSelected(null));
|
||||
|
||||
@ -91,7 +93,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestStringFiltering()
|
||||
{
|
||||
AddRooms(4);
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(4));
|
||||
|
||||
AddUntilStep("4 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 4);
|
||||
|
||||
@ -107,21 +109,21 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestRulesetFiltering()
|
||||
{
|
||||
AddRooms(2, new OsuRuleset().RulesetInfo);
|
||||
AddRooms(3, new CatchRuleset().RulesetInfo);
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(2, new OsuRuleset().RulesetInfo));
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(3, new CatchRuleset().RulesetInfo));
|
||||
|
||||
// Todo: What even is this case...?
|
||||
AddStep("set empty filter criteria", () => container.Filter(null));
|
||||
AddUntilStep("5 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 5);
|
||||
|
||||
AddStep("filter osu! rooms", () => container.Filter(new FilterCriteria { Ruleset = new OsuRuleset().RulesetInfo }));
|
||||
|
||||
AddUntilStep("2 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 2);
|
||||
|
||||
AddStep("filter catch rooms", () => container.Filter(new FilterCriteria { Ruleset = new CatchRuleset().RulesetInfo }));
|
||||
|
||||
AddUntilStep("3 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 3);
|
||||
}
|
||||
|
||||
private bool checkRoomSelected(Room room) => Room == room;
|
||||
private bool checkRoomSelected(Room room) => SelectedRoom.Value == room;
|
||||
|
||||
private void joinRequested(Room room) => room.Status.Value = new JoinedRoomStatus();
|
||||
|
||||
|
@ -11,11 +11,12 @@ using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMatchBeatmapDetailArea : RoomTestScene
|
||||
public class TestSceneMatchBeatmapDetailArea : OnlinePlayTestScene
|
||||
{
|
||||
[Resolved]
|
||||
private BeatmapManager beatmapManager { get; set; }
|
||||
@ -26,6 +27,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
SelectedRoom.Value = new Room();
|
||||
|
||||
Child = new MatchBeatmapDetailArea
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
@ -37,9 +40,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private void createNewItem()
|
||||
{
|
||||
Room.Playlist.Add(new PlaylistItem
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem
|
||||
{
|
||||
ID = Room.Playlist.Count,
|
||||
ID = SelectedRoom.Value.Playlist.Count,
|
||||
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
|
||||
Ruleset = { Value = new OsuRuleset().RulesetInfo },
|
||||
RequiredMods =
|
||||
|
@ -7,46 +7,49 @@ using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.OnlinePlay.Match.Components;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMatchHeader : RoomTestScene
|
||||
public class TestSceneMatchHeader : OnlinePlayTestScene
|
||||
{
|
||||
public TestSceneMatchHeader()
|
||||
{
|
||||
Child = new Header();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
Room.Playlist.Add(new PlaylistItem
|
||||
SelectedRoom.Value = new Room
|
||||
{
|
||||
Beatmap =
|
||||
Name = { Value = "A very awesome room" },
|
||||
Host = { Value = new User { Id = 2, Username = "peppy" } },
|
||||
Playlist =
|
||||
{
|
||||
Value = new BeatmapInfo
|
||||
new PlaylistItem
|
||||
{
|
||||
Metadata = new BeatmapMetadata
|
||||
Beatmap =
|
||||
{
|
||||
Title = "Title",
|
||||
Artist = "Artist",
|
||||
AuthorString = "Author",
|
||||
Value = new BeatmapInfo
|
||||
{
|
||||
Metadata = new BeatmapMetadata
|
||||
{
|
||||
Title = "Title",
|
||||
Artist = "Artist",
|
||||
AuthorString = "Author",
|
||||
},
|
||||
Version = "Version",
|
||||
Ruleset = new OsuRuleset().RulesetInfo
|
||||
}
|
||||
},
|
||||
Version = "Version",
|
||||
Ruleset = new OsuRuleset().RulesetInfo
|
||||
RequiredMods =
|
||||
{
|
||||
new OsuModDoubleTime(),
|
||||
new OsuModNoFail(),
|
||||
new OsuModRelax(),
|
||||
}
|
||||
}
|
||||
},
|
||||
RequiredMods =
|
||||
{
|
||||
new OsuModDoubleTime(),
|
||||
new OsuModNoFail(),
|
||||
new OsuModRelax(),
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Room.Name.Value = "A very awesome room";
|
||||
Room.Host.Value = new User { Id = 2, Username = "peppy" };
|
||||
Child = new Header();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2,72 +2,74 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
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.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay.Match.Components;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMatchLeaderboard : RoomTestScene
|
||||
public class TestSceneMatchLeaderboard : OnlinePlayTestScene
|
||||
{
|
||||
protected override bool UseOnlineAPI => true;
|
||||
|
||||
public TestSceneMatchLeaderboard()
|
||||
{
|
||||
Add(new MatchLeaderboard
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Size = new Vector2(550f, 450f),
|
||||
Scope = MatchLeaderboardScope.Overall,
|
||||
});
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IAPIProvider api)
|
||||
private void load()
|
||||
{
|
||||
var req = new GetRoomScoresRequest();
|
||||
req.Success += v => { };
|
||||
req.Failure += _ => { };
|
||||
((DummyAPIAccess)API).HandleRequest = r =>
|
||||
{
|
||||
switch (r)
|
||||
{
|
||||
case GetRoomLeaderboardRequest leaderboardRequest:
|
||||
leaderboardRequest.TriggerSuccess(new APILeaderboard
|
||||
{
|
||||
Leaderboard = new List<APIUserScoreAggregate>
|
||||
{
|
||||
new APIUserScoreAggregate
|
||||
{
|
||||
UserID = 2,
|
||||
User = new User { Id = 2, Username = "peppy" },
|
||||
TotalScore = 995533,
|
||||
RoomID = 3,
|
||||
CompletedBeatmaps = 1,
|
||||
TotalAttempts = 6,
|
||||
Accuracy = 0.9851
|
||||
},
|
||||
new APIUserScoreAggregate
|
||||
{
|
||||
UserID = 1040328,
|
||||
User = new User { Id = 1040328, Username = "smoogipoo" },
|
||||
TotalScore = 981100,
|
||||
RoomID = 3,
|
||||
CompletedBeatmaps = 1,
|
||||
TotalAttempts = 9,
|
||||
Accuracy = 0.937
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
api.Queue(req);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
Room.RoomID.Value = 3;
|
||||
SelectedRoom.Value = new Room { RoomID = { Value = 3 } };
|
||||
|
||||
Child = new MatchLeaderboard
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Size = new Vector2(550f, 450f),
|
||||
Scope = MatchLeaderboardScope.Overall,
|
||||
};
|
||||
});
|
||||
|
||||
private class GetRoomScoresRequest : APIRequest<List<RoomScore>>
|
||||
{
|
||||
protected override string Target => "rooms/3/leaderboard";
|
||||
}
|
||||
|
||||
private class RoomScore
|
||||
{
|
||||
[JsonProperty("user")]
|
||||
public User User { get; set; }
|
||||
|
||||
[JsonProperty("accuracy")]
|
||||
public double Accuracy { get; set; }
|
||||
|
||||
[JsonProperty("total_score")]
|
||||
public int TotalScore { get; set; }
|
||||
|
||||
[JsonProperty("pp")]
|
||||
public double PP { get; set; }
|
||||
|
||||
[JsonProperty("attempts")]
|
||||
public int TotalAttempts { get; set; }
|
||||
|
||||
[JsonProperty("completed")]
|
||||
public int CompletedAttempts { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,74 +3,40 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Online.Spectator;
|
||||
using osu.Game.Rulesets.Osu.Scoring;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Tests.Visual.Spectator;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiSpectatorLeaderboard : MultiplayerTestScene
|
||||
{
|
||||
[Cached(typeof(SpectatorClient))]
|
||||
private TestSpectatorClient spectatorClient = new TestSpectatorClient();
|
||||
|
||||
[Cached(typeof(UserLookupCache))]
|
||||
private UserLookupCache lookupCache = new TestUserLookupCache();
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
private readonly Container content;
|
||||
|
||||
private readonly Dictionary<int, ManualClock> clocks = new Dictionary<int, ManualClock>
|
||||
{
|
||||
{ PLAYER_1_ID, new ManualClock() },
|
||||
{ PLAYER_2_ID, new ManualClock() }
|
||||
};
|
||||
|
||||
public TestSceneMultiSpectatorLeaderboard()
|
||||
{
|
||||
base.Content.AddRange(new Drawable[]
|
||||
{
|
||||
spectatorClient,
|
||||
lookupCache,
|
||||
content = new Container { RelativeSizeAxes = Axes.Both }
|
||||
});
|
||||
}
|
||||
private Dictionary<int, ManualClock> clocks;
|
||||
private MultiSpectatorLeaderboard leaderboard;
|
||||
|
||||
[SetUpSteps]
|
||||
public new void SetUpSteps()
|
||||
{
|
||||
MultiSpectatorLeaderboard leaderboard = null;
|
||||
|
||||
AddStep("reset", () =>
|
||||
{
|
||||
Clear();
|
||||
|
||||
foreach (var (userId, clock) in clocks)
|
||||
clocks = new Dictionary<int, ManualClock>
|
||||
{
|
||||
spectatorClient.EndPlay(userId);
|
||||
clock.CurrentTime = 0;
|
||||
}
|
||||
{ PLAYER_1_ID, new ManualClock() },
|
||||
{ PLAYER_2_ID, new ManualClock() }
|
||||
};
|
||||
|
||||
foreach (var (userId, _) in clocks)
|
||||
SpectatorClient.StartPlay(userId, 0);
|
||||
});
|
||||
|
||||
AddStep("create leaderboard", () =>
|
||||
{
|
||||
foreach (var (userId, _) in clocks)
|
||||
spectatorClient.StartPlay(userId, 0);
|
||||
|
||||
Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value);
|
||||
|
||||
var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value);
|
||||
var scoreProcessor = new OsuScoreProcessor();
|
||||
scoreProcessor.ApplyBeatmap(playable);
|
||||
@ -96,10 +62,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
// For player 2, send frames in sets of 10.
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
spectatorClient.SendFrames(PLAYER_1_ID, i, 1);
|
||||
SpectatorClient.SendFrames(PLAYER_1_ID, 1);
|
||||
|
||||
if (i % 10 == 0)
|
||||
spectatorClient.SendFrames(PLAYER_2_ID, i, 10);
|
||||
SpectatorClient.SendFrames(PLAYER_2_ID, 10);
|
||||
}
|
||||
});
|
||||
|
||||
@ -145,17 +111,5 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private void assertCombo(int userId, int expectedCombo)
|
||||
=> AddUntilStep($"player {userId} has {expectedCombo} combo", () => this.ChildrenOfType<GameplayLeaderboardScore>().Single(s => s.User?.Id == userId).Combo.Value == expectedCombo);
|
||||
|
||||
private class TestUserLookupCache : UserLookupCache
|
||||
{
|
||||
protected override Task<User> ComputeValueAsync(int lookup, CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(new User
|
||||
{
|
||||
Id = lookup,
|
||||
Username = $"User {lookup}"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,31 +3,20 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Online.Spectator;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Tests.Beatmaps.IO;
|
||||
using osu.Game.Tests.Visual.Spectator;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiSpectatorScreen : MultiplayerTestScene
|
||||
{
|
||||
[Cached(typeof(SpectatorClient))]
|
||||
private TestSpectatorClient spectatorClient = new TestSpectatorClient();
|
||||
|
||||
[Cached(typeof(UserLookupCache))]
|
||||
private UserLookupCache lookupCache = new TestUserLookupCache();
|
||||
|
||||
[Resolved]
|
||||
private OsuGameBase game { get; set; }
|
||||
|
||||
@ -37,7 +26,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
private MultiSpectatorScreen spectatorScreen;
|
||||
|
||||
private readonly List<int> playingUserIds = new List<int>();
|
||||
private readonly Dictionary<int, int> nextFrame = new Dictionary<int, int>();
|
||||
|
||||
private BeatmapSetInfo importedSet;
|
||||
private BeatmapInfo importedBeatmap;
|
||||
@ -51,25 +39,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
importedBeatmapId = importedBeatmap.OnlineBeatmapID ?? -1;
|
||||
}
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("reset sent frames", () => nextFrame.Clear());
|
||||
|
||||
AddStep("add streaming client", () =>
|
||||
{
|
||||
Remove(spectatorClient);
|
||||
Add(spectatorClient);
|
||||
});
|
||||
|
||||
AddStep("finish previous gameplay", () =>
|
||||
{
|
||||
foreach (var id in playingUserIds)
|
||||
spectatorClient.EndPlay(id);
|
||||
playingUserIds.Clear();
|
||||
});
|
||||
}
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() => playingUserIds.Clear());
|
||||
|
||||
[Test]
|
||||
public void TestDelayedStart()
|
||||
@ -80,18 +51,16 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
Client.CurrentMatchPlayingUserIds.Add(PLAYER_2_ID);
|
||||
playingUserIds.Add(PLAYER_1_ID);
|
||||
playingUserIds.Add(PLAYER_2_ID);
|
||||
nextFrame[PLAYER_1_ID] = 0;
|
||||
nextFrame[PLAYER_2_ID] = 0;
|
||||
});
|
||||
|
||||
loadSpectateScreen(false);
|
||||
|
||||
AddWaitStep("wait a bit", 10);
|
||||
AddStep("load player first_player_id", () => spectatorClient.StartPlay(PLAYER_1_ID, importedBeatmapId));
|
||||
AddStep("load player first_player_id", () => SpectatorClient.StartPlay(PLAYER_1_ID, importedBeatmapId));
|
||||
AddUntilStep("one player added", () => spectatorScreen.ChildrenOfType<Player>().Count() == 1);
|
||||
|
||||
AddWaitStep("wait a bit", 10);
|
||||
AddStep("load player second_player_id", () => spectatorClient.StartPlay(PLAYER_2_ID, importedBeatmapId));
|
||||
AddStep("load player second_player_id", () => SpectatorClient.StartPlay(PLAYER_2_ID, importedBeatmapId));
|
||||
AddUntilStep("two players added", () => spectatorScreen.ChildrenOfType<Player>().Count() == 2);
|
||||
}
|
||||
|
||||
@ -107,6 +76,23 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddWaitStep("wait a bit", 20);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTimeDoesNotProgressWhileAllPlayersPaused()
|
||||
{
|
||||
start(new[] { PLAYER_1_ID, PLAYER_2_ID });
|
||||
loadSpectateScreen();
|
||||
|
||||
sendFrames(PLAYER_1_ID, 40);
|
||||
sendFrames(PLAYER_2_ID, 20);
|
||||
|
||||
checkPaused(PLAYER_2_ID, true);
|
||||
checkPausedInstant(PLAYER_1_ID, false);
|
||||
AddAssert("master clock still running", () => this.ChildrenOfType<MasterGameplayClockContainer>().Single().IsRunning);
|
||||
|
||||
checkPaused(PLAYER_1_ID, true);
|
||||
AddUntilStep("master clock paused", () => !this.ChildrenOfType<MasterGameplayClockContainer>().Single().IsRunning);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPlayersMustStartSimultaneously()
|
||||
{
|
||||
@ -182,7 +168,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
// Send initial frames for both players. A few more for player 1.
|
||||
sendFrames(PLAYER_1_ID, 1000);
|
||||
sendFrames(PLAYER_2_ID, 10);
|
||||
sendFrames(PLAYER_2_ID, 30);
|
||||
checkPausedInstant(PLAYER_1_ID, false);
|
||||
checkPausedInstant(PLAYER_2_ID, false);
|
||||
|
||||
@ -210,8 +196,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
sendFrames(PLAYER_1_ID, 10);
|
||||
sendFrames(PLAYER_2_ID, 20);
|
||||
assertMuted(PLAYER_1_ID, false);
|
||||
assertMuted(PLAYER_2_ID, true);
|
||||
checkPaused(PLAYER_1_ID, false);
|
||||
assertOneNotMuted();
|
||||
|
||||
checkPaused(PLAYER_1_ID, true);
|
||||
assertMuted(PLAYER_1_ID, true);
|
||||
@ -229,6 +215,36 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
assertMuted(PLAYER_2_ID, true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSpectatingDuringGameplay()
|
||||
{
|
||||
var players = new[] { PLAYER_1_ID, PLAYER_2_ID };
|
||||
|
||||
start(players);
|
||||
sendFrames(players, 300);
|
||||
|
||||
loadSpectateScreen();
|
||||
sendFrames(players, 300);
|
||||
|
||||
AddUntilStep("playing from correct point in time", () => this.ChildrenOfType<DrawableRuleset>().All(r => r.FrameStableClock.CurrentTime > 30000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSpectatingDuringGameplayWithLateFrames()
|
||||
{
|
||||
start(new[] { PLAYER_1_ID, PLAYER_2_ID });
|
||||
sendFrames(new[] { PLAYER_1_ID, PLAYER_2_ID }, 300);
|
||||
|
||||
loadSpectateScreen();
|
||||
sendFrames(PLAYER_1_ID, 300);
|
||||
|
||||
AddWaitStep("wait maximum start delay seconds", (int)(CatchUpSyncManager.MAXIMUM_START_DELAY / TimePerAction));
|
||||
checkPaused(PLAYER_1_ID, false);
|
||||
|
||||
sendFrames(PLAYER_2_ID, 300);
|
||||
AddUntilStep("player 2 playing from correct point in time", () => getPlayer(PLAYER_2_ID).ChildrenOfType<DrawableRuleset>().Single().FrameStableClock.CurrentTime > 30000);
|
||||
}
|
||||
|
||||
private void loadSpectateScreen(bool waitForPlayerLoad = true)
|
||||
{
|
||||
AddStep("load screen", () =>
|
||||
@ -242,8 +258,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded && (!waitForPlayerLoad || spectatorScreen.AllPlayersLoaded));
|
||||
}
|
||||
|
||||
private void start(int userId, int? beatmapId = null) => start(new[] { userId }, beatmapId);
|
||||
|
||||
private void start(int[] userIds, int? beatmapId = null)
|
||||
{
|
||||
AddStep("start play", () =>
|
||||
@ -251,23 +265,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
foreach (int id in userIds)
|
||||
{
|
||||
Client.CurrentMatchPlayingUserIds.Add(id);
|
||||
spectatorClient.StartPlay(id, beatmapId ?? importedBeatmapId);
|
||||
SpectatorClient.StartPlay(id, beatmapId ?? importedBeatmapId);
|
||||
playingUserIds.Add(id);
|
||||
nextFrame[id] = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void finish(int userId)
|
||||
{
|
||||
AddStep("end play", () =>
|
||||
{
|
||||
spectatorClient.EndPlay(userId);
|
||||
playingUserIds.Remove(userId);
|
||||
nextFrame.Remove(userId);
|
||||
});
|
||||
}
|
||||
|
||||
private void sendFrames(int userId, int count = 10) => sendFrames(new[] { userId }, count);
|
||||
|
||||
private void sendFrames(int[] userIds, int count = 10)
|
||||
@ -275,10 +278,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("send frames", () =>
|
||||
{
|
||||
foreach (int id in userIds)
|
||||
{
|
||||
spectatorClient.SendFrames(id, nextFrame[id], count);
|
||||
nextFrame[id] += count;
|
||||
}
|
||||
SpectatorClient.SendFrames(id, count);
|
||||
});
|
||||
}
|
||||
|
||||
@ -286,7 +286,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
=> AddUntilStep($"{userId} is {(state ? "paused" : "playing")}", () => getPlayer(userId).ChildrenOfType<GameplayClockContainer>().First().GameplayClock.IsRunning != state);
|
||||
|
||||
private void checkPausedInstant(int userId, bool state)
|
||||
=> AddAssert($"{userId} is {(state ? "paused" : "playing")}", () => getPlayer(userId).ChildrenOfType<GameplayClockContainer>().First().GameplayClock.IsRunning != state);
|
||||
{
|
||||
checkPaused(userId, state);
|
||||
|
||||
// Todo: The following should work, but is broken because SpectatorScreen retrieves the WorkingBeatmap via the BeatmapManager, bypassing the test scene clock and running real-time.
|
||||
// AddAssert($"{userId} is {(state ? "paused" : "playing")}", () => getPlayer(userId).ChildrenOfType<GameplayClockContainer>().First().GameplayClock.IsRunning != state);
|
||||
}
|
||||
|
||||
private void assertOneNotMuted() => AddAssert("one player not muted", () => spectatorScreen.ChildrenOfType<PlayerArea>().Count(p => !p.Mute) == 1);
|
||||
|
||||
private void assertMuted(int userId, bool muted)
|
||||
=> AddAssert($"{userId} {(muted ? "is" : "is not")} muted", () => getInstance(userId).Mute == muted);
|
||||
@ -297,17 +304,5 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
private Player getPlayer(int userId) => getInstance(userId).ChildrenOfType<Player>().Single();
|
||||
|
||||
private PlayerArea getInstance(int userId) => spectatorScreen.ChildrenOfType<PlayerArea>().Single(p => p.UserId == userId);
|
||||
|
||||
internal class TestUserLookupCache : UserLookupCache
|
||||
{
|
||||
protected override Task<User> ComputeValueAsync(int lookup, CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(new User
|
||||
{
|
||||
Id = lookup,
|
||||
Username = $"User {lookup}"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Match.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
@ -30,14 +31,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiplayer : ScreenTestScene
|
||||
{
|
||||
private TestMultiplayer multiplayerScreen;
|
||||
|
||||
private BeatmapManager beatmaps;
|
||||
private RulesetStore rulesets;
|
||||
private BeatmapSetInfo importedSet;
|
||||
|
||||
private TestMultiplayerClient client => multiplayerScreen.Client;
|
||||
private Room room => client.APIRoom;
|
||||
private DependenciesScreen dependenciesScreen;
|
||||
private TestMultiplayer multiplayerScreen;
|
||||
private TestMultiplayerClient client;
|
||||
|
||||
public TestSceneMultiplayer()
|
||||
{
|
||||
@ -229,30 +229,43 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private void loadMultiplayer()
|
||||
{
|
||||
AddStep("show", () =>
|
||||
AddStep("create multiplayer screen", () => multiplayerScreen = new TestMultiplayer());
|
||||
|
||||
AddStep("load dependencies", () =>
|
||||
{
|
||||
multiplayerScreen = new TestMultiplayer();
|
||||
client = new TestMultiplayerClient(multiplayerScreen.RoomManager);
|
||||
|
||||
// Needs to be added at a higher level since the multiplayer screen becomes non-current.
|
||||
Child = multiplayerScreen.Client;
|
||||
// The screen gets suspended so it stops receiving updates.
|
||||
Child = client;
|
||||
|
||||
LoadScreen(multiplayerScreen);
|
||||
LoadScreen(dependenciesScreen = new DependenciesScreen(client));
|
||||
});
|
||||
|
||||
AddUntilStep("wait for loaded", () => multiplayerScreen.IsLoaded);
|
||||
AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded);
|
||||
|
||||
AddStep("load multiplayer", () => LoadScreen(multiplayerScreen));
|
||||
AddUntilStep("wait for multiplayer to load", () => multiplayerScreen.IsLoaded);
|
||||
}
|
||||
|
||||
private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer
|
||||
/// <summary>
|
||||
/// Used for the sole purpose of adding <see cref="TestMultiplayerClient"/> as a resolvable dependency.
|
||||
/// </summary>
|
||||
private class DependenciesScreen : OsuScreen
|
||||
{
|
||||
[Cached(typeof(MultiplayerClient))]
|
||||
public readonly TestMultiplayerClient Client;
|
||||
|
||||
public TestMultiplayer()
|
||||
public DependenciesScreen(TestMultiplayerClient client)
|
||||
{
|
||||
Client = new TestMultiplayerClient((TestMultiplayerRoomManager)RoomManager);
|
||||
Client = client;
|
||||
}
|
||||
}
|
||||
|
||||
protected override RoomManager CreateRoomManager() => new TestMultiplayerRoomManager();
|
||||
private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer
|
||||
{
|
||||
public new TestMultiplayerRoomManager RoomManager { get; private set; }
|
||||
|
||||
protected override RoomManager CreateRoomManager() => RoomManager = new TestMultiplayerRoomManager();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,12 +6,11 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Spectator;
|
||||
using osu.Game.Replays.Legacy;
|
||||
@ -19,37 +18,20 @@ using osu.Game.Rulesets.Osu.Scoring;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Tests.Visual.Online;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osu.Game.Tests.Visual.Spectator;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiplayerGameplayLeaderboard : MultiplayerTestScene
|
||||
{
|
||||
private const int users = 16;
|
||||
private static IEnumerable<int> users => Enumerable.Range(0, 16);
|
||||
|
||||
[Cached(typeof(SpectatorClient))]
|
||||
private TestMultiplayerSpectatorClient spectatorClient = new TestMultiplayerSpectatorClient();
|
||||
|
||||
[Cached(typeof(UserLookupCache))]
|
||||
private UserLookupCache lookupCache = new TestSceneCurrentlyPlayingDisplay.TestUserLookupCache();
|
||||
public new TestMultiplayerSpectatorClient SpectatorClient => (TestMultiplayerSpectatorClient)OnlinePlayDependencies?.SpectatorClient;
|
||||
|
||||
private MultiplayerGameplayLeaderboard leaderboard;
|
||||
|
||||
protected override Container<Drawable> Content { get; } = new Container { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
private OsuConfigManager config;
|
||||
|
||||
public TestSceneMultiplayerGameplayLeaderboard()
|
||||
{
|
||||
base.Content.Children = new Drawable[]
|
||||
{
|
||||
spectatorClient,
|
||||
lookupCache,
|
||||
Content
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
@ -59,7 +41,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[SetUpSteps]
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = lookupCache.GetUserAsync(1).Result);
|
||||
AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = LookupCache.GetUserAsync(1).Result);
|
||||
|
||||
AddStep("create leaderboard", () =>
|
||||
{
|
||||
@ -70,14 +52,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value);
|
||||
|
||||
for (int i = 0; i < users; i++)
|
||||
spectatorClient.StartPlay(i, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0);
|
||||
foreach (var user in users)
|
||||
SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0);
|
||||
|
||||
spectatorClient.Schedule(() =>
|
||||
{
|
||||
Client.CurrentMatchPlayingUserIds.Clear();
|
||||
Client.CurrentMatchPlayingUserIds.AddRange(spectatorClient.PlayingUsers);
|
||||
});
|
||||
// Todo: This is REALLY bad.
|
||||
Client.CurrentMatchPlayingUserIds.AddRange(users);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -86,7 +65,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
scoreProcessor.ApplyBeatmap(playable);
|
||||
|
||||
LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, spectatorClient.PlayingUsers.ToArray())
|
||||
LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, users.ToArray())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@ -100,24 +79,32 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestScoreUpdates()
|
||||
{
|
||||
AddRepeatStep("update state", () => spectatorClient.RandomlyUpdateState(), 100);
|
||||
AddRepeatStep("update state", () => SpectatorClient.RandomlyUpdateState(), 100);
|
||||
AddToggleStep("switch compact mode", expanded => leaderboard.Expanded.Value = expanded);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUserQuit()
|
||||
{
|
||||
AddRepeatStep("mark user quit", () => Client.CurrentMatchPlayingUserIds.RemoveAt(0), users);
|
||||
foreach (var user in users)
|
||||
AddStep($"mark user {user} quit", () => Client.RemoveUser(LookupCache.GetUserAsync(user).Result.AsNonNull()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChangeScoringMode()
|
||||
{
|
||||
AddRepeatStep("update state", () => spectatorClient.RandomlyUpdateState(), 5);
|
||||
AddRepeatStep("update state", () => SpectatorClient.RandomlyUpdateState(), 5);
|
||||
AddStep("change to classic", () => config.SetValue(OsuSetting.ScoreDisplayMode, ScoringMode.Classic));
|
||||
AddStep("change to standardised", () => config.SetValue(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised));
|
||||
}
|
||||
|
||||
protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies();
|
||||
|
||||
protected class TestDependencies : MultiplayerTestSceneDependencies
|
||||
{
|
||||
protected override TestSpectatorClient CreateSpectatorClient() => new TestMultiplayerSpectatorClient();
|
||||
}
|
||||
|
||||
public class TestMultiplayerSpectatorClient : TestSpectatorClient
|
||||
{
|
||||
private readonly Dictionary<int, FrameHeader> lastHeaders = new Dictionary<int, FrameHeader>();
|
||||
|
@ -1,7 +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.Allocation;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
||||
@ -10,18 +10,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiplayerMatchFooter : MultiplayerTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly OnlinePlayBeatmapAvailabilityTracker availablilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
SelectedRoom.Value = new Room();
|
||||
|
||||
Child = new MultiplayerMatchFooter
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Height = 50
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ using osu.Game.Screens.Select;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiplayerMatchSongSelect : RoomTestScene
|
||||
public class TestSceneMultiplayerMatchSongSelect : MultiplayerTestScene
|
||||
{
|
||||
private BeatmapManager manager;
|
||||
private RulesetStore rulesets;
|
||||
|
@ -49,13 +49,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
Room.Name.Value = "Test Room";
|
||||
SelectedRoom.Value = new Room { Name = { Value = "Test Room" } };
|
||||
});
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetupSteps()
|
||||
{
|
||||
AddStep("load match", () => LoadScreen(screen = new MultiplayerMatchSubScreen(Room)));
|
||||
AddStep("load match", () => LoadScreen(screen = new MultiplayerMatchSubScreen(SelectedRoom.Value)));
|
||||
AddUntilStep("wait for load", () => screen.IsCurrentScreen());
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("set playlist", () =>
|
||||
{
|
||||
Room.Playlist.Add(new PlaylistItem
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
|
||||
Ruleset = { Value = new OsuRuleset().RulesetInfo },
|
||||
@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("set playlist", () =>
|
||||
{
|
||||
Room.Playlist.Add(new PlaylistItem
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
|
||||
Ruleset = { Value = new OsuRuleset().RulesetInfo },
|
||||
@ -102,7 +102,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("set playlist", () =>
|
||||
{
|
||||
Room.Playlist.Add(new PlaylistItem
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo },
|
||||
Ruleset = { Value = new OsuRuleset().RulesetInfo },
|
||||
|
@ -22,8 +22,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiplayerParticipantsList : MultiplayerTestScene
|
||||
{
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(createNewParticipantsList);
|
||||
[SetUpSteps]
|
||||
public void SetupSteps()
|
||||
{
|
||||
createNewParticipantsList();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAddUser()
|
||||
@ -88,7 +91,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public void TestCorrectInitialState()
|
||||
{
|
||||
AddStep("set to downloading map", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(0)));
|
||||
AddStep("recreate list", createNewParticipantsList);
|
||||
createNewParticipantsList();
|
||||
checkProgressBarVisibility(true);
|
||||
}
|
||||
|
||||
@ -233,7 +236,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private void createNewParticipantsList()
|
||||
{
|
||||
Child = new ParticipantsList { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Y, Size = new Vector2(380, 0.7f) };
|
||||
ParticipantsList participantsList = null;
|
||||
|
||||
AddStep("create new list", () => Child = participantsList = new ParticipantsList
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Size = new Vector2(380, 0.7f)
|
||||
});
|
||||
|
||||
AddUntilStep("wait for list to load", () => participantsList.IsLoaded);
|
||||
}
|
||||
|
||||
private void checkProgressBarVisibility(bool visible) =>
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
@ -27,7 +28,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public class TestSceneMultiplayerReadyButton : MultiplayerTestScene
|
||||
{
|
||||
private MultiplayerReadyButton button;
|
||||
private OnlinePlayBeatmapAvailabilityTracker beatmapTracker;
|
||||
private BeatmapSetInfo importedSet;
|
||||
|
||||
private readonly Bindable<PlaylistItem> selectedItem = new Bindable<PlaylistItem>();
|
||||
@ -43,18 +43,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
|
||||
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
|
||||
beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
|
||||
|
||||
Add(beatmapTracker = new OnlinePlayBeatmapAvailabilityTracker
|
||||
{
|
||||
SelectedItem = { BindTarget = selectedItem }
|
||||
});
|
||||
|
||||
Dependencies.Cache(beatmapTracker);
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
AvailabilityTracker.SelectedItem.BindTo(selectedItem);
|
||||
|
||||
importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First();
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First());
|
||||
selectedItem.Value = new PlaylistItem
|
||||
@ -71,18 +66,22 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(200, 50),
|
||||
OnReadyClick = async () =>
|
||||
OnReadyClick = () =>
|
||||
{
|
||||
readyClickOperation = OngoingOperationTracker.BeginOperation();
|
||||
|
||||
if (Client.IsHost && Client.LocalUser?.State == MultiplayerUserState.Ready)
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Client.StartMatch();
|
||||
return;
|
||||
}
|
||||
if (Client.IsHost && Client.LocalUser?.State == MultiplayerUserState.Ready)
|
||||
{
|
||||
await Client.StartMatch();
|
||||
return;
|
||||
}
|
||||
|
||||
await Client.ToggleReady();
|
||||
readyClickOperation.Dispose();
|
||||
await Client.ToggleReady();
|
||||
|
||||
readyClickOperation.Dispose();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -3,37 +3,38 @@
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class TestSceneMultiplayerRoomManager : RoomTestScene
|
||||
public class TestSceneMultiplayerRoomManager : MultiplayerTestScene
|
||||
{
|
||||
private TestMultiplayerRoomContainer roomContainer;
|
||||
private TestMultiplayerRoomManager roomManager => roomContainer.RoomManager;
|
||||
protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies();
|
||||
|
||||
public TestSceneMultiplayerRoomManager()
|
||||
: base(false)
|
||||
{
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPollsInitially()
|
||||
{
|
||||
AddStep("create room manager with a few rooms", () =>
|
||||
{
|
||||
createRoomManager().With(d => d.OnLoadComplete += _ =>
|
||||
{
|
||||
roomManager.CreateRoom(createRoom(r => r.Name.Value = "1"));
|
||||
roomManager.PartRoom();
|
||||
roomManager.CreateRoom(createRoom(r => r.Name.Value = "2"));
|
||||
roomManager.PartRoom();
|
||||
roomManager.ClearRooms();
|
||||
});
|
||||
RoomManager.CreateRoom(createRoom(r => r.Name.Value = "1"));
|
||||
RoomManager.PartRoom();
|
||||
RoomManager.CreateRoom(createRoom(r => r.Name.Value = "2"));
|
||||
RoomManager.PartRoom();
|
||||
RoomManager.ClearRooms();
|
||||
});
|
||||
|
||||
AddAssert("manager polled for rooms", () => ((RoomManager)roomManager).Rooms.Count == 2);
|
||||
AddAssert("initial rooms received", () => roomManager.InitialRoomsReceived.Value);
|
||||
AddAssert("manager polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 2);
|
||||
AddAssert("initial rooms received", () => RoomManager.InitialRoomsReceived.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -41,19 +42,16 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("create room manager with a few rooms", () =>
|
||||
{
|
||||
createRoomManager().With(d => d.OnLoadComplete += _ =>
|
||||
{
|
||||
roomManager.CreateRoom(createRoom());
|
||||
roomManager.PartRoom();
|
||||
roomManager.CreateRoom(createRoom());
|
||||
roomManager.PartRoom();
|
||||
});
|
||||
RoomManager.CreateRoom(createRoom());
|
||||
RoomManager.PartRoom();
|
||||
RoomManager.CreateRoom(createRoom());
|
||||
RoomManager.PartRoom();
|
||||
});
|
||||
|
||||
AddStep("disconnect", () => roomContainer.Client.Disconnect());
|
||||
AddStep("disconnect", () => Client.Disconnect());
|
||||
|
||||
AddAssert("rooms cleared", () => ((RoomManager)roomManager).Rooms.Count == 0);
|
||||
AddAssert("initial rooms not received", () => !roomManager.InitialRoomsReceived.Value);
|
||||
AddAssert("rooms cleared", () => ((RoomManager)RoomManager).Rooms.Count == 0);
|
||||
AddAssert("initial rooms not received", () => !RoomManager.InitialRoomsReceived.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -61,20 +59,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("create room manager with a few rooms", () =>
|
||||
{
|
||||
createRoomManager().With(d => d.OnLoadComplete += _ =>
|
||||
{
|
||||
roomManager.CreateRoom(createRoom());
|
||||
roomManager.PartRoom();
|
||||
roomManager.CreateRoom(createRoom());
|
||||
roomManager.PartRoom();
|
||||
});
|
||||
RoomManager.CreateRoom(createRoom());
|
||||
RoomManager.PartRoom();
|
||||
RoomManager.CreateRoom(createRoom());
|
||||
RoomManager.PartRoom();
|
||||
});
|
||||
|
||||
AddStep("disconnect", () => roomContainer.Client.Disconnect());
|
||||
AddStep("connect", () => roomContainer.Client.Connect());
|
||||
AddStep("disconnect", () => Client.Disconnect());
|
||||
AddStep("connect", () => Client.Connect());
|
||||
|
||||
AddAssert("manager polled for rooms", () => ((RoomManager)roomManager).Rooms.Count == 2);
|
||||
AddAssert("initial rooms received", () => roomManager.InitialRoomsReceived.Value);
|
||||
AddAssert("manager polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 2);
|
||||
AddAssert("initial rooms received", () => RoomManager.InitialRoomsReceived.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -82,15 +77,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("create room manager with a room", () =>
|
||||
{
|
||||
createRoomManager().With(d => d.OnLoadComplete += _ =>
|
||||
{
|
||||
roomManager.CreateRoom(createRoom());
|
||||
roomManager.ClearRooms();
|
||||
});
|
||||
RoomManager.CreateRoom(createRoom());
|
||||
RoomManager.ClearRooms();
|
||||
});
|
||||
|
||||
AddAssert("manager not polled for rooms", () => ((RoomManager)roomManager).Rooms.Count == 0);
|
||||
AddAssert("initial rooms not received", () => !roomManager.InitialRoomsReceived.Value);
|
||||
AddAssert("manager not polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 0);
|
||||
AddAssert("initial rooms not received", () => !RoomManager.InitialRoomsReceived.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -98,13 +90,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("create room manager with a room", () =>
|
||||
{
|
||||
createRoomManager().With(d => d.OnLoadComplete += _ =>
|
||||
{
|
||||
roomManager.CreateRoom(createRoom());
|
||||
});
|
||||
RoomManager.CreateRoom(createRoom());
|
||||
});
|
||||
|
||||
AddUntilStep("multiplayer room joined", () => roomContainer.Client.Room != null);
|
||||
AddUntilStep("multiplayer room joined", () => Client.Room != null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -112,14 +101,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("create room manager with a room", () =>
|
||||
{
|
||||
createRoomManager().With(d => d.OnLoadComplete += _ =>
|
||||
{
|
||||
roomManager.CreateRoom(createRoom());
|
||||
roomManager.PartRoom();
|
||||
});
|
||||
RoomManager.CreateRoom(createRoom());
|
||||
RoomManager.PartRoom();
|
||||
});
|
||||
|
||||
AddAssert("multiplayer room parted", () => roomContainer.Client.Room == null);
|
||||
AddAssert("multiplayer room parted", () => Client.Room == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -127,16 +113,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("create room manager with a room", () =>
|
||||
{
|
||||
createRoomManager().With(d => d.OnLoadComplete += _ =>
|
||||
{
|
||||
var r = createRoom();
|
||||
roomManager.CreateRoom(r);
|
||||
roomManager.PartRoom();
|
||||
roomManager.JoinRoom(r);
|
||||
});
|
||||
var r = createRoom();
|
||||
RoomManager.CreateRoom(r);
|
||||
RoomManager.PartRoom();
|
||||
RoomManager.JoinRoom(r);
|
||||
});
|
||||
|
||||
AddUntilStep("multiplayer room joined", () => roomContainer.Client.Room != null);
|
||||
AddUntilStep("multiplayer room joined", () => Client.Room != null);
|
||||
}
|
||||
|
||||
private Room createRoom(Action<Room> initFunc = null)
|
||||
@ -161,18 +144,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
return room;
|
||||
}
|
||||
|
||||
private TestMultiplayerRoomManager createRoomManager()
|
||||
private class TestDependencies : MultiplayerTestSceneDependencies
|
||||
{
|
||||
Child = roomContainer = new TestMultiplayerRoomContainer
|
||||
public TestDependencies()
|
||||
{
|
||||
RoomManager =
|
||||
{
|
||||
TimeBetweenListingPolls = { Value = 1 },
|
||||
TimeBetweenSelectionPolls = { Value = 1 }
|
||||
}
|
||||
};
|
||||
|
||||
return roomManager;
|
||||
// Need to set these values as early as possible.
|
||||
RoomManager.TimeBetweenListingPolls.Value = 1;
|
||||
RoomManager.TimeBetweenSelectionPolls.Value = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
@ -37,40 +38,19 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private IDisposable readyClickOperation;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
private readonly Container content;
|
||||
|
||||
public TestSceneMultiplayerSpectateButton()
|
||||
{
|
||||
base.Content.Add(content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
});
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, AudioManager audio)
|
||||
{
|
||||
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
|
||||
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
|
||||
|
||||
var beatmapTracker = new OnlinePlayBeatmapAvailabilityTracker { SelectedItem = { BindTarget = selectedItem } };
|
||||
base.Content.Add(beatmapTracker);
|
||||
Dependencies.Cache(beatmapTracker);
|
||||
|
||||
beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
AvailabilityTracker.SelectedItem.BindTo(selectedItem);
|
||||
|
||||
importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First();
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First());
|
||||
selectedItem.Value = new PlaylistItem
|
||||
@ -90,11 +70,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(200, 50),
|
||||
OnSpectateClick = async () =>
|
||||
OnSpectateClick = () =>
|
||||
{
|
||||
readyClickOperation = OngoingOperationTracker.BeginOperation();
|
||||
await Client.ToggleSpectate();
|
||||
readyClickOperation.Dispose();
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Client.ToggleSpectate();
|
||||
readyClickOperation.Dispose();
|
||||
});
|
||||
}
|
||||
},
|
||||
readyButton = new MultiplayerReadyButton
|
||||
@ -102,18 +86,22 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(200, 50),
|
||||
OnReadyClick = async () =>
|
||||
OnReadyClick = () =>
|
||||
{
|
||||
readyClickOperation = OngoingOperationTracker.BeginOperation();
|
||||
|
||||
if (Client.IsHost && Client.LocalUser?.State == MultiplayerUserState.Ready)
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Client.StartMatch();
|
||||
return;
|
||||
}
|
||||
if (Client.IsHost && Client.LocalUser?.State == MultiplayerUserState.Ready)
|
||||
{
|
||||
await Client.StartMatch();
|
||||
return;
|
||||
}
|
||||
|
||||
await Client.ToggleReady();
|
||||
readyClickOperation.Dispose();
|
||||
await Client.ToggleReady();
|
||||
|
||||
readyClickOperation.Dispose();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,16 +14,18 @@ using osu.Framework.Platform;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Playlists;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestScenePlaylistsSongSelect : RoomTestScene
|
||||
public class TestScenePlaylistsSongSelect : OnlinePlayTestScene
|
||||
{
|
||||
[Resolved]
|
||||
private BeatmapManager beatmapManager { get; set; }
|
||||
@ -85,6 +87,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("reset", () =>
|
||||
{
|
||||
SelectedRoom.Value = new Room();
|
||||
Ruleset.Value = new OsuRuleset().RulesetInfo;
|
||||
Beatmap.SetDefault();
|
||||
SelectedMods.Value = Array.Empty<Mod>();
|
||||
@ -98,14 +101,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public void TestItemAddedIfEmptyOnStart()
|
||||
{
|
||||
AddStep("finalise selection", () => songSelect.FinaliseSelection());
|
||||
AddAssert("playlist has 1 item", () => Room.Playlist.Count == 1);
|
||||
AddAssert("playlist has 1 item", () => SelectedRoom.Value.Playlist.Count == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestItemAddedWhenCreateNewItemClicked()
|
||||
{
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
AddAssert("playlist has 1 item", () => Room.Playlist.Count == 1);
|
||||
AddAssert("playlist has 1 item", () => SelectedRoom.Value.Playlist.Count == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -113,7 +116,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
AddStep("finalise selection", () => songSelect.FinaliseSelection());
|
||||
AddAssert("playlist has 1 item", () => Room.Playlist.Count == 1);
|
||||
AddAssert("playlist has 1 item", () => SelectedRoom.Value.Playlist.Count == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -121,7 +124,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", () => Room.Playlist.Count == 2);
|
||||
AddAssert("playlist has 2 items", () => SelectedRoom.Value.Playlist.Count == 2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -131,13 +134,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
AddStep("rearrange", () =>
|
||||
{
|
||||
var item = Room.Playlist[0];
|
||||
Room.Playlist.RemoveAt(0);
|
||||
Room.Playlist.Add(item);
|
||||
var item = SelectedRoom.Value.Playlist[0];
|
||||
SelectedRoom.Value.Playlist.RemoveAt(0);
|
||||
SelectedRoom.Value.Playlist.Add(item);
|
||||
});
|
||||
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
AddAssert("new item has id 2", () => Room.Playlist.Last().ID == 2);
|
||||
AddAssert("new item has id 2", () => SelectedRoom.Value.Playlist.Last().ID == 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -151,8 +154,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("change mod rate", () => ((OsuModDoubleTime)SelectedMods.Value[0]).SpeedChange.Value = 2);
|
||||
AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
|
||||
AddAssert("item 1 has rate 1.5", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)Room.Playlist.First().RequiredMods[0]).SpeedChange.Value));
|
||||
AddAssert("item 2 has rate 2", () => Precision.AlmostEquals(2, ((OsuModDoubleTime)Room.Playlist.Last().RequiredMods[0]).SpeedChange.Value));
|
||||
AddAssert("item 1 has rate 1.5", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)SelectedRoom.Value.Playlist.First().RequiredMods[0]).SpeedChange.Value));
|
||||
AddAssert("item 2 has rate 2", () => Precision.AlmostEquals(2, ((OsuModDoubleTime)SelectedRoom.Value.Playlist.Last().RequiredMods[0]).SpeedChange.Value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -174,7 +177,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
|
||||
AddStep("change stored mod rate", () => mod.SpeedChange.Value = 2);
|
||||
AddAssert("item has rate 1.5", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)Room.Playlist.First().RequiredMods[0]).SpeedChange.Value));
|
||||
AddAssert("item has rate 1.5", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)SelectedRoom.Value.Playlist.First().RequiredMods[0]).SpeedChange.Value));
|
||||
}
|
||||
|
||||
private class TestPlaylistsSongSelect : PlaylistsSongSelect
|
||||
|
@ -0,0 +1,40 @@
|
||||
// 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.Game.Beatmaps;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneStarRatingRangeDisplay : OnlinePlayTestScene
|
||||
{
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
SelectedRoom.Value = new Room();
|
||||
|
||||
Child = new StarRatingRangeDisplay
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestRange([Values(0, 2, 3, 4, 6, 7)] double min, [Values(0, 2, 3, 4, 6, 7)] double max)
|
||||
{
|
||||
AddStep("set playlist", () =>
|
||||
{
|
||||
SelectedRoom.Value.Playlist.AddRange(new[]
|
||||
{
|
||||
new PlaylistItem { Beatmap = { Value = new BeatmapInfo { StarDifficulty = min } } },
|
||||
new PlaylistItem { Beatmap = { Value = new BeatmapInfo { StarDifficulty = max } } },
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ using osu.Game.Overlays;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Tests.Beatmaps.IO;
|
||||
using osuTK.Input;
|
||||
using static osu.Game.Tests.Visual.Navigation.TestSceneScreenNavigation;
|
||||
|
||||
@ -57,8 +58,11 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
[Test]
|
||||
public void TestPerformAtSongSelectFromPlayerLoader()
|
||||
{
|
||||
AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait());
|
||||
PushAndConfirm(() => new TestPlaySongSelect());
|
||||
PushAndConfirm(() => new PlayerLoader(() => new SoloPlayer()));
|
||||
|
||||
AddStep("Press enter", () => InputManager.Key(Key.Enter));
|
||||
AddUntilStep("Wait for new screen", () => Game.ScreenStack.CurrentScreen is PlayerLoader);
|
||||
|
||||
AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true, new[] { typeof(TestPlaySongSelect) }));
|
||||
AddUntilStep("returned to song select", () => Game.ScreenStack.CurrentScreen is TestPlaySongSelect);
|
||||
@ -68,8 +72,11 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
[Test]
|
||||
public void TestPerformAtMenuFromPlayerLoader()
|
||||
{
|
||||
AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait());
|
||||
PushAndConfirm(() => new TestPlaySongSelect());
|
||||
PushAndConfirm(() => new PlayerLoader(() => new SoloPlayer()));
|
||||
|
||||
AddStep("Press enter", () => InputManager.Key(Key.Enter));
|
||||
AddUntilStep("Wait for new screen", () => Game.ScreenStack.CurrentScreen is PlayerLoader);
|
||||
|
||||
AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true));
|
||||
AddUntilStep("returned to song select", () => Game.ScreenStack.CurrentScreen is MainMenu);
|
||||
|
@ -82,7 +82,8 @@ namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
switch (req)
|
||||
{
|
||||
case JoinChannelRequest _:
|
||||
case JoinChannelRequest joinChannel:
|
||||
joinChannel.TriggerSuccess();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3,25 +3,21 @@
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Playlists;
|
||||
using osu.Game.Tests.Visual.Multiplayer;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
public class TestScenePlaylistsLoungeSubScreen : RoomManagerTestScene
|
||||
public class TestScenePlaylistsLoungeSubScreen : OnlinePlayTestScene
|
||||
{
|
||||
private LoungeSubScreen loungeScreen;
|
||||
protected new BasicTestRoomManager RoomManager => (BasicTestRoomManager)base.RoomManager;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
}
|
||||
private LoungeSubScreen loungeScreen;
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
@ -37,7 +33,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
[Test]
|
||||
public void TestScrollSelectedIntoView()
|
||||
{
|
||||
AddRooms(30);
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(30));
|
||||
|
||||
AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms.First()));
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -12,26 +11,28 @@ using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Playlists;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
public class TestScenePlaylistsMatchSettingsOverlay : RoomTestScene
|
||||
public class TestScenePlaylistsMatchSettingsOverlay : OnlinePlayTestScene
|
||||
{
|
||||
[Cached(Type = typeof(IRoomManager))]
|
||||
private TestRoomManager roomManager = new TestRoomManager();
|
||||
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
|
||||
|
||||
private TestRoomSettings settings;
|
||||
|
||||
protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies();
|
||||
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
settings = new TestRoomSettings
|
||||
SelectedRoom.Value = new Room();
|
||||
|
||||
Child = settings = new TestRoomSettings
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
State = { Value = Visibility.Visible }
|
||||
};
|
||||
|
||||
Child = settings;
|
||||
});
|
||||
|
||||
[Test]
|
||||
@ -39,19 +40,19 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
AddStep("clear name and beatmap", () =>
|
||||
{
|
||||
Room.Name.Value = "";
|
||||
Room.Playlist.Clear();
|
||||
SelectedRoom.Value.Name.Value = "";
|
||||
SelectedRoom.Value.Playlist.Clear();
|
||||
});
|
||||
|
||||
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
|
||||
|
||||
AddStep("set name", () => Room.Name.Value = "Room name");
|
||||
AddStep("set name", () => SelectedRoom.Value.Name.Value = "Room name");
|
||||
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
|
||||
|
||||
AddStep("set beatmap", () => Room.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(Ruleset.Value).BeatmapInfo } }));
|
||||
AddStep("set beatmap", () => SelectedRoom.Value.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(Ruleset.Value).BeatmapInfo } }));
|
||||
AddAssert("button enabled", () => settings.ApplyButton.Enabled.Value);
|
||||
|
||||
AddStep("clear name", () => Room.Name.Value = "");
|
||||
AddStep("clear name", () => SelectedRoom.Value.Name.Value = "");
|
||||
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
|
||||
}
|
||||
|
||||
@ -67,9 +68,9 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
settings.NameField.Current.Value = expected_name;
|
||||
settings.DurationField.Current.Value = expectedDuration;
|
||||
Room.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(Ruleset.Value).BeatmapInfo } });
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(Ruleset.Value).BeatmapInfo } });
|
||||
|
||||
roomManager.CreateRequested = r =>
|
||||
RoomManager.CreateRequested = r =>
|
||||
{
|
||||
createdRoom = r;
|
||||
return true;
|
||||
@ -88,11 +89,11 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
|
||||
AddStep("setup", () =>
|
||||
{
|
||||
Room.Name.Value = "Test Room";
|
||||
Room.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(Ruleset.Value).BeatmapInfo } });
|
||||
SelectedRoom.Value.Name.Value = "Test Room";
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(Ruleset.Value).BeatmapInfo } });
|
||||
|
||||
fail = true;
|
||||
roomManager.CreateRequested = _ => !fail;
|
||||
RoomManager.CreateRequested = _ => !fail;
|
||||
});
|
||||
AddAssert("error not displayed", () => !settings.ErrorText.IsPresent);
|
||||
|
||||
@ -119,7 +120,12 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
public OsuSpriteText ErrorText => ((MatchSettings)Settings).ErrorText;
|
||||
}
|
||||
|
||||
private class TestRoomManager : IRoomManager
|
||||
private class TestDependencies : OnlinePlayTestSceneDependencies
|
||||
{
|
||||
protected override IRoomManager CreateRoomManager() => new TestRoomManager();
|
||||
}
|
||||
|
||||
protected class TestRoomManager : IRoomManager
|
||||
{
|
||||
public const string FAILED_TEXT = "failed";
|
||||
|
||||
|
@ -3,21 +3,23 @@
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
public class TestScenePlaylistsParticipantsList : RoomTestScene
|
||||
public class TestScenePlaylistsParticipantsList : OnlinePlayTestScene
|
||||
{
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
Room.RoomID.Value = 7;
|
||||
SelectedRoom.Value = new Room { RoomID = { Value = 7 } };
|
||||
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
Room.RecentParticipants.Add(new User
|
||||
SelectedRoom.Value.RecentParticipants.Add(new User
|
||||
{
|
||||
Username = "peppy",
|
||||
Statistics = new UserStatistics { GlobalRank = 1234 },
|
||||
|
@ -15,20 +15,17 @@ using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Playlists;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osu.Game.Users;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
public class TestScenePlaylistsRoomSubScreen : RoomTestScene
|
||||
public class TestScenePlaylistsRoomSubScreen : OnlinePlayTestScene
|
||||
{
|
||||
[Cached(typeof(IRoomManager))]
|
||||
private readonly TestRoomManager roomManager = new TestRoomManager();
|
||||
|
||||
private BeatmapManager manager;
|
||||
private RulesetStore rulesets;
|
||||
|
||||
@ -40,8 +37,6 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
|
||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
|
||||
|
||||
manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait();
|
||||
|
||||
((DummyAPIAccess)API).HandleRequest = req =>
|
||||
{
|
||||
switch (req)
|
||||
@ -58,7 +53,9 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
[SetUpSteps]
|
||||
public void SetupSteps()
|
||||
{
|
||||
AddStep("load match", () => LoadScreen(match = new TestPlaylistsRoomSubScreen(Room)));
|
||||
AddStep("set room", () => SelectedRoom.Value = new Room());
|
||||
AddStep("ensure has beatmap", () => manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait());
|
||||
AddStep("load match", () => LoadScreen(match = new TestPlaylistsRoomSubScreen(SelectedRoom.Value)));
|
||||
AddUntilStep("wait for load", () => match.IsCurrentScreen());
|
||||
}
|
||||
|
||||
@ -67,12 +64,12 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
AddStep("set room properties", () =>
|
||||
{
|
||||
Room.RoomID.Value = 1;
|
||||
Room.Name.Value = "my awesome room";
|
||||
Room.Host.Value = new User { Id = 2, Username = "peppy" };
|
||||
Room.RecentParticipants.Add(Room.Host.Value);
|
||||
Room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5);
|
||||
Room.Playlist.Add(new PlaylistItem
|
||||
SelectedRoom.Value.RoomID.Value = 1;
|
||||
SelectedRoom.Value.Name.Value = "my awesome room";
|
||||
SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" };
|
||||
SelectedRoom.Value.RecentParticipants.Add(SelectedRoom.Value.Host.Value);
|
||||
SelectedRoom.Value.EndDate.Value = DateTimeOffset.Now.AddMinutes(5);
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
|
||||
Ruleset = { Value = new OsuRuleset().RulesetInfo }
|
||||
@ -88,9 +85,9 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
AddStep("set room properties", () =>
|
||||
{
|
||||
Room.Name.Value = "my awesome room";
|
||||
Room.Host.Value = new User { Id = 2, Username = "peppy" };
|
||||
Room.Playlist.Add(new PlaylistItem
|
||||
SelectedRoom.Value.Name.Value = "my awesome room";
|
||||
SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" };
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
|
||||
Ruleset = { Value = new OsuRuleset().RulesetInfo }
|
||||
@ -104,17 +101,34 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
|
||||
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
AddAssert("first playlist item selected", () => match.SelectedItem.Value == Room.Playlist[0]);
|
||||
AddAssert("first playlist item selected", () => match.SelectedItem.Value == SelectedRoom.Value.Playlist[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBeatmapUpdatedOnReImport()
|
||||
{
|
||||
BeatmapSetInfo importedSet = null;
|
||||
TestBeatmap beatmap = null;
|
||||
|
||||
// this step is required to make sure the further imports actually get online IDs.
|
||||
// all the playlist logic relies on online ID matching.
|
||||
AddStep("remove all matching online IDs", () =>
|
||||
{
|
||||
beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo);
|
||||
|
||||
var existing = manager.QueryBeatmapSets(s => s.OnlineBeatmapSetID == beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID).ToList();
|
||||
|
||||
foreach (var s in existing)
|
||||
{
|
||||
s.OnlineBeatmapSetID = null;
|
||||
foreach (var b in s.Beatmaps)
|
||||
b.OnlineBeatmapID = null;
|
||||
manager.Update(s);
|
||||
}
|
||||
});
|
||||
|
||||
AddStep("import altered beatmap", () =>
|
||||
{
|
||||
var beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo);
|
||||
beatmap.BeatmapInfo.BaseDifficulty.CircleSize = 1;
|
||||
|
||||
importedSet = manager.Import(beatmap.BeatmapInfo.BeatmapSet).Result;
|
||||
@ -122,9 +136,9 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
|
||||
AddStep("load room", () =>
|
||||
{
|
||||
Room.Name.Value = "my awesome room";
|
||||
Room.Host.Value = new User { Id = 2, Username = "peppy" };
|
||||
Room.Playlist.Add(new PlaylistItem
|
||||
SelectedRoom.Value.Name.Value = "my awesome room";
|
||||
SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" };
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = importedSet.Beatmaps[0] },
|
||||
Ruleset = { Value = new OsuRuleset().RulesetInfo }
|
||||
@ -155,30 +169,5 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class TestRoomManager : IRoomManager
|
||||
{
|
||||
public event Action RoomsUpdated
|
||||
{
|
||||
add => throw new NotImplementedException();
|
||||
remove => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IBindable<bool> InitialRoomsReceived { get; } = new Bindable<bool>(true);
|
||||
|
||||
public IBindableList<Room> Rooms { get; } = new BindableList<Room>();
|
||||
|
||||
public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
|
||||
{
|
||||
room.RoomID.Value = 1;
|
||||
onSuccess?.Invoke(room);
|
||||
}
|
||||
|
||||
public void JoinRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null) => onSuccess?.Invoke(room);
|
||||
|
||||
public void PartRoom()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
91
osu.Game.Tests/Visual/UserInterface/TestSceneColourPicker.cs
Normal file
91
osu.Game.Tests/Visual/UserInterface/TestSceneColourPicker.cs
Normal file
@ -0,0 +1,91 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
public class TestSceneColourPicker : OsuTestScene
|
||||
{
|
||||
private readonly Bindable<Colour4> colour = new Bindable<Colour4>(Colour4.Aquamarine);
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("create pickers", () => Child = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(),
|
||||
new Dimension()
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = @"No OverlayColourProvider",
|
||||
Font = OsuFont.Default.With(size: 40)
|
||||
},
|
||||
new OsuColourPicker
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Current = { BindTarget = colour },
|
||||
}
|
||||
}
|
||||
},
|
||||
new ColourProvidingContainer(OverlayColourScheme.Blue)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = @"With blue OverlayColourProvider",
|
||||
Font = OsuFont.Default.With(size: 40)
|
||||
},
|
||||
new OsuColourPicker
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Current = { BindTarget = colour },
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AddStep("set green", () => colour.Value = Colour4.LimeGreen);
|
||||
AddStep("set white", () => colour.Value = Colour4.White);
|
||||
AddStep("set red", () => colour.Value = Colour4.Red);
|
||||
}
|
||||
|
||||
private class ColourProvidingContainer : Container
|
||||
{
|
||||
[Cached]
|
||||
private OverlayColourProvider provider { get; }
|
||||
|
||||
public ColourProvidingContainer(OverlayColourScheme colourScheme)
|
||||
{
|
||||
provider = new OverlayColourProvider(colourScheme);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
<PackageReference Include="DeepEqual" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
</ItemGroup>
|
||||
|
@ -149,7 +149,8 @@ namespace osu.Game.Tournament.Tests.NonVisual
|
||||
private TournamentGameBase loadOsu(GameHost host)
|
||||
{
|
||||
var osu = new TournamentGameBase();
|
||||
Task.Run(() => host.Run(osu));
|
||||
Task.Run(() => host.Run(osu))
|
||||
.ContinueWith(t => Assert.Fail($"Host threw exception {t.Exception}"), TaskContinuationOptions.OnlyOnFaulted);
|
||||
waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time");
|
||||
return osu;
|
||||
}
|
||||
|
@ -55,7 +55,8 @@ namespace osu.Game.Tournament.Tests.NonVisual
|
||||
private TournamentGameBase loadOsu(GameHost host)
|
||||
{
|
||||
var osu = new TournamentGameBase();
|
||||
Task.Run(() => host.Run(osu));
|
||||
Task.Run(() => host.Run(osu))
|
||||
.ContinueWith(t => Assert.Fail($"Host threw exception {t.Exception}"), TaskContinuationOptions.OnlyOnFaulted);
|
||||
waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time");
|
||||
return osu;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
|
@ -181,8 +181,13 @@ namespace osu.Game.Beatmaps
|
||||
if (existingOnlineId != null)
|
||||
{
|
||||
Delete(existingOnlineId);
|
||||
beatmaps.PurgeDeletable(s => s.ID == existingOnlineId.ID);
|
||||
LogForModel(beatmapSet, $"Found existing beatmap set with same OnlineBeatmapSetID ({beatmapSet.OnlineBeatmapSetID}). It has been purged.");
|
||||
|
||||
// in order to avoid a unique key constraint, immediately remove the online ID from the previous set.
|
||||
existingOnlineId.OnlineBeatmapSetID = null;
|
||||
foreach (var b in existingOnlineId.Beatmaps)
|
||||
b.OnlineBeatmapID = null;
|
||||
|
||||
LogForModel(beatmapSet, $"Found existing beatmap set with same OnlineBeatmapSetID ({beatmapSet.OnlineBeatmapSetID}). It has been deleted.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,12 +89,18 @@ namespace osu.Game.Database
|
||||
if (IsDisposed)
|
||||
throw new ObjectDisposedException(nameof(RealmContextFactory));
|
||||
|
||||
Logger.Log(@"Blocking realm operations.", LoggingTarget.Database);
|
||||
|
||||
blockingLock.Wait();
|
||||
flushContexts();
|
||||
|
||||
return new InvokeOnDisposal<RealmContextFactory>(this, endBlockingSection);
|
||||
|
||||
static void endBlockingSection(RealmContextFactory factory) => factory.blockingLock.Release();
|
||||
static void endBlockingSection(RealmContextFactory factory)
|
||||
{
|
||||
factory.blockingLock.Release();
|
||||
Logger.Log(@"Restoring realm operations.", LoggingTarget.Database);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
@ -147,6 +153,8 @@ namespace osu.Game.Database
|
||||
|
||||
private void flushContexts()
|
||||
{
|
||||
Logger.Log(@"Flushing realm contexts...", LoggingTarget.Database);
|
||||
|
||||
var previousContext = context;
|
||||
context = null;
|
||||
|
||||
@ -155,6 +163,8 @@ namespace osu.Game.Database
|
||||
Thread.Sleep(50);
|
||||
|
||||
previousContext?.Dispose();
|
||||
|
||||
Logger.Log(@"Realm contexts flushed.", LoggingTarget.Database);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
|
@ -32,10 +32,10 @@ namespace osu.Game.Graphics
|
||||
return Pink;
|
||||
|
||||
case DifficultyRating.Expert:
|
||||
return useLighterColour ? PurpleLight : Purple;
|
||||
return PurpleLight;
|
||||
|
||||
case DifficultyRating.ExpertPlus:
|
||||
return useLighterColour ? Gray9 : Gray0;
|
||||
return useLighterColour ? Gray9 : Color4Extensions.FromHex("#121415");
|
||||
}
|
||||
}
|
||||
|
||||
|
19
osu.Game/Graphics/UserInterfaceV2/OsuColourPicker.cs
Normal file
19
osu.Game/Graphics/UserInterfaceV2/OsuColourPicker.cs
Normal file
@ -0,0 +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.
|
||||
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
public class OsuColourPicker : ColourPicker
|
||||
{
|
||||
public OsuColourPicker()
|
||||
{
|
||||
CornerRadius = 10;
|
||||
Masking = true;
|
||||
}
|
||||
|
||||
protected override HSVColourPicker CreateHSVColourPicker() => new OsuHSVColourPicker();
|
||||
protected override HexColourPicker CreateHexColourPicker() => new OsuHexColourPicker();
|
||||
}
|
||||
}
|
129
osu.Game/Graphics/UserInterfaceV2/OsuHSVColourPicker.cs
Normal file
129
osu.Game/Graphics/UserInterfaceV2/OsuHSVColourPicker.cs
Normal file
@ -0,0 +1,129 @@
|
||||
// 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 JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
public class OsuHSVColourPicker : HSVColourPicker
|
||||
{
|
||||
private const float spacing = 10;
|
||||
private const float corner_radius = 10;
|
||||
private const float control_border_thickness = 3;
|
||||
|
||||
protected override HueSelector CreateHueSelector() => new OsuHueSelector();
|
||||
protected override SaturationValueSelector CreateSaturationValueSelector() => new OsuSaturationValueSelector();
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load([CanBeNull] OverlayColourProvider colourProvider, OsuColour osuColour)
|
||||
{
|
||||
Background.Colour = colourProvider?.Dark5 ?? osuColour.GreySeafoamDark;
|
||||
|
||||
Content.Padding = new MarginPadding(spacing);
|
||||
Content.Spacing = new Vector2(0, spacing);
|
||||
}
|
||||
|
||||
private static EdgeEffectParameters createShadowParameters() => new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Offset = new Vector2(0, 1),
|
||||
Radius = 3,
|
||||
Colour = Colour4.Black.Opacity(0.3f)
|
||||
};
|
||||
|
||||
private class OsuHueSelector : HueSelector
|
||||
{
|
||||
public OsuHueSelector()
|
||||
{
|
||||
SliderBar.CornerRadius = corner_radius;
|
||||
SliderBar.Masking = true;
|
||||
}
|
||||
|
||||
protected override Drawable CreateSliderNub() => new SliderNub(this);
|
||||
|
||||
private class SliderNub : CompositeDrawable
|
||||
{
|
||||
private readonly Bindable<float> hue;
|
||||
private readonly Box fill;
|
||||
|
||||
public SliderNub(OsuHueSelector osuHueSelector)
|
||||
{
|
||||
hue = osuHueSelector.Hue.GetBoundCopy();
|
||||
|
||||
InternalChild = new CircularContainer
|
||||
{
|
||||
Height = 35,
|
||||
Width = 10,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Masking = true,
|
||||
BorderColour = Colour4.White,
|
||||
BorderThickness = control_border_thickness,
|
||||
EdgeEffect = createShadowParameters(),
|
||||
Child = fill = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
hue.BindValueChanged(h => fill.Colour = Colour4.FromHSV(h.NewValue, 1, 1), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class OsuSaturationValueSelector : SaturationValueSelector
|
||||
{
|
||||
public OsuSaturationValueSelector()
|
||||
{
|
||||
SelectionArea.CornerRadius = corner_radius;
|
||||
SelectionArea.Masking = true;
|
||||
// purposefully use hard non-AA'd masking to avoid edge artifacts.
|
||||
SelectionArea.MaskingSmoothness = 0;
|
||||
}
|
||||
|
||||
protected override Marker CreateMarker() => new OsuMarker();
|
||||
|
||||
private class OsuMarker : Marker
|
||||
{
|
||||
private readonly Box previewBox;
|
||||
|
||||
public OsuMarker()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
InternalChild = new CircularContainer
|
||||
{
|
||||
Size = new Vector2(20),
|
||||
Masking = true,
|
||||
BorderColour = Colour4.White,
|
||||
BorderThickness = control_border_thickness,
|
||||
EdgeEffect = createShadowParameters(),
|
||||
Child = previewBox = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Current.BindValueChanged(colour => previewBox.Colour = colour.NewValue, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
57
osu.Game/Graphics/UserInterfaceV2/OsuHexColourPicker.cs
Normal file
57
osu.Game/Graphics/UserInterfaceV2/OsuHexColourPicker.cs
Normal file
@ -0,0 +1,57 @@
|
||||
// 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 JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
public class OsuHexColourPicker : HexColourPicker
|
||||
{
|
||||
public OsuHexColourPicker()
|
||||
{
|
||||
Padding = new MarginPadding(20);
|
||||
Spacing = 20;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour osuColour)
|
||||
{
|
||||
Background.Colour = overlayColourProvider?.Dark6 ?? osuColour.GreySeafoamDarker;
|
||||
}
|
||||
|
||||
protected override TextBox CreateHexCodeTextBox() => new OsuTextBox();
|
||||
protected override ColourPreview CreateColourPreview() => new OsuColourPreview();
|
||||
|
||||
private class OsuColourPreview : ColourPreview
|
||||
{
|
||||
private readonly Box preview;
|
||||
|
||||
public OsuColourPreview()
|
||||
{
|
||||
InternalChild = new CircularContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Child = preview = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Current.BindValueChanged(colour => preview.Colour = colour.NewValue, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,11 +9,11 @@ namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class PostMessageRequest : APIRequest<Message>
|
||||
{
|
||||
private readonly Message message;
|
||||
public readonly Message Message;
|
||||
|
||||
public PostMessageRequest(Message message)
|
||||
{
|
||||
this.message = message;
|
||||
Message = message;
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
@ -21,12 +21,12 @@ namespace osu.Game.Online.API.Requests
|
||||
var req = base.CreateWebRequest();
|
||||
|
||||
req.Method = HttpMethod.Post;
|
||||
req.AddParameter(@"is_action", message.IsAction.ToString().ToLowerInvariant());
|
||||
req.AddParameter(@"message", message.Content);
|
||||
req.AddParameter(@"is_action", Message.IsAction.ToString().ToLowerInvariant());
|
||||
req.AddParameter(@"message", Message.Content);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
protected override string Target => $@"chat/channels/{message.ChannelId}/messages";
|
||||
protected override string Target => $@"chat/channels/{Message.ChannelId}/messages";
|
||||
}
|
||||
}
|
||||
|
@ -225,7 +225,7 @@ namespace osu.Game.Online.Chat
|
||||
switch (command)
|
||||
{
|
||||
case "np":
|
||||
AddInternal(new NowPlayingCommand());
|
||||
AddInternal(new NowPlayingCommand(target));
|
||||
break;
|
||||
|
||||
case "me":
|
||||
@ -235,7 +235,7 @@ namespace osu.Game.Online.Chat
|
||||
break;
|
||||
}
|
||||
|
||||
PostMessage(content, true);
|
||||
PostMessage(content, true, target);
|
||||
break;
|
||||
|
||||
case "join":
|
||||
|
@ -21,6 +21,17 @@ namespace osu.Game.Online.Chat
|
||||
[Resolved]
|
||||
private Bindable<WorkingBeatmap> currentBeatmap { get; set; }
|
||||
|
||||
private readonly Channel target;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="NowPlayingCommand"/> to post the currently-playing beatmap to a parenting <see cref="IChannelPostTarget"/>.
|
||||
/// </summary>
|
||||
/// <param name="target">The target channel to post to. If <c>null</c>, the currently-selected channel will be posted to.</param>
|
||||
public NowPlayingCommand(Channel target = null)
|
||||
{
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
@ -48,7 +59,7 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
var beatmapString = beatmap.OnlineBeatmapID.HasValue ? $"[{api.WebsiteRootUrl}/b/{beatmap.OnlineBeatmapID} {beatmap}]" : beatmap.ToString();
|
||||
|
||||
channelManager.PostMessage($"is {verb} {beatmapString}", true);
|
||||
channelManager.PostMessage($"is {verb} {beatmapString}", true, target);
|
||||
Expire();
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +223,20 @@ namespace osu.Game
|
||||
|
||||
// bind config int to database RulesetInfo
|
||||
configRuleset = LocalConfig.GetBindable<int>(OsuSetting.Ruleset);
|
||||
Ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value) ?? RulesetStore.AvailableRulesets.First();
|
||||
|
||||
var preferredRuleset = RulesetStore.GetRuleset(configRuleset.Value);
|
||||
|
||||
try
|
||||
{
|
||||
Ruleset.Value = preferredRuleset ?? RulesetStore.AvailableRulesets.First();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// on startup, a ruleset may be selected which has compatibility issues.
|
||||
Logger.Error(e, $@"Failed to switch to preferred ruleset {preferredRuleset}.");
|
||||
Ruleset.Value = RulesetStore.AvailableRulesets.First();
|
||||
}
|
||||
|
||||
Ruleset.ValueChanged += r => configRuleset.Value = r.NewValue.ID ?? 0;
|
||||
|
||||
// bind config int to database SkinInfo
|
||||
@ -478,6 +491,10 @@ namespace osu.Game
|
||||
public override Task Import(params ImportTask[] imports)
|
||||
{
|
||||
// encapsulate task as we don't want to begin the import process until in a ready state.
|
||||
|
||||
// ReSharper disable once AsyncVoidLambda
|
||||
// TODO: This is bad because `new Task` doesn't have a Func<Task?> override.
|
||||
// Only used for android imports and a bit of a mess. Probably needs rethinking overall.
|
||||
var importTask = new Task(async () => await base.Import(imports).ConfigureAwait(false));
|
||||
|
||||
waitForReady(() => this, _ => importTask.Start());
|
||||
|
@ -422,11 +422,15 @@ namespace osu.Game
|
||||
|
||||
public void Migrate(string path)
|
||||
{
|
||||
Logger.Log($@"Migrating osu! data from ""{Storage.GetFullPath(string.Empty)}"" to ""{path}""...");
|
||||
|
||||
using (realmFactory.BlockAllOperations())
|
||||
{
|
||||
contextFactory.FlushConnections();
|
||||
(Storage as OsuStorage)?.Migrate(Host.GetStorage(path));
|
||||
}
|
||||
|
||||
Logger.Log(@"Migration complete!");
|
||||
}
|
||||
|
||||
protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager();
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using Humanizer;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osuTK.Graphics;
|
||||
@ -17,11 +18,11 @@ namespace osu.Game.Overlays.Changelog
|
||||
Width *= 2;
|
||||
}
|
||||
|
||||
protected override string MainText => Value.DisplayName;
|
||||
protected override LocalisableString MainText => Value.DisplayName;
|
||||
|
||||
protected override string AdditionalText => Value.LatestBuild.DisplayVersion;
|
||||
protected override LocalisableString AdditionalText => Value.LatestBuild.DisplayVersion;
|
||||
|
||||
protected override string InfoText => Value.LatestBuild.Users > 0 ? $"{"user".ToQuantity(Value.LatestBuild.Users, "N0")} online" : null;
|
||||
protected override LocalisableString InfoText => Value.LatestBuild.Users > 0 ? $"{"user".ToQuantity(Value.LatestBuild.Users, "N0")} online" : null;
|
||||
|
||||
protected override Color4 GetBarColour(OsuColour colours) => Value.Colour;
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
// 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.ComponentModel;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.Dashboard
|
||||
{
|
||||
@ -13,13 +16,14 @@ namespace osu.Game.Overlays.Dashboard
|
||||
{
|
||||
public DashboardTitle()
|
||||
{
|
||||
Title = "dashboard";
|
||||
Title = HomeStrings.UserTitle;
|
||||
Description = "view your friends and other information";
|
||||
IconTexture = "Icons/Hexacons/social";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[LocalisableEnum(typeof(DashboardOverlayTabsEnumLocalisationMapper))]
|
||||
public enum DashboardOverlayTabs
|
||||
{
|
||||
Friends,
|
||||
@ -27,4 +31,22 @@ namespace osu.Game.Overlays.Dashboard
|
||||
[Description("Currently Playing")]
|
||||
CurrentlyPlaying
|
||||
}
|
||||
|
||||
public class DashboardOverlayTabsEnumLocalisationMapper : EnumLocalisationMapper<DashboardOverlayTabs>
|
||||
{
|
||||
public override LocalisableString Map(DashboardOverlayTabs value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case DashboardOverlayTabs.Friends:
|
||||
return FriendsStrings.TitleCompact;
|
||||
|
||||
case DashboardOverlayTabs.CurrentlyPlaying:
|
||||
return @"Currently Playing";
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(value), value, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -14,9 +16,9 @@ namespace osu.Game.Overlays.Dashboard.Friends
|
||||
{
|
||||
}
|
||||
|
||||
protected override string MainText => Value.Status.ToString();
|
||||
protected override LocalisableString MainText => Value.Status.GetLocalisableDescription();
|
||||
|
||||
protected override string AdditionalText => Value.Count.ToString();
|
||||
protected override LocalisableString AdditionalText => Value.Count.ToString();
|
||||
|
||||
protected override Color4 GetBarColour(OsuColour colours)
|
||||
{
|
||||
|
@ -1,12 +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 System;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.Dashboard.Friends
|
||||
{
|
||||
[LocalisableEnum(typeof(OnlineStatusEnumLocalisationMapper))]
|
||||
public enum OnlineStatus
|
||||
{
|
||||
All,
|
||||
Online,
|
||||
Offline
|
||||
}
|
||||
|
||||
public class OnlineStatusEnumLocalisationMapper : EnumLocalisationMapper<OnlineStatus>
|
||||
{
|
||||
public override LocalisableString Map(OnlineStatus value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case OnlineStatus.All:
|
||||
return SortStrings.All;
|
||||
|
||||
case OnlineStatus.Online:
|
||||
return UsersStrings.StatusOnline;
|
||||
|
||||
case OnlineStatus.Offline:
|
||||
return UsersStrings.StatusOffline;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(value), value, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
// 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.ComponentModel;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.Dashboard.Friends
|
||||
{
|
||||
@ -9,6 +12,7 @@ namespace osu.Game.Overlays.Dashboard.Friends
|
||||
{
|
||||
}
|
||||
|
||||
[LocalisableEnum(typeof(UserSortCriteriaEnumLocalisationMappper))]
|
||||
public enum UserSortCriteria
|
||||
{
|
||||
[Description(@"Recently Active")]
|
||||
@ -16,4 +20,25 @@ namespace osu.Game.Overlays.Dashboard.Friends
|
||||
Rank,
|
||||
Username
|
||||
}
|
||||
|
||||
public class UserSortCriteriaEnumLocalisationMappper : EnumLocalisationMapper<UserSortCriteria>
|
||||
{
|
||||
public override LocalisableString Map(UserSortCriteria value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case UserSortCriteria.LastVisit:
|
||||
return SortStrings.LastVisit;
|
||||
|
||||
case UserSortCriteria.Rank:
|
||||
return SortStrings.Rank;
|
||||
|
||||
case UserSortCriteria.Username:
|
||||
return SortStrings.Username;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(value), value, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,9 @@ using osu.Framework.Allocation;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Localisation;
|
||||
using System;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Framework.Extensions;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
@ -57,7 +60,7 @@ namespace osu.Game.Overlays
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; }
|
||||
|
||||
public LocalisableString TooltipText => $@"{Value} view";
|
||||
public LocalisableString TooltipText => Value.GetLocalisableDescription();
|
||||
|
||||
private readonly SpriteIcon icon;
|
||||
|
||||
@ -98,10 +101,32 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
}
|
||||
|
||||
[LocalisableEnum(typeof(OverlayPanelDisplayStyleEnumLocalisationMapper))]
|
||||
public enum OverlayPanelDisplayStyle
|
||||
{
|
||||
Card,
|
||||
List,
|
||||
Brick
|
||||
}
|
||||
|
||||
public class OverlayPanelDisplayStyleEnumLocalisationMapper : EnumLocalisationMapper<OverlayPanelDisplayStyle>
|
||||
{
|
||||
public override LocalisableString Map(OverlayPanelDisplayStyle value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case OverlayPanelDisplayStyle.Card:
|
||||
return UsersStrings.ViewModeCard;
|
||||
|
||||
case OverlayPanelDisplayStyle.List:
|
||||
return UsersStrings.ViewModeList;
|
||||
|
||||
case OverlayPanelDisplayStyle.Brick:
|
||||
return UsersStrings.ViewModeBrick;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(value), value, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -118,7 +119,7 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
});
|
||||
|
||||
TooltipText = "Scroll to top";
|
||||
TooltipText = CommonStrings.ButtonsBackToTop;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
@ -12,6 +12,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
@ -88,11 +89,11 @@ namespace osu.Game.Overlays
|
||||
SelectedItem.BindValueChanged(_ => updateState(), true);
|
||||
}
|
||||
|
||||
protected abstract string MainText { get; }
|
||||
protected abstract LocalisableString MainText { get; }
|
||||
|
||||
protected abstract string AdditionalText { get; }
|
||||
protected abstract LocalisableString AdditionalText { get; }
|
||||
|
||||
protected virtual string InfoText => string.Empty;
|
||||
protected virtual LocalisableString InfoText => string.Empty;
|
||||
|
||||
protected abstract Color4 GetBarColour(OsuColour colours);
|
||||
|
||||
|
@ -114,8 +114,8 @@ namespace osu.Game.Rulesets.Mods
|
||||
[JsonIgnore]
|
||||
public virtual bool UserPlayable => true;
|
||||
|
||||
[Obsolete("Going forward, the concept of \"ranked\" doesn't exist. The only exceptions are automation mods, which should now override and set UserPlayable to true.")] // Can be removed 20211009
|
||||
public virtual bool IsRanked => false;
|
||||
[Obsolete("Going forward, the concept of \"ranked\" doesn't exist. The only exceptions are automation mods, which should now override and set UserPlayable to false.")] // Can be removed 20211009
|
||||
public virtual bool Ranked => false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this mod requires configuration to apply changes to the game.
|
||||
|
@ -46,7 +46,7 @@ namespace osu.Game.Scoring
|
||||
[JsonIgnore]
|
||||
public int Combo { get; set; } // Todo: Shouldn't exist in here
|
||||
|
||||
[JsonIgnore]
|
||||
[JsonProperty("ruleset_id")]
|
||||
public int RulesetID { get; set; }
|
||||
|
||||
[JsonProperty("passed")]
|
||||
|
@ -0,0 +1,93 @@
|
||||
// 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.Specialized;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Screens.Ranking.Expanded;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Components
|
||||
{
|
||||
public class StarRatingRangeDisplay : OnlinePlayComposite
|
||||
{
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
private StarRatingDisplay minDisplay;
|
||||
private Drawable minBackground;
|
||||
private StarRatingDisplay maxDisplay;
|
||||
private Drawable maxBackground;
|
||||
|
||||
public StarRatingRangeDisplay()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
CornerRadius = 1,
|
||||
Children = new[]
|
||||
{
|
||||
minBackground = new Box
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Size = new Vector2(0.5f),
|
||||
},
|
||||
maxBackground = new Box
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Size = new Vector2(0.5f),
|
||||
},
|
||||
}
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
minDisplay = new StarRatingDisplay(default),
|
||||
maxDisplay = new StarRatingDisplay(default)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Playlist.BindCollectionChanged(updateRange, true);
|
||||
}
|
||||
|
||||
private void updateRange(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
var orderedDifficulties = Playlist.Select(p => p.Beatmap.Value).OrderBy(b => b.StarDifficulty).ToArray();
|
||||
|
||||
StarDifficulty minDifficulty = new StarDifficulty(orderedDifficulties.Length > 0 ? orderedDifficulties[0].StarDifficulty : 0, 0);
|
||||
StarDifficulty maxDifficulty = new StarDifficulty(orderedDifficulties.Length > 0 ? orderedDifficulties[^1].StarDifficulty : 0, 0);
|
||||
|
||||
minDisplay.Current.Value = minDifficulty;
|
||||
maxDisplay.Current.Value = maxDifficulty;
|
||||
|
||||
minBackground.Colour = colours.ForDifficultyRating(minDifficulty.DifficultyRating, true);
|
||||
maxBackground.Colour = colours.ForDifficultyRating(maxDifficulty.DifficultyRating, true);
|
||||
}
|
||||
}
|
||||
}
|
@ -93,7 +93,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
{
|
||||
bool matchingFilter = true;
|
||||
|
||||
matchingFilter &= r.Room.Playlist.Count == 0 || r.Room.Playlist.Any(i => i.Ruleset.Value.Equals(criteria.Ruleset));
|
||||
matchingFilter &= r.Room.Playlist.Count == 0 || criteria.Ruleset == null || r.Room.Playlist.Any(i => i.Ruleset.Value.Equals(criteria.Ruleset));
|
||||
|
||||
if (!string.IsNullOrEmpty(criteria.SearchString))
|
||||
matchingFilter &= r.FilterTerms.Any(term => term.Contains(criteria.SearchString, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
@ -34,7 +34,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
|
||||
public void Stop() => IsRunning = false;
|
||||
|
||||
public bool Seek(double position) => true;
|
||||
public bool Seek(double position)
|
||||
{
|
||||
CurrentTime = position;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ResetSpeedAdjustments()
|
||||
{
|
||||
|
@ -1,8 +1,11 @@
|
||||
// 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;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Timing;
|
||||
|
||||
@ -28,16 +31,22 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
/// </summary>
|
||||
public const double MAXIMUM_START_DELAY = 15000;
|
||||
|
||||
public event Action ReadyToStart;
|
||||
|
||||
/// <summary>
|
||||
/// The master clock which is used to control the timing of all player clocks clocks.
|
||||
/// </summary>
|
||||
public IAdjustableClock MasterClock { get; }
|
||||
|
||||
public IBindable<MasterClockState> MasterState => masterState;
|
||||
|
||||
/// <summary>
|
||||
/// The player clocks.
|
||||
/// </summary>
|
||||
private readonly List<ISpectatorPlayerClock> playerClocks = new List<ISpectatorPlayerClock>();
|
||||
|
||||
private readonly Bindable<MasterClockState> masterState = new Bindable<MasterClockState>();
|
||||
|
||||
private bool hasStarted;
|
||||
private double? firstStartAttemptTime;
|
||||
|
||||
@ -46,7 +55,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
MasterClock = master;
|
||||
}
|
||||
|
||||
public void AddPlayerClock(ISpectatorPlayerClock clock) => playerClocks.Add(clock);
|
||||
public void AddPlayerClock(ISpectatorPlayerClock clock)
|
||||
{
|
||||
Debug.Assert(!playerClocks.Contains(clock));
|
||||
playerClocks.Add(clock);
|
||||
}
|
||||
|
||||
public void RemovePlayerClock(ISpectatorPlayerClock clock) => playerClocks.Remove(clock);
|
||||
|
||||
@ -62,8 +75,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
return;
|
||||
}
|
||||
|
||||
updateCatchup();
|
||||
updateMasterClock();
|
||||
updatePlayerCatchup();
|
||||
updateMasterState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -81,14 +94,20 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
int readyCount = playerClocks.Count(s => !s.WaitingOnFrames.Value);
|
||||
|
||||
if (readyCount == playerClocks.Count)
|
||||
return hasStarted = true;
|
||||
return performStart();
|
||||
|
||||
if (readyCount > 0)
|
||||
{
|
||||
firstStartAttemptTime ??= Time.Current;
|
||||
|
||||
if (Time.Current - firstStartAttemptTime > MAXIMUM_START_DELAY)
|
||||
return hasStarted = true;
|
||||
return performStart();
|
||||
}
|
||||
|
||||
bool performStart()
|
||||
{
|
||||
ReadyToStart?.Invoke();
|
||||
return hasStarted = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -97,7 +116,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
/// <summary>
|
||||
/// Updates the catchup states of all player clocks clocks.
|
||||
/// </summary>
|
||||
private void updateCatchup()
|
||||
private void updatePlayerCatchup()
|
||||
{
|
||||
for (int i = 0; i < playerClocks.Count; i++)
|
||||
{
|
||||
@ -135,19 +154,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the master clock's running state.
|
||||
/// Updates the state of the master clock.
|
||||
/// </summary>
|
||||
private void updateMasterClock()
|
||||
private void updateMasterState()
|
||||
{
|
||||
bool anyInSync = playerClocks.Any(s => !s.IsCatchingUp);
|
||||
|
||||
if (MasterClock.IsRunning != anyInSync)
|
||||
{
|
||||
if (anyInSync)
|
||||
MasterClock.Start();
|
||||
else
|
||||
MasterClock.Stop();
|
||||
}
|
||||
masterState.Value = anyInSync ? MasterClockState.Synchronised : MasterClockState.TooFarAhead;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Timing;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
@ -10,11 +12,21 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
/// </summary>
|
||||
public interface ISyncManager
|
||||
{
|
||||
/// <summary>
|
||||
/// An event which is invoked when gameplay is ready to start.
|
||||
/// </summary>
|
||||
event Action ReadyToStart;
|
||||
|
||||
/// <summary>
|
||||
/// The master clock which player clocks should synchronise to.
|
||||
/// </summary>
|
||||
IAdjustableClock MasterClock { get; }
|
||||
|
||||
/// <summary>
|
||||
/// An event which is invoked when the state of <see cref="MasterClock"/> is changed.
|
||||
/// </summary>
|
||||
IBindable<MasterClockState> MasterState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds an <see cref="ISpectatorPlayerClock"/> to manage.
|
||||
/// </summary>
|
||||
|
@ -0,0 +1,18 @@
|
||||
// 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.
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
{
|
||||
public enum MasterClockState
|
||||
{
|
||||
/// <summary>
|
||||
/// The master clock is synchronised with at least one player clock.
|
||||
/// </summary>
|
||||
Synchronised,
|
||||
|
||||
/// <summary>
|
||||
/// The master clock is too far ahead of any player clock and needs to slow down.
|
||||
/// </summary>
|
||||
TooFarAhead
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ using System;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
@ -42,6 +43,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
private PlayerGrid grid;
|
||||
private MultiSpectatorLeaderboard leaderboard;
|
||||
private PlayerArea currentAudioSource;
|
||||
private bool canStartMasterClock;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="MultiSpectatorScreen"/>.
|
||||
@ -100,15 +102,24 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
Expanded = { Value = true },
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
}, leaderboardContainer.Add);
|
||||
}, l =>
|
||||
{
|
||||
foreach (var instance in instances)
|
||||
leaderboard.AddClock(instance.UserId, instance.GameplayClock);
|
||||
|
||||
leaderboardContainer.Add(leaderboard);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
masterClockContainer.Stop();
|
||||
masterClockContainer.Reset();
|
||||
masterClockContainer.Stop();
|
||||
|
||||
syncManager.ReadyToStart += onReadyToStart;
|
||||
syncManager.MasterState.BindValueChanged(onMasterStateChanged, true);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
@ -129,19 +140,45 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
private bool isCandidateAudioSource([CanBeNull] ISpectatorPlayerClock clock)
|
||||
=> clock?.IsRunning == true && !clock.IsCatchingUp && !clock.WaitingOnFrames.Value;
|
||||
|
||||
private void onReadyToStart()
|
||||
{
|
||||
// Seek the master clock to the gameplay time.
|
||||
// This is chosen as the first available frame in the players' replays, which matches the seek by each individual SpectatorPlayer.
|
||||
var startTime = instances.Where(i => i.Score != null)
|
||||
.SelectMany(i => i.Score.Replay.Frames)
|
||||
.Select(f => f.Time)
|
||||
.DefaultIfEmpty(0)
|
||||
.Min();
|
||||
|
||||
masterClockContainer.Seek(startTime);
|
||||
masterClockContainer.Start();
|
||||
|
||||
// Although the clock has been started, this flag is set to allow for later synchronisation state changes to also be able to start it.
|
||||
canStartMasterClock = true;
|
||||
}
|
||||
|
||||
private void onMasterStateChanged(ValueChangedEvent<MasterClockState> state)
|
||||
{
|
||||
switch (state.NewValue)
|
||||
{
|
||||
case MasterClockState.Synchronised:
|
||||
if (canStartMasterClock)
|
||||
masterClockContainer.Start();
|
||||
|
||||
break;
|
||||
|
||||
case MasterClockState.TooFarAhead:
|
||||
masterClockContainer.Stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnUserStateChanged(int userId, SpectatorState spectatorState)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void StartGameplay(int userId, GameplayState gameplayState)
|
||||
{
|
||||
var instance = instances.Single(i => i.UserId == userId);
|
||||
|
||||
instance.LoadScore(gameplayState.Score);
|
||||
|
||||
syncManager.AddPlayerClock(instance.GameplayClock);
|
||||
leaderboard.AddClock(instance.UserId, instance.GameplayClock);
|
||||
}
|
||||
=> instances.Single(i => i.UserId == userId).LoadScore(gameplayState.Score);
|
||||
|
||||
protected override void EndGameplay(int userId)
|
||||
{
|
||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
/// <summary>
|
||||
/// Whether a <see cref="Player"/> is loaded in the area.
|
||||
/// </summary>
|
||||
public bool PlayerLoaded => stack?.CurrentScreen is Player;
|
||||
public bool PlayerLoaded => (stack?.CurrentScreen as Player)?.IsLoaded == true;
|
||||
|
||||
/// <summary>
|
||||
/// The user id this <see cref="PlayerArea"/> corresponds to.
|
||||
|
@ -47,8 +47,9 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
base.StartGameplay();
|
||||
|
||||
// Start gameplay along with the very first arrival frame (the latest one).
|
||||
score.Replay.Frames.Clear();
|
||||
spectatorClient.OnNewFrames += userSentFrames;
|
||||
seekToGameplay();
|
||||
}
|
||||
|
||||
private void userSentFrames(int userId, FrameDataBundle bundle)
|
||||
@ -62,6 +63,8 @@ namespace osu.Game.Screens.Play
|
||||
if (!this.IsCurrentScreen())
|
||||
return;
|
||||
|
||||
bool isFirstBundle = score.Replay.Frames.Count == 0;
|
||||
|
||||
foreach (var frame in bundle.Frames)
|
||||
{
|
||||
IConvertibleReplayFrame convertibleFrame = GameplayRuleset.CreateConvertibleReplayFrame();
|
||||
@ -73,19 +76,8 @@ namespace osu.Game.Screens.Play
|
||||
score.Replay.Frames.Add(convertedFrame);
|
||||
}
|
||||
|
||||
seekToGameplay();
|
||||
}
|
||||
|
||||
private bool seekedToGameplay;
|
||||
|
||||
private void seekToGameplay()
|
||||
{
|
||||
if (seekedToGameplay || score.Replay.Frames.Count == 0)
|
||||
return;
|
||||
|
||||
NonFrameStableSeek(score.Replay.Frames[0].Time);
|
||||
|
||||
seekedToGameplay = true;
|
||||
if (isFirstBundle && score.Replay.Frames.Count > 0)
|
||||
NonFrameStableSeek(score.Replay.Frames[0].Time);
|
||||
}
|
||||
|
||||
protected override Score CreateScore() => score;
|
||||
|
@ -4,9 +4,7 @@
|
||||
using System.Globalization;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
@ -111,12 +109,9 @@ namespace osu.Game.Screens.Ranking.Expanded
|
||||
|
||||
var rating = Current.Value.DifficultyRating;
|
||||
|
||||
background.Colour = rating == DifficultyRating.ExpertPlus
|
||||
? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959"))
|
||||
: (ColourInfo)colours.ForDifficultyRating(rating);
|
||||
background.Colour = colours.ForDifficultyRating(rating, true);
|
||||
|
||||
textFlow.Clear();
|
||||
|
||||
textFlow.AddText($"{wholePart}", s =>
|
||||
{
|
||||
s.Colour = Color4.Black;
|
||||
|
@ -127,6 +127,7 @@ namespace osu.Game.Skinning.Editor
|
||||
public override bool HandleFlip(Direction direction)
|
||||
{
|
||||
var selectionQuad = getSelectionQuad();
|
||||
Vector2 scaleFactor = direction == Direction.Horizontal ? new Vector2(-1, 1) : new Vector2(1, -1);
|
||||
|
||||
foreach (var b in SelectedBlueprints)
|
||||
{
|
||||
@ -136,10 +137,8 @@ namespace osu.Game.Skinning.Editor
|
||||
|
||||
updateDrawablePosition(drawableItem, flippedPosition);
|
||||
|
||||
drawableItem.Scale *= new Vector2(
|
||||
direction == Direction.Horizontal ? -1 : 1,
|
||||
direction == Direction.Vertical ? -1 : 1
|
||||
);
|
||||
drawableItem.Scale *= scaleFactor;
|
||||
drawableItem.Rotation -= drawableItem.Rotation % 180 * 2;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
57
osu.Game/Skinning/ResourceStoreBackedSkin.cs
Normal file
57
osu.Game/Skinning/ResourceStoreBackedSkin.cs
Normal file
@ -0,0 +1,57 @@
|
||||
// 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 enable
|
||||
|
||||
using System;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.OpenGL.Textures;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Audio;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="ISkin"/> that uses an underlying <see cref="IResourceStore{T}"/> with namespaces for resources retrieval.
|
||||
/// </summary>
|
||||
public class ResourceStoreBackedSkin : ISkin, IDisposable
|
||||
{
|
||||
private readonly TextureStore textures;
|
||||
private readonly ISampleStore samples;
|
||||
|
||||
public ResourceStoreBackedSkin(IResourceStore<byte[]> resources, GameHost host, AudioManager audio)
|
||||
{
|
||||
textures = new TextureStore(host.CreateTextureLoaderStore(new NamespacedResourceStore<byte[]>(resources, @"Textures")));
|
||||
samples = audio.GetSampleStore(new NamespacedResourceStore<byte[]>(resources, @"Samples"));
|
||||
}
|
||||
|
||||
public Drawable? GetDrawableComponent(ISkinComponent component) => null;
|
||||
|
||||
public Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => textures.Get(componentName, wrapModeS, wrapModeT);
|
||||
|
||||
public ISample? GetSample(ISampleInfo sampleInfo)
|
||||
{
|
||||
foreach (var lookup in sampleInfo.LookupNames)
|
||||
{
|
||||
ISample? sample = samples.Get(lookup);
|
||||
if (sample != null)
|
||||
return sample;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup) => null;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
textures.Dispose();
|
||||
samples.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,14 @@
|
||||
// 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 JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.UI;
|
||||
@ -44,11 +48,16 @@ namespace osu.Game.Skinning
|
||||
|
||||
private ISkinSource parentSource;
|
||||
|
||||
private ResourceStoreBackedSkin rulesetResourcesSkin;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
parentSource = parent.Get<ISkinSource>();
|
||||
parentSource.SourceChanged += OnSourceChanged;
|
||||
|
||||
if (Ruleset.CreateResourceStore() is IResourceStore<byte[]> resources)
|
||||
rulesetResourcesSkin = new ResourceStoreBackedSkin(resources, parent.Get<GameHost>(), parent.Get<AudioManager>());
|
||||
|
||||
// ensure sources are populated and ready for use before childrens' asynchronous load flow.
|
||||
UpdateSkinSources();
|
||||
|
||||
@ -78,6 +87,16 @@ namespace osu.Game.Skinning
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int lastDefaultSkinIndex = SkinSources.IndexOf(SkinSources.OfType<DefaultSkin>().LastOrDefault());
|
||||
|
||||
// Ruleset resources should be given the ability to override game-wide defaults
|
||||
// This is achieved by placing them before the last instance of DefaultSkin.
|
||||
// Note that DefaultSkin may not be present in some test scenes.
|
||||
if (lastDefaultSkinIndex >= 0)
|
||||
SkinSources.Insert(lastDefaultSkinIndex, rulesetResourcesSkin);
|
||||
else
|
||||
SkinSources.Add(rulesetResourcesSkin);
|
||||
}
|
||||
|
||||
protected ISkin GetLegacyRulesetTransformedSkin(ISkin legacySkin)
|
||||
@ -98,6 +117,8 @@ namespace osu.Game.Skinning
|
||||
|
||||
if (parentSource != null)
|
||||
parentSource.SourceChanged -= OnSourceChanged;
|
||||
|
||||
rulesetResourcesSkin?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
// 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.Game.Database;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osu.Game.Tests.Visual.Spectator;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface that defines the dependencies required for multiplayer test scenes.
|
||||
/// </summary>
|
||||
public interface IMultiplayerTestSceneDependencies : IOnlinePlayTestSceneDependencies
|
||||
{
|
||||
/// <summary>
|
||||
/// The cached <see cref="MultiplayerClient"/>.
|
||||
/// </summary>
|
||||
TestMultiplayerClient Client { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The cached <see cref="IRoomManager"/>.
|
||||
/// </summary>
|
||||
new TestMultiplayerRoomManager RoomManager { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The cached <see cref="UserLookupCache"/>.
|
||||
/// </summary>
|
||||
TestUserLookupCache LookupCache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The cached <see cref="osu.Game.Online.Spectator.SpectatorClient"/>.
|
||||
/// </summary>
|
||||
TestSpectatorClient SpectatorClient { get; }
|
||||
}
|
||||
}
|
@ -2,66 +2,55 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osu.Game.Tests.Visual.Spectator;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public abstract class MultiplayerTestScene : RoomTestScene
|
||||
/// <summary>
|
||||
/// The base test scene for all multiplayer components and screens.
|
||||
/// </summary>
|
||||
public abstract class MultiplayerTestScene : OnlinePlayTestScene, IMultiplayerTestSceneDependencies
|
||||
{
|
||||
public const int PLAYER_1_ID = 55;
|
||||
public const int PLAYER_2_ID = 56;
|
||||
|
||||
[Cached(typeof(MultiplayerClient))]
|
||||
public TestMultiplayerClient Client { get; }
|
||||
public TestMultiplayerClient Client => OnlinePlayDependencies.Client;
|
||||
public new TestMultiplayerRoomManager RoomManager => OnlinePlayDependencies.RoomManager;
|
||||
public TestUserLookupCache LookupCache => OnlinePlayDependencies?.LookupCache;
|
||||
public TestSpectatorClient SpectatorClient => OnlinePlayDependencies?.SpectatorClient;
|
||||
|
||||
[Cached(typeof(IRoomManager))]
|
||||
public TestMultiplayerRoomManager RoomManager { get; }
|
||||
|
||||
[Cached]
|
||||
public Bindable<FilterCriteria> Filter { get; }
|
||||
|
||||
[Cached]
|
||||
public OngoingOperationTracker OngoingOperationTracker { get; }
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
private readonly TestMultiplayerRoomContainer content;
|
||||
protected new MultiplayerTestSceneDependencies OnlinePlayDependencies => (MultiplayerTestSceneDependencies)base.OnlinePlayDependencies;
|
||||
|
||||
private readonly bool joinRoom;
|
||||
|
||||
protected MultiplayerTestScene(bool joinRoom = true)
|
||||
{
|
||||
this.joinRoom = joinRoom;
|
||||
base.Content.Add(content = new TestMultiplayerRoomContainer { RelativeSizeAxes = Axes.Both });
|
||||
|
||||
Client = content.Client;
|
||||
RoomManager = content.RoomManager;
|
||||
Filter = content.Filter;
|
||||
OngoingOperationTracker = content.OngoingOperationTracker;
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
RoomManager.Schedule(() => RoomManager.PartRoom());
|
||||
|
||||
if (joinRoom)
|
||||
{
|
||||
Room.Name.Value = "test name";
|
||||
Room.Playlist.Add(new PlaylistItem
|
||||
var room = new Room
|
||||
{
|
||||
Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo },
|
||||
Ruleset = { Value = Ruleset.Value }
|
||||
});
|
||||
Name = { Value = "test name" },
|
||||
Playlist =
|
||||
{
|
||||
new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo },
|
||||
Ruleset = { Value = Ruleset.Value }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
RoomManager.Schedule(() => RoomManager.CreateRoom(Room));
|
||||
RoomManager.CreateRoom(room);
|
||||
SelectedRoom.Value = room;
|
||||
}
|
||||
});
|
||||
|
||||
@ -72,5 +61,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
if (joinRoom)
|
||||
AddUntilStep("wait for room join", () => Client.Room != null);
|
||||
}
|
||||
|
||||
protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new MultiplayerTestSceneDependencies();
|
||||
}
|
||||
}
|
||||
|
@ -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.Game.Database;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Spectator;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osu.Game.Tests.Visual.Spectator;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the basic dependencies of multiplayer test scenes.
|
||||
/// </summary>
|
||||
public class MultiplayerTestSceneDependencies : OnlinePlayTestSceneDependencies, IMultiplayerTestSceneDependencies
|
||||
{
|
||||
public TestMultiplayerClient Client { get; }
|
||||
public TestUserLookupCache LookupCache { get; }
|
||||
public TestSpectatorClient SpectatorClient { get; }
|
||||
public new TestMultiplayerRoomManager RoomManager => (TestMultiplayerRoomManager)base.RoomManager;
|
||||
|
||||
public MultiplayerTestSceneDependencies()
|
||||
{
|
||||
Client = new TestMultiplayerClient(RoomManager);
|
||||
LookupCache = new TestUserLookupCache();
|
||||
SpectatorClient = CreateSpectatorClient();
|
||||
|
||||
CacheAs<MultiplayerClient>(Client);
|
||||
CacheAs<UserLookupCache>(LookupCache);
|
||||
CacheAs<SpectatorClient>(SpectatorClient);
|
||||
}
|
||||
|
||||
protected override IRoomManager CreateRoomManager() => new TestMultiplayerRoomManager();
|
||||
|
||||
protected virtual TestSpectatorClient CreateSpectatorClient() => new TestSpectatorClient();
|
||||
}
|
||||
}
|
@ -20,6 +20,9 @@ using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="MultiplayerClient"/> for use in multiplayer test scenes. Should generally not be used by itself outside of a <see cref="MultiplayerTestScene"/>.
|
||||
/// </summary>
|
||||
public class TestMultiplayerClient : MultiplayerClient
|
||||
{
|
||||
public override IBindable<bool> IsConnected => isConnected;
|
||||
|
@ -1,48 +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.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestMultiplayerRoomContainer : Container
|
||||
{
|
||||
protected override Container<Drawable> Content => content;
|
||||
private readonly Container content;
|
||||
|
||||
[Cached(typeof(MultiplayerClient))]
|
||||
public readonly TestMultiplayerClient Client;
|
||||
|
||||
[Cached(typeof(IRoomManager))]
|
||||
public readonly TestMultiplayerRoomManager RoomManager;
|
||||
|
||||
[Cached]
|
||||
public readonly Bindable<FilterCriteria> Filter = new Bindable<FilterCriteria>(new FilterCriteria());
|
||||
|
||||
[Cached]
|
||||
public readonly OngoingOperationTracker OngoingOperationTracker;
|
||||
|
||||
public TestMultiplayerRoomContainer()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
RoomManager = new TestMultiplayerRoomManager();
|
||||
Client = new TestMultiplayerClient(RoomManager);
|
||||
OngoingOperationTracker = new OngoingOperationTracker();
|
||||
|
||||
AddRangeInternal(new Drawable[]
|
||||
{
|
||||
Client,
|
||||
RoomManager,
|
||||
OngoingOperationTracker,
|
||||
content = new Container { RelativeSizeAxes = Axes.Both }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -11,11 +11,15 @@ using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="RoomManager"/> for use in multiplayer test scenes. Should generally not be used by itself outside of a <see cref="MultiplayerTestScene"/>.
|
||||
/// </summary>
|
||||
public class TestMultiplayerRoomManager : MultiplayerRoomManager
|
||||
{
|
||||
[Resolved]
|
||||
@ -29,10 +33,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
public new readonly List<Room> Rooms = new List<Room>();
|
||||
|
||||
protected override void LoadComplete()
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
int currentScoreId = 0;
|
||||
int currentRoomId = 0;
|
||||
int currentPlaylistItemId = 0;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user