mirror of
https://github.com/ppy/osu.git
synced 2025-01-18 14:52:56 +08:00
Rewrite everything to better support spectator server messaging
This commit is contained in:
parent
95fe8d67e4
commit
0093af8f55
@ -95,6 +95,14 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
/// <param name="beatmapAvailability">The new beatmap availability state of the user.</param>
|
/// <param name="beatmapAvailability">The new beatmap availability state of the user.</param>
|
||||||
Task UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability);
|
Task UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signals that a user in this room changed their style.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId">The ID of the user whose style changed.</param>
|
||||||
|
/// <param name="beatmapId">The user's beatmap.</param>
|
||||||
|
/// <param name="rulesetId">The user's ruleset.</param>
|
||||||
|
Task UserStyleChanged(int userId, int? beatmapId, int? rulesetId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Signals that a user in this room changed their local mods.
|
/// Signals that a user in this room changed their local mods.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -57,6 +57,13 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
/// <param name="newBeatmapAvailability">The proposed new beatmap availability state.</param>
|
/// <param name="newBeatmapAvailability">The proposed new beatmap availability state.</param>
|
||||||
Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability);
|
Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Change the local user's style in the currently joined room.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="beatmapId">The beatmap.</param>
|
||||||
|
/// <param name="rulesetId">The ruleset.</param>
|
||||||
|
Task ChangeUserStyle(int? beatmapId, int? rulesetId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Change the local user's mods in the currently joined room.
|
/// Change the local user's mods in the currently joined room.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -359,6 +359,8 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
|
|
||||||
public abstract Task DisconnectInternal();
|
public abstract Task DisconnectInternal();
|
||||||
|
|
||||||
|
public abstract Task ChangeUserStyle(int? beatmapId, int? rulesetId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Change the local user's mods in the currently joined room.
|
/// Change the local user's mods in the currently joined room.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -652,6 +654,25 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task UserStyleChanged(int userId, int? beatmapId, int? rulesetId)
|
||||||
|
{
|
||||||
|
Scheduler.Add(() =>
|
||||||
|
{
|
||||||
|
var user = Room?.Users.SingleOrDefault(u => u.UserID == userId);
|
||||||
|
|
||||||
|
// errors here are not critical - user style is mostly for display.
|
||||||
|
if (user == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
user.BeatmapId = beatmapId;
|
||||||
|
user.RulesetId = rulesetId;
|
||||||
|
|
||||||
|
RoomUpdated?.Invoke();
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
public Task UserModsChanged(int userId, IEnumerable<APIMod> mods)
|
public Task UserModsChanged(int userId, IEnumerable<APIMod> mods)
|
||||||
{
|
{
|
||||||
Scheduler.Add(() =>
|
Scheduler.Add(() =>
|
||||||
|
@ -22,9 +22,6 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
[Key(1)]
|
[Key(1)]
|
||||||
public MultiplayerUserState State { get; set; } = MultiplayerUserState.Idle;
|
public MultiplayerUserState State { get; set; } = MultiplayerUserState.Idle;
|
||||||
|
|
||||||
[Key(4)]
|
|
||||||
public MatchUserState? MatchState { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The availability state of the current beatmap.
|
/// The availability state of the current beatmap.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -37,6 +34,21 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
[Key(3)]
|
[Key(3)]
|
||||||
public IEnumerable<APIMod> Mods { get; set; } = Enumerable.Empty<APIMod>();
|
public IEnumerable<APIMod> Mods { get; set; } = Enumerable.Empty<APIMod>();
|
||||||
|
|
||||||
|
[Key(4)]
|
||||||
|
public MatchUserState? MatchState { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Any ruleset applicable only to the local user.
|
||||||
|
/// </summary>
|
||||||
|
[Key(5)]
|
||||||
|
public int? RulesetId;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Any beatmap applicable only to the local user.
|
||||||
|
/// </summary>
|
||||||
|
[Key(6)]
|
||||||
|
public int? BeatmapId;
|
||||||
|
|
||||||
[IgnoreMember]
|
[IgnoreMember]
|
||||||
public APIUser? User { get; set; }
|
public APIUser? User { get; set; }
|
||||||
|
|
||||||
|
@ -60,6 +60,7 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
connection.On(nameof(IMultiplayerClient.GameplayStarted), ((IMultiplayerClient)this).GameplayStarted);
|
connection.On(nameof(IMultiplayerClient.GameplayStarted), ((IMultiplayerClient)this).GameplayStarted);
|
||||||
connection.On<GameplayAbortReason>(nameof(IMultiplayerClient.GameplayAborted), ((IMultiplayerClient)this).GameplayAborted);
|
connection.On<GameplayAbortReason>(nameof(IMultiplayerClient.GameplayAborted), ((IMultiplayerClient)this).GameplayAborted);
|
||||||
connection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady);
|
connection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady);
|
||||||
|
connection.On<int, int?, int?>(nameof(IMultiplayerClient.UserStyleChanged), ((IMultiplayerClient)this).UserStyleChanged);
|
||||||
connection.On<int, IEnumerable<APIMod>>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged);
|
connection.On<int, IEnumerable<APIMod>>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged);
|
||||||
connection.On<int, BeatmapAvailability>(nameof(IMultiplayerClient.UserBeatmapAvailabilityChanged), ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged);
|
connection.On<int, BeatmapAvailability>(nameof(IMultiplayerClient.UserBeatmapAvailabilityChanged), ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged);
|
||||||
connection.On<MatchRoomState>(nameof(IMultiplayerClient.MatchRoomStateChanged), ((IMultiplayerClient)this).MatchRoomStateChanged);
|
connection.On<MatchRoomState>(nameof(IMultiplayerClient.MatchRoomStateChanged), ((IMultiplayerClient)this).MatchRoomStateChanged);
|
||||||
@ -186,6 +187,16 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeBeatmapAvailability), newBeatmapAvailability);
|
return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeBeatmapAvailability), newBeatmapAvailability);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Task ChangeUserStyle(int? beatmapId, int? rulesetId)
|
||||||
|
{
|
||||||
|
if (!IsConnected.Value)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
Debug.Assert(connection != null);
|
||||||
|
|
||||||
|
return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeUserStyle), beatmapId, rulesetId);
|
||||||
|
}
|
||||||
|
|
||||||
public override Task ChangeUserMods(IEnumerable<APIMod> newMods)
|
public override Task ChangeUserMods(IEnumerable<APIMod> newMods)
|
||||||
{
|
{
|
||||||
if (!IsConnected.Value)
|
if (!IsConnected.Value)
|
||||||
|
@ -4,14 +4,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Extensions.ObjectExtensions;
|
|
||||||
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.Graphics.Cursor;
|
||||||
@ -28,6 +26,7 @@ using osu.Game.Rulesets.Mods;
|
|||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
using osu.Game.Screens.OnlinePlay.Match.Components;
|
using osu.Game.Screens.OnlinePlay.Match.Components;
|
||||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||||
|
using osu.Game.Utils;
|
||||||
using Container = osu.Framework.Graphics.Containers.Container;
|
using Container = osu.Framework.Graphics.Containers.Container;
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay.Match
|
namespace osu.Game.Screens.OnlinePlay.Match
|
||||||
@ -37,18 +36,6 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
{
|
{
|
||||||
public readonly Bindable<PlaylistItem?> SelectedItem = new Bindable<PlaylistItem?>();
|
public readonly Bindable<PlaylistItem?> SelectedItem = new Bindable<PlaylistItem?>();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// When players are freely allowed to select their own gameplay style (selected item has a non-null beatmapset id),
|
|
||||||
/// a non-null value indicates a local beatmap selection from the same beatmapset as the selected item.
|
|
||||||
/// </summary>
|
|
||||||
public readonly Bindable<BeatmapInfo?> DifficultyOverride = new Bindable<BeatmapInfo?>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// When players are freely allowed to select their own gameplay style (selected item has a non-null beatmapset id),
|
|
||||||
/// a non-null value indicates a local ruleset selection.
|
|
||||||
/// </summary>
|
|
||||||
public readonly Bindable<RulesetInfo?> RulesetOverride = new Bindable<RulesetInfo?>();
|
|
||||||
|
|
||||||
public override bool? ApplyModTrackAdjustments => true;
|
public override bool? ApplyModTrackAdjustments => true;
|
||||||
|
|
||||||
protected override BackgroundScreen CreateBackground() => new RoomBackgroundScreen(Room.Playlist.FirstOrDefault())
|
protected override BackgroundScreen CreateBackground() => new RoomBackgroundScreen(Room.Playlist.FirstOrDefault())
|
||||||
@ -65,13 +52,13 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
protected Drawable? UserModsSection;
|
protected Drawable? UserModsSection;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A container that provides controls for selection of the user's difficulty override.
|
/// A container that provides controls for selection of the user style.
|
||||||
/// This will be shown/hidden automatically when applicable.
|
/// This will be shown/hidden automatically when applicable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected Drawable? UserDifficultySection;
|
protected Drawable? UserStyleSection;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A container that will display the user's difficulty override.
|
/// A container that will display the user's style.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected Container<DrawableRoomPlaylistItem>? UserStyleDisplayContainer;
|
protected Container<DrawableRoomPlaylistItem>? UserStyleDisplayContainer;
|
||||||
|
|
||||||
@ -82,6 +69,18 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly Bindable<IReadOnlyList<Mod>> UserMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
protected readonly Bindable<IReadOnlyList<Mod>> UserMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When players are freely allowed to select their own gameplay style (selected item has a non-null beatmapset id),
|
||||||
|
/// a non-null value indicates a local beatmap selection from the same beatmapset as the selected item.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Bindable<BeatmapInfo?> UserBeatmap = new Bindable<BeatmapInfo?>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When players are freely allowed to select their own gameplay style (selected item has a non-null beatmapset id),
|
||||||
|
/// a non-null value indicates a local ruleset selection.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Bindable<RulesetInfo?> UserRuleset = new Bindable<RulesetInfo?>();
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private IOverlayManager? overlayManager { get; set; }
|
private IOverlayManager? overlayManager { get; set; }
|
||||||
|
|
||||||
@ -272,13 +271,25 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(selectedItemChanged));
|
SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(OnSelectedItemChanged));
|
||||||
UserMods.BindValueChanged(_ => Scheduler.AddOnce(UpdateMods));
|
|
||||||
DifficultyOverride.BindValueChanged(_ => Scheduler.AddOnce(updateStyleOverride));
|
UserMods.BindValueChanged(_ => Scheduler.AddOnce(updateMods));
|
||||||
RulesetOverride.BindValueChanged(_ => Scheduler.AddOnce(updateStyleOverride));
|
|
||||||
|
UserBeatmap.BindValueChanged(_ => Scheduler.AddOnce(() =>
|
||||||
|
{
|
||||||
|
updateBeatmap();
|
||||||
|
updateUserStyle();
|
||||||
|
}));
|
||||||
|
|
||||||
|
UserRuleset.BindValueChanged(_ => Scheduler.AddOnce(() =>
|
||||||
|
{
|
||||||
|
updateUserMods();
|
||||||
|
updateRuleset();
|
||||||
|
updateUserStyle();
|
||||||
|
}));
|
||||||
|
|
||||||
beatmapAvailabilityTracker.SelectedItem.BindTo(SelectedItem);
|
beatmapAvailabilityTracker.SelectedItem.BindTo(SelectedItem);
|
||||||
beatmapAvailabilityTracker.Availability.BindValueChanged(_ => updateWorkingBeatmap());
|
beatmapAvailabilityTracker.Availability.BindValueChanged(_ => updateBeatmap());
|
||||||
|
|
||||||
userModsSelectOverlayRegistration = overlayManager?.RegisterBlockingOverlay(UserModsSelectOverlay);
|
userModsSelectOverlayRegistration = overlayManager?.RegisterBlockingOverlay(UserModsSelectOverlay);
|
||||||
|
|
||||||
@ -347,7 +358,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
public override void OnSuspending(ScreenTransitionEvent e)
|
public override void OnSuspending(ScreenTransitionEvent e)
|
||||||
{
|
{
|
||||||
// Should be a noop in most cases, but let's ensure beyond doubt that the beatmap is in a correct state.
|
// Should be a noop in most cases, but let's ensure beyond doubt that the beatmap is in a correct state.
|
||||||
updateWorkingBeatmap();
|
updateBeatmap();
|
||||||
|
|
||||||
onLeaving();
|
onLeaving();
|
||||||
base.OnSuspending(e);
|
base.OnSuspending(e);
|
||||||
@ -356,10 +367,11 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
public override void OnResuming(ScreenTransitionEvent e)
|
public override void OnResuming(ScreenTransitionEvent e)
|
||||||
{
|
{
|
||||||
base.OnResuming(e);
|
base.OnResuming(e);
|
||||||
updateWorkingBeatmap();
|
updateBeatmap();
|
||||||
beginHandlingTrack();
|
beginHandlingTrack();
|
||||||
Scheduler.AddOnce(UpdateMods);
|
Scheduler.AddOnce(updateMods);
|
||||||
Scheduler.AddOnce(updateRuleset);
|
Scheduler.AddOnce(updateRuleset);
|
||||||
|
Scheduler.AddOnce(updateUserStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool ExitConfirmed { get; private set; }
|
protected bool ExitConfirmed { get; private set; }
|
||||||
@ -409,9 +421,13 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
|
|
||||||
protected void StartPlay()
|
protected void StartPlay()
|
||||||
{
|
{
|
||||||
if (GetGameplayItem() is not PlaylistItem item)
|
if (SelectedItem.Value is not PlaylistItem item)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
item = item.With(
|
||||||
|
ruleset: GetGameplayRuleset().OnlineID,
|
||||||
|
beatmap: new Optional<IBeatmapInfo>(GetGameplayBeatmap()));
|
||||||
|
|
||||||
// User may be at song select or otherwise when the host starts gameplay.
|
// User may be at song select or otherwise when the host starts gameplay.
|
||||||
// Ensure that they first return to this screen, else global bindables (beatmap etc.) may be in a bad state.
|
// Ensure that they first return to this screen, else global bindables (beatmap etc.) may be in a bad state.
|
||||||
if (!this.IsCurrentScreen())
|
if (!this.IsCurrentScreen())
|
||||||
@ -437,31 +453,26 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
/// <returns>The screen to enter.</returns>
|
/// <returns>The screen to enter.</returns>
|
||||||
protected abstract Screen CreateGameplayScreen(PlaylistItem selectedItem);
|
protected abstract Screen CreateGameplayScreen(PlaylistItem selectedItem);
|
||||||
|
|
||||||
private void selectedItemChanged()
|
protected void OnSelectedItemChanged()
|
||||||
{
|
{
|
||||||
if (SelectedItem.Value is not PlaylistItem selected)
|
if (!this.IsCurrentScreen() || SelectedItem.Value is not PlaylistItem item)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (selected.BeatmapSetId == null || selected.BeatmapSetId != DifficultyOverride.Value?.BeatmapSet.AsNonNull().OnlineID)
|
// Reset user style if no longer valid.
|
||||||
|
// Todo: In the future this can be made more lenient, such as allowing a non-null ruleset as the set changes.
|
||||||
|
if (item.BeatmapSetId == null || item.BeatmapSetId != UserBeatmap.Value?.BeatmapSet!.OnlineID)
|
||||||
{
|
{
|
||||||
DifficultyOverride.Value = null;
|
UserBeatmap.Value = null;
|
||||||
RulesetOverride.Value = null;
|
UserRuleset.Value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateStyleOverride();
|
updateUserMods();
|
||||||
updateWorkingBeatmap();
|
updateBeatmap();
|
||||||
|
updateMods();
|
||||||
var rulesetInstance = Rulesets.GetRuleset(selected.RulesetID)?.CreateInstance();
|
|
||||||
Debug.Assert(rulesetInstance != null);
|
|
||||||
var allowedMods = selected.AllowedMods.Select(m => m.ToMod(rulesetInstance));
|
|
||||||
|
|
||||||
// Remove any user mods that are no longer allowed.
|
|
||||||
UserMods.Value = UserMods.Value.Where(m => allowedMods.Any(a => m.GetType() == a.GetType())).ToList();
|
|
||||||
|
|
||||||
UpdateMods();
|
|
||||||
updateRuleset();
|
updateRuleset();
|
||||||
|
updateUserStyle();
|
||||||
|
|
||||||
if (!selected.AllowedMods.Any())
|
if (!item.AllowedMods.Any())
|
||||||
{
|
{
|
||||||
UserModsSection?.Hide();
|
UserModsSection?.Hide();
|
||||||
UserModsSelectOverlay.Hide();
|
UserModsSelectOverlay.Hide();
|
||||||
@ -470,100 +481,89 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
UserModsSection?.Show();
|
UserModsSection?.Show();
|
||||||
|
|
||||||
|
var rulesetInstance = GetGameplayRuleset().CreateInstance();
|
||||||
|
var allowedMods = item.AllowedMods.Select(m => m.ToMod(rulesetInstance));
|
||||||
UserModsSelectOverlay.IsValidMod = m => allowedMods.Any(a => a.GetType() == m.GetType());
|
UserModsSelectOverlay.IsValidMod = m => allowedMods.Any(a => a.GetType() == m.GetType());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selected.BeatmapSetId == null)
|
if (item.BeatmapSetId == null)
|
||||||
UserDifficultySection?.Hide();
|
UserStyleSection?.Hide();
|
||||||
else
|
else
|
||||||
UserDifficultySection?.Show();
|
UserStyleSection?.Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateWorkingBeatmap()
|
private void updateUserMods()
|
||||||
{
|
{
|
||||||
if (GetGameplayItem() is not PlaylistItem item || !this.IsCurrentScreen())
|
if (!this.IsCurrentScreen() || SelectedItem.Value is not PlaylistItem item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Remove any user mods that are no longer allowed.
|
||||||
|
var rulesetInstance = GetGameplayRuleset().CreateInstance();
|
||||||
|
var allowedMods = item.AllowedMods.Select(m => m.ToMod(rulesetInstance));
|
||||||
|
UserMods.Value = UserMods.Value.Where(m => allowedMods.Any(a => m.GetType() == a.GetType())).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateBeatmap()
|
||||||
|
{
|
||||||
|
if (!this.IsCurrentScreen() || SelectedItem.Value is not PlaylistItem)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info
|
// Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info
|
||||||
var localBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineID == item.Beatmap.OnlineID);
|
int beatmapId = GetGameplayBeatmap().OnlineID;
|
||||||
|
var localBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineID == beatmapId);
|
||||||
UserModsSelectOverlay.Beatmap.Value = Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
|
Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
|
||||||
|
UserModsSelectOverlay.Beatmap.Value = Beatmap.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void UpdateMods()
|
private void updateMods()
|
||||||
{
|
{
|
||||||
if (GetGameplayItem() is not PlaylistItem item || !this.IsCurrentScreen())
|
if (!this.IsCurrentScreen() || SelectedItem.Value is not PlaylistItem)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var rulesetInstance = Rulesets.GetRuleset(item.RulesetID)?.CreateInstance();
|
var rulesetInstance = GetGameplayRuleset().CreateInstance();
|
||||||
Debug.Assert(rulesetInstance != null);
|
Mods.Value = GetGameplayMods().Select(m => m.ToMod(rulesetInstance)).ToArray();
|
||||||
Mods.Value = UserMods.Value.Concat(item.RequiredMods.Select(m => m.ToMod(rulesetInstance))).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateStyleOverride()
|
|
||||||
{
|
|
||||||
if (SelectedItem.Value == null || !this.IsCurrentScreen())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (UserStyleDisplayContainer == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
PlaylistItem gameplayItem = GetGameplayItem()!;
|
|
||||||
|
|
||||||
if (UserStyleDisplayContainer.SingleOrDefault()?.Item.Equals(gameplayItem) == true)
|
|
||||||
return;
|
|
||||||
|
|
||||||
UserStyleDisplayContainer.Child = new DrawableRoomPlaylistItem(gameplayItem)
|
|
||||||
{
|
|
||||||
AllowReordering = false,
|
|
||||||
AllowEditing = true,
|
|
||||||
RequestEdit = _ => openStyleSelection()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected PlaylistItem? GetGameplayItem()
|
|
||||||
{
|
|
||||||
PlaylistItem? selectedItemWithOverride = SelectedItem.Value;
|
|
||||||
|
|
||||||
if (selectedItemWithOverride?.BeatmapSetId == null)
|
|
||||||
return selectedItemWithOverride;
|
|
||||||
|
|
||||||
// Sanity check.
|
|
||||||
if (DifficultyOverride.Value?.BeatmapSet?.OnlineID != selectedItemWithOverride.BeatmapSetId)
|
|
||||||
return selectedItemWithOverride;
|
|
||||||
|
|
||||||
if (DifficultyOverride.Value != null)
|
|
||||||
selectedItemWithOverride = selectedItemWithOverride.With(beatmap: DifficultyOverride.Value);
|
|
||||||
|
|
||||||
if (RulesetOverride.Value != null)
|
|
||||||
selectedItemWithOverride = selectedItemWithOverride.With(ruleset: RulesetOverride.Value.OnlineID);
|
|
||||||
|
|
||||||
return selectedItemWithOverride;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openStyleSelection()
|
|
||||||
{
|
|
||||||
if (SelectedItem.Value == null || !this.IsCurrentScreen())
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.Push(new MultiplayerMatchStyleSelect(Room, SelectedItem.Value, (beatmap, ruleset) =>
|
|
||||||
{
|
|
||||||
if (SelectedItem.Value?.BeatmapSetId == null || SelectedItem.Value.BeatmapSetId != beatmap.BeatmapSet?.OnlineID)
|
|
||||||
return;
|
|
||||||
|
|
||||||
DifficultyOverride.Value = beatmap;
|
|
||||||
RulesetOverride.Value = ruleset;
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateRuleset()
|
private void updateRuleset()
|
||||||
{
|
{
|
||||||
if (GetGameplayItem() is not PlaylistItem item || !this.IsCurrentScreen())
|
if (!this.IsCurrentScreen() || SelectedItem.Value is not PlaylistItem)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Ruleset.Value = Rulesets.GetRuleset(item.RulesetID);
|
Ruleset.Value = GetGameplayRuleset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateUserStyle()
|
||||||
|
{
|
||||||
|
if (!this.IsCurrentScreen() || SelectedItem.Value is not PlaylistItem)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (UserStyleDisplayContainer != null)
|
||||||
|
{
|
||||||
|
PlaylistItem gameplayItem = SelectedItem.Value.With(
|
||||||
|
ruleset: GetGameplayRuleset().OnlineID,
|
||||||
|
beatmap: new Optional<IBeatmapInfo>(GetGameplayBeatmap()));
|
||||||
|
|
||||||
|
UserStyleDisplayContainer.Child = new DrawableRoomPlaylistItem(gameplayItem)
|
||||||
|
{
|
||||||
|
AllowReordering = false,
|
||||||
|
AllowEditing = true,
|
||||||
|
RequestEdit = _ => OpenStyleSelection()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual APIMod[] GetGameplayMods()
|
||||||
|
=> UserMods.Value.Select(m => new APIMod(m)).Concat(SelectedItem.Value!.RequiredMods).ToArray();
|
||||||
|
|
||||||
|
protected virtual RulesetInfo GetGameplayRuleset()
|
||||||
|
=> Rulesets.GetRuleset(UserRuleset.Value?.OnlineID ?? SelectedItem.Value!.RulesetID)!;
|
||||||
|
|
||||||
|
protected virtual IBeatmapInfo GetGameplayBeatmap()
|
||||||
|
=> UserBeatmap.Value ?? SelectedItem.Value!.Beatmap;
|
||||||
|
|
||||||
|
protected abstract void OpenStyleSelection();
|
||||||
|
|
||||||
private void beginHandlingTrack()
|
private void beginHandlingTrack()
|
||||||
{
|
{
|
||||||
Beatmap.BindValueChanged(applyLoopingToTrack, true);
|
Beatmap.BindValueChanged(applyLoopingToTrack, true);
|
||||||
|
@ -2,106 +2,88 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Humanizer;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Database;
|
using osu.Game.Online.Multiplayer;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Rulesets;
|
|
||||||
using osu.Game.Screens.Select;
|
|
||||||
using osu.Game.Users;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||||
{
|
{
|
||||||
public partial class MultiplayerMatchStyleSelect : SongSelect, IOnlinePlaySubScreen, IHandlePresentBeatmap
|
public partial class MultiplayerMatchStyleSelect : OnlinePlayStyleSelect
|
||||||
{
|
{
|
||||||
public string ShortTitle => "style selection";
|
[Resolved]
|
||||||
|
private MultiplayerClient client { get; set; } = null!;
|
||||||
|
|
||||||
public override string Title => ShortTitle.Humanize();
|
[Resolved]
|
||||||
|
private OngoingOperationTracker operationTracker { get; set; } = null!;
|
||||||
|
|
||||||
public override bool AllowEditing => false;
|
private readonly IBindable<bool> operationInProgress = new Bindable<bool>();
|
||||||
|
|
||||||
protected override UserActivity InitialActivity => new UserActivity.InLobby(room);
|
private LoadingLayer loadingLayer = null!;
|
||||||
|
private IDisposable? selectionOperation;
|
||||||
|
|
||||||
private readonly Room room;
|
public MultiplayerMatchStyleSelect(Room room, PlaylistItem item)
|
||||||
private readonly PlaylistItem item;
|
: base(room, item)
|
||||||
private readonly Action<BeatmapInfo, RulesetInfo> onSelect;
|
|
||||||
|
|
||||||
public MultiplayerMatchStyleSelect(Room room, PlaylistItem item, Action<BeatmapInfo, RulesetInfo> onSelect)
|
|
||||||
{
|
{
|
||||||
this.room = room;
|
|
||||||
this.item = item;
|
|
||||||
this.onSelect = onSelect;
|
|
||||||
|
|
||||||
Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
LeftArea.Padding = new MarginPadding { Top = Header.HEIGHT };
|
AddInternal(loadingLayer = new LoadingLayer(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override FilterControl CreateFilterControl() => new DifficultySelectFilterControl(item);
|
protected override void LoadComplete()
|
||||||
|
|
||||||
protected override IEnumerable<(FooterButton, OverlayContainer?)> CreateSongSelectFooterButtons()
|
|
||||||
{
|
{
|
||||||
// Required to create the drawable components.
|
base.LoadComplete();
|
||||||
base.CreateSongSelectFooterButtons();
|
|
||||||
return Enumerable.Empty<(FooterButton, OverlayContainer?)>();
|
operationInProgress.BindTo(operationTracker.InProgress);
|
||||||
|
operationInProgress.BindValueChanged(_ => updateLoadingLayer(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea();
|
private void updateLoadingLayer()
|
||||||
|
{
|
||||||
|
if (operationInProgress.Value)
|
||||||
|
loadingLayer.Show();
|
||||||
|
else
|
||||||
|
loadingLayer.Hide();
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnStart()
|
protected override bool OnStart()
|
||||||
{
|
{
|
||||||
onSelect(Beatmap.Value.BeatmapInfo, Ruleset.Value);
|
if (operationInProgress.Value)
|
||||||
this.Exit();
|
{
|
||||||
|
Logger.Log($"{nameof(OnStart)} aborted due to {nameof(operationInProgress)}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectionOperation = operationTracker.BeginOperation();
|
||||||
|
|
||||||
|
client.ChangeUserStyle(Beatmap.Value.BeatmapInfo.OnlineID, Ruleset.Value.OnlineID)
|
||||||
|
.FireAndForget(onSuccess: () =>
|
||||||
|
{
|
||||||
|
selectionOperation.Dispose();
|
||||||
|
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
// If an error or server side trigger occurred this screen may have already exited by external means.
|
||||||
|
if (this.IsCurrentScreen())
|
||||||
|
this.Exit();
|
||||||
|
});
|
||||||
|
}, onError: _ =>
|
||||||
|
{
|
||||||
|
selectionOperation.Dispose();
|
||||||
|
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
Carousel.AllowSelection = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PresentBeatmap(WorkingBeatmap beatmap, RulesetInfo ruleset)
|
|
||||||
{
|
|
||||||
// This screen cannot present beatmaps.
|
|
||||||
}
|
|
||||||
|
|
||||||
private partial class DifficultySelectFilterControl : FilterControl
|
|
||||||
{
|
|
||||||
private readonly PlaylistItem item;
|
|
||||||
private double itemLength;
|
|
||||||
|
|
||||||
public DifficultySelectFilterControl(PlaylistItem item)
|
|
||||||
{
|
|
||||||
this.item = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(RealmAccess realm)
|
|
||||||
{
|
|
||||||
int beatmapId = item.Beatmap.OnlineID;
|
|
||||||
itemLength = realm.Run(r => r.All<BeatmapInfo>().FirstOrDefault(b => b.OnlineID == beatmapId)?.Length ?? 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override FilterCriteria CreateCriteria()
|
|
||||||
{
|
|
||||||
var criteria = base.CreateCriteria();
|
|
||||||
|
|
||||||
// Must be from the same set as the playlist item.
|
|
||||||
criteria.BeatmapSetId = item.BeatmapSetId;
|
|
||||||
|
|
||||||
// Must be within 30s of the playlist item.
|
|
||||||
criteria.Length.Min = itemLength - 30000;
|
|
||||||
criteria.Length.Max = itemLength + 30000;
|
|
||||||
criteria.Length.IsLowerInclusive = true;
|
|
||||||
criteria.Length.IsUpperInclusive = true;
|
|
||||||
|
|
||||||
return criteria;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.Cursor;
|
using osu.Game.Graphics.Cursor;
|
||||||
using osu.Game.Online;
|
using osu.Game.Online;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
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.Overlays;
|
using osu.Game.Overlays;
|
||||||
@ -188,7 +190,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UserDifficultySection = new FillFlowContainer
|
UserStyleSection = new FillFlowContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
@ -251,6 +253,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
this.Push(new MultiplayerMatchSongSelect(Room, itemToEdit));
|
this.Push(new MultiplayerMatchSongSelect(Room, itemToEdit));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OpenStyleSelection()
|
||||||
|
{
|
||||||
|
if (!this.IsCurrentScreen() || SelectedItem.Value is not PlaylistItem item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.Push(new MultiplayerMatchStyleSelect(Room, item));
|
||||||
|
}
|
||||||
|
|
||||||
protected override Drawable CreateFooter() => new MultiplayerMatchFooter
|
protected override Drawable CreateFooter() => new MultiplayerMatchFooter
|
||||||
{
|
{
|
||||||
SelectedItem = SelectedItem
|
SelectedItem = SelectedItem
|
||||||
@ -261,16 +271,22 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
SelectedItem = SelectedItem
|
SelectedItem = SelectedItem
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override void UpdateMods()
|
protected override APIMod[] GetGameplayMods()
|
||||||
{
|
{
|
||||||
if (GetGameplayItem() is not PlaylistItem item || client.LocalUser == null || !this.IsCurrentScreen())
|
// Using the room's reported status makes the server authoritative.
|
||||||
return;
|
return client.LocalUser?.Mods.Concat(SelectedItem.Value!.RequiredMods).ToArray()!;
|
||||||
|
}
|
||||||
|
|
||||||
// update local mods based on room's reported status for the local user (omitting the base call implementation).
|
protected override RulesetInfo GetGameplayRuleset()
|
||||||
// this makes the server authoritative, and avoids the local user potentially setting mods that the server is not aware of (ie. if the match was started during the selection being changed).
|
{
|
||||||
var rulesetInstance = Rulesets.GetRuleset(item.RulesetID)?.CreateInstance();
|
// Using the room's reported status makes the server authoritative.
|
||||||
Debug.Assert(rulesetInstance != null);
|
return client.LocalUser?.RulesetId != null ? Rulesets.GetRuleset(client.LocalUser.RulesetId.Value)! : base.GetGameplayRuleset();
|
||||||
Mods.Value = client.LocalUser.Mods.Select(m => m.ToMod(rulesetInstance)).Concat(item.RequiredMods.Select(m => m.ToMod(rulesetInstance))).ToList();
|
}
|
||||||
|
|
||||||
|
protected override IBeatmapInfo GetGameplayBeatmap()
|
||||||
|
{
|
||||||
|
// Using the room's reported status makes the server authoritative.
|
||||||
|
return client.LocalUser?.BeatmapId != null ? new APIBeatmap { OnlineID = client.LocalUser.BeatmapId.Value } : base.GetGameplayBeatmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Resolved(canBeNull: true)]
|
[Resolved(canBeNull: true)]
|
||||||
@ -376,7 +392,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
|
|
||||||
addItemButton.Alpha = localUserCanAddItem ? 1 : 0;
|
addItemButton.Alpha = localUserCanAddItem ? 1 : 0;
|
||||||
|
|
||||||
Scheduler.AddOnce(UpdateMods);
|
// Forcefully update the selected item so that the user state is applied.
|
||||||
|
Scheduler.AddOnce(OnSelectedItemChanged);
|
||||||
|
|
||||||
Activity.Value = new UserActivity.InLobby(Room);
|
Activity.Value = new UserActivity.InLobby(Room);
|
||||||
}
|
}
|
||||||
|
98
osu.Game/Screens/OnlinePlay/OnlinePlayStyleSelect.cs
Normal file
98
osu.Game/Screens/OnlinePlay/OnlinePlayStyleSelect.cs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// 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 Humanizer;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Screens;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Database;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Screens.Select;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.OnlinePlay
|
||||||
|
{
|
||||||
|
public abstract partial class OnlinePlayStyleSelect : SongSelect, IOnlinePlaySubScreen, IHandlePresentBeatmap
|
||||||
|
{
|
||||||
|
public string ShortTitle => "style selection";
|
||||||
|
|
||||||
|
public override string Title => ShortTitle.Humanize();
|
||||||
|
|
||||||
|
public override bool AllowEditing => false;
|
||||||
|
|
||||||
|
protected override UserActivity InitialActivity => new UserActivity.InLobby(room);
|
||||||
|
|
||||||
|
private readonly Room room;
|
||||||
|
private readonly PlaylistItem item;
|
||||||
|
|
||||||
|
protected OnlinePlayStyleSelect(Room room, PlaylistItem item)
|
||||||
|
{
|
||||||
|
this.room = room;
|
||||||
|
this.item = item;
|
||||||
|
|
||||||
|
Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING };
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
LeftArea.Padding = new MarginPadding { Top = Header.HEIGHT };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override FilterControl CreateFilterControl() => new DifficultySelectFilterControl(item);
|
||||||
|
|
||||||
|
protected override IEnumerable<(FooterButton, OverlayContainer?)> CreateSongSelectFooterButtons()
|
||||||
|
{
|
||||||
|
// Required to create the drawable components.
|
||||||
|
base.CreateSongSelectFooterButtons();
|
||||||
|
return Enumerable.Empty<(FooterButton, OverlayContainer?)>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea();
|
||||||
|
|
||||||
|
public void PresentBeatmap(WorkingBeatmap beatmap, RulesetInfo ruleset)
|
||||||
|
{
|
||||||
|
// This screen cannot present beatmaps.
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class DifficultySelectFilterControl : FilterControl
|
||||||
|
{
|
||||||
|
private readonly PlaylistItem item;
|
||||||
|
private double itemLength;
|
||||||
|
|
||||||
|
public DifficultySelectFilterControl(PlaylistItem item)
|
||||||
|
{
|
||||||
|
this.item = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(RealmAccess realm)
|
||||||
|
{
|
||||||
|
int beatmapId = item.Beatmap.OnlineID;
|
||||||
|
itemLength = realm.Run(r => r.All<BeatmapInfo>().FirstOrDefault(b => b.OnlineID == beatmapId)?.Length ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override FilterCriteria CreateCriteria()
|
||||||
|
{
|
||||||
|
var criteria = base.CreateCriteria();
|
||||||
|
|
||||||
|
// Must be from the same set as the playlist item.
|
||||||
|
criteria.BeatmapSetId = item.BeatmapSetId;
|
||||||
|
|
||||||
|
// Must be within 30s of the playlist item.
|
||||||
|
criteria.Length.Min = itemLength - 30000;
|
||||||
|
criteria.Length.Max = itemLength + 30000;
|
||||||
|
criteria.Length.IsLowerInclusive = true;
|
||||||
|
criteria.Length.IsUpperInclusive = true;
|
||||||
|
|
||||||
|
return criteria;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
// 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.Framework.Screens;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||||
|
{
|
||||||
|
public partial class PlaylistsRoomStyleSelect : OnlinePlayStyleSelect
|
||||||
|
{
|
||||||
|
public new readonly Bindable<BeatmapInfo?> Beatmap = new Bindable<BeatmapInfo?>();
|
||||||
|
public new readonly Bindable<RulesetInfo?> Ruleset = new Bindable<RulesetInfo?>();
|
||||||
|
|
||||||
|
public PlaylistsRoomStyleSelect(Room room, PlaylistItem item)
|
||||||
|
: base(room, item)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnStart()
|
||||||
|
{
|
||||||
|
Beatmap.Value = base.Beatmap.Value.BeatmapInfo;
|
||||||
|
Ruleset.Value = base.Ruleset.Value;
|
||||||
|
this.Exit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -213,7 +213,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UserDifficultySection = new FillFlowContainer
|
UserStyleSection = new FillFlowContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
@ -299,6 +299,18 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected override void OpenStyleSelection()
|
||||||
|
{
|
||||||
|
if (!this.IsCurrentScreen() || SelectedItem.Value is not PlaylistItem item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.Push(new PlaylistsRoomStyleSelect(Room, item)
|
||||||
|
{
|
||||||
|
Beatmap = { BindTarget = UserBeatmap },
|
||||||
|
Ruleset = { BindTarget = UserRuleset }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void updatePollingRate()
|
private void updatePollingRate()
|
||||||
{
|
{
|
||||||
selectionPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 30000 : 5000;
|
selectionPollingComponent.TimeBetweenPolls.Value = isIdle.Value ? 30000 : 5000;
|
||||||
|
@ -335,6 +335,23 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Task ChangeUserStyle(int? beatmapId, int? rulesetId)
|
||||||
|
{
|
||||||
|
ChangeUserStyle(api.LocalUser.Value.Id, beatmapId, rulesetId);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ChangeUserStyle(int userId, int? beatmapId, int? rulesetId)
|
||||||
|
{
|
||||||
|
Debug.Assert(ServerRoom != null);
|
||||||
|
|
||||||
|
var user = ServerRoom.Users.Single(u => u.UserID == userId);
|
||||||
|
user.BeatmapId = beatmapId;
|
||||||
|
user.RulesetId = rulesetId;
|
||||||
|
|
||||||
|
((IMultiplayerClient)this).UserStyleChanged(userId, beatmapId, rulesetId);
|
||||||
|
}
|
||||||
|
|
||||||
public void ChangeUserMods(int userId, IEnumerable<Mod> newMods)
|
public void ChangeUserMods(int userId, IEnumerable<Mod> newMods)
|
||||||
=> ChangeUserMods(userId, newMods.Select(m => new APIMod(m)));
|
=> ChangeUserMods(userId, newMods.Select(m => new APIMod(m)));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user