mirror of
https://github.com/ppy/osu.git
synced 2026-05-13 20:33:35 +08:00
Merge branch 'master' into refactor-room-panels
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Tests.Visual.Multiplayer;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Tests.Online
|
||||
{
|
||||
[HeadlessTest]
|
||||
public partial class TestSceneMultiplayerBeatmapAvailabilityTracker : MultiplayerTestScene
|
||||
{
|
||||
private BeatmapManager beatmapManager = null!;
|
||||
private BeatmapInfo availableBeatmap = null!;
|
||||
private BeatmapInfo unavailableBeatmap = null!;
|
||||
|
||||
private MultiplayerBeatmapAvailabilityTracker tracker = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host)
|
||||
{
|
||||
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, null, Audio, Resources, host, Beatmap.Default));
|
||||
Dependencies.Cache(Realm);
|
||||
|
||||
beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely();
|
||||
|
||||
var importedSet = beatmapManager.GetAllUsableBeatmapSets().First();
|
||||
availableBeatmap = importedSet.Beatmaps[0];
|
||||
unavailableBeatmap = importedSet.Beatmaps[1];
|
||||
|
||||
Realm.Write(r => r.Remove(r.Find<BeatmapInfo>(unavailableBeatmap.ID)!));
|
||||
}
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("setup tracker", () =>
|
||||
{
|
||||
DummyAPIAccess api = (DummyAPIAccess)API;
|
||||
Func<APIRequest, bool>? defaultRequestHandler = api.HandleRequest;
|
||||
|
||||
api.HandleRequest = req =>
|
||||
{
|
||||
switch (req)
|
||||
{
|
||||
case GetBeatmapsRequest beatmapsReq:
|
||||
var availableApiBeatmap = CreateAPIBeatmap();
|
||||
availableApiBeatmap.OnlineID = availableBeatmap.OnlineID;
|
||||
availableApiBeatmap.OnlineBeatmapSetID = availableBeatmap.BeatmapSet!.OnlineID;
|
||||
availableApiBeatmap.Checksum = availableBeatmap.MD5Hash;
|
||||
availableApiBeatmap.BeatmapSet!.OnlineID = availableBeatmap.BeatmapSet!.OnlineID;
|
||||
|
||||
var unavailableApiBeatmap = CreateAPIBeatmap();
|
||||
unavailableApiBeatmap.OnlineID = unavailableBeatmap.OnlineID;
|
||||
unavailableApiBeatmap.OnlineBeatmapSetID = unavailableBeatmap.BeatmapSet!.OnlineID;
|
||||
unavailableApiBeatmap.Checksum = unavailableBeatmap.MD5Hash;
|
||||
unavailableApiBeatmap.BeatmapSet!.OnlineID = unavailableBeatmap.BeatmapSet!.OnlineID;
|
||||
|
||||
beatmapsReq.TriggerSuccess(new GetBeatmapsResponse
|
||||
{
|
||||
Beatmaps = new List<APIBeatmap>
|
||||
{
|
||||
availableApiBeatmap,
|
||||
unavailableApiBeatmap
|
||||
}
|
||||
});
|
||||
return true;
|
||||
|
||||
default:
|
||||
return defaultRequestHandler?.Invoke(req) ?? false;
|
||||
}
|
||||
};
|
||||
|
||||
Child = tracker = new MultiplayerBeatmapAvailabilityTracker();
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEnterRoomWithNotDownloadedBeatmap()
|
||||
{
|
||||
AddStep("join room", () =>
|
||||
{
|
||||
var room = CreateDefaultRoom();
|
||||
room.Playlist = [new PlaylistItem(unavailableBeatmap)];
|
||||
JoinRoom(room);
|
||||
});
|
||||
|
||||
WaitForJoined();
|
||||
|
||||
AddUntilStep("beatmap is not available", () => tracker.Availability.Value.State, () => Is.EqualTo(DownloadState.NotDownloaded));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEnterRoomWithLocallyAvailableBeatmap()
|
||||
{
|
||||
AddStep("join room", () =>
|
||||
{
|
||||
var room = CreateDefaultRoom();
|
||||
room.Playlist = [new PlaylistItem(availableBeatmap)];
|
||||
JoinRoom(room);
|
||||
});
|
||||
|
||||
WaitForJoined();
|
||||
|
||||
AddUntilStep("beatmap is available", () => tracker.Availability.Value.State, () => Is.EqualTo(DownloadState.LocallyAvailable));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAvailabilityUpdatesOnItemEdit()
|
||||
{
|
||||
AddStep("join room", () =>
|
||||
{
|
||||
var room = CreateDefaultRoom();
|
||||
room.Playlist = [new PlaylistItem(availableBeatmap)];
|
||||
JoinRoom(room);
|
||||
});
|
||||
|
||||
WaitForJoined();
|
||||
|
||||
AddUntilStep("beatmap is available", () => tracker.Availability.Value.State, () => Is.EqualTo(DownloadState.LocallyAvailable));
|
||||
|
||||
AddStep("change item to not downloaded beatmap", () =>
|
||||
{
|
||||
PlaylistItem newItem = new PlaylistItem(MultiplayerClient.ClientRoom!.CurrentPlaylistItem).With(beatmap: new Optional<IBeatmapInfo>(unavailableBeatmap));
|
||||
MultiplayerClient.EditPlaylistItem(new MultiplayerPlaylistItem(newItem)).WaitSafely();
|
||||
});
|
||||
|
||||
AddUntilStep("beatmap is not available", () => tracker.Availability.Value.State, () => Is.EqualTo(DownloadState.NotDownloaded));
|
||||
|
||||
AddStep("change item to downloaded beatmap", () =>
|
||||
{
|
||||
PlaylistItem newItem = new PlaylistItem(MultiplayerClient.ClientRoom!.CurrentPlaylistItem).With(beatmap: new Optional<IBeatmapInfo>(availableBeatmap));
|
||||
MultiplayerClient.EditPlaylistItem(new MultiplayerPlaylistItem(newItem)).WaitSafely();
|
||||
});
|
||||
|
||||
AddUntilStep("beatmap is available", () => tracker.Availability.Value.State, () => Is.EqualTo(DownloadState.LocallyAvailable));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAvailabilityUpdatesOnSettingsChange()
|
||||
{
|
||||
AddStep("join room", () =>
|
||||
{
|
||||
var room = CreateDefaultRoom();
|
||||
room.Playlist = [new PlaylistItem(availableBeatmap), new PlaylistItem(unavailableBeatmap)];
|
||||
JoinRoom(room);
|
||||
});
|
||||
|
||||
WaitForJoined();
|
||||
|
||||
AddUntilStep("beatmap is available", () => tracker.Availability.Value.State, () => Is.EqualTo(DownloadState.LocallyAvailable));
|
||||
|
||||
AddStep("change settings to not downloaded beatmap", () => MultiplayerClient.ChangeServerRoomSettings(new MultiplayerRoomSettings(MultiplayerClient.ClientAPIRoom!)
|
||||
{
|
||||
PlaylistItemId = MultiplayerClient.ServerRoom!.Playlist[1].ID
|
||||
}).WaitSafely());
|
||||
|
||||
AddUntilStep("beatmap is not available", () => tracker.Availability.Value.State, () => Is.EqualTo(DownloadState.NotDownloaded));
|
||||
|
||||
AddStep("change settings to downloaded beatmap", () => MultiplayerClient.ChangeServerRoomSettings(new MultiplayerRoomSettings(MultiplayerClient.ClientAPIRoom!)
|
||||
{
|
||||
PlaylistItemId = MultiplayerClient.ServerRoom!.Playlist[0].ID
|
||||
}).WaitSafely());
|
||||
|
||||
AddUntilStep("beatmap is available", () => tracker.Availability.Value.State, () => Is.EqualTo(DownloadState.LocallyAvailable));
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
-31
@@ -1,18 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using JetBrains.Annotations;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.IO.Stores;
|
||||
@@ -27,31 +23,29 @@ using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Playlists;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tests.Online
|
||||
{
|
||||
[HeadlessTest]
|
||||
public partial class TestSceneOnlinePlayBeatmapAvailabilityTracker : OsuTestScene
|
||||
public partial class TestScenePlaylistsBeatmapAvailabilityTracker : OsuTestScene
|
||||
{
|
||||
private RulesetStore rulesets;
|
||||
private TestBeatmapManager beatmaps;
|
||||
private TestBeatmapModelDownloader beatmapDownloader;
|
||||
private TestBeatmapManager beatmaps = null!;
|
||||
private TestBeatmapModelDownloader beatmapDownloader = null!;
|
||||
|
||||
private string testBeatmapFile;
|
||||
private BeatmapInfo testBeatmapInfo;
|
||||
private BeatmapSetInfo testBeatmapSet;
|
||||
private string testBeatmapFile = null!;
|
||||
private BeatmapInfo testBeatmapInfo = null!;
|
||||
private BeatmapSetInfo testBeatmapSet = null!;
|
||||
|
||||
private readonly Bindable<PlaylistItem> selectedItem = new Bindable<PlaylistItem>();
|
||||
private OnlinePlayBeatmapAvailabilityTracker availabilityTracker;
|
||||
private OnlinePlayBeatmapAvailabilityTracker availabilityTracker = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio, GameHost host)
|
||||
{
|
||||
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||
Dependencies.CacheAs<BeatmapManager>(beatmaps = new TestBeatmapManager(LocalStorage, Realm, rulesets, API, audio, Resources, host, Beatmap.Default));
|
||||
Dependencies.CacheAs<BeatmapManager>(beatmaps = new TestBeatmapManager(LocalStorage, Realm, API, audio, Resources, host, Beatmap.Default));
|
||||
Dependencies.CacheAs<BeatmapModelDownloader>(beatmapDownloader = new TestBeatmapModelDownloader(beatmaps, API));
|
||||
}
|
||||
|
||||
@@ -82,16 +76,11 @@ namespace osu.Game.Tests.Online
|
||||
testBeatmapFile = TestResources.GetQuickTestBeatmapForImport();
|
||||
|
||||
testBeatmapInfo = getTestBeatmapInfo(testBeatmapFile);
|
||||
testBeatmapSet = testBeatmapInfo.BeatmapSet;
|
||||
testBeatmapSet = testBeatmapInfo.BeatmapSet!;
|
||||
|
||||
Realm.Write(r => r.RemoveAll<BeatmapSetInfo>());
|
||||
Realm.Write(r => r.RemoveAll<BeatmapInfo>());
|
||||
|
||||
selectedItem.Value = new PlaylistItem(testBeatmapInfo)
|
||||
{
|
||||
RulesetID = testBeatmapInfo.Ruleset.OnlineID,
|
||||
};
|
||||
|
||||
recreateChildren();
|
||||
});
|
||||
|
||||
@@ -108,9 +97,15 @@ namespace osu.Game.Tests.Online
|
||||
Children = new Drawable[]
|
||||
{
|
||||
beatmapLookupCache,
|
||||
availabilityTracker = new OnlinePlayBeatmapAvailabilityTracker
|
||||
availabilityTracker = new PlaylistsBeatmapAvailabilityTracker
|
||||
{
|
||||
SelectedItem = { BindTarget = selectedItem, }
|
||||
PlaylistItem =
|
||||
{
|
||||
Value = new PlaylistItem(testBeatmapInfo)
|
||||
{
|
||||
RulesetID = testBeatmapInfo.Ruleset.OnlineID,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -125,10 +120,10 @@ namespace osu.Game.Tests.Online
|
||||
AddStep("start downloading", () => beatmapDownloader.Download(testBeatmapSet));
|
||||
addAvailabilityCheckStep("state downloading 0%", () => BeatmapAvailability.Downloading(0.0f));
|
||||
|
||||
AddStep("set progress 40%", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet))!.SetProgress(0.4f));
|
||||
AddStep("set progress 40%", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet)!).SetProgress(0.4f));
|
||||
addAvailabilityCheckStep("state downloading 40%", () => BeatmapAvailability.Downloading(0.4f));
|
||||
|
||||
AddStep("finish download", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet))!.TriggerSuccess(testBeatmapFile));
|
||||
AddStep("finish download", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet)!).TriggerSuccess(testBeatmapFile));
|
||||
addAvailabilityCheckStep("state importing", BeatmapAvailability.Importing);
|
||||
|
||||
AddStep("allow importing", () => beatmaps.AllowImport.Set());
|
||||
@@ -203,10 +198,10 @@ namespace osu.Game.Tests.Online
|
||||
{
|
||||
public readonly ManualResetEventSlim AllowImport = new ManualResetEventSlim();
|
||||
|
||||
public Live<BeatmapSetInfo> CurrentImport { get; private set; }
|
||||
public Live<BeatmapSetInfo>? CurrentImport { get; private set; }
|
||||
|
||||
public TestBeatmapManager(Storage storage, RealmAccess realm, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore<byte[]> resources,
|
||||
GameHost host = null, WorkingBeatmap defaultBeatmap = null)
|
||||
public TestBeatmapManager(Storage storage, RealmAccess realm, IAPIProvider api, AudioManager audioManager, IResourceStore<byte[]> resources,
|
||||
GameHost? host = null, WorkingBeatmap? defaultBeatmap = null)
|
||||
: base(storage, realm, api, audioManager, resources, host, defaultBeatmap)
|
||||
{
|
||||
}
|
||||
@@ -226,12 +221,13 @@ namespace osu.Game.Tests.Online
|
||||
this.testBeatmapManager = testBeatmapManager;
|
||||
}
|
||||
|
||||
public override Live<BeatmapSetInfo> ImportModel(BeatmapSetInfo item, ArchiveReader archive = null, ImportParameters parameters = default, CancellationToken cancellationToken = default)
|
||||
public override Live<BeatmapSetInfo>? ImportModel(BeatmapSetInfo item, ArchiveReader? archive = null, ImportParameters parameters = default,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!testBeatmapManager.AllowImport.Wait(TimeSpan.FromSeconds(10), cancellationToken))
|
||||
throw new TimeoutException("Timeout waiting for import to be allowed.");
|
||||
|
||||
return (testBeatmapManager.CurrentImport = base.ImportModel(item, archive, parameters, cancellationToken));
|
||||
return testBeatmapManager.CurrentImport = base.ImportModel(item, archive, parameters, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge;
|
||||
@@ -40,14 +41,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
var mockLounge = new Mock<IOnlinePlayLounge>();
|
||||
mockLounge
|
||||
.Setup(l => l.Join(It.IsAny<Room>(), It.IsAny<string>(), It.IsAny<Action<Room>>(), It.IsAny<Action<string>>()))
|
||||
.Callback<Room, string, Action<Room>, Action<string>>((_, _, _, d) =>
|
||||
.Setup(l => l.Join(It.IsAny<Room>(), It.IsAny<string>(), It.IsAny<Action<Room>>(), It.IsAny<Action<string, Exception?>>()))
|
||||
.Callback<Room, string, Action<Room>, Action<string, Exception?>>((_, _, _, d) =>
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
allowResponseCallback.Wait(10000);
|
||||
allowResponseCallback.Reset();
|
||||
Schedule(() => d?.Invoke("Incorrect password"));
|
||||
Schedule(() => d?.Invoke("Incorrect password", new InvalidPasswordException()));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
@@ -16,19 +18,33 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("create footer", () =>
|
||||
{
|
||||
Child = new PopoverContainer
|
||||
MultiplayerBeatmapAvailabilityTracker tracker = new MultiplayerBeatmapAvailabilityTracker();
|
||||
|
||||
Child = new DependencyProvidingContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 50,
|
||||
Child = new MultiplayerMatchFooter()
|
||||
}
|
||||
CachedDependencies =
|
||||
[
|
||||
(typeof(OnlinePlayBeatmapAvailabilityTracker), tracker)
|
||||
],
|
||||
Children =
|
||||
[
|
||||
tracker,
|
||||
new PopoverContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 50,
|
||||
Child = new MultiplayerMatchFooter()
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osuTK;
|
||||
@@ -52,34 +54,46 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("create button", () =>
|
||||
{
|
||||
AvailabilityTracker.SelectedItem.Value = room.Playlist.First();
|
||||
|
||||
importedSet = beatmaps.GetAllUsableBeatmapSets().First();
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First());
|
||||
|
||||
Child = new PopoverContainer
|
||||
MultiplayerBeatmapAvailabilityTracker tracker = new MultiplayerBeatmapAvailabilityTracker();
|
||||
|
||||
Child = new DependencyProvidingContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
CachedDependencies =
|
||||
[
|
||||
(typeof(OnlinePlayBeatmapAvailabilityTracker), tracker)
|
||||
],
|
||||
Children =
|
||||
[
|
||||
tracker,
|
||||
new PopoverContainer
|
||||
{
|
||||
spectateButton = new MultiplayerSpectateButton
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(200, 50)
|
||||
},
|
||||
startControl = new MatchStartControl
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(200, 50)
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
spectateButton = new MultiplayerSpectateButton
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(200, 50)
|
||||
},
|
||||
startControl = new MatchStartControl
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(200, 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public partial class TestSceneMultiplayerUserModDisplay : MultiplayerTestScene
|
||||
{
|
||||
private MultiplayerUserModDisplay modDisplay = null!;
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("join room", () => JoinRoom(CreateDefaultRoom()));
|
||||
WaitForJoined();
|
||||
|
||||
AddStep("add display", () => Child = modDisplay = new MultiplayerUserModDisplay
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChangeMods()
|
||||
{
|
||||
AddStep("set DT", () => MultiplayerClient.ChangeUserMods([new OsuModDoubleTime()]).WaitSafely());
|
||||
AddUntilStep("mod displayed", () => modDisplay.ChildrenOfType<ModIcon>().Count() == 1);
|
||||
|
||||
AddStep("set DT, HR", () => MultiplayerClient.ChangeUserMods([new OsuModDoubleTime(), new OsuModHardRock()]).WaitSafely());
|
||||
AddUntilStep("mods displayed", () => modDisplay.ChildrenOfType<ModIcon>().Count() == 2);
|
||||
|
||||
AddStep("set no mods", () => MultiplayerClient.ChangeUserMods(Enumerable.Empty<APIMod>()).WaitSafely());
|
||||
AddUntilStep("no mods displayed", () => !modDisplay.ChildrenOfType<ModIcon>().Any());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
{
|
||||
private BeatmapCarousel carousel = null!;
|
||||
|
||||
private TestSceneOnlinePlayBeatmapAvailabilityTracker.TestBeatmapModelDownloader beatmapDownloader = null!;
|
||||
private TestScenePlaylistsBeatmapAvailabilityTracker.TestBeatmapModelDownloader beatmapDownloader = null!;
|
||||
|
||||
private BeatmapSetInfo testBeatmapSetInfo = null!;
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
var importer = parent.Get<BeatmapManager>();
|
||||
|
||||
dependencies.CacheAs<BeatmapModelDownloader>(beatmapDownloader = new TestSceneOnlinePlayBeatmapAvailabilityTracker.TestBeatmapModelDownloader(importer, API));
|
||||
dependencies.CacheAs<BeatmapModelDownloader>(beatmapDownloader = new TestScenePlaylistsBeatmapAvailabilityTracker.TestBeatmapModelDownloader(importer, API));
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
AddUntilStep("progress download to completion", () =>
|
||||
{
|
||||
if (downloadRequest is TestSceneOnlinePlayBeatmapAvailabilityTracker.TestDownloadRequest testRequest)
|
||||
if (downloadRequest is TestScenePlaylistsBeatmapAvailabilityTracker.TestDownloadRequest testRequest)
|
||||
{
|
||||
testRequest.SetProgress(testRequest.Progress + 0.1f);
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
AddUntilStep("progress download to failure", () =>
|
||||
{
|
||||
if (downloadRequest is TestSceneOnlinePlayBeatmapAvailabilityTracker.TestDownloadRequest testRequest)
|
||||
if (downloadRequest is TestScenePlaylistsBeatmapAvailabilityTracker.TestDownloadRequest testRequest)
|
||||
{
|
||||
testRequest.SetProgress(testRequest.Progress + 0.1f);
|
||||
|
||||
@@ -226,7 +226,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
AddUntilStep("progress download to completion", () =>
|
||||
{
|
||||
if (downloadRequest is TestSceneOnlinePlayBeatmapAvailabilityTracker.TestDownloadRequest testRequest)
|
||||
if (downloadRequest is TestScenePlaylistsBeatmapAvailabilityTracker.TestDownloadRequest testRequest)
|
||||
{
|
||||
testRequest.SetProgress(testRequest.Progress + 0.1f);
|
||||
|
||||
|
||||
@@ -71,8 +71,8 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Plum);
|
||||
|
||||
[Cached]
|
||||
private readonly OnlinePlayBeatmapAvailabilityTracker beatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
|
||||
[Cached(typeof(OnlinePlayBeatmapAvailabilityTracker))]
|
||||
private readonly DailyChallengeBeatmapAvailabilityTracker beatmapAvailabilityTracker;
|
||||
|
||||
[Resolved]
|
||||
private OsuGame? game { get; set; }
|
||||
@@ -113,8 +113,11 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
public DailyChallenge(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
|
||||
playlistItem = room.Playlist.Single();
|
||||
Padding = new MarginPadding { Horizontal = -HORIZONTAL_OVERFLOW_PADDING };
|
||||
|
||||
beatmapAvailabilityTracker = new DailyChallengeBeatmapAvailabilityTracker(playlistItem);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@@ -378,7 +381,6 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
beatmapAvailabilityTracker.SelectedItem.Value = playlistItem;
|
||||
beatmapAvailabilityTracker.Availability.BindValueChanged(_ => TrySetDailyChallengeBeatmap(this, beatmapManager, rulesets, musicController, playlistItem), true);
|
||||
|
||||
userModsSelectOverlayRegistration = overlayManager?.RegisterBlockingOverlay(userModsSelectOverlay);
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// 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.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
{
|
||||
public partial class DailyChallengeBeatmapAvailabilityTracker : OnlinePlayBeatmapAvailabilityTracker
|
||||
{
|
||||
public DailyChallengeBeatmapAvailabilityTracker(PlaylistItem item)
|
||||
{
|
||||
PlaylistItem.Value = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,8 +56,8 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Plum);
|
||||
|
||||
[Cached]
|
||||
private readonly OnlinePlayBeatmapAvailabilityTracker beatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
|
||||
[Cached(typeof(OnlinePlayBeatmapAvailabilityTracker))]
|
||||
private readonly DailyChallengeBeatmapAvailabilityTracker beatmapAvailabilityTracker;
|
||||
|
||||
private bool shouldBePlayingMusic;
|
||||
|
||||
@@ -91,6 +91,8 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
item = room.Playlist.Single();
|
||||
|
||||
ValidForResume = false;
|
||||
|
||||
beatmapAvailabilityTracker = new DailyChallengeBeatmapAvailabilityTracker(item);
|
||||
}
|
||||
|
||||
protected override BackgroundScreen CreateBackground() => new DailyChallengeIntroBackgroundScreen(colourProvider);
|
||||
@@ -352,7 +354,6 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
{
|
||||
base.OnEntering(e);
|
||||
|
||||
beatmapAvailabilityTracker.SelectedItem.Value = item;
|
||||
beatmapAvailabilityTracker.Availability.BindValueChanged(availability =>
|
||||
{
|
||||
if (shouldBePlayingMusic && availability.NewValue.State == DownloadState.LocallyAvailable)
|
||||
|
||||
@@ -14,8 +14,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
/// <param name="room">The room to join.</param>
|
||||
/// <param name="password">The password.</param>
|
||||
/// <param name="onSuccess">A delegate to invoke if the user joined the room.</param>
|
||||
/// <param name="onFailure">A delegate to invoke if the user is not able join the room.</param>
|
||||
void Join(Room room, string? password, Action<Room>? onSuccess = null, Action<string>? onFailure = null);
|
||||
/// <param name="onFailure">A delegate to invoke if the user is not able to join the room.</param>
|
||||
void Join(Room room, string? password, Action<Room>? onSuccess = null, Action<string, Exception?>? onFailure = null);
|
||||
|
||||
/// <summary>
|
||||
/// Copies the given room and opens it as a fresh (not-yet-created) one.
|
||||
|
||||
@@ -17,6 +17,7 @@ using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@@ -306,13 +307,14 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
GetContainingFocusManager()?.TriggerFocusContention(passwordTextBox);
|
||||
}
|
||||
|
||||
private void joinFailed(string error) => Schedule(() =>
|
||||
private void joinFailed(string message, Exception? exception) => Schedule(() =>
|
||||
{
|
||||
passwordTextBox.Text = string.Empty;
|
||||
|
||||
GetContainingFocusManager()!.ChangeFocus(passwordTextBox);
|
||||
|
||||
errorText.Text = error;
|
||||
Logger.Log($"Failed to join room with password. {exception}");
|
||||
errorText.Text = message;
|
||||
errorText
|
||||
.FadeIn()
|
||||
.FlashColour(Color4.White, 200)
|
||||
|
||||
@@ -334,7 +334,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
popoverContainer.HidePopover();
|
||||
}
|
||||
|
||||
public void Join(Room room, string? password, Action<Room>? onSuccess = null, Action<string>? onFailure = null) => Schedule(() =>
|
||||
public void Join(Room room, string? password, Action<Room>? onSuccess = null, Action<string, Exception?>? onFailure = null) => Schedule(() =>
|
||||
{
|
||||
if (joiningRoomOperation != null)
|
||||
return;
|
||||
@@ -347,19 +347,19 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
joiningRoomOperation?.Dispose();
|
||||
joiningRoomOperation = null;
|
||||
onSuccess?.Invoke(room);
|
||||
}, error =>
|
||||
}, (message, exception) =>
|
||||
{
|
||||
joiningRoomOperation?.Dispose();
|
||||
joiningRoomOperation = null;
|
||||
|
||||
if (onFailure != null)
|
||||
onFailure(error);
|
||||
onFailure(message, exception);
|
||||
else
|
||||
Logger.Log(error, level: LogLevel.Error);
|
||||
Logger.Error(exception, message);
|
||||
});
|
||||
});
|
||||
|
||||
protected abstract void JoinInternal(Room room, string? password, Action<Room> onSuccess, Action<string> onFailure);
|
||||
protected abstract void JoinInternal(Room room, string? password, Action<Room> onSuccess, Action<string, Exception?> onFailure);
|
||||
|
||||
public void OpenCopy(Room room)
|
||||
{
|
||||
|
||||
@@ -94,8 +94,8 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
[Resolved(canBeNull: true)]
|
||||
protected IDialogOverlay? DialogOverlay { get; private set; }
|
||||
|
||||
[Cached]
|
||||
private readonly OnlinePlayBeatmapAvailabilityTracker beatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
|
||||
[Cached(typeof(OnlinePlayBeatmapAvailabilityTracker))]
|
||||
private readonly MultiplayerBeatmapAvailabilityTracker beatmapAvailabilityTracker = new MultiplayerBeatmapAvailabilityTracker();
|
||||
|
||||
protected IBindable<BeatmapAvailability> BeatmapAvailability => beatmapAvailabilityTracker.Availability;
|
||||
|
||||
@@ -263,7 +263,6 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
SelectedItem.BindValueChanged(_ => updateSpecifics());
|
||||
UserMods.BindValueChanged(_ => updateSpecifics());
|
||||
|
||||
beatmapAvailabilityTracker.SelectedItem.BindTo(SelectedItem);
|
||||
beatmapAvailabilityTracker.Availability.BindValueChanged(_ => updateSpecifics());
|
||||
|
||||
userModsSelectOverlayRegistration = overlayManager?.RegisterBlockingOverlay(UserModsSelectOverlay);
|
||||
|
||||
@@ -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 osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
{
|
||||
public partial class MultiplayerBeatmapAvailabilityTracker : OnlinePlayBeatmapAvailabilityTracker
|
||||
{
|
||||
[Resolved]
|
||||
private MultiplayerClient client { get; set; } = null!;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
client.RoomUpdated += onRoomUpdated;
|
||||
onRoomUpdated();
|
||||
}
|
||||
|
||||
private void onRoomUpdated()
|
||||
{
|
||||
if (client.Room == null)
|
||||
return;
|
||||
|
||||
PlaylistItem.Value = new PlaylistItem(client.Room.CurrentPlaylistItem);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (client.IsNotNull())
|
||||
client.RoomUpdated -= onRoomUpdated;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
|
||||
protected override OnlinePlaySubScreen CreateRoomSubScreen(Room room) => new MultiplayerMatchSubScreen(room);
|
||||
|
||||
protected override void JoinInternal(Room room, string? password, Action<Room> onSuccess, Action<string> onFailure)
|
||||
protected override void JoinInternal(Room room, string? password, Action<Room> onSuccess, Action<string, Exception?> onFailure)
|
||||
{
|
||||
client.JoinRoom(room, password).ContinueWith(result =>
|
||||
{
|
||||
@@ -86,9 +86,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
Exception? exception = result.Exception?.AsSingular();
|
||||
|
||||
if (exception?.GetHubExceptionMessage() is string message)
|
||||
onFailure(message);
|
||||
onFailure(message, exception);
|
||||
else
|
||||
onFailure($"Failed to join multiplayer room: {exception?.Message}");
|
||||
onFailure($"Failed to join multiplayer room. {exception?.Message}", exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Participants;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
using ParticipantsList = osu.Game.Screens.OnlinePlay.Multiplayer.Participants.ParticipantsList;
|
||||
@@ -180,11 +179,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
Text = "Select",
|
||||
Action = ShowUserModSelect,
|
||||
},
|
||||
new ModDisplay
|
||||
new MultiplayerUserModDisplay
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Current = UserMods,
|
||||
Scale = new Vector2(0.8f),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
{
|
||||
public partial class MultiplayerUserModDisplay : CompositeDrawable
|
||||
{
|
||||
[Resolved]
|
||||
private MultiplayerClient client { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; } = null!;
|
||||
|
||||
private ModDisplay modDisplay = null!;
|
||||
|
||||
public MultiplayerUserModDisplay()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChild = modDisplay = new ModDisplay();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
client.RoomUpdated += onRoomUpdated;
|
||||
onRoomUpdated();
|
||||
}
|
||||
|
||||
private void onRoomUpdated() => Scheduler.AddOnce(() =>
|
||||
{
|
||||
if (client.Room == null || client.LocalUser == null)
|
||||
return;
|
||||
|
||||
MultiplayerPlaylistItem currentItem = client.Room.CurrentPlaylistItem;
|
||||
Ruleset ruleset = rulesets.GetRuleset(client.LocalUser.RulesetId ?? currentItem.RulesetID)!.CreateInstance();
|
||||
Mod[] userMods = client.LocalUser.Mods.Select(m => m.ToMod(ruleset)).ToArray();
|
||||
|
||||
if (!userMods.SequenceEqual(modDisplay.Current.Value))
|
||||
modDisplay.Current.Value = userMods;
|
||||
});
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (client.IsNotNull())
|
||||
client.RoomUpdated -= onRoomUpdated;
|
||||
}
|
||||
}
|
||||
}
|
||||
+58
-66
@@ -1,8 +1,6 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
@@ -16,10 +14,12 @@ using osu.Framework.Logging;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
using Realms;
|
||||
|
||||
namespace osu.Game.Online.Rooms
|
||||
namespace osu.Game.Screens.OnlinePlay
|
||||
{
|
||||
/// <summary>
|
||||
/// Represent a checksum-verifying beatmap availability tracker usable for online play screens.
|
||||
@@ -27,9 +27,17 @@ namespace osu.Game.Online.Rooms
|
||||
/// This differs from a regular download tracking composite as this accounts for the
|
||||
/// databased beatmap set's checksum, to disallow from playing with an altered version of the beatmap.
|
||||
/// </summary>
|
||||
public partial class OnlinePlayBeatmapAvailabilityTracker : CompositeComponent
|
||||
public abstract partial class OnlinePlayBeatmapAvailabilityTracker : CompositeComponent
|
||||
{
|
||||
public readonly Bindable<PlaylistItem> SelectedItem = new Bindable<PlaylistItem>();
|
||||
/// <summary>
|
||||
/// The current availability of <see cref="PlaylistItem"/>'s beatmap.
|
||||
/// </summary>
|
||||
public virtual IBindable<BeatmapAvailability> Availability => availability; // Virtual for mocking in some tests.
|
||||
|
||||
/// <summary>
|
||||
/// The playlist item to track the availability of.
|
||||
/// </summary>
|
||||
protected readonly Bindable<PlaylistItem?> PlaylistItem = new Bindable<PlaylistItem?>();
|
||||
|
||||
[Resolved]
|
||||
private RealmAccess realm { get; set; } = null!;
|
||||
@@ -37,23 +45,17 @@ namespace osu.Game.Online.Rooms
|
||||
[Resolved]
|
||||
private BeatmapLookupCache beatmapLookupCache { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The availability state of the currently selected playlist item.
|
||||
/// </summary>
|
||||
public virtual IBindable<BeatmapAvailability> Availability => availability;
|
||||
|
||||
private readonly Bindable<BeatmapAvailability> availability = new Bindable<BeatmapAvailability>(BeatmapAvailability.NotDownloaded());
|
||||
|
||||
private ScheduledDelegate progressUpdate;
|
||||
private BeatmapDownloadTracker downloadTracker;
|
||||
private IDisposable realmSubscription;
|
||||
private APIBeatmap selectedBeatmap;
|
||||
private ScheduledDelegate? progressUpdate;
|
||||
private BeatmapDownloadTracker? downloadTracker;
|
||||
private IDisposable? realmSubscription;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
SelectedItem.BindValueChanged(item =>
|
||||
PlaylistItem.BindValueChanged(item =>
|
||||
{
|
||||
// the underlying playlist is regularly cleared for maintenance purposes (things which probably need to be fixed eventually).
|
||||
// to avoid exposing a state change when there may actually be none, ignore all nulls for now.
|
||||
@@ -69,30 +71,29 @@ namespace osu.Game.Online.Rooms
|
||||
// This is just for safety.
|
||||
availability.Value = BeatmapAvailability.Unknown();
|
||||
|
||||
downloadTracker?.RemoveAndDisposeImmediately();
|
||||
selectedBeatmap = null;
|
||||
cancelTracking();
|
||||
|
||||
beatmapLookupCache.GetBeatmapAsync(item.NewValue.Beatmap.OnlineID).ContinueWith(task => Schedule(() =>
|
||||
{
|
||||
var beatmap = task.GetResultSafely();
|
||||
|
||||
if (beatmap != null && SelectedItem.Value?.Beatmap.OnlineID == beatmap.OnlineID)
|
||||
{
|
||||
selectedBeatmap = beatmap;
|
||||
beginTracking();
|
||||
}
|
||||
if (beatmap != null && PlaylistItem.Value?.Beatmap.OnlineID == beatmap.OnlineID)
|
||||
startTracking(beatmap);
|
||||
}), TaskContinuationOptions.OnlyOnRanToCompletion);
|
||||
}, true);
|
||||
}
|
||||
|
||||
private void beginTracking()
|
||||
private void cancelTracking()
|
||||
{
|
||||
Debug.Assert(selectedBeatmap.BeatmapSet != null);
|
||||
downloadTracker?.RemoveAndDisposeImmediately();
|
||||
realmSubscription?.Dispose();
|
||||
}
|
||||
|
||||
downloadTracker = new BeatmapDownloadTracker(selectedBeatmap.BeatmapSet);
|
||||
|
||||
AddInternal(downloadTracker);
|
||||
private void startTracking(APIBeatmap beatmap)
|
||||
{
|
||||
Debug.Assert(beatmap.BeatmapSet != null);
|
||||
|
||||
downloadTracker = new BeatmapDownloadTracker(beatmap.BeatmapSet);
|
||||
downloadTracker.State.BindValueChanged(_ => Scheduler.AddOnce(updateAvailability), true);
|
||||
downloadTracker.Progress.BindValueChanged(_ =>
|
||||
{
|
||||
@@ -105,64 +106,55 @@ namespace osu.Game.Online.Rooms
|
||||
progressUpdate = Scheduler.AddDelayed(updateAvailability, progressUpdate == null ? 0 : 500);
|
||||
}, true);
|
||||
|
||||
AddInternal(downloadTracker);
|
||||
|
||||
// handles changes to hash that didn't occur from the import process (ie. a user editing the beatmap in the editor, somehow).
|
||||
realmSubscription?.Dispose();
|
||||
realmSubscription = realm.RegisterForNotifications(_ => filteredBeatmaps(), (_, changes) =>
|
||||
realmSubscription = realm.RegisterForNotifications(_ => queryBeatmap(), (_, changes) =>
|
||||
{
|
||||
if (changes == null)
|
||||
return;
|
||||
|
||||
Scheduler.AddOnce(updateAvailability);
|
||||
});
|
||||
}
|
||||
|
||||
private void updateAvailability()
|
||||
{
|
||||
if (downloadTracker == null || selectedBeatmap == null)
|
||||
return;
|
||||
|
||||
switch (downloadTracker.State.Value)
|
||||
void updateAvailability()
|
||||
{
|
||||
case DownloadState.Unknown:
|
||||
availability.Value = BeatmapAvailability.Unknown();
|
||||
break;
|
||||
switch (downloadTracker.State.Value)
|
||||
{
|
||||
case DownloadState.Unknown:
|
||||
availability.Value = BeatmapAvailability.Unknown();
|
||||
break;
|
||||
|
||||
case DownloadState.NotDownloaded:
|
||||
availability.Value = BeatmapAvailability.NotDownloaded();
|
||||
break;
|
||||
case DownloadState.NotDownloaded:
|
||||
availability.Value = BeatmapAvailability.NotDownloaded();
|
||||
break;
|
||||
|
||||
case DownloadState.Downloading:
|
||||
availability.Value = BeatmapAvailability.Downloading((float)downloadTracker.Progress.Value);
|
||||
break;
|
||||
case DownloadState.Downloading:
|
||||
availability.Value = BeatmapAvailability.Downloading((float)downloadTracker.Progress.Value);
|
||||
break;
|
||||
|
||||
case DownloadState.Importing:
|
||||
availability.Value = BeatmapAvailability.Importing();
|
||||
break;
|
||||
case DownloadState.Importing:
|
||||
availability.Value = BeatmapAvailability.Importing();
|
||||
break;
|
||||
|
||||
case DownloadState.LocallyAvailable:
|
||||
bool available = filteredBeatmaps().Any();
|
||||
case DownloadState.LocallyAvailable:
|
||||
bool available = queryBeatmap().Any();
|
||||
|
||||
availability.Value = available ? BeatmapAvailability.LocallyAvailable() : BeatmapAvailability.NotDownloaded();
|
||||
availability.Value = available ? BeatmapAvailability.LocallyAvailable() : BeatmapAvailability.NotDownloaded();
|
||||
|
||||
// only display a message to the user if a download seems to have just completed.
|
||||
if (!available && downloadTracker.Progress.Value == 1)
|
||||
Logger.Log("The imported beatmap set does not match the online version.", LoggingTarget.Runtime, LogLevel.Important);
|
||||
// only display a message to the user if a download seems to have just completed.
|
||||
if (!available && downloadTracker.Progress.Value == 1)
|
||||
Logger.Log("The imported beatmap set does not match the online version.", LoggingTarget.Runtime, LogLevel.Important);
|
||||
|
||||
break;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IQueryable<BeatmapInfo> filteredBeatmaps()
|
||||
{
|
||||
int onlineId = selectedBeatmap.OnlineID;
|
||||
string checksum = selectedBeatmap.MD5Hash;
|
||||
|
||||
return realm.Realm
|
||||
.All<BeatmapInfo>()
|
||||
.Filter("OnlineID == $0 && MD5Hash == $1 && BeatmapSet.DeletePending == false", onlineId, checksum);
|
||||
IQueryable<BeatmapInfo> queryBeatmap() =>
|
||||
realm.Realm.All<BeatmapInfo>().Filter("OnlineID == $0 && MD5Hash == $1 && BeatmapSet.DeletePending == false", beatmap.OnlineID, beatmap.MD5Hash);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
@@ -0,0 +1,13 @@
|
||||
// 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.Bindables;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
{
|
||||
public partial class PlaylistsBeatmapAvailabilityTracker : OnlinePlayBeatmapAvailabilityTracker
|
||||
{
|
||||
public new Bindable<PlaylistItem?> PlaylistItem => base.PlaylistItem;
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
return criteria;
|
||||
}
|
||||
|
||||
protected override void JoinInternal(Room room, string? password, Action<Room> onSuccess, Action<string> onFailure)
|
||||
protected override void JoinInternal(Room room, string? password, Action<Room> onSuccess, Action<string, Exception?> onFailure)
|
||||
{
|
||||
var joinRoomRequest = new JoinRoomRequest(room, password);
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
joinRoomRequest.Failure += exception =>
|
||||
{
|
||||
if (exception is not OperationCanceledException)
|
||||
onFailure(exception.Message);
|
||||
onFailure($"Failed to open playlist. {exception.Message}", exception);
|
||||
};
|
||||
|
||||
api.Queue(joinRoomRequest);
|
||||
|
||||
@@ -110,8 +110,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
[Resolved]
|
||||
private IDialogOverlay? dialogOverlay { get; set; }
|
||||
|
||||
[Cached]
|
||||
private readonly OnlinePlayBeatmapAvailabilityTracker beatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
|
||||
[Cached(typeof(OnlinePlayBeatmapAvailabilityTracker))]
|
||||
private readonly PlaylistsBeatmapAvailabilityTracker beatmapAvailabilityTracker;
|
||||
|
||||
protected readonly Bindable<PlaylistItem?> SelectedItem = new Bindable<PlaylistItem?>();
|
||||
protected readonly Bindable<BeatmapInfo?> UserBeatmap = new Bindable<BeatmapInfo?>();
|
||||
@@ -146,6 +146,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
Activity.Value = new UserActivity.InLobby(room);
|
||||
|
||||
Padding = new MarginPadding { Top = Header.HEIGHT };
|
||||
|
||||
beatmapAvailabilityTracker = new PlaylistsBeatmapAvailabilityTracker
|
||||
{
|
||||
PlaylistItem = { BindTarget = SelectedItem }
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@@ -450,12 +455,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
|
||||
isIdle.BindValueChanged(_ => updatePollingRate(), true);
|
||||
|
||||
SelectedItem.BindValueChanged(onSelectedItemChanged);
|
||||
|
||||
beatmapAvailabilityTracker.SelectedItem.BindTo(SelectedItem);
|
||||
beatmapAvailabilityTracker.Availability.BindValueChanged(_ => updateGameplayState());
|
||||
|
||||
SelectedItem.BindValueChanged(onSelectedItemChanged);
|
||||
UserBeatmap.BindValueChanged(_ => updateGameplayState());
|
||||
UserMods.BindValueChanged(_ => updateGameplayState());
|
||||
UserRuleset.BindValueChanged(_ =>
|
||||
|
||||
@@ -298,14 +298,24 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
return ((IMultiplayerClient)this).UserKicked(clone(user));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simulates a change to the server-side room's settings without any other change.
|
||||
/// </summary>
|
||||
public async Task ChangeServerRoomSettings(MultiplayerRoomSettings settings)
|
||||
{
|
||||
Debug.Assert(ServerRoom != null);
|
||||
|
||||
ServerRoom.Settings = settings;
|
||||
|
||||
await ((IMultiplayerClient)this).SettingsChanged(clone(settings)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public override async Task ChangeSettings(MultiplayerRoomSettings settings)
|
||||
{
|
||||
settings = clone(settings);
|
||||
|
||||
Debug.Assert(ServerRoom != null);
|
||||
Debug.Assert(currentItem != null);
|
||||
|
||||
// Server is authoritative for the time being.
|
||||
settings = clone(settings);
|
||||
settings.PlaylistItemId = ServerRoom.Settings.PlaylistItemId;
|
||||
ServerRoom.Settings = settings;
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
|
||||
namespace osu.Game.Tests.Visual.OnlinePlay
|
||||
@@ -17,11 +16,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
|
||||
/// </summary>
|
||||
OngoingOperationTracker OngoingOperationTracker { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The cached <see cref="OnlinePlayBeatmapAvailabilityTracker"/>.
|
||||
/// </summary>
|
||||
OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The cached <see cref="UserLookupCache"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -22,7 +22,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
|
||||
public abstract partial class OnlinePlayTestScene : ScreenTestScene, IOnlinePlayTestSceneDependencies
|
||||
{
|
||||
public OngoingOperationTracker OngoingOperationTracker => OnlinePlayDependencies.OngoingOperationTracker;
|
||||
public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker => OnlinePlayDependencies.AvailabilityTracker;
|
||||
public TestUserLookupCache UserLookupCache => OnlinePlayDependencies.UserLookupCache;
|
||||
public BeatmapLookupCache BeatmapLookupCache => OnlinePlayDependencies.BeatmapLookupCache;
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
|
||||
@@ -18,7 +17,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
|
||||
public class OnlinePlayTestSceneDependencies : IReadOnlyDependencyContainer, IOnlinePlayTestSceneDependencies
|
||||
{
|
||||
public OngoingOperationTracker OngoingOperationTracker { get; }
|
||||
public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker { get; }
|
||||
public TestRoomRequestsHandler RequestsHandler { get; }
|
||||
public TestUserLookupCache UserLookupCache { get; }
|
||||
public BeatmapLookupCache BeatmapLookupCache { get; }
|
||||
@@ -35,7 +33,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
|
||||
{
|
||||
RequestsHandler = new TestRoomRequestsHandler();
|
||||
OngoingOperationTracker = new OngoingOperationTracker();
|
||||
AvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
|
||||
UserLookupCache = new TestUserLookupCache();
|
||||
BeatmapLookupCache = new BeatmapLookupCache();
|
||||
|
||||
@@ -43,7 +40,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay
|
||||
|
||||
CacheAs(RequestsHandler);
|
||||
CacheAs(OngoingOperationTracker);
|
||||
CacheAs(AvailabilityTracker);
|
||||
CacheAs(new OverlayColourProvider(OverlayColourScheme.Plum));
|
||||
CacheAs<UserLookupCache>(UserLookupCache);
|
||||
CacheAs(BeatmapLookupCache);
|
||||
|
||||
Reference in New Issue
Block a user