1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 15:26:07 +08:00

Merge branch 'master' into fix-multiplayer-mod-propagation-race

This commit is contained in:
Dan Balasescu 2021-02-17 16:22:51 +09:00 committed by GitHub
commit 57ede6eb37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 156 additions and 48 deletions

View File

@ -69,6 +69,20 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("last room joined", () => RoomManager.Rooms.Last().Status.Value is JoinedRoomStatus); AddAssert("last room joined", () => RoomManager.Rooms.Last().Status.Value is JoinedRoomStatus);
} }
[Test]
public void TestClickDeselection()
{
AddRooms(1);
AddAssert("no selection", () => checkRoomSelected(null));
press(Key.Down);
AddAssert("first room selected", () => checkRoomSelected(RoomManager.Rooms.First()));
AddStep("click away", () => InputManager.Click(MouseButton.Left));
AddAssert("no selection", () => checkRoomSelected(null));
}
private void press(Key down) private void press(Key down)
{ {
AddStep($"press {down}", () => InputManager.Key(down)); AddStep($"press {down}", () => InputManager.Key(down));

View File

@ -22,7 +22,7 @@ namespace osu.Game.Online.API.Requests.Responses
public double? PP { get; set; } public double? PP { get; set; }
[JsonProperty(@"room_id")] [JsonProperty(@"room_id")]
public int RoomID { get; set; } public long RoomID { get; set; }
[JsonProperty("total_score")] [JsonProperty("total_score")]
public long TotalScore { get; set; } public long TotalScore { get; set; }

View File

@ -95,7 +95,7 @@ namespace osu.Game.Online.Multiplayer
private Room? apiRoom; private Room? apiRoom;
// Todo: This is temporary, until the multiplayer server returns the item id on match start or otherwise. // Todo: This is temporary, until the multiplayer server returns the item id on match start or otherwise.
private int playlistItemId; private long playlistItemId;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()

View File

@ -9,11 +9,11 @@ namespace osu.Game.Online.Rooms
{ {
public class CreateRoomScoreRequest : APIRequest<APIScoreToken> public class CreateRoomScoreRequest : APIRequest<APIScoreToken>
{ {
private readonly int roomId; private readonly long roomId;
private readonly int playlistItemId; private readonly long playlistItemId;
private readonly string versionHash; private readonly string versionHash;
public CreateRoomScoreRequest(int roomId, int playlistItemId, string versionHash) public CreateRoomScoreRequest(long roomId, long playlistItemId, string versionHash)
{ {
this.roomId = roomId; this.roomId = roomId;
this.playlistItemId = playlistItemId; this.playlistItemId = playlistItemId;

View File

@ -7,9 +7,9 @@ namespace osu.Game.Online.Rooms
{ {
public class GetRoomLeaderboardRequest : APIRequest<APILeaderboard> public class GetRoomLeaderboardRequest : APIRequest<APILeaderboard>
{ {
private readonly int roomId; private readonly long roomId;
public GetRoomLeaderboardRequest(int roomId) public GetRoomLeaderboardRequest(long roomId)
{ {
this.roomId = roomId; this.roomId = roomId;
} }

View File

@ -7,9 +7,9 @@ namespace osu.Game.Online.Rooms
{ {
public class GetRoomRequest : APIRequest<Room> public class GetRoomRequest : APIRequest<Room>
{ {
public readonly int RoomId; public readonly long RoomId;
public GetRoomRequest(int roomId) public GetRoomRequest(long roomId)
{ {
RoomId = roomId; RoomId = roomId;
} }

View File

@ -15,8 +15,8 @@ namespace osu.Game.Online.Rooms
/// </summary> /// </summary>
public class IndexPlaylistScoresRequest : APIRequest<IndexedMultiplayerScores> public class IndexPlaylistScoresRequest : APIRequest<IndexedMultiplayerScores>
{ {
public readonly int RoomId; public readonly long RoomId;
public readonly int PlaylistItemId; public readonly long PlaylistItemId;
[CanBeNull] [CanBeNull]
public readonly Cursor Cursor; public readonly Cursor Cursor;
@ -24,13 +24,13 @@ namespace osu.Game.Online.Rooms
[CanBeNull] [CanBeNull]
public readonly IndexScoresParams IndexParams; public readonly IndexScoresParams IndexParams;
public IndexPlaylistScoresRequest(int roomId, int playlistItemId) public IndexPlaylistScoresRequest(long roomId, long playlistItemId)
{ {
RoomId = roomId; RoomId = roomId;
PlaylistItemId = playlistItemId; PlaylistItemId = playlistItemId;
} }
public IndexPlaylistScoresRequest(int roomId, int playlistItemId, [NotNull] Cursor cursor, [NotNull] IndexScoresParams indexParams) public IndexPlaylistScoresRequest(long roomId, long playlistItemId, [NotNull] Cursor cursor, [NotNull] IndexScoresParams indexParams)
: this(roomId, playlistItemId) : this(roomId, playlistItemId)
{ {
Cursor = cursor; Cursor = cursor;

View File

@ -0,0 +1,19 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using Newtonsoft.Json;
namespace osu.Game.Online.Rooms
{
/// <summary>
/// Represents attempts on a specific playlist item.
/// </summary>
public class ItemAttemptsCount
{
[JsonProperty("id")]
public int PlaylistItemID { get; set; }
[JsonProperty("attempts")]
public int Attempts { get; set; }
}
}

View File

@ -18,7 +18,7 @@ namespace osu.Game.Online.Rooms
public class MultiplayerScore public class MultiplayerScore
{ {
[JsonProperty("id")] [JsonProperty("id")]
public int ID { get; set; } public long ID { get; set; }
[JsonProperty("user")] [JsonProperty("user")]
public User User { get; set; } public User User { get; set; }

View File

@ -0,0 +1,16 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using Newtonsoft.Json;
namespace osu.Game.Online.Rooms
{
/// <summary>
/// Represents aggregated score for the local user for a playlist.
/// </summary>
public class PlaylistAggregateScore
{
[JsonProperty("playlist_item_attempts")]
public ItemAttemptsCount[] PlaylistItemAttempts { get; set; }
}
}

View File

@ -15,7 +15,7 @@ namespace osu.Game.Online.Rooms
public class PlaylistItem : IEquatable<PlaylistItem> public class PlaylistItem : IEquatable<PlaylistItem>
{ {
[JsonProperty("id")] [JsonProperty("id")]
public int ID { get; set; } public long ID { get; set; }
[JsonProperty("beatmap_id")] [JsonProperty("beatmap_id")]
public int BeatmapID { get; set; } public int BeatmapID { get; set; }

View File

@ -17,7 +17,7 @@ namespace osu.Game.Online.Rooms
{ {
[Cached] [Cached]
[JsonProperty("id")] [JsonProperty("id")]
public readonly Bindable<int?> RoomID = new Bindable<int?>(); public readonly Bindable<long?> RoomID = new Bindable<long?>();
[Cached] [Cached]
[JsonProperty("name")] [JsonProperty("name")]
@ -72,6 +72,10 @@ namespace osu.Game.Online.Rooms
[JsonIgnore] [JsonIgnore]
public readonly Bindable<int?> MaxParticipants = new Bindable<int?>(); public readonly Bindable<int?> MaxParticipants = new Bindable<int?>();
[Cached]
[JsonProperty("current_user_score")]
public readonly Bindable<PlaylistAggregateScore> UserScore = new Bindable<PlaylistAggregateScore>();
[Cached] [Cached]
[JsonProperty("recent_participants")] [JsonProperty("recent_participants")]
public readonly BindableList<User> RecentParticipants = new BindableList<User>(); public readonly BindableList<User> RecentParticipants = new BindableList<User>();
@ -144,6 +148,7 @@ namespace osu.Game.Online.Rooms
MaxParticipants.Value = other.MaxParticipants.Value; MaxParticipants.Value = other.MaxParticipants.Value;
ParticipantCount.Value = other.ParticipantCount.Value; ParticipantCount.Value = other.ParticipantCount.Value;
EndDate.Value = other.EndDate.Value; EndDate.Value = other.EndDate.Value;
UserScore.Value = other.UserScore.Value;
if (EndDate.Value != null && DateTimeOffset.Now >= EndDate.Value) if (EndDate.Value != null && DateTimeOffset.Now >= EndDate.Value)
Status.Value = new RoomStatusEnded(); Status.Value = new RoomStatusEnded();

View File

@ -7,11 +7,11 @@ namespace osu.Game.Online.Rooms
{ {
public class ShowPlaylistUserScoreRequest : APIRequest<MultiplayerScore> public class ShowPlaylistUserScoreRequest : APIRequest<MultiplayerScore>
{ {
private readonly int roomId; private readonly long roomId;
private readonly int playlistItemId; private readonly long playlistItemId;
private readonly long userId; private readonly long userId;
public ShowPlaylistUserScoreRequest(int roomId, int playlistItemId, long userId) public ShowPlaylistUserScoreRequest(long roomId, long playlistItemId, long userId)
{ {
this.roomId = roomId; this.roomId = roomId;
this.playlistItemId = playlistItemId; this.playlistItemId = playlistItemId;

View File

@ -11,12 +11,12 @@ namespace osu.Game.Online.Rooms
{ {
public class SubmitRoomScoreRequest : APIRequest<MultiplayerScore> public class SubmitRoomScoreRequest : APIRequest<MultiplayerScore>
{ {
private readonly int scoreId; private readonly long scoreId;
private readonly int roomId; private readonly long roomId;
private readonly int playlistItemId; private readonly long playlistItemId;
private readonly ScoreInfo scoreInfo; private readonly ScoreInfo scoreInfo;
public SubmitRoomScoreRequest(int scoreId, int roomId, int playlistItemId, ScoreInfo scoreInfo) public SubmitRoomScoreRequest(long scoreId, long roomId, long playlistItemId, ScoreInfo scoreInfo)
{ {
this.scoreId = scoreId; this.scoreId = scoreId;
this.roomId = roomId; this.roomId = roomId;

View File

@ -9,7 +9,6 @@ using osuTK;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Users.Drawables; using osu.Game.Users.Drawables;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Utils;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using System.Linq; using System.Linq;
@ -245,11 +244,32 @@ namespace osu.Game.Overlays.Comments
if (Comment.EditedAt.HasValue) if (Comment.EditedAt.HasValue)
{ {
info.Add(new OsuSpriteText var font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular);
var colour = colourProvider.Foreground1;
info.Add(new FillFlowContainer
{ {
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular), AutoSizeAxes = Axes.Both,
Text = $@"edited {HumanizerUtils.Humanize(Comment.EditedAt.Value)} by {Comment.EditedUser.Username}", Children = new Drawable[]
Colour = colourProvider.Foreground1 {
new OsuSpriteText
{
Font = font,
Text = "edited ",
Colour = colour
},
new DrawableDate(Comment.EditedAt.Value)
{
Font = font,
Colour = colour
},
new OsuSpriteText
{
Font = font,
Text = $@" by {Comment.EditedUser.Username}",
Colour = colour
},
}
}); });
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -39,12 +40,26 @@ namespace osu.Game.Screens.OnlinePlay.Components
{ {
base.LoadComplete(); base.LoadComplete();
MaxAttempts.BindValueChanged(attempts => MaxAttempts.BindValueChanged(_ => updateAttempts());
UserScore.BindValueChanged(_ => updateAttempts(), true);
}
private void updateAttempts()
{
if (MaxAttempts.Value != null)
{ {
attemptDisplay.Text = attempts.NewValue == null attemptDisplay.Text = $"Maximum attempts: {MaxAttempts.Value:N0}";
? string.Empty
: $"Maximum attempts: {attempts.NewValue:N0}"; if (UserScore.Value != null)
}, true); {
int remaining = MaxAttempts.Value.Value - UserScore.Value.PlaylistItemAttempts.Sum(a => a.Attempts);
attemptDisplay.Text += $" ({remaining} remaining)";
}
}
else
{
attemptDisplay.Text = string.Empty;
}
} }
} }
} }

View File

@ -116,7 +116,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
joinedRoom.Value = null; joinedRoom.Value = null;
} }
private readonly HashSet<int> ignoredRooms = new HashSet<int>(); private readonly HashSet<long> ignoredRooms = new HashSet<long>();
private void onRoomsReceived(List<Room> received) private void onRoomsReceived(List<Room> received)
{ {

View File

@ -11,6 +11,7 @@ using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Extensions; using osu.Game.Extensions;
using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Cursor;
@ -42,6 +43,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private LoungeSubScreen loungeSubScreen { get; set; } private LoungeSubScreen loungeSubScreen { get; set; }
// handle deselection
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
public RoomsContainer() public RoomsContainer()
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
@ -69,8 +73,16 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
rooms.BindTo(roomManager.Rooms); rooms.BindTo(roomManager.Rooms);
filter?.BindValueChanged(criteria => Filter(criteria.NewValue)); filter?.BindValueChanged(criteria => Filter(criteria.NewValue));
selectedRoom.BindValueChanged(selection =>
{
updateSelection();
}, true);
} }
private void updateSelection() =>
roomFlow.Children.ForEach(r => r.State = r.Room == selectedRoom.Value ? SelectionState.Selected : SelectionState.NotSelected);
public void Filter(FilterCriteria criteria) public void Filter(FilterCriteria criteria)
{ {
roomFlow.Children.ForEach(r => roomFlow.Children.ForEach(r =>
@ -125,6 +137,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
} }
Filter(filter?.Value); Filter(filter?.Value);
updateSelection();
} }
private void removeRooms(IEnumerable<Room> rooms) private void removeRooms(IEnumerable<Room> rooms)
@ -146,11 +160,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
roomFlow.SetLayoutPosition(room, room.Room.Position.Value); roomFlow.SetLayoutPosition(room, room.Room.Position.Value);
} }
private void selectRoom(Room room) private void selectRoom(Room room) => selectedRoom.Value = room;
{
roomFlow.Children.ForEach(r => r.State = r.Room == room ? SelectionState.Selected : SelectionState.NotSelected);
selectedRoom.Value = room;
}
private void joinSelected() private void joinSelected()
{ {
@ -159,6 +169,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
JoinRequested?.Invoke(selectedRoom.Value); JoinRequested?.Invoke(selectedRoom.Value);
} }
protected override bool OnClick(ClickEvent e)
{
selectRoom(null);
return base.OnClick(e);
}
#region Key selection logic (shared with BeatmapCarousel) #region Key selection logic (shared with BeatmapCarousel)
public bool OnPressed(GlobalAction action) public bool OnPressed(GlobalAction action)

View File

@ -11,7 +11,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
public class MatchChatDisplay : StandAloneChatDisplay public class MatchChatDisplay : StandAloneChatDisplay
{ {
[Resolved(typeof(Room), nameof(Room.RoomID))] [Resolved(typeof(Room), nameof(Room.RoomID))]
private Bindable<int?> roomId { get; set; } private Bindable<long?> roomId { get; set; }
[Resolved(typeof(Room), nameof(Room.ChannelId))] [Resolved(typeof(Room), nameof(Room.ChannelId))]
private Bindable<int> channelId { get; set; } private Bindable<int> channelId { get; set; }

View File

@ -15,7 +15,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
public class MatchLeaderboard : Leaderboard<MatchLeaderboardScope, APIUserScoreAggregate> public class MatchLeaderboard : Leaderboard<MatchLeaderboardScope, APIUserScoreAggregate>
{ {
[Resolved(typeof(Room), nameof(Room.RoomID))] [Resolved(typeof(Room), nameof(Room.RoomID))]
private Bindable<int?> roomId { get; set; } private Bindable<long?> roomId { get; set; }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()

View File

@ -357,7 +357,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
public class CreateOrUpdateButton : TriangleButton public class CreateOrUpdateButton : TriangleButton
{ {
[Resolved(typeof(Room), nameof(Room.RoomID))] [Resolved(typeof(Room), nameof(Room.RoomID))]
private Bindable<int?> roomId { get; set; } private Bindable<long?> roomId { get; set; }
protected override void LoadComplete() protected override void LoadComplete()
{ {

View File

@ -9,7 +9,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
{ {
public class MultiplayerResultsScreen : PlaylistsResultsScreen public class MultiplayerResultsScreen : PlaylistsResultsScreen
{ {
public MultiplayerResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem) public MultiplayerResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem)
: base(score, roomId, playlistItem, false, false) : base(score, roomId, playlistItem, false, false)
{ {
} }

View File

@ -13,7 +13,7 @@ namespace osu.Game.Screens.OnlinePlay
public class OnlinePlayComposite : CompositeDrawable public class OnlinePlayComposite : CompositeDrawable
{ {
[Resolved(typeof(Room))] [Resolved(typeof(Room))]
protected Bindable<int?> RoomID { get; private set; } protected Bindable<long?> RoomID { get; private set; }
[Resolved(typeof(Room), nameof(Room.Name))] [Resolved(typeof(Room), nameof(Room.Name))]
protected Bindable<string> RoomName { get; private set; } protected Bindable<string> RoomName { get; private set; }
@ -42,6 +42,9 @@ namespace osu.Game.Screens.OnlinePlay
[Resolved(typeof(Room))] [Resolved(typeof(Room))]
protected Bindable<int?> MaxAttempts { get; private set; } protected Bindable<int?> MaxAttempts { get; private set; }
[Resolved(typeof(Room))]
public Bindable<PlaylistAggregateScore> UserScore { get; private set; }
[Resolved(typeof(Room))] [Resolved(typeof(Room))]
protected Bindable<DateTimeOffset?> EndDate { get; private set; } protected Bindable<DateTimeOffset?> EndDate { get; private set; }

View File

@ -24,7 +24,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
public Action Exited; public Action Exited;
[Resolved(typeof(Room), nameof(Room.RoomID))] [Resolved(typeof(Room), nameof(Room.RoomID))]
protected Bindable<int?> RoomId { get; private set; } protected Bindable<long?> RoomId { get; private set; }
protected readonly PlaylistItem PlaylistItem; protected readonly PlaylistItem PlaylistItem;

View File

@ -19,7 +19,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
{ {
public class PlaylistsResultsScreen : ResultsScreen public class PlaylistsResultsScreen : ResultsScreen
{ {
private readonly int roomId; private readonly long roomId;
private readonly PlaylistItem playlistItem; private readonly PlaylistItem playlistItem;
protected LoadingSpinner LeftSpinner { get; private set; } protected LoadingSpinner LeftSpinner { get; private set; }
@ -32,7 +32,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
[Resolved] [Resolved]
private IAPIProvider api { get; set; } private IAPIProvider api { get; set; }
public PlaylistsResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem, bool allowRetry, bool allowWatchingReplay = true) public PlaylistsResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem, bool allowRetry, bool allowWatchingReplay = true)
: base(score, allowRetry, allowWatchingReplay) : base(score, allowRetry, allowWatchingReplay)
{ {
this.roomId = roomId; this.roomId = roomId;

View File

@ -25,7 +25,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
public override string ShortTitle => "playlist"; public override string ShortTitle => "playlist";
[Resolved(typeof(Room), nameof(Room.RoomID))] [Resolved(typeof(Room), nameof(Room.RoomID))]
private Bindable<int?> roomId { get; set; } private Bindable<long?> roomId { get; set; }
private MatchSettingsOverlay settingsOverlay; private MatchSettingsOverlay settingsOverlay;
private MatchLeaderboard leaderboard; private MatchLeaderboard leaderboard;