mirror of
https://github.com/ppy/osu.git
synced 2025-03-23 18:10:01 +08:00
Merge branch 'master' into pre-realm-score-tidy
This commit is contained in:
commit
cdb8237a4b
@ -27,10 +27,10 @@
|
||||
]
|
||||
},
|
||||
"ppy.localisationanalyser.tools": {
|
||||
"version": "2021.725.0",
|
||||
"version": "2021.1210.0",
|
||||
"commands": [
|
||||
"localisation"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1203.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1207.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1210.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
|
@ -87,9 +87,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private void addItem(Func<BeatmapInfo> beatmap)
|
||||
{
|
||||
AddStep("click edit button", () =>
|
||||
AddStep("click add button", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<MultiplayerMatchSubScreen>().Single().AddOrEditPlaylistButton);
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<MultiplayerMatchSubScreen.AddItemButton>().Single());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
|
@ -255,6 +255,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
p.AllowDeletion = true;
|
||||
p.AllowShowingResults = true;
|
||||
p.AllowEditing = true;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osuTK.Input;
|
||||
|
||||
@ -74,11 +75,19 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("api room updated", () => Client.APIRoom?.QueueMode.Value == QueueMode.AllPlayers);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAddItemsAsHost()
|
||||
{
|
||||
addItem(() => OtherBeatmap);
|
||||
|
||||
AddAssert("playlist contains two items", () => Client.APIRoom?.Playlist.Count == 2);
|
||||
}
|
||||
|
||||
private void selectNewItem(Func<BeatmapInfo> beatmap)
|
||||
{
|
||||
AddStep("click edit button", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<MultiplayerMatchSubScreen>().Single().AddOrEditPlaylistButton);
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<DrawableRoomPlaylistItem.PlaylistEditButton>().First());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
@ -90,5 +99,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen);
|
||||
AddUntilStep("selected item is new beatmap", () => Client.CurrentMatchPlayingItem.Value?.Beatmap.Value?.OnlineID == otherBeatmap.OnlineID);
|
||||
}
|
||||
|
||||
private void addItem(Func<BeatmapInfo> beatmap)
|
||||
{
|
||||
AddStep("click add button", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<MultiplayerMatchSubScreen.AddItemButton>().Single());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for song select", () => CurrentSubScreen is Screens.Select.SongSelect select && select.BeatmapSetsLoaded);
|
||||
AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(beatmap()));
|
||||
AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -416,8 +416,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("Enter song select", () =>
|
||||
{
|
||||
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerScreenStack.CurrentScreen).CurrentSubScreen;
|
||||
|
||||
((MultiplayerMatchSubScreen)currentSubScreen).SelectBeatmap();
|
||||
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(client.CurrentMatchPlayingItem.Value);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for song select", () => this.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault()?.BeatmapSetsLoaded == true);
|
||||
|
@ -179,7 +179,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public new BeatmapCarousel Carousel => base.Carousel;
|
||||
|
||||
public TestMultiplayerMatchSongSelect(Room room, WorkingBeatmap beatmap = null, RulesetInfo ruleset = null)
|
||||
: base(room, beatmap, ruleset)
|
||||
: base(room, null, beatmap, ruleset)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,12 @@ namespace osu.Game.Online.Multiplayer
|
||||
/// <param name="item">The item to add.</param>
|
||||
Task AddPlaylistItem(MultiplayerPlaylistItem item);
|
||||
|
||||
/// <summary>
|
||||
/// Edits an existing playlist item with new values.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to edit, containing new properties. Must have an ID.</param>
|
||||
Task EditPlaylistItem(MultiplayerPlaylistItem item);
|
||||
|
||||
/// <summary>
|
||||
/// Removes an item from the playlist.
|
||||
/// </summary>
|
||||
|
@ -335,6 +335,8 @@ namespace osu.Game.Online.Multiplayer
|
||||
|
||||
public abstract Task AddPlaylistItem(MultiplayerPlaylistItem item);
|
||||
|
||||
public abstract Task EditPlaylistItem(MultiplayerPlaylistItem item);
|
||||
|
||||
public abstract Task RemovePlaylistItem(long playlistItemId);
|
||||
|
||||
Task IMultiplayerClient.RoomStateChanged(MultiplayerRoomState state)
|
||||
|
@ -162,6 +162,14 @@ namespace osu.Game.Online.Multiplayer
|
||||
return connection.InvokeAsync(nameof(IMultiplayerServer.AddPlaylistItem), item);
|
||||
}
|
||||
|
||||
public override Task EditPlaylistItem(MultiplayerPlaylistItem item)
|
||||
{
|
||||
if (!IsConnected.Value)
|
||||
return Task.CompletedTask;
|
||||
|
||||
return connection.InvokeAsync(nameof(IMultiplayerServer.EditPlaylistItem), item);
|
||||
}
|
||||
|
||||
public override Task RemovePlaylistItem(long playlistItemId)
|
||||
{
|
||||
if (!IsConnected.Value)
|
||||
|
@ -7,6 +7,7 @@ using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Development;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Online.API;
|
||||
@ -107,6 +108,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
|
||||
public void AddOrUpdateRoom(Room room)
|
||||
{
|
||||
Debug.Assert(ThreadSafety.IsUpdateThread);
|
||||
Debug.Assert(room.RoomID.Value != null);
|
||||
|
||||
if (ignoredRooms.Contains(room.RoomID.Value.Value))
|
||||
@ -136,12 +138,16 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
|
||||
public void RemoveRoom(Room room)
|
||||
{
|
||||
Debug.Assert(ThreadSafety.IsUpdateThread);
|
||||
|
||||
rooms.Remove(room);
|
||||
notifyRoomsUpdated();
|
||||
}
|
||||
|
||||
public void ClearRooms()
|
||||
{
|
||||
Debug.Assert(ThreadSafety.IsUpdateThread);
|
||||
|
||||
rooms.Clear();
|
||||
notifyRoomsUpdated();
|
||||
}
|
||||
|
@ -33,6 +33,11 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
/// </summary>
|
||||
public Action<PlaylistItem> RequestResults;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when an item requests to be edited.
|
||||
/// </summary>
|
||||
public Action<PlaylistItem> RequestEdit;
|
||||
|
||||
private bool allowReordering;
|
||||
|
||||
/// <summary>
|
||||
@ -104,6 +109,24 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
}
|
||||
}
|
||||
|
||||
private bool allowEditing;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to allow items to be edited.
|
||||
/// If <c>true</c>, requests to edit items may be satisfied via <see cref="RequestEdit"/>.
|
||||
/// </summary>
|
||||
public bool AllowEditing
|
||||
{
|
||||
get => allowEditing;
|
||||
set
|
||||
{
|
||||
allowEditing = value;
|
||||
|
||||
foreach (var item in ListContainer.OfType<DrawableRoomPlaylistItem>())
|
||||
item.AllowEditing = value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool showItemOwners;
|
||||
|
||||
/// <summary>
|
||||
@ -135,12 +158,14 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
{
|
||||
d.SelectedItem.BindTarget = SelectedItem;
|
||||
d.RequestDeletion = i => RequestDeletion?.Invoke(i);
|
||||
d.RequestResults = i => RequestResults?.Invoke(i);
|
||||
d.RequestEdit = i => RequestEdit?.Invoke(i);
|
||||
d.AllowReordering = AllowReordering;
|
||||
d.AllowDeletion = AllowDeletion;
|
||||
d.AllowSelection = AllowSelection;
|
||||
d.AllowShowingResults = AllowShowingResults;
|
||||
d.AllowEditing = AllowEditing;
|
||||
d.ShowItemOwner = ShowItemOwners;
|
||||
d.RequestResults = i => RequestResults?.Invoke(i);
|
||||
});
|
||||
|
||||
protected virtual DrawableRoomPlaylistItem CreateDrawablePlaylistItem(PlaylistItem item) => new DrawableRoomPlaylistItem(item);
|
||||
|
@ -51,6 +51,11 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
/// </summary>
|
||||
public Action<PlaylistItem> RequestResults;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when this item requests to be edited.
|
||||
/// </summary>
|
||||
public Action<PlaylistItem> RequestEdit;
|
||||
|
||||
/// <summary>
|
||||
/// The currently-selected item, used to show a border around this item.
|
||||
/// May be updated by this item if <see cref="AllowSelection"/> is <c>true</c>.
|
||||
@ -74,6 +79,7 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
private FillFlowContainer buttonsFlow;
|
||||
private UpdateableAvatar ownerAvatar;
|
||||
private Drawable showResultsButton;
|
||||
private Drawable editButton;
|
||||
private Drawable removeButton;
|
||||
private PanelBackground panelBackground;
|
||||
private FillFlowContainer mainFillFlow;
|
||||
@ -213,6 +219,23 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
}
|
||||
}
|
||||
|
||||
private bool allowEditing;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this item can be edited.
|
||||
/// </summary>
|
||||
public bool AllowEditing
|
||||
{
|
||||
get => allowEditing;
|
||||
set
|
||||
{
|
||||
allowEditing = value;
|
||||
|
||||
if (editButton != null)
|
||||
editButton.Alpha = value ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
private bool showItemOwner;
|
||||
|
||||
/// <summary>
|
||||
@ -416,6 +439,13 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
TooltipText = "View results"
|
||||
},
|
||||
Item.Beatmap.Value == null ? Empty() : new PlaylistDownloadButton(Item),
|
||||
editButton = new PlaylistEditButton
|
||||
{
|
||||
Size = new Vector2(30, 30),
|
||||
Alpha = AllowEditing ? 1 : 0,
|
||||
Action = () => RequestEdit?.Invoke(Item),
|
||||
TooltipText = "Edit"
|
||||
},
|
||||
removeButton = new PlaylistRemoveButton
|
||||
{
|
||||
Size = new Vector2(30, 30),
|
||||
@ -432,6 +462,14 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
return true;
|
||||
}
|
||||
|
||||
public class PlaylistEditButton : GrayButton
|
||||
{
|
||||
public PlaylistEditButton()
|
||||
: base(FontAwesome.Solid.Edit)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class PlaylistRemoveButton : GrayButton
|
||||
{
|
||||
public PlaylistRemoveButton()
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -19,6 +20,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
|
||||
{
|
||||
public readonly Bindable<MultiplayerPlaylistDisplayMode> DisplayMode = new Bindable<MultiplayerPlaylistDisplayMode>();
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when an item requests to be edited.
|
||||
/// </summary>
|
||||
public Action<PlaylistItem> RequestEdit;
|
||||
|
||||
private MultiplayerQueueList queueList;
|
||||
private MultiplayerHistoryList historyList;
|
||||
private bool firstPopulation = true;
|
||||
@ -46,7 +52,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
|
||||
queueList = new MultiplayerQueueList
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
SelectedItem = { BindTarget = SelectedItem }
|
||||
SelectedItem = { BindTarget = SelectedItem },
|
||||
RequestEdit = item => RequestEdit?.Invoke(item)
|
||||
},
|
||||
historyList = new MultiplayerHistoryList
|
||||
{
|
||||
|
@ -78,8 +78,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
|
||||
|
||||
private void updateDeleteButtonVisibility()
|
||||
{
|
||||
AllowDeletion = (Item.OwnerID == api.LocalUser.Value.OnlineID || multiplayerClient.IsHost)
|
||||
&& SelectedItem.Value != Item;
|
||||
bool isItemOwner = Item.OwnerID == api.LocalUser.Value.OnlineID || multiplayerClient.IsHost;
|
||||
|
||||
AllowDeletion = isItemOwner && SelectedItem.Value != Item;
|
||||
AllowEditing = isItemOwner;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Logging;
|
||||
@ -24,17 +25,22 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
[Resolved]
|
||||
private MultiplayerClient client { get; set; }
|
||||
|
||||
private readonly PlaylistItem itemToEdit;
|
||||
|
||||
private LoadingLayer loadingLayer;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new instance of multiplayer song select.
|
||||
/// </summary>
|
||||
/// <param name="room">The room.</param>
|
||||
/// <param name="itemToEdit">The item to be edited. May be null, in which case a new item will be added to the playlist.</param>
|
||||
/// <param name="beatmap">An optional initial beatmap selection to perform.</param>
|
||||
/// <param name="ruleset">An optional initial ruleset selection to perform.</param>
|
||||
public MultiplayerMatchSongSelect(Room room, WorkingBeatmap beatmap = null, RulesetInfo ruleset = null)
|
||||
public MultiplayerMatchSongSelect(Room room, PlaylistItem itemToEdit = null, WorkingBeatmap beatmap = null, RulesetInfo ruleset = null)
|
||||
: base(room)
|
||||
{
|
||||
this.itemToEdit = itemToEdit;
|
||||
|
||||
if (beatmap != null || ruleset != null)
|
||||
{
|
||||
Schedule(() =>
|
||||
@ -59,14 +65,19 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
{
|
||||
loadingLayer.Show();
|
||||
|
||||
client.AddPlaylistItem(new MultiplayerPlaylistItem
|
||||
var multiplayerItem = new MultiplayerPlaylistItem
|
||||
{
|
||||
ID = itemToEdit?.ID ?? 0,
|
||||
BeatmapID = item.BeatmapID,
|
||||
BeatmapChecksum = item.Beatmap.Value.MD5Hash,
|
||||
RulesetID = item.RulesetID,
|
||||
RequiredMods = item.RequiredMods.Select(m => new APIMod(m)).ToArray(),
|
||||
AllowedMods = item.AllowedMods.Select(m => new APIMod(m)).ToArray()
|
||||
}).ContinueWith(t =>
|
||||
};
|
||||
|
||||
Task task = itemToEdit != null ? client.EditPlaylistItem(multiplayerItem) : client.AddPlaylistItem(multiplayerItem);
|
||||
|
||||
task.ContinueWith(t =>
|
||||
{
|
||||
Schedule(() =>
|
||||
{
|
||||
|
@ -14,7 +14,6 @@ using osu.Framework.Screens;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
@ -44,8 +43,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
|
||||
public override string ShortTitle => "room";
|
||||
|
||||
public OsuButton AddOrEditPlaylistButton { get; private set; }
|
||||
|
||||
[Resolved]
|
||||
private MultiplayerClient client { get; set; }
|
||||
|
||||
@ -57,6 +54,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
[CanBeNull]
|
||||
private IDisposable readyClickOperation;
|
||||
|
||||
private AddItemButton addItemButton;
|
||||
|
||||
public MultiplayerMatchSubScreen(Room room)
|
||||
: base(room)
|
||||
{
|
||||
@ -134,12 +133,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
new Drawable[] { new OverlinedHeader("Beatmap") },
|
||||
new Drawable[]
|
||||
{
|
||||
AddOrEditPlaylistButton = new PurpleTriangleButton
|
||||
addItemButton = new AddItemButton
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 40,
|
||||
Action = SelectBeatmap,
|
||||
Alpha = 0
|
||||
Text = "Add item",
|
||||
Action = () => OpenSongSelection()
|
||||
},
|
||||
},
|
||||
null,
|
||||
@ -147,7 +146,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
{
|
||||
new MultiplayerPlaylist
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RequestEdit = OpenSongSelection
|
||||
}
|
||||
},
|
||||
new[]
|
||||
@ -220,12 +220,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
}
|
||||
};
|
||||
|
||||
internal void SelectBeatmap()
|
||||
/// <summary>
|
||||
/// Opens the song selection screen to add or edit an item.
|
||||
/// </summary>
|
||||
/// <param name="itemToEdit">An optional playlist item to edit. If null, a new item will be added instead.</param>
|
||||
internal void OpenSongSelection([CanBeNull] PlaylistItem itemToEdit = null)
|
||||
{
|
||||
if (!this.IsCurrentScreen())
|
||||
return;
|
||||
|
||||
this.Push(new MultiplayerMatchSongSelect(Room));
|
||||
this.Push(new MultiplayerMatchSongSelect(Room, itemToEdit));
|
||||
}
|
||||
|
||||
protected override Drawable CreateFooter() => new MultiplayerMatchFooter
|
||||
@ -385,23 +389,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
return;
|
||||
}
|
||||
|
||||
switch (client.Room.Settings.QueueMode)
|
||||
{
|
||||
case QueueMode.HostOnly:
|
||||
AddOrEditPlaylistButton.Text = "Edit beatmap";
|
||||
AddOrEditPlaylistButton.Alpha = client.IsHost ? 1 : 0;
|
||||
break;
|
||||
|
||||
case QueueMode.AllPlayers:
|
||||
case QueueMode.AllPlayersRoundRobin:
|
||||
AddOrEditPlaylistButton.Text = "Add beatmap";
|
||||
AddOrEditPlaylistButton.Alpha = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
AddOrEditPlaylistButton.Alpha = 0;
|
||||
break;
|
||||
}
|
||||
addItemButton.Alpha = client.IsHost || Room.QueueMode.Value != QueueMode.HostOnly ? 1 : 0;
|
||||
|
||||
Scheduler.AddOnce(UpdateMods);
|
||||
}
|
||||
@ -466,7 +454,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
return;
|
||||
}
|
||||
|
||||
this.Push(new MultiplayerMatchSongSelect(Room, beatmap, ruleset));
|
||||
this.Push(new MultiplayerMatchSongSelect(Room, SelectedItem.Value, beatmap, ruleset));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
@ -481,5 +469,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
|
||||
modSettingChangeTracker?.Dispose();
|
||||
}
|
||||
|
||||
public class AddItemButton : PurpleTriangleButton
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,14 +33,14 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
[Resolved(typeof(Room), nameof(Room.Playlist))]
|
||||
protected BindableList<PlaylistItem> Playlist { get; private set; }
|
||||
|
||||
[CanBeNull]
|
||||
[Resolved(CanBeNull = true)]
|
||||
protected IBindable<PlaylistItem> SelectedItem { get; private set; }
|
||||
|
||||
protected override UserActivity InitialActivity => new UserActivity.InLobby(room);
|
||||
|
||||
protected readonly Bindable<IReadOnlyList<Mod>> FreeMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||
|
||||
[CanBeNull]
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IBindable<PlaylistItem> selectedItem { get; set; }
|
||||
|
||||
private readonly FreeModSelectOverlay freeModSelectOverlay;
|
||||
private readonly Room room;
|
||||
|
||||
@ -80,8 +80,8 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
|
||||
// At this point, Mods contains both the required and allowed mods. For selection purposes, it should only contain the required mods.
|
||||
// Similarly, freeMods is currently empty but should only contain the allowed mods.
|
||||
Mods.Value = selectedItem?.Value?.RequiredMods.Select(m => m.DeepClone()).ToArray() ?? Array.Empty<Mod>();
|
||||
FreeMods.Value = selectedItem?.Value?.AllowedMods.Select(m => m.DeepClone()).ToArray() ?? Array.Empty<Mod>();
|
||||
Mods.Value = SelectedItem?.Value?.RequiredMods.Select(m => m.DeepClone()).ToArray() ?? Array.Empty<Mod>();
|
||||
FreeMods.Value = SelectedItem?.Value?.AllowedMods.Select(m => m.DeepClone()).ToArray() ?? Array.Empty<Mod>();
|
||||
|
||||
Mods.BindValueChanged(onModsChanged);
|
||||
Ruleset.BindValueChanged(onRulesetChanged);
|
||||
|
@ -5,7 +5,9 @@ using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using Humanizer;
|
||||
using Humanizer.Localisation;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@ -14,6 +16,8 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Screens.OnlinePlay.Match.Components;
|
||||
@ -69,6 +73,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IRoomManager manager { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
private readonly Room room;
|
||||
|
||||
public MatchSettings(Room room)
|
||||
@ -134,19 +141,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
Child = DurationField = new DurationDropdown
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Items = new[]
|
||||
{
|
||||
TimeSpan.FromMinutes(30),
|
||||
TimeSpan.FromHours(1),
|
||||
TimeSpan.FromHours(2),
|
||||
TimeSpan.FromHours(4),
|
||||
TimeSpan.FromHours(8),
|
||||
TimeSpan.FromHours(12),
|
||||
//TimeSpan.FromHours(16),
|
||||
TimeSpan.FromHours(24),
|
||||
TimeSpan.FromDays(3),
|
||||
TimeSpan.FromDays(7)
|
||||
}
|
||||
}
|
||||
},
|
||||
new Section("Allowed attempts (across all playlist items)")
|
||||
@ -303,10 +297,40 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
MaxAttempts.BindValueChanged(count => MaxAttemptsField.Text = count.NewValue?.ToString(), true);
|
||||
Duration.BindValueChanged(duration => DurationField.Current.Value = duration.NewValue ?? TimeSpan.FromMinutes(30), true);
|
||||
|
||||
api.LocalUser.BindValueChanged(populateDurations, true);
|
||||
|
||||
playlist.Items.BindTo(Playlist);
|
||||
Playlist.BindCollectionChanged(onPlaylistChanged, true);
|
||||
}
|
||||
|
||||
private void populateDurations(ValueChangedEvent<APIUser> user)
|
||||
{
|
||||
DurationField.Items = new[]
|
||||
{
|
||||
TimeSpan.FromMinutes(30),
|
||||
TimeSpan.FromHours(1),
|
||||
TimeSpan.FromHours(2),
|
||||
TimeSpan.FromHours(4),
|
||||
TimeSpan.FromHours(8),
|
||||
TimeSpan.FromHours(12),
|
||||
TimeSpan.FromHours(24),
|
||||
TimeSpan.FromDays(3),
|
||||
TimeSpan.FromDays(7),
|
||||
TimeSpan.FromDays(14),
|
||||
};
|
||||
|
||||
// TODO: show these in the interface at all times.
|
||||
if (user.NewValue.IsSupporter)
|
||||
{
|
||||
// roughly correct (see https://github.com/Humanizr/Humanizer/blob/18167e56c082449cc4fe805b8429e3127a7b7f93/readme.md?plain=1#L427)
|
||||
// if we want this to be more accurate we might consider sending an actual end time, not a time span. probably not required though.
|
||||
const int days_in_month = 31;
|
||||
|
||||
DurationField.AddDropdownItem(TimeSpan.FromDays(days_in_month));
|
||||
DurationField.AddDropdownItem(TimeSpan.FromDays(days_in_month * 3));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@ -405,7 +429,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
Menu.MaxHeight = 100;
|
||||
}
|
||||
|
||||
protected override LocalisableString GenerateItemText(TimeSpan item) => item.Humanize();
|
||||
protected override LocalisableString GenerateItemText(TimeSpan item) => item.Humanize(maxUnit: TimeUnit.Month);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ namespace osu.Game.Screens.Select
|
||||
private FillFlowContainer infoLabelContainer;
|
||||
private Container bpmLabelContainer;
|
||||
|
||||
private readonly WorkingBeatmap beatmap;
|
||||
private readonly WorkingBeatmap working;
|
||||
private readonly RulesetInfo ruleset;
|
||||
|
||||
[Resolved]
|
||||
@ -171,10 +171,10 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private ModSettingChangeTracker settingChangeTracker;
|
||||
|
||||
public WedgeInfoText(WorkingBeatmap beatmap, RulesetInfo userRuleset)
|
||||
public WedgeInfoText(WorkingBeatmap working, RulesetInfo userRuleset)
|
||||
{
|
||||
this.beatmap = beatmap;
|
||||
ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset;
|
||||
this.working = working;
|
||||
ruleset = userRuleset ?? working.BeatmapInfo.Ruleset;
|
||||
}
|
||||
|
||||
private CancellationTokenSource cancellationSource;
|
||||
@ -183,8 +183,8 @@ namespace osu.Game.Screens.Select
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, LocalisationManager localisation, BeatmapDifficultyCache difficultyCache)
|
||||
{
|
||||
var beatmapInfo = beatmap.BeatmapInfo;
|
||||
var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
|
||||
var beatmapInfo = working.BeatmapInfo;
|
||||
var metadata = beatmapInfo.Metadata ?? working.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
@ -330,36 +330,9 @@ namespace osu.Game.Screens.Select
|
||||
addInfoLabels();
|
||||
}
|
||||
|
||||
private void setMetadata(string source)
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
ArtistLabel.Text = artistBinding.Value;
|
||||
TitleLabel.Text = string.IsNullOrEmpty(source) ? titleBinding.Value : source + " — " + titleBinding.Value;
|
||||
}
|
||||
|
||||
private void addInfoLabels()
|
||||
{
|
||||
if (beatmap.Beatmap?.HitObjects?.Any() != true)
|
||||
return;
|
||||
|
||||
infoLabelContainer.Children = new Drawable[]
|
||||
{
|
||||
new InfoLabel(new BeatmapStatistic
|
||||
{
|
||||
Name = "Length",
|
||||
CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length),
|
||||
Content = beatmap.BeatmapInfo.Length.ToFormattedDuration().ToString(),
|
||||
}),
|
||||
bpmLabelContainer = new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Spacing = new Vector2(20, 0),
|
||||
Children = getRulesetInfoLabels()
|
||||
}
|
||||
};
|
||||
base.LoadComplete();
|
||||
|
||||
mods.BindValueChanged(m =>
|
||||
{
|
||||
@ -372,6 +345,38 @@ namespace osu.Game.Screens.Select
|
||||
}, true);
|
||||
}
|
||||
|
||||
private void setMetadata(string source)
|
||||
{
|
||||
ArtistLabel.Text = artistBinding.Value;
|
||||
TitleLabel.Text = string.IsNullOrEmpty(source) ? titleBinding.Value : source + " — " + titleBinding.Value;
|
||||
}
|
||||
|
||||
private void addInfoLabels()
|
||||
{
|
||||
if (working.Beatmap?.HitObjects?.Any() != true)
|
||||
return;
|
||||
|
||||
infoLabelContainer.Children = new Drawable[]
|
||||
{
|
||||
new InfoLabel(new BeatmapStatistic
|
||||
{
|
||||
Name = "Length",
|
||||
CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length),
|
||||
Content = working.BeatmapInfo.Length.ToFormattedDuration().ToString(),
|
||||
}),
|
||||
bpmLabelContainer = new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Spacing = new Vector2(20, 0),
|
||||
Children = getRulesetInfoLabels()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private InfoLabel[] getRulesetInfoLabels()
|
||||
{
|
||||
try
|
||||
@ -381,12 +386,12 @@ namespace osu.Game.Screens.Select
|
||||
try
|
||||
{
|
||||
// Try to get the beatmap with the user's ruleset
|
||||
playableBeatmap = beatmap.GetPlayableBeatmap(ruleset, Array.Empty<Mod>());
|
||||
playableBeatmap = working.GetPlayableBeatmap(ruleset, Array.Empty<Mod>());
|
||||
}
|
||||
catch (BeatmapInvalidForRulesetException)
|
||||
{
|
||||
// Can't be converted to the user's ruleset, so use the beatmap's own ruleset
|
||||
playableBeatmap = beatmap.GetPlayableBeatmap(beatmap.BeatmapInfo.Ruleset, Array.Empty<Mod>());
|
||||
playableBeatmap = working.GetPlayableBeatmap(working.BeatmapInfo.Ruleset, Array.Empty<Mod>());
|
||||
}
|
||||
|
||||
return playableBeatmap.GetStatistics().Select(s => new InfoLabel(s)).ToArray();
|
||||
@ -401,8 +406,9 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private void refreshBPMLabel()
|
||||
{
|
||||
var b = beatmap.Beatmap;
|
||||
if (b == null)
|
||||
var beatmap = working.Beatmap;
|
||||
|
||||
if (beatmap == null || bpmLabelContainer == null)
|
||||
return;
|
||||
|
||||
// this doesn't consider mods which apply variable rates, yet.
|
||||
@ -410,9 +416,9 @@ namespace osu.Game.Screens.Select
|
||||
foreach (var mod in mods.Value.OfType<IApplicableToRate>())
|
||||
rate = mod.ApplyToRate(0, rate);
|
||||
|
||||
double bpmMax = b.ControlPointInfo.BPMMaximum * rate;
|
||||
double bpmMin = b.ControlPointInfo.BPMMinimum * rate;
|
||||
double mostCommonBPM = 60000 / b.GetMostCommonBeatLength() * rate;
|
||||
double bpmMax = beatmap.ControlPointInfo.BPMMaximum * rate;
|
||||
double bpmMin = beatmap.ControlPointInfo.BPMMinimum * rate;
|
||||
double mostCommonBPM = 60000 / beatmap.GetMostCommonBeatLength() * rate;
|
||||
|
||||
string labelText = Precision.AlmostEquals(bpmMin, bpmMax)
|
||||
? $"{bpmMin:0}"
|
||||
|
@ -314,39 +314,46 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
item.OwnerID = userId;
|
||||
|
||||
switch (Room.Settings.QueueMode)
|
||||
{
|
||||
case QueueMode.HostOnly:
|
||||
// In host-only mode, the current item is re-used.
|
||||
item.ID = currentItem.ID;
|
||||
item.PlaylistOrder = currentItem.PlaylistOrder;
|
||||
|
||||
serverSidePlaylist[currentIndex] = item;
|
||||
await ((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false);
|
||||
|
||||
// Note: Unlike the server, this is the easiest way to update the current item at this point.
|
||||
await updateCurrentItem(Room, false).ConfigureAwait(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
await addItem(item).ConfigureAwait(false);
|
||||
|
||||
// The current item can change as a result of an item being added. For example, if all items earlier in the queue were expired.
|
||||
await updateCurrentItem(Room).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
await addItem(item).ConfigureAwait(false);
|
||||
await updateCurrentItem(Room).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public override Task AddPlaylistItem(MultiplayerPlaylistItem item) => AddUserPlaylistItem(api.LocalUser.Value.OnlineID, item);
|
||||
|
||||
public async Task EditUserPlaylistItem(int userId, MultiplayerPlaylistItem item)
|
||||
{
|
||||
Debug.Assert(Room != null);
|
||||
Debug.Assert(APIRoom != null);
|
||||
Debug.Assert(currentItem != null);
|
||||
|
||||
item.OwnerID = userId;
|
||||
|
||||
var existingItem = serverSidePlaylist.SingleOrDefault(i => i.ID == item.ID);
|
||||
|
||||
if (existingItem == null)
|
||||
throw new InvalidOperationException("Attempted to change an item that doesn't exist.");
|
||||
|
||||
if (existingItem.OwnerID != userId && Room.Host?.UserID != LocalUser?.UserID)
|
||||
throw new InvalidOperationException("Attempted to change an item which is not owned by the user.");
|
||||
|
||||
if (existingItem.Expired)
|
||||
throw new InvalidOperationException("Attempted to change an item which has already been played.");
|
||||
|
||||
// Ensure the playlist order doesn't change.
|
||||
item.PlaylistOrder = existingItem.PlaylistOrder;
|
||||
|
||||
serverSidePlaylist[serverSidePlaylist.IndexOf(existingItem)] = item;
|
||||
|
||||
await ((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public override Task EditPlaylistItem(MultiplayerPlaylistItem item) => EditUserPlaylistItem(api.LocalUser.Value.OnlineID, item);
|
||||
|
||||
public async Task RemoveUserPlaylistItem(int userId, long playlistItemId)
|
||||
{
|
||||
Debug.Assert(Room != null);
|
||||
Debug.Assert(APIRoom != null);
|
||||
|
||||
if (Room.Settings.QueueMode == QueueMode.HostOnly)
|
||||
throw new InvalidOperationException("Items cannot be removed in host-only mode.");
|
||||
|
||||
var item = serverSidePlaylist.Find(i => i.ID == playlistItemId);
|
||||
|
||||
if (item == null)
|
||||
|
@ -20,7 +20,7 @@
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="AutoMapper" Version="10.1.1" />
|
||||
<PackageReference Include="DiffPlex" Version="1.7.0" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.38" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.39" />
|
||||
<PackageReference Include="Humanizer" Version="2.13.14" />
|
||||
<PackageReference Include="MessagePack" Version="2.3.85" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="5.0.11" />
|
||||
@ -31,15 +31,15 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.NETCore.Targets" Version="3.1.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="ppy.LocalisationAnalyser" Version="2021.725.0">
|
||||
<PackageReference Include="ppy.LocalisationAnalyser" Version="2021.1210.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="10.7.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.1207.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.1210.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1203.0" />
|
||||
<PackageReference Include="Sentry" Version="3.11.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.30.0" />
|
||||
<PackageReference Include="Sentry" Version="3.12.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
<PackageReference Include="TagLibSharp" Version="2.2.0" />
|
||||
|
@ -60,7 +60,7 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.1207.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.1210.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1203.0" />
|
||||
</ItemGroup>
|
||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||
@ -83,7 +83,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.1207.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.1210.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.30.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user