mirror of
https://github.com/ppy/osu.git
synced 2025-01-12 22:33:05 +08:00
Merge branch 'master' into mp-countdown-sfx
This commit is contained in:
commit
64ba24c4f7
@ -52,7 +52,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.404.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.404.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.325.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.404.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||||
|
@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
||||||
|
|
||||||
[TestCase(4.0505463516206195d, "diffcalc-test")]
|
[TestCase(4.0505463516206195d, 127, "diffcalc-test")]
|
||||||
public void Test(double expected, string name)
|
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> base.Test(expected, name);
|
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
||||||
|
|
||||||
[TestCase(5.1696411260785498d, "diffcalc-test")]
|
[TestCase(5.1696411260785498d, 127, "diffcalc-test")]
|
||||||
public void TestClockRateAdjusted(double expected, string name)
|
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> Test(expected, name, new CatchModDoubleTime());
|
=> Test(expectedStarRating, expectedMaxCombo, name, new CatchModDoubleTime());
|
||||||
|
|
||||||
protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset().RulesetInfo, beatmap);
|
protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset().RulesetInfo, beatmap);
|
||||||
|
|
||||||
|
@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
||||||
|
|
||||||
[TestCase(2.3449735700206298d, "diffcalc-test")]
|
[TestCase(2.3449735700206298d, 151, "diffcalc-test")]
|
||||||
public void Test(double expected, string name)
|
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> base.Test(expected, name);
|
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
||||||
|
|
||||||
[TestCase(2.7879104989252959d, "diffcalc-test")]
|
[TestCase(2.7879104989252959d, 151, "diffcalc-test")]
|
||||||
public void TestClockRateAdjusted(double expected, string name)
|
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> Test(expected, name, new ManiaModDoubleTime());
|
=> Test(expectedStarRating, expectedMaxCombo, name, new ManiaModDoubleTime());
|
||||||
|
|
||||||
protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset().RulesetInfo, beatmap);
|
protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset().RulesetInfo, beatmap);
|
||||||
|
|
||||||
|
@ -15,15 +15,20 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
||||||
|
|
||||||
[TestCase(6.6972307565739273d, "diffcalc-test")]
|
[TestCase(6.6972307565739273d, 206, "diffcalc-test")]
|
||||||
[TestCase(1.4484754139145539d, "zero-length-sliders")]
|
[TestCase(1.4484754139145539d, 45, "zero-length-sliders")]
|
||||||
public void Test(double expected, string name)
|
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> base.Test(expected, name);
|
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
||||||
|
|
||||||
[TestCase(8.9382559208689809d, "diffcalc-test")]
|
[TestCase(8.9382559208689809d, 206, "diffcalc-test")]
|
||||||
[TestCase(1.7548875851757628d, "zero-length-sliders")]
|
[TestCase(1.7548875851757628d, 45, "zero-length-sliders")]
|
||||||
public void TestClockRateAdjusted(double expected, string name)
|
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> Test(expected, name, new OsuModDoubleTime());
|
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime());
|
||||||
|
|
||||||
|
[TestCase(6.6972307218715166d, 239, "diffcalc-test")]
|
||||||
|
[TestCase(1.4484754139145537d, 54, "zero-length-sliders")]
|
||||||
|
public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
|
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic());
|
||||||
|
|
||||||
protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset().RulesetInfo, beatmap);
|
protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset().RulesetInfo, beatmap);
|
||||||
|
|
||||||
|
@ -61,10 +61,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
||||||
double drainRate = beatmap.Difficulty.DrainRate;
|
double drainRate = beatmap.Difficulty.DrainRate;
|
||||||
|
int maxCombo = beatmap.GetMaxCombo();
|
||||||
int maxCombo = beatmap.HitObjects.Count;
|
|
||||||
// Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
|
|
||||||
maxCombo += beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count - 1);
|
|
||||||
|
|
||||||
int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle);
|
int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle);
|
||||||
int sliderCount = beatmap.HitObjects.Count(h => h is Slider);
|
int sliderCount = beatmap.HitObjects.Count(h => h is Slider);
|
||||||
|
@ -14,15 +14,15 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
|
||||||
|
|
||||||
[TestCase(2.2420075288523802d, "diffcalc-test")]
|
[TestCase(2.2420075288523802d, 200, "diffcalc-test")]
|
||||||
[TestCase(2.2420075288523802d, "diffcalc-test-strong")]
|
[TestCase(2.2420075288523802d, 200, "diffcalc-test-strong")]
|
||||||
public void Test(double expected, string name)
|
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> base.Test(expected, name);
|
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
||||||
|
|
||||||
[TestCase(3.134084469440479d, "diffcalc-test")]
|
[TestCase(3.134084469440479d, 200, "diffcalc-test")]
|
||||||
[TestCase(3.134084469440479d, "diffcalc-test-strong")]
|
[TestCase(3.134084469440479d, 200, "diffcalc-test-strong")]
|
||||||
public void TestClockRateAdjusted(double expected, string name)
|
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> Test(expected, name, new TaikoModDoubleTime());
|
=> Test(expectedStarRating, expectedMaxCombo, name, new TaikoModDoubleTime());
|
||||||
|
|
||||||
protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset().RulesetInfo, beatmap);
|
protected override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset().RulesetInfo, beatmap);
|
||||||
|
|
||||||
|
@ -2,10 +2,13 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Overlays.Toolbar;
|
using osu.Game.Overlays.Toolbar;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -15,7 +18,10 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneToolbarClock : OsuManualInputManagerTestScene
|
public class TestSceneToolbarClock : OsuManualInputManagerTestScene
|
||||||
{
|
{
|
||||||
|
private Bindable<ToolbarClockDisplayMode> clockDisplayMode;
|
||||||
|
|
||||||
private readonly Container mainContainer;
|
private readonly Container mainContainer;
|
||||||
|
private readonly ToolbarClock toolbarClock;
|
||||||
|
|
||||||
public TestSceneToolbarClock()
|
public TestSceneToolbarClock()
|
||||||
{
|
{
|
||||||
@ -49,7 +55,7 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Width = 2,
|
Width = 2,
|
||||||
},
|
},
|
||||||
new ToolbarClock(),
|
toolbarClock = new ToolbarClock(),
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
Colour = Color4.DarkRed,
|
Colour = Color4.DarkRed,
|
||||||
@ -65,6 +71,12 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
AddSliderStep("scale", 0.5, 4, 1, scale => mainContainer.Scale = new Vector2((float)scale));
|
AddSliderStep("scale", 0.5, 4, 1, scale => mainContainer.Scale = new Vector2((float)scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuConfigManager config)
|
||||||
|
{
|
||||||
|
clockDisplayMode = config.GetBindable<ToolbarClockDisplayMode>(OsuSetting.ToolbarClockDisplayMode);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestRealGameTime()
|
public void TestRealGameTime()
|
||||||
{
|
{
|
||||||
@ -76,5 +88,20 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
{
|
{
|
||||||
AddStep("Set game time long", () => mainContainer.Clock = new FramedOffsetClock(Clock, false) { Offset = 3600.0 * 24 * 1000 * 98 });
|
AddStep("Set game time long", () => mainContainer.Clock = new FramedOffsetClock(Clock, false) { Offset = 3600.0 * 24 * 1000 * 98 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDisplayModeChange()
|
||||||
|
{
|
||||||
|
AddStep("Set clock display mode", () => clockDisplayMode.Value = ToolbarClockDisplayMode.Full);
|
||||||
|
|
||||||
|
AddStep("Trigger click", () => toolbarClock.TriggerClick());
|
||||||
|
AddAssert("State is digital with runtime", () => clockDisplayMode.Value == ToolbarClockDisplayMode.DigitalWithRuntime);
|
||||||
|
AddStep("Trigger click", () => toolbarClock.TriggerClick());
|
||||||
|
AddAssert("State is digital", () => clockDisplayMode.Value == ToolbarClockDisplayMode.Digital);
|
||||||
|
AddStep("Trigger click", () => toolbarClock.TriggerClick());
|
||||||
|
AddAssert("State is analog", () => clockDisplayMode.Value == ToolbarClockDisplayMode.Analog);
|
||||||
|
AddStep("Trigger click", () => toolbarClock.TriggerClick());
|
||||||
|
AddAssert("State is full", () => clockDisplayMode.Value == ToolbarClockDisplayMode.Full);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ using osu.Framework.Testing;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
@ -183,14 +184,41 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
assertItemInHistoryListStep(2, 0);
|
assertItemInHistoryListStep(2, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestInsertedItemDoesNotRefreshAllOthers()
|
||||||
|
{
|
||||||
|
AddStep("change to round robin queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayersRoundRobin }).WaitSafely());
|
||||||
|
|
||||||
|
// Add a few items for the local user.
|
||||||
|
addItemStep();
|
||||||
|
addItemStep();
|
||||||
|
addItemStep();
|
||||||
|
addItemStep();
|
||||||
|
addItemStep();
|
||||||
|
|
||||||
|
DrawableRoomPlaylistItem[] drawableItems = null;
|
||||||
|
AddStep("get drawable items", () => drawableItems = this.ChildrenOfType<DrawableRoomPlaylistItem>().ToArray());
|
||||||
|
|
||||||
|
// Add 1 item for another user.
|
||||||
|
AddStep("join second user", () => MultiplayerClient.AddUser(new APIUser { Id = 10 }));
|
||||||
|
addItemStep(userId: 10);
|
||||||
|
|
||||||
|
// New item inserted towards the top of the list.
|
||||||
|
assertItemInQueueListStep(7, 1);
|
||||||
|
AddAssert("all previous playlist items remained", () => drawableItems.All(this.ChildrenOfType<DrawableRoomPlaylistItem>().Contains));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a step to create a new playlist item.
|
/// Adds a step to create a new playlist item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void addItemStep(bool expired = false) => AddStep("add item", () => MultiplayerClient.AddPlaylistItem(new MultiplayerPlaylistItem(new PlaylistItem(importedBeatmap)
|
private void addItemStep(bool expired = false, int? userId = null) => AddStep("add item", () =>
|
||||||
{
|
{
|
||||||
Expired = expired,
|
MultiplayerClient.AddUserPlaylistItem(userId ?? API.LocalUser.Value.OnlineID, new MultiplayerPlaylistItem(new PlaylistItem(importedBeatmap)
|
||||||
PlayedAt = DateTimeOffset.Now
|
{
|
||||||
})));
|
Expired = expired,
|
||||||
|
PlayedAt = DateTimeOffset.Now
|
||||||
|
})).WaitSafely();
|
||||||
|
});
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Asserts the position of a given playlist item in the queue list.
|
/// Asserts the position of a given playlist item in the queue list.
|
||||||
|
@ -5,9 +5,11 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps.Drawables.Cards;
|
using osu.Game.Beatmaps.Drawables.Cards;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
@ -29,6 +31,14 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
private BeatmapListingSearchControl searchControl => overlay.ChildrenOfType<BeatmapListingSearchControl>().Single();
|
private BeatmapListingSearchControl searchControl => overlay.ChildrenOfType<BeatmapListingSearchControl>().Single();
|
||||||
|
|
||||||
|
private OsuConfigManager localConfig;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Dependencies.Cache(localConfig = new OsuConfigManager(LocalStorage));
|
||||||
|
}
|
||||||
|
|
||||||
[SetUpSteps]
|
[SetUpSteps]
|
||||||
public void SetUpSteps()
|
public void SetUpSteps()
|
||||||
{
|
{
|
||||||
@ -61,6 +71,8 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Id = API.LocalUser.Value.Id + 1,
|
Id = API.LocalUser.Value.Id + 1,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddStep("reset size", () => localConfig.SetValue(OsuSetting.BeatmapListingCardSize, BeatmapCardSize.Normal));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -121,23 +133,23 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCardSizeSwitching()
|
public void TestCardSizeSwitching([Values] bool viaConfig)
|
||||||
{
|
{
|
||||||
AddAssert("is visible", () => overlay.State.Value == Visibility.Visible);
|
AddAssert("is visible", () => overlay.State.Value == Visibility.Visible);
|
||||||
|
|
||||||
AddStep("show many results", () => fetchFor(Enumerable.Repeat(CreateAPIBeatmapSet(Ruleset.Value), 100).ToArray()));
|
AddStep("show many results", () => fetchFor(Enumerable.Repeat(CreateAPIBeatmapSet(Ruleset.Value), 100).ToArray()));
|
||||||
assertAllCardsOfType<BeatmapCardNormal>(100);
|
assertAllCardsOfType<BeatmapCardNormal>(100);
|
||||||
|
|
||||||
setCardSize(BeatmapCardSize.Extra);
|
setCardSize(BeatmapCardSize.Extra, viaConfig);
|
||||||
assertAllCardsOfType<BeatmapCardExtra>(100);
|
assertAllCardsOfType<BeatmapCardExtra>(100);
|
||||||
|
|
||||||
setCardSize(BeatmapCardSize.Normal);
|
setCardSize(BeatmapCardSize.Normal, viaConfig);
|
||||||
assertAllCardsOfType<BeatmapCardNormal>(100);
|
assertAllCardsOfType<BeatmapCardNormal>(100);
|
||||||
|
|
||||||
AddStep("fetch for 0 beatmaps", () => fetchFor());
|
AddStep("fetch for 0 beatmaps", () => fetchFor());
|
||||||
AddUntilStep("placeholder shown", () => overlay.ChildrenOfType<BeatmapListingOverlay.NotFoundDrawable>().SingleOrDefault()?.IsPresent == true);
|
AddUntilStep("placeholder shown", () => overlay.ChildrenOfType<BeatmapListingOverlay.NotFoundDrawable>().SingleOrDefault()?.IsPresent == true);
|
||||||
|
|
||||||
setCardSize(BeatmapCardSize.Extra);
|
setCardSize(BeatmapCardSize.Extra, viaConfig);
|
||||||
AddAssert("placeholder shown", () => overlay.ChildrenOfType<BeatmapListingOverlay.NotFoundDrawable>().SingleOrDefault()?.IsPresent == true);
|
AddAssert("placeholder shown", () => overlay.ChildrenOfType<BeatmapListingOverlay.NotFoundDrawable>().SingleOrDefault()?.IsPresent == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,7 +373,13 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
AddUntilStep("\"no maps found\" placeholder not shown", () => !overlay.ChildrenOfType<BeatmapListingOverlay.NotFoundDrawable>().Any(d => d.IsPresent));
|
AddUntilStep("\"no maps found\" placeholder not shown", () => !overlay.ChildrenOfType<BeatmapListingOverlay.NotFoundDrawable>().Any(d => d.IsPresent));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCardSize(BeatmapCardSize cardSize) => AddStep($"set card size to {cardSize}", () => overlay.ChildrenOfType<BeatmapListingCardSizeTabControl>().Single().Current.Value = cardSize);
|
private void setCardSize(BeatmapCardSize cardSize, bool viaConfig) => AddStep($"set card size to {cardSize}", () =>
|
||||||
|
{
|
||||||
|
if (viaConfig)
|
||||||
|
localConfig.SetValue(OsuSetting.BeatmapListingCardSize, cardSize);
|
||||||
|
else
|
||||||
|
overlay.ChildrenOfType<BeatmapListingCardSizeTabControl>().Single().Current.Value = cardSize;
|
||||||
|
});
|
||||||
|
|
||||||
private void assertAllCardsOfType<T>(int expectedCount)
|
private void assertAllCardsOfType<T>(int expectedCount)
|
||||||
where T : BeatmapCard =>
|
where T : BeatmapCard =>
|
||||||
@ -370,5 +388,11 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
int loadedCorrectCount = this.ChildrenOfType<BeatmapCard>().Count(card => card.IsLoaded && card.GetType() == typeof(T));
|
int loadedCorrectCount = this.ChildrenOfType<BeatmapCard>().Count(card => card.IsLoaded && card.GetType() == typeof(T));
|
||||||
return loadedCorrectCount > 0 && loadedCorrectCount == expectedCount;
|
return loadedCorrectCount > 0 && loadedCorrectCount == expectedCount;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
localConfig?.Dispose();
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,9 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(),
|
Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(),
|
Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(),
|
||||||
}
|
},
|
||||||
|
PassCount = RNG.Next(0, 999),
|
||||||
|
PlayCount = RNG.Next(1000, 1999),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
122
osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs
Normal file
122
osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Online.Chat;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Chat;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneChatTextBox : OsuTestScene
|
||||||
|
{
|
||||||
|
[Cached]
|
||||||
|
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink);
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private readonly Bindable<Channel> currentChannel = new Bindable<Channel>();
|
||||||
|
|
||||||
|
private OsuSpriteText commitText;
|
||||||
|
private OsuSpriteText searchText;
|
||||||
|
private ChatTextBar bar;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp()
|
||||||
|
{
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
Child = new GridContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
RowDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(GridSizeMode.Absolute, 30),
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
},
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
ColumnDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(),
|
||||||
|
new Dimension(),
|
||||||
|
},
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
commitText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Font = OsuFont.Default.With(size: 20),
|
||||||
|
},
|
||||||
|
searchText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Font = OsuFont.Default.With(size: 20),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
bar = new ChatTextBar
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Width = 0.99f,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
bar.OnChatMessageCommitted += text =>
|
||||||
|
{
|
||||||
|
commitText.Text = $"{nameof(bar.OnChatMessageCommitted)}: {text}";
|
||||||
|
commitText.FadeOutFromOne(1000, Easing.InQuint);
|
||||||
|
};
|
||||||
|
|
||||||
|
bar.OnSearchTermsChanged += text =>
|
||||||
|
{
|
||||||
|
searchText.Text = $"{nameof(bar.OnSearchTermsChanged)}: {text}";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestVisual()
|
||||||
|
{
|
||||||
|
AddStep("Public Channel", () => currentChannel.Value = createPublicChannel("#osu"));
|
||||||
|
AddStep("Public Channel Long Name", () => currentChannel.Value = createPublicChannel("#public-channel-long-name"));
|
||||||
|
AddStep("Private Channel", () => currentChannel.Value = createPrivateChannel("peppy", 2));
|
||||||
|
AddStep("Private Long Name", () => currentChannel.Value = createPrivateChannel("test user long name", 3));
|
||||||
|
|
||||||
|
AddStep("Chat Mode Channel", () => bar.ShowSearch.Value = false);
|
||||||
|
AddStep("Chat Mode Search", () => bar.ShowSearch.Value = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Channel createPublicChannel(string name)
|
||||||
|
=> new Channel { Name = name, Type = ChannelType.Public, Id = 1234 };
|
||||||
|
|
||||||
|
private static Channel createPrivateChannel(string username, int id)
|
||||||
|
=> new Channel(new APIUser { Id = id, Username = username });
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
|||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
{
|
{
|
||||||
@ -70,4 +71,27 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
new IReadOnlyList<T> HitObjects { get; }
|
new IReadOnlyList<T> HitObjects { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class BeatmapExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Finds the maximum achievable combo by hitting all <see cref="HitObject"/>s in a beatmap.
|
||||||
|
/// </summary>
|
||||||
|
public static int GetMaxCombo(this IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
int combo = 0;
|
||||||
|
foreach (var h in beatmap.HitObjects)
|
||||||
|
addCombo(h, ref combo);
|
||||||
|
return combo;
|
||||||
|
|
||||||
|
static void addCombo(HitObject hitObject, ref int combo)
|
||||||
|
{
|
||||||
|
if (hitObject.CreateJudgement().MaxResult.AffectsCombo())
|
||||||
|
combo++;
|
||||||
|
|
||||||
|
foreach (var nested in hitObject.NestedHitObjects)
|
||||||
|
addCombo(nested, ref combo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ using osu.Framework.Extensions.LocalisationExtensions;
|
|||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps.Drawables.Cards;
|
||||||
using osu.Game.Input;
|
using osu.Game.Input;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Localisation;
|
using osu.Game.Localisation;
|
||||||
@ -44,6 +45,8 @@ namespace osu.Game.Configuration
|
|||||||
|
|
||||||
SetDefault(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f);
|
SetDefault(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f);
|
||||||
|
|
||||||
|
SetDefault(OsuSetting.BeatmapListingCardSize, BeatmapCardSize.Normal);
|
||||||
|
|
||||||
SetDefault(OsuSetting.ToolbarClockDisplayMode, ToolbarClockDisplayMode.Full);
|
SetDefault(OsuSetting.ToolbarClockDisplayMode, ToolbarClockDisplayMode.Full);
|
||||||
|
|
||||||
// Online settings
|
// Online settings
|
||||||
@ -297,6 +300,7 @@ namespace osu.Game.Configuration
|
|||||||
RandomSelectAlgorithm,
|
RandomSelectAlgorithm,
|
||||||
ShowFpsDisplay,
|
ShowFpsDisplay,
|
||||||
ChatDisplayHeight,
|
ChatDisplayHeight,
|
||||||
|
BeatmapListingCardSize,
|
||||||
ToolbarClockDisplayMode,
|
ToolbarClockDisplayMode,
|
||||||
Version,
|
Version,
|
||||||
ShowConvertedBeatmaps,
|
ShowConvertedBeatmaps,
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Online.Rooms
|
namespace osu.Game.Online.Rooms
|
||||||
{
|
{
|
||||||
@ -84,6 +85,19 @@ namespace osu.Game.Online.Rooms
|
|||||||
Beatmap = beatmap;
|
Beatmap = beatmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PlaylistItem(MultiplayerPlaylistItem item)
|
||||||
|
: this(new APIBeatmap { OnlineID = item.BeatmapID })
|
||||||
|
{
|
||||||
|
ID = item.ID;
|
||||||
|
OwnerID = item.OwnerID;
|
||||||
|
RulesetID = item.RulesetID;
|
||||||
|
Expired = item.Expired;
|
||||||
|
PlaylistOrder = item.PlaylistOrder;
|
||||||
|
PlayedAt = item.PlayedAt;
|
||||||
|
RequiredMods = item.RequiredMods.ToArray();
|
||||||
|
AllowedMods = item.AllowedMods.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
public void MarkInvalid() => valid.Value = false;
|
public void MarkInvalid() => valid.Value = false;
|
||||||
|
|
||||||
#region Newtonsoft.Json implicit ShouldSerialize() methods
|
#region Newtonsoft.Json implicit ShouldSerialize() methods
|
||||||
@ -101,13 +115,13 @@ namespace osu.Game.Online.Rooms
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public PlaylistItem With(IBeatmapInfo beatmap) => new PlaylistItem(beatmap)
|
public PlaylistItem With(Optional<IBeatmapInfo> beatmap = default, Optional<ushort?> playlistOrder = default) => new PlaylistItem(beatmap.GetOr(Beatmap))
|
||||||
{
|
{
|
||||||
ID = ID,
|
ID = ID,
|
||||||
OwnerID = OwnerID,
|
OwnerID = OwnerID,
|
||||||
RulesetID = RulesetID,
|
RulesetID = RulesetID,
|
||||||
Expired = Expired,
|
Expired = Expired,
|
||||||
PlaylistOrder = PlaylistOrder,
|
PlaylistOrder = playlistOrder.GetOr(PlaylistOrder),
|
||||||
PlayedAt = PlayedAt,
|
PlayedAt = PlayedAt,
|
||||||
AllowedMods = AllowedMods,
|
AllowedMods = AllowedMods,
|
||||||
RequiredMods = RequiredMods,
|
RequiredMods = RequiredMods,
|
||||||
@ -119,6 +133,7 @@ namespace osu.Game.Online.Rooms
|
|||||||
&& Beatmap.OnlineID == other.Beatmap.OnlineID
|
&& Beatmap.OnlineID == other.Beatmap.OnlineID
|
||||||
&& RulesetID == other.RulesetID
|
&& RulesetID == other.RulesetID
|
||||||
&& Expired == other.Expired
|
&& Expired == other.Expired
|
||||||
|
&& PlaylistOrder == other.PlaylistOrder
|
||||||
&& AllowedMods.SequenceEqual(other.AllowedMods)
|
&& AllowedMods.SequenceEqual(other.AllowedMods)
|
||||||
&& RequiredMods.SequenceEqual(other.RequiredMods);
|
&& RequiredMods.SequenceEqual(other.RequiredMods);
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Game.Beatmaps.Drawables.Cards;
|
using osu.Game.Beatmaps.Drawables.Cards;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
@ -53,7 +54,9 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The currently selected <see cref="BeatmapCardSize"/>.
|
/// The currently selected <see cref="BeatmapCardSize"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IBindable<BeatmapCardSize> CardSize { get; } = new Bindable<BeatmapCardSize>();
|
public IBindable<BeatmapCardSize> CardSize => cardSize;
|
||||||
|
|
||||||
|
private readonly Bindable<BeatmapCardSize> cardSize = new Bindable<BeatmapCardSize>();
|
||||||
|
|
||||||
private readonly BeatmapListingSearchControl searchControl;
|
private readonly BeatmapListingSearchControl searchControl;
|
||||||
private readonly BeatmapListingSortTabControl sortControl;
|
private readonly BeatmapListingSortTabControl sortControl;
|
||||||
@ -128,6 +131,9 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuConfigManager config { get; set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OverlayColourProvider colourProvider, IAPIProvider api)
|
private void load(OverlayColourProvider colourProvider, IAPIProvider api)
|
||||||
{
|
{
|
||||||
@ -141,6 +147,8 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
|
config.BindWith(OsuSetting.BeatmapListingCardSize, cardSize);
|
||||||
|
|
||||||
var sortCriteria = sortControl.Current;
|
var sortCriteria = sortControl.Current;
|
||||||
var sortDirection = sortControl.SortDirection;
|
var sortDirection = sortControl.SortDirection;
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Extensions.LocalisationExtensions;
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -19,7 +21,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
protected readonly FailRetryGraph Graph;
|
protected readonly FailRetryGraph Graph;
|
||||||
|
|
||||||
private readonly FillFlowContainer header;
|
private readonly FillFlowContainer header;
|
||||||
private readonly OsuSpriteText successPercent;
|
private readonly SuccessRatePercentage successPercent;
|
||||||
private readonly Bar successRate;
|
private readonly Bar successRate;
|
||||||
private readonly Container percentContainer;
|
private readonly Container percentContainer;
|
||||||
|
|
||||||
@ -45,6 +47,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
|
|
||||||
float rate = playCount != 0 ? (float)passCount / playCount : 0;
|
float rate = playCount != 0 ? (float)passCount / playCount : 0;
|
||||||
successPercent.Text = rate.ToLocalisableString(@"0.#%");
|
successPercent.Text = rate.ToLocalisableString(@"0.#%");
|
||||||
|
successPercent.TooltipText = $"{passCount} / {playCount}";
|
||||||
successRate.Length = rate;
|
successRate.Length = rate;
|
||||||
percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);
|
percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);
|
||||||
|
|
||||||
@ -80,7 +83,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Width = 0f,
|
Width = 0f,
|
||||||
Child = successPercent = new OsuSpriteText
|
Child = successPercent = new SuccessRatePercentage
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
@ -121,5 +124,10 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
|
|
||||||
Graph.Padding = new MarginPadding { Top = header.DrawHeight };
|
Graph.Padding = new MarginPadding { Top = header.DrawHeight };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class SuccessRatePercentage : OsuSpriteText, IHasTooltip
|
||||||
|
{
|
||||||
|
public LocalisableString TooltipText { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
163
osu.Game/Overlays/Chat/ChatTextBar.cs
Normal file
163
osu.Game/Overlays/Chat/ChatTextBar.cs
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Online.Chat;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Chat
|
||||||
|
{
|
||||||
|
public class ChatTextBar : Container
|
||||||
|
{
|
||||||
|
public readonly BindableBool ShowSearch = new BindableBool();
|
||||||
|
|
||||||
|
public event Action<string>? OnChatMessageCommitted;
|
||||||
|
|
||||||
|
public event Action<string>? OnSearchTermsChanged;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private Bindable<Channel> currentChannel { get; set; } = null!;
|
||||||
|
|
||||||
|
private OsuTextFlowContainer chattingTextContainer = null!;
|
||||||
|
private Container searchIconContainer = null!;
|
||||||
|
private ChatTextBox chatTextBox = null!;
|
||||||
|
|
||||||
|
private const float chatting_text_width = 180;
|
||||||
|
private const float search_icon_width = 40;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OverlayColourProvider colourProvider)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
Height = 60;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colourProvider.Background5,
|
||||||
|
},
|
||||||
|
new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
ColumnDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
new Dimension(),
|
||||||
|
},
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
chattingTextContainer = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 20))
|
||||||
|
{
|
||||||
|
Masking = true,
|
||||||
|
Width = chatting_text_width,
|
||||||
|
Padding = new MarginPadding { Left = 10 },
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
TextAnchor = Anchor.CentreRight,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Colour = colourProvider.Background1,
|
||||||
|
},
|
||||||
|
searchIconContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = search_icon_width,
|
||||||
|
Child = new SpriteIcon
|
||||||
|
{
|
||||||
|
Icon = FontAwesome.Solid.Search,
|
||||||
|
Origin = Anchor.CentreRight,
|
||||||
|
Anchor = Anchor.CentreRight,
|
||||||
|
Size = new Vector2(20),
|
||||||
|
Margin = new MarginPadding { Right = 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Right = 5 },
|
||||||
|
Child = chatTextBox = new ChatTextBox
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
ShowSearch = { BindTarget = ShowSearch },
|
||||||
|
HoldFocus = true,
|
||||||
|
ReleaseFocusOnCommit = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
chatTextBox.Current.ValueChanged += chatTextBoxChange;
|
||||||
|
chatTextBox.OnCommit += chatTextBoxCommit;
|
||||||
|
|
||||||
|
ShowSearch.BindValueChanged(change =>
|
||||||
|
{
|
||||||
|
bool showSearch = change.NewValue;
|
||||||
|
|
||||||
|
chattingTextContainer.FadeTo(showSearch ? 0 : 1);
|
||||||
|
searchIconContainer.FadeTo(showSearch ? 1 : 0);
|
||||||
|
|
||||||
|
// Clear search terms if any exist when switching back to chat mode
|
||||||
|
if (!showSearch)
|
||||||
|
OnSearchTermsChanged?.Invoke(string.Empty);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
currentChannel.BindValueChanged(change =>
|
||||||
|
{
|
||||||
|
Channel newChannel = change.NewValue;
|
||||||
|
|
||||||
|
switch (newChannel?.Type)
|
||||||
|
{
|
||||||
|
case ChannelType.Public:
|
||||||
|
chattingTextContainer.Text = $"chatting in {newChannel.Name}";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ChannelType.PM:
|
||||||
|
chattingTextContainer.Text = $"chatting with {newChannel.Name}";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
chattingTextContainer.Text = string.Empty;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void chatTextBoxChange(ValueChangedEvent<string> change)
|
||||||
|
{
|
||||||
|
if (ShowSearch.Value)
|
||||||
|
OnSearchTermsChanged?.Invoke(change.NewValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void chatTextBoxCommit(TextBox sender, bool newText)
|
||||||
|
{
|
||||||
|
if (ShowSearch.Value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
OnChatMessageCommitted?.Invoke(sender.Text);
|
||||||
|
sender.Text = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
osu.Game/Overlays/Chat/ChatTextBox.cs
Normal file
38
osu.Game/Overlays/Chat/ChatTextBox.cs
Normal file
@ -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.
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Chat
|
||||||
|
{
|
||||||
|
public class ChatTextBox : FocusedTextBox
|
||||||
|
{
|
||||||
|
public readonly BindableBool ShowSearch = new BindableBool();
|
||||||
|
|
||||||
|
public override bool HandleLeftRightArrows => !ShowSearch.Value;
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
ShowSearch.BindValueChanged(change =>
|
||||||
|
{
|
||||||
|
bool showSearch = change.NewValue;
|
||||||
|
|
||||||
|
PlaceholderText = showSearch ? "type here to search" : "type here";
|
||||||
|
Text = string.Empty;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Commit()
|
||||||
|
{
|
||||||
|
if (ShowSearch.Value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
base.Commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -190,7 +190,7 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
|
|
||||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||||
{
|
{
|
||||||
if (e.Action == Hotkey)
|
if (e.Action == Hotkey && !e.Repeat)
|
||||||
{
|
{
|
||||||
TriggerClick();
|
TriggerClick();
|
||||||
return true;
|
return true;
|
||||||
|
@ -15,6 +15,7 @@ using osu.Game.Rulesets.Difficulty.Preprocessing;
|
|||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Difficulty
|
namespace osu.Game.Rulesets.Difficulty
|
||||||
{
|
{
|
||||||
@ -119,15 +120,23 @@ namespace osu.Game.Rulesets.Difficulty
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the difficulty of the beatmap using all mod combinations applicable to the beatmap.
|
/// Calculates the difficulty of the beatmap using all mod combinations applicable to the beatmap.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This can only be used to compute difficulties for legacy mod combinations.
|
||||||
|
/// </remarks>
|
||||||
/// <returns>A collection of structures describing the difficulty of the beatmap for each mod combination.</returns>
|
/// <returns>A collection of structures describing the difficulty of the beatmap for each mod combination.</returns>
|
||||||
public IEnumerable<DifficultyAttributes> CalculateAll(CancellationToken cancellationToken = default)
|
public IEnumerable<DifficultyAttributes> CalculateAllLegacyCombinations(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
var rulesetInstance = ruleset.CreateInstance();
|
||||||
|
|
||||||
foreach (var combination in CreateDifficultyAdjustmentModCombinations())
|
foreach (var combination in CreateDifficultyAdjustmentModCombinations())
|
||||||
{
|
{
|
||||||
if (combination is MultiMod multi)
|
Mod classicMod = rulesetInstance.CreateAllMods().SingleOrDefault(m => m is ModClassic);
|
||||||
yield return Calculate(multi.Mods, cancellationToken);
|
|
||||||
else
|
var finalCombination = ModUtils.FlattenMod(combination);
|
||||||
yield return Calculate(combination.Yield(), cancellationToken);
|
if (classicMod != null)
|
||||||
|
finalCombination = finalCombination.Append(classicMod);
|
||||||
|
|
||||||
|
yield return Calculate(finalCombination.ToArray(), cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
|||||||
|
|
||||||
private Sample countdownTickSample;
|
private Sample countdownTickSample;
|
||||||
private Sample countdownTickFinalSample;
|
private Sample countdownTickFinalSample;
|
||||||
private int? lastTickPlayed;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
@ -60,17 +59,39 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
|||||||
countdownChangeTime = DateTimeOffset.Now;
|
countdownChangeTime = DateTimeOffset.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scheduleNextCountdownUpdate();
|
||||||
|
|
||||||
|
updateButtonText();
|
||||||
|
updateButtonColour();
|
||||||
|
});
|
||||||
|
|
||||||
|
private void scheduleNextCountdownUpdate()
|
||||||
|
{
|
||||||
if (countdown != null)
|
if (countdown != null)
|
||||||
countdownUpdateDelegate ??= Scheduler.AddDelayed(updateButtonText, 100, true);
|
{
|
||||||
|
// If a countdown is active, schedule relevant components to update on the next whole second.
|
||||||
|
double timeToNextSecond = countdownTimeRemaining.TotalMilliseconds % 1000;
|
||||||
|
|
||||||
|
countdownUpdateDelegate = Scheduler.AddDelayed(onCountdownTick, timeToNextSecond);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
countdownUpdateDelegate?.Cancel();
|
countdownUpdateDelegate?.Cancel();
|
||||||
countdownUpdateDelegate = null;
|
countdownUpdateDelegate = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateButtonText();
|
void onCountdownTick()
|
||||||
updateButtonColour();
|
{
|
||||||
});
|
updateButtonText();
|
||||||
|
|
||||||
|
int secondsRemaining = countdownTimeRemaining.Seconds;
|
||||||
|
|
||||||
|
if (secondsRemaining < 10) countdownTickSample?.Play();
|
||||||
|
if (secondsRemaining <= 3) countdownTickFinalSample?.Play();
|
||||||
|
|
||||||
|
scheduleNextCountdownUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateButtonText()
|
private void updateButtonText()
|
||||||
{
|
{
|
||||||
@ -88,25 +109,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
|||||||
|
|
||||||
if (countdown != null)
|
if (countdown != null)
|
||||||
{
|
{
|
||||||
TimeSpan timeElapsed = DateTimeOffset.Now - countdownChangeTime;
|
string countdownText = $"Starting in {countdownTimeRemaining:mm\\:ss}";
|
||||||
TimeSpan countdownRemaining;
|
|
||||||
|
|
||||||
if (timeElapsed > countdown.TimeRemaining)
|
|
||||||
countdownRemaining = TimeSpan.Zero;
|
|
||||||
else
|
|
||||||
countdownRemaining = countdown.TimeRemaining - timeElapsed;
|
|
||||||
|
|
||||||
if (countdownRemaining.Seconds <= 10 && (lastTickPlayed == null || lastTickPlayed != countdownRemaining.Seconds))
|
|
||||||
{
|
|
||||||
countdownTickSample?.Play();
|
|
||||||
|
|
||||||
if (countdownRemaining.Seconds <= 3)
|
|
||||||
countdownTickFinalSample?.Play();
|
|
||||||
|
|
||||||
lastTickPlayed = countdownRemaining.Seconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
string countdownText = $"Starting in {countdownRemaining:mm\\:ss}";
|
|
||||||
|
|
||||||
switch (localUser?.State)
|
switch (localUser?.State)
|
||||||
{
|
{
|
||||||
@ -139,6 +142,22 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TimeSpan countdownTimeRemaining
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
TimeSpan timeElapsed = DateTimeOffset.Now - countdownChangeTime;
|
||||||
|
TimeSpan remaining;
|
||||||
|
|
||||||
|
if (timeElapsed > countdown.TimeRemaining)
|
||||||
|
remaining = TimeSpan.Zero;
|
||||||
|
else
|
||||||
|
remaining = countdown.TimeRemaining - timeElapsed;
|
||||||
|
|
||||||
|
return remaining;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateButtonColour()
|
private void updateButtonColour()
|
||||||
{
|
{
|
||||||
if (room == null)
|
if (room == null)
|
||||||
|
@ -117,8 +117,24 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
|
|||||||
{
|
{
|
||||||
base.PlaylistItemChanged(item);
|
base.PlaylistItemChanged(item);
|
||||||
|
|
||||||
removeItemFromLists(item.ID);
|
var newApiItem = Playlist.SingleOrDefault(i => i.ID == item.ID);
|
||||||
addItemToLists(item);
|
var existingApiItemInQueue = queueList.Items.SingleOrDefault(i => i.ID == item.ID);
|
||||||
|
|
||||||
|
// Test if the only change between the two playlist items is the order.
|
||||||
|
if (newApiItem != null && existingApiItemInQueue != null && existingApiItemInQueue.With(playlistOrder: newApiItem.PlaylistOrder).Equals(newApiItem))
|
||||||
|
{
|
||||||
|
// Set the new playlist order directly without refreshing the DrawablePlaylistItem.
|
||||||
|
existingApiItemInQueue.PlaylistOrder = newApiItem.PlaylistOrder;
|
||||||
|
|
||||||
|
// The following isn't really required, but is here for safety and explicitness.
|
||||||
|
// MultiplayerQueueList internally binds to changes in Playlist to invalidate its own layout, which is mutated on every playlist operation.
|
||||||
|
queueList.Invalidate();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
removeItemFromLists(item.ID);
|
||||||
|
addItemToLists(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addItemToLists(MultiplayerPlaylistItem item)
|
private void addItemToLists(MultiplayerPlaylistItem item)
|
||||||
|
@ -24,6 +24,7 @@ using osu.Game.Database;
|
|||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
using osu.Game.IO.Archives;
|
using osu.Game.IO.Archives;
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
@ -144,20 +145,26 @@ namespace osu.Game.Skinning
|
|||||||
if (!s.Protected)
|
if (!s.Protected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
string[] existingSkinNames = realm.Run(r => r.All<SkinInfo>()
|
||||||
|
.Where(skin => !skin.DeletePending)
|
||||||
|
.AsEnumerable()
|
||||||
|
.Select(skin => skin.Name).ToArray());
|
||||||
|
|
||||||
// if the user is attempting to save one of the default skin implementations, create a copy first.
|
// if the user is attempting to save one of the default skin implementations, create a copy first.
|
||||||
var result = skinModelManager.Import(new SkinInfo
|
var skinInfo = new SkinInfo
|
||||||
{
|
{
|
||||||
Name = s.Name + @" (modified)",
|
|
||||||
Creator = s.Creator,
|
Creator = s.Creator,
|
||||||
InstantiationInfo = s.InstantiationInfo,
|
InstantiationInfo = s.InstantiationInfo,
|
||||||
});
|
Name = NamingUtils.GetNextBestName(existingSkinNames, $"{s.Name} (modified)")
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = skinModelManager.Import(skinInfo);
|
||||||
|
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
// save once to ensure the required json content is populated.
|
// save once to ensure the required json content is populated.
|
||||||
// currently this only happens on save.
|
// currently this only happens on save.
|
||||||
result.PerformRead(skin => Save(skin.CreateInstance(this)));
|
result.PerformRead(skin => Save(skin.CreateInstance(this)));
|
||||||
|
|
||||||
CurrentSkinInfo.Value = result;
|
CurrentSkinInfo.Value = result;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -22,10 +22,13 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
|
|
||||||
protected abstract string ResourceAssembly { get; }
|
protected abstract string ResourceAssembly { get; }
|
||||||
|
|
||||||
protected void Test(double expected, string name, params Mod[] mods)
|
protected void Test(double expectedStarRating, int expectedMaxCombo, string name, params Mod[] mods)
|
||||||
{
|
{
|
||||||
|
var attributes = CreateDifficultyCalculator(getBeatmap(name)).Calculate(mods);
|
||||||
|
|
||||||
// Platform-dependent math functions (Pow, Cbrt, Exp, etc) may result in minute differences.
|
// Platform-dependent math functions (Pow, Cbrt, Exp, etc) may result in minute differences.
|
||||||
Assert.That(CreateDifficultyCalculator(getBeatmap(name)).Calculate(mods).StarRating, Is.EqualTo(expected).Within(0.00001));
|
Assert.That(attributes.StarRating, Is.EqualTo(expectedStarRating).Within(0.00001));
|
||||||
|
Assert.That(attributes.MaxCombo, Is.EqualTo(expectedMaxCombo));
|
||||||
}
|
}
|
||||||
|
|
||||||
private IWorkingBeatmap getBeatmap(string name)
|
private IWorkingBeatmap getBeatmap(string name)
|
||||||
|
@ -31,7 +31,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
public override IBindable<bool> IsConnected => isConnected;
|
public override IBindable<bool> IsConnected => isConnected;
|
||||||
private readonly Bindable<bool> isConnected = new Bindable<bool>(true);
|
private readonly Bindable<bool> isConnected = new Bindable<bool>(true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The local client's <see cref="Room"/>. This is not always equivalent to the server-side room.
|
||||||
|
/// </summary>
|
||||||
public new Room? APIRoom => base.APIRoom;
|
public new Room? APIRoom => base.APIRoom;
|
||||||
|
|
||||||
public Action<MultiplayerRoom>? RoomSetupAction;
|
public Action<MultiplayerRoom>? RoomSetupAction;
|
||||||
|
|
||||||
public bool RoomJoined { get; private set; }
|
public bool RoomJoined { get; private set; }
|
||||||
@ -46,6 +50,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly List<MultiplayerPlaylistItem> serverSidePlaylist = new List<MultiplayerPlaylistItem>();
|
private readonly List<MultiplayerPlaylistItem> serverSidePlaylist = new List<MultiplayerPlaylistItem>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Guaranteed up-to-date API room.
|
||||||
|
/// </summary>
|
||||||
|
private Room? serverSideAPIRoom;
|
||||||
|
|
||||||
private MultiplayerPlaylistItem? currentItem => Room?.Playlist[currentIndex];
|
private MultiplayerPlaylistItem? currentItem => Room?.Playlist[currentIndex];
|
||||||
private int currentIndex;
|
private int currentIndex;
|
||||||
private long lastPlaylistItemId;
|
private long lastPlaylistItemId;
|
||||||
@ -192,13 +201,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
|
|
||||||
protected override async Task<MultiplayerRoom> JoinRoom(long roomId, string? password = null)
|
protected override async Task<MultiplayerRoom> JoinRoom(long roomId, string? password = null)
|
||||||
{
|
{
|
||||||
var apiRoom = roomManager.ServerSideRooms.Single(r => r.RoomID.Value == roomId);
|
serverSideAPIRoom = roomManager.ServerSideRooms.Single(r => r.RoomID.Value == roomId);
|
||||||
|
|
||||||
if (password != apiRoom.Password.Value)
|
if (password != serverSideAPIRoom.Password.Value)
|
||||||
throw new InvalidOperationException("Invalid password.");
|
throw new InvalidOperationException("Invalid password.");
|
||||||
|
|
||||||
serverSidePlaylist.Clear();
|
serverSidePlaylist.Clear();
|
||||||
serverSidePlaylist.AddRange(apiRoom.Playlist.Select(item => new MultiplayerPlaylistItem(item)));
|
serverSidePlaylist.AddRange(serverSideAPIRoom.Playlist.Select(item => new MultiplayerPlaylistItem(item)));
|
||||||
lastPlaylistItemId = serverSidePlaylist.Max(item => item.ID);
|
lastPlaylistItemId = serverSidePlaylist.Max(item => item.ID);
|
||||||
|
|
||||||
var localUser = new MultiplayerRoomUser(api.LocalUser.Value.Id)
|
var localUser = new MultiplayerRoomUser(api.LocalUser.Value.Id)
|
||||||
@ -210,11 +219,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
{
|
{
|
||||||
Settings =
|
Settings =
|
||||||
{
|
{
|
||||||
Name = apiRoom.Name.Value,
|
Name = serverSideAPIRoom.Name.Value,
|
||||||
MatchType = apiRoom.Type.Value,
|
MatchType = serverSideAPIRoom.Type.Value,
|
||||||
Password = password,
|
Password = password,
|
||||||
QueueMode = apiRoom.QueueMode.Value,
|
QueueMode = serverSideAPIRoom.QueueMode.Value,
|
||||||
AutoStartDuration = apiRoom.AutoStartDuration.Value
|
AutoStartDuration = serverSideAPIRoom.AutoStartDuration.Value
|
||||||
},
|
},
|
||||||
Playlist = serverSidePlaylist.ToList(),
|
Playlist = serverSidePlaylist.ToList(),
|
||||||
Users = { localUser },
|
Users = { localUser },
|
||||||
@ -449,8 +458,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
public async Task EditUserPlaylistItem(int userId, MultiplayerPlaylistItem item)
|
public async Task EditUserPlaylistItem(int userId, MultiplayerPlaylistItem item)
|
||||||
{
|
{
|
||||||
Debug.Assert(Room != null);
|
Debug.Assert(Room != null);
|
||||||
Debug.Assert(APIRoom != null);
|
|
||||||
Debug.Assert(currentItem != null);
|
Debug.Assert(currentItem != null);
|
||||||
|
Debug.Assert(serverSideAPIRoom != null);
|
||||||
|
|
||||||
item.OwnerID = userId;
|
item.OwnerID = userId;
|
||||||
|
|
||||||
@ -469,6 +478,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
item.PlaylistOrder = existingItem.PlaylistOrder;
|
item.PlaylistOrder = existingItem.PlaylistOrder;
|
||||||
|
|
||||||
serverSidePlaylist[serverSidePlaylist.IndexOf(existingItem)] = item;
|
serverSidePlaylist[serverSidePlaylist.IndexOf(existingItem)] = item;
|
||||||
|
serverSideAPIRoom.Playlist[serverSideAPIRoom.Playlist.IndexOf(serverSideAPIRoom.Playlist.Single(i => i.ID == item.ID))] = new PlaylistItem(item);
|
||||||
|
|
||||||
await ((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false);
|
await ((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@ -479,6 +489,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
{
|
{
|
||||||
Debug.Assert(Room != null);
|
Debug.Assert(Room != null);
|
||||||
Debug.Assert(APIRoom != null);
|
Debug.Assert(APIRoom != null);
|
||||||
|
Debug.Assert(serverSideAPIRoom != null);
|
||||||
|
|
||||||
var item = serverSidePlaylist.Find(i => i.ID == playlistItemId);
|
var item = serverSidePlaylist.Find(i => i.ID == playlistItemId);
|
||||||
|
|
||||||
@ -495,6 +506,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
throw new InvalidOperationException("Attempted to remove an item which has already been played.");
|
throw new InvalidOperationException("Attempted to remove an item which has already been played.");
|
||||||
|
|
||||||
serverSidePlaylist.Remove(item);
|
serverSidePlaylist.Remove(item);
|
||||||
|
serverSideAPIRoom.Playlist.RemoveAll(i => i.ID == item.ID);
|
||||||
await ((IMultiplayerClient)this).PlaylistItemRemoved(playlistItemId).ConfigureAwait(false);
|
await ((IMultiplayerClient)this).PlaylistItemRemoved(playlistItemId).ConfigureAwait(false);
|
||||||
|
|
||||||
await updateCurrentItem(Room).ConfigureAwait(false);
|
await updateCurrentItem(Room).ConfigureAwait(false);
|
||||||
@ -576,10 +588,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
private async Task addItem(MultiplayerPlaylistItem item)
|
private async Task addItem(MultiplayerPlaylistItem item)
|
||||||
{
|
{
|
||||||
Debug.Assert(Room != null);
|
Debug.Assert(Room != null);
|
||||||
|
Debug.Assert(serverSideAPIRoom != null);
|
||||||
|
|
||||||
item.ID = ++lastPlaylistItemId;
|
item.ID = ++lastPlaylistItemId;
|
||||||
|
|
||||||
serverSidePlaylist.Add(item);
|
serverSidePlaylist.Add(item);
|
||||||
|
serverSideAPIRoom.Playlist.Add(new PlaylistItem(item));
|
||||||
await ((IMultiplayerClient)this).PlaylistItemAdded(item).ConfigureAwait(false);
|
await ((IMultiplayerClient)this).PlaylistItemAdded(item).ConfigureAwait(false);
|
||||||
|
|
||||||
await updatePlaylistOrder(Room).ConfigureAwait(false);
|
await updatePlaylistOrder(Room).ConfigureAwait(false);
|
||||||
@ -603,6 +617,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
|
|
||||||
private async Task updatePlaylistOrder(MultiplayerRoom room)
|
private async Task updatePlaylistOrder(MultiplayerRoom room)
|
||||||
{
|
{
|
||||||
|
Debug.Assert(serverSideAPIRoom != null);
|
||||||
|
|
||||||
List<MultiplayerPlaylistItem> orderedActiveItems;
|
List<MultiplayerPlaylistItem> orderedActiveItems;
|
||||||
|
|
||||||
switch (room.Settings.QueueMode)
|
switch (room.Settings.QueueMode)
|
||||||
@ -648,6 +664,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
|
|
||||||
await ((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false);
|
await ((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also ensure that the API room's playlist is correct.
|
||||||
|
foreach (var item in serverSideAPIRoom.Playlist)
|
||||||
|
item.PlaylistOrder = serverSidePlaylist.Single(i => i.ID == item.ID).PlaylistOrder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="10.10.0" />
|
<PackageReference Include="Realm" Version="10.10.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2022.325.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2022.404.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.404.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.404.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.14.1" />
|
<PackageReference Include="Sentry" Version="3.14.1" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.325.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.404.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.404.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.404.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net6.0) -->
|
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net6.0) -->
|
||||||
@ -84,7 +84,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.14" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.14" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.14" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.14" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2022.325.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2022.404.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||||
|
Loading…
Reference in New Issue
Block a user