Compare commits
10 Commits
fb7ccba4ba
...
d76d4d9a35
| Author | SHA1 | Date | |
|---|---|---|---|
| d76d4d9a35 | |||
|
|
bb7417c099 | ||
|
|
066e093987 | ||
|
|
56e0c3e65d | ||
|
|
89d7726903 | ||
|
|
36f1bfef07 | ||
|
|
118f07878a | ||
|
|
e144968893 | ||
|
|
d2ffea41c6 | ||
|
|
a8be9b1381 |
@@ -10,7 +10,7 @@
|
|||||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2025.1205.1" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2025.1209.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
public override ModType Type => ModType.Conversion;
|
public override ModType Type => ModType.Conversion;
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModRandom)).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModRandom)).ToArray();
|
||||||
|
public override bool Ranked => true;
|
||||||
|
|
||||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
|
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
|
||||||
@@ -149,5 +150,33 @@ namespace osu.Game.Tests.Online.Matchmaking
|
|||||||
Assert.AreEqual(5, state.Users.GetOrAdd(5).Placement);
|
Assert.AreEqual(5, state.Users.GetOrAdd(5).Placement);
|
||||||
Assert.AreEqual(6, state.Users.GetOrAdd(6).Placement);
|
Assert.AreEqual(6, state.Users.GetOrAdd(6).Placement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AbandonOrder()
|
||||||
|
{
|
||||||
|
var state = new MatchmakingRoomState();
|
||||||
|
|
||||||
|
state.AdvanceRound();
|
||||||
|
state.RecordScores(
|
||||||
|
[
|
||||||
|
new SoloScoreInfo { UserID = 1, TotalScore = 1000 },
|
||||||
|
new SoloScoreInfo { UserID = 2, TotalScore = 500 },
|
||||||
|
], placement_points);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, state.Users.GetOrAdd(1).Placement);
|
||||||
|
Assert.AreEqual(2, state.Users.GetOrAdd(2).Placement);
|
||||||
|
|
||||||
|
state.Users.GetOrAdd(1).AbandonedAt = DateTimeOffset.Now;
|
||||||
|
state.RecordScores([], placement_points);
|
||||||
|
|
||||||
|
Assert.AreEqual(2, state.Users.GetOrAdd(1).Placement);
|
||||||
|
Assert.AreEqual(1, state.Users.GetOrAdd(2).Placement);
|
||||||
|
|
||||||
|
state.Users.GetOrAdd(2).AbandonedAt = DateTimeOffset.Now - TimeSpan.FromMinutes(1);
|
||||||
|
state.RecordScores([], placement_points);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, state.Users.GetOrAdd(1).Placement);
|
||||||
|
Assert.AreEqual(2, state.Users.GetOrAdd(2).Placement);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
@@ -26,8 +27,15 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
|||||||
|
|
||||||
AddStep("join room", () => JoinRoom(CreateDefaultRoom(MatchType.Matchmaking)));
|
AddStep("join room", () => JoinRoom(CreateDefaultRoom(MatchType.Matchmaking)));
|
||||||
WaitForJoined();
|
WaitForJoined();
|
||||||
|
}
|
||||||
|
|
||||||
setupRequestHandler();
|
[TestCase(2)]
|
||||||
|
[TestCase(4)]
|
||||||
|
[TestCase(8)]
|
||||||
|
[TestCase(16)]
|
||||||
|
public void TestDisplayScores(int scoreCount)
|
||||||
|
{
|
||||||
|
setupRequestHandler(scoreCount);
|
||||||
|
|
||||||
AddStep("load screen", () =>
|
AddStep("load screen", () =>
|
||||||
{
|
{
|
||||||
@@ -40,7 +48,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupRequestHandler()
|
private void setupRequestHandler(int scoreCount)
|
||||||
{
|
{
|
||||||
AddStep("setup request handler", () =>
|
AddStep("setup request handler", () =>
|
||||||
{
|
{
|
||||||
@@ -71,7 +79,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
|||||||
case IndexPlaylistScoresRequest index:
|
case IndexPlaylistScoresRequest index:
|
||||||
var result = new IndexedMultiplayerScores();
|
var result = new IndexedMultiplayerScores();
|
||||||
|
|
||||||
for (int i = 0; i < 8; ++i)
|
for (int i = 0; i < scoreCount; ++i)
|
||||||
{
|
{
|
||||||
result.Scores.Add(new MultiplayerScore
|
result.Scores.Add(new MultiplayerScore
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -603,7 +603,7 @@ namespace osu.Game.Online.API
|
|||||||
cancellationToken.Cancel();
|
cancellationToken.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class WebRequestFlushedException : Exception
|
internal class WebRequestFlushedException : Exception
|
||||||
{
|
{
|
||||||
public WebRequestFlushedException(APIState state)
|
public WebRequestFlushedException(APIState state)
|
||||||
: base($@"Request failed from flush operation (state {state})")
|
: base($@"Request failed from flush operation (state {state})")
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Signals that a user has requested to skip the beatmap intro.
|
/// Signals that a user has requested to skip the beatmap intro.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task UserVotedToSkipIntro(int userId);
|
Task UserVotedToSkipIntro(int userId, bool voted);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Signals that the vote to skip the beatmap intro has passed.
|
/// Signals that the vote to skip the beatmap intro has passed.
|
||||||
|
|||||||
@@ -36,5 +36,11 @@ namespace osu.Game.Online.Multiplayer.MatchTypes.Matchmaking
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Key(3)]
|
[Key(3)]
|
||||||
public MatchmakingRoundList Rounds { get; set; } = new MatchmakingRoundList();
|
public MatchmakingRoundList Rounds { get; set; } = new MatchmakingRoundList();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time at which this user abandoned the match.
|
||||||
|
/// </summary>
|
||||||
|
[Key(4)]
|
||||||
|
public DateTimeOffset? AbandonedAt { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,42 +23,53 @@ namespace osu.Game.Online.Multiplayer.MatchTypes.Matchmaking
|
|||||||
ArgumentNullException.ThrowIfNull(x);
|
ArgumentNullException.ThrowIfNull(x);
|
||||||
ArgumentNullException.ThrowIfNull(y);
|
ArgumentNullException.ThrowIfNull(y);
|
||||||
|
|
||||||
// X appears earlier in the list if it has more points.
|
int compare = compareAbandonedAt(x, y);
|
||||||
if (x.Points > y.Points)
|
if (compare != 0)
|
||||||
return -1;
|
return compare;
|
||||||
|
|
||||||
// Y appears earlier in the list if it has more points.
|
compare = comparePoints(x, y);
|
||||||
if (y.Points > x.Points)
|
if (compare != 0)
|
||||||
return 1;
|
return compare;
|
||||||
|
|
||||||
// Tiebreaker 1 (likely): From each user's point-of-view, their earliest and best placement.
|
compare = compareRoundPlacements(x, y);
|
||||||
|
if (compare != 0)
|
||||||
|
return compare;
|
||||||
|
|
||||||
|
return compareUserIds(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int compareAbandonedAt(MatchmakingUser x, MatchmakingUser y)
|
||||||
|
{
|
||||||
|
DateTimeOffset xAbandonedAt = x.AbandonedAt ?? DateTimeOffset.MaxValue;
|
||||||
|
DateTimeOffset yAbandonedAt = y.AbandonedAt ?? DateTimeOffset.MaxValue;
|
||||||
|
return -xAbandonedAt.CompareTo(yAbandonedAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int comparePoints(MatchmakingUser x, MatchmakingUser y)
|
||||||
|
{
|
||||||
|
return -x.Points.CompareTo(y.Points);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int compareRoundPlacements(MatchmakingUser x, MatchmakingUser y)
|
||||||
|
{
|
||||||
for (int r = 1; r <= rounds; r++)
|
for (int r = 1; r <= rounds; r++)
|
||||||
{
|
{
|
||||||
MatchmakingRound? xRound;
|
x.Rounds.RoundsDictionary.TryGetValue(r, out var xRound);
|
||||||
x.Rounds.RoundsDictionary.TryGetValue(r, out xRound);
|
y.Rounds.RoundsDictionary.TryGetValue(r, out var yRound);
|
||||||
|
|
||||||
MatchmakingRound? yRound;
|
int xPlacement = xRound?.Placement ?? int.MaxValue;
|
||||||
y.Rounds.RoundsDictionary.TryGetValue(r, out yRound);
|
int yPlacement = yRound?.Placement ?? int.MaxValue;
|
||||||
|
|
||||||
// Nothing to do if both players haven't played this round.
|
int compare = xPlacement.CompareTo(yPlacement);
|
||||||
if (xRound == null && yRound == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// X appears later in the list if it hasn't played this round.
|
|
||||||
if (xRound == null)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
// Y appears later in the list if it hasn't played this round.
|
|
||||||
if (yRound == null)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// X appears earlier in the list if it has a better placement in the round.
|
|
||||||
int compare = xRound.Placement.CompareTo(yRound.Placement);
|
|
||||||
if (compare != 0)
|
if (compare != 0)
|
||||||
return compare;
|
return compare;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tiebreaker 2 (unlikely): User ID.
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int compareUserIds(MatchmakingUser x, MatchmakingUser y)
|
||||||
|
{
|
||||||
return x.UserId.CompareTo(y.UserId);
|
return x.UserId.CompareTo(y.UserId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
public event Action<int, long>? MatchmakingItemDeselected;
|
public event Action<int, long>? MatchmakingItemDeselected;
|
||||||
public event Action<MatchRoomState>? MatchRoomStateChanged;
|
public event Action<MatchRoomState>? MatchRoomStateChanged;
|
||||||
|
|
||||||
public event Action<int>? UserVotedToSkipIntro;
|
public event Action<int, bool>? UserVotedToSkipIntro;
|
||||||
public event Action? VoteToSkipIntroPassed;
|
public event Action? VoteToSkipIntroPassed;
|
||||||
|
|
||||||
public event Action<MultiplayerRoomUser, BeatmapAvailability>? BeatmapAvailabilityChanged;
|
public event Action<MultiplayerRoomUser, BeatmapAvailability>? BeatmapAvailabilityChanged;
|
||||||
@@ -854,10 +854,6 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
handleRoomRequest(() =>
|
handleRoomRequest(() =>
|
||||||
{
|
{
|
||||||
Debug.Assert(Room != null);
|
Debug.Assert(Room != null);
|
||||||
|
|
||||||
foreach (var user in Room.Users)
|
|
||||||
user.VotedToSkipIntro = false;
|
|
||||||
|
|
||||||
GameplayStarted?.Invoke();
|
GameplayStarted?.Invoke();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -928,7 +924,7 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
Task IMultiplayerClient.UserVotedToSkipIntro(int userId)
|
Task IMultiplayerClient.UserVotedToSkipIntro(int userId, bool voted)
|
||||||
{
|
{
|
||||||
handleRoomRequest(() =>
|
handleRoomRequest(() =>
|
||||||
{
|
{
|
||||||
@@ -940,9 +936,8 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
if (user == null)
|
if (user == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
user.VotedToSkipIntro = true;
|
user.VotedToSkipIntro = voted;
|
||||||
|
UserVotedToSkipIntro?.Invoke(userId, voted);
|
||||||
UserVotedToSkipIntro?.Invoke(userId);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@@ -1117,7 +1112,7 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
|
|
||||||
Task IMatchmakingClient.MatchmakingItemSelected(int userId, long playlistItemId)
|
Task IMatchmakingClient.MatchmakingItemSelected(int userId, long playlistItemId)
|
||||||
{
|
{
|
||||||
Scheduler.Add(() =>
|
handleRoomRequest(() =>
|
||||||
{
|
{
|
||||||
MatchmakingItemSelected?.Invoke(userId, playlistItemId);
|
MatchmakingItemSelected?.Invoke(userId, playlistItemId);
|
||||||
RoomUpdated?.Invoke();
|
RoomUpdated?.Invoke();
|
||||||
@@ -1128,7 +1123,7 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
|
|
||||||
Task IMatchmakingClient.MatchmakingItemDeselected(int userId, long playlistItemId)
|
Task IMatchmakingClient.MatchmakingItemDeselected(int userId, long playlistItemId)
|
||||||
{
|
{
|
||||||
Scheduler.Add(() =>
|
handleRoomRequest(() =>
|
||||||
{
|
{
|
||||||
MatchmakingItemDeselected?.Invoke(userId, playlistItemId);
|
MatchmakingItemDeselected?.Invoke(userId, playlistItemId);
|
||||||
RoomUpdated?.Invoke();
|
RoomUpdated?.Invoke();
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
connection.On<MultiplayerPlaylistItem>(nameof(IMultiplayerClient.PlaylistItemAdded), ((IMultiplayerClient)this).PlaylistItemAdded);
|
connection.On<MultiplayerPlaylistItem>(nameof(IMultiplayerClient.PlaylistItemAdded), ((IMultiplayerClient)this).PlaylistItemAdded);
|
||||||
connection.On<long>(nameof(IMultiplayerClient.PlaylistItemRemoved), ((IMultiplayerClient)this).PlaylistItemRemoved);
|
connection.On<long>(nameof(IMultiplayerClient.PlaylistItemRemoved), ((IMultiplayerClient)this).PlaylistItemRemoved);
|
||||||
connection.On<MultiplayerPlaylistItem>(nameof(IMultiplayerClient.PlaylistItemChanged), ((IMultiplayerClient)this).PlaylistItemChanged);
|
connection.On<MultiplayerPlaylistItem>(nameof(IMultiplayerClient.PlaylistItemChanged), ((IMultiplayerClient)this).PlaylistItemChanged);
|
||||||
connection.On<int>(nameof(IMultiplayerClient.UserVotedToSkipIntro), ((IMultiplayerClient)this).UserVotedToSkipIntro);
|
connection.On<int, bool>(nameof(IMultiplayerClient.UserVotedToSkipIntro), ((IMultiplayerClient)this).UserVotedToSkipIntro);
|
||||||
connection.On(nameof(IMultiplayerClient.VoteToSkipIntroPassed), ((IMultiplayerClient)this).VoteToSkipIntroPassed);
|
connection.On(nameof(IMultiplayerClient.VoteToSkipIntroPassed), ((IMultiplayerClient)this).VoteToSkipIntroPassed);
|
||||||
|
|
||||||
connection.On(nameof(IMatchmakingClient.MatchmakingQueueJoined), ((IMatchmakingClient)this).MatchmakingQueueJoined);
|
connection.On(nameof(IMatchmakingClient.MatchmakingQueueJoined), ((IMatchmakingClient)this).MatchmakingQueueJoined);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match
|
|||||||
resetPlaceholderText();
|
resetPlaceholderText();
|
||||||
|
|
||||||
TextBox.HoldFocus = false;
|
TextBox.HoldFocus = false;
|
||||||
TextBox.ReleaseFocusOnCommit = true;
|
TextBox.ReleaseFocusOnCommit = false;
|
||||||
TextBox.Focus = () => TextBox.PlaceholderText = ChatStrings.InputPlaceholder;
|
TextBox.Focus = () => TextBox.PlaceholderText = ChatStrings.InputPlaceholder;
|
||||||
TextBox.FocusLost = resetPlaceholderText;
|
TextBox.FocusLost = resetPlaceholderText;
|
||||||
|
|
||||||
|
|||||||
@@ -520,6 +520,9 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match
|
|||||||
|
|
||||||
private void onBeatmapAvailabilityChanged(MultiplayerRoomUser user, BeatmapAvailability availability) => Scheduler.Add(() =>
|
private void onBeatmapAvailabilityChanged(MultiplayerRoomUser user, BeatmapAvailability availability) => Scheduler.Add(() =>
|
||||||
{
|
{
|
||||||
|
if (!user.Equals(RoomUser))
|
||||||
|
return;
|
||||||
|
|
||||||
if (availability.State == DownloadState.Downloading)
|
if (availability.State == DownloadState.Downloading)
|
||||||
downloadProgressBar.FadeIn(200, Easing.OutPow10);
|
downloadProgressBar.FadeIn(200, Easing.OutPow10);
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ using osu.Game.Online.Rooms;
|
|||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.RoundResults
|
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.RoundResults
|
||||||
{
|
{
|
||||||
@@ -31,8 +32,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.RoundResults
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class SubScreenRoundResults : MatchmakingSubScreen
|
public partial class SubScreenRoundResults : MatchmakingSubScreen
|
||||||
{
|
{
|
||||||
private const int panel_spacing = 5;
|
|
||||||
|
|
||||||
public override PanelDisplayStyle PlayersDisplayStyle => PanelDisplayStyle.Hidden;
|
public override PanelDisplayStyle PlayersDisplayStyle => PanelDisplayStyle.Hidden;
|
||||||
public override Drawable? PlayersDisplayArea => null;
|
public override Drawable? PlayersDisplayArea => null;
|
||||||
|
|
||||||
@@ -51,7 +50,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.RoundResults
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private RulesetStore rulesets { get; set; } = null!;
|
private RulesetStore rulesets { get; set; } = null!;
|
||||||
|
|
||||||
private AutoScrollContainer scrollContainer = null!;
|
private PanelContainer panelContainer = null!;
|
||||||
private LoadingSpinner loadingSpinner = null!;
|
private LoadingSpinner loadingSpinner = null!;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@@ -59,7 +58,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.RoundResults
|
|||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
scrollContainer = new AutoScrollContainer
|
panelContainer = new PanelContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
},
|
},
|
||||||
@@ -136,78 +135,57 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.RoundResults
|
|||||||
|
|
||||||
private void setScores(ScoreInfo[] scores) => Scheduler.Add(() =>
|
private void setScores(ScoreInfo[] scores) => Scheduler.Add(() =>
|
||||||
{
|
{
|
||||||
Container panels;
|
panelContainer.ChildrenEnumerable = scores.Select(s => new RoundResultsScorePanel(s)
|
||||||
|
|
||||||
scrollContainer.Child = panels = new Container
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Y,
|
Anchor = Anchor.Centre,
|
||||||
Width = scores.Length * (ScorePanel.CONTRACTED_WIDTH + panel_spacing),
|
Origin = Anchor.Centre
|
||||||
ChildrenEnumerable = scores.Select(s => new RoundResultsScorePanel(s)
|
});
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; i < panels.Count; i++)
|
|
||||||
{
|
|
||||||
panels[i].MoveToX(panels.DrawWidth * 2)
|
|
||||||
.Delay(i * 100)
|
|
||||||
.MoveToX((ScorePanel.CONTRACTED_WIDTH + panel_spacing) * i, 500, Easing.OutQuint);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
private partial class RoundResultsScorePanel : CompositeDrawable
|
private partial class RoundResultsScorePanel : CompositeDrawable
|
||||||
{
|
{
|
||||||
public RoundResultsScorePanel(ScoreInfo score)
|
public RoundResultsScorePanel(ScoreInfo score)
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both;
|
Size = new Vector2(ScorePanel.CONTRACTED_WIDTH, ScorePanel.CONTRACTED_HEIGHT);
|
||||||
InternalChild = new InstantSizingScorePanel(score);
|
|
||||||
|
InternalChild = new ScorePanel(score);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool PropagateNonPositionalInputSubTree => false;
|
public override bool PropagateNonPositionalInputSubTree => false;
|
||||||
public override bool PropagatePositionalInputSubTree => false;
|
public override bool PropagatePositionalInputSubTree => false;
|
||||||
|
|
||||||
private partial class InstantSizingScorePanel : ScorePanel
|
|
||||||
{
|
|
||||||
public InstantSizingScorePanel(ScoreInfo score, bool isNewLocalScore = false)
|
|
||||||
: base(score, isNewLocalScore)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
FinishTransforms(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class AutoScrollContainer : UserTrackingScrollContainer
|
private partial class PanelContainer : Container<RoundResultsScorePanel>
|
||||||
{
|
{
|
||||||
private const float initial_offset = -0.5f;
|
protected override Container<RoundResultsScorePanel> Content => flowContainer;
|
||||||
private const double scroll_duration = 20000;
|
|
||||||
|
|
||||||
private double? scrollStartTime;
|
private readonly Container centreingContainer;
|
||||||
|
private readonly Container<RoundResultsScorePanel> flowContainer;
|
||||||
|
|
||||||
public AutoScrollContainer()
|
public PanelContainer()
|
||||||
: base(Direction.Horizontal)
|
|
||||||
{
|
{
|
||||||
|
InternalChild = new OsuScrollContainer(Direction.Horizontal)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = centreingContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Child = flowContainer = new FillFlowContainer<RoundResultsScorePanel>
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
Spacing = new Vector2(5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
centreingContainer.Width = Math.Max(DrawWidth, flowContainer.DrawWidth);
|
||||||
if (!UserScrolling && Children.Count > 0)
|
|
||||||
{
|
|
||||||
scrollStartTime ??= Time.Current;
|
|
||||||
|
|
||||||
double scrollOffset = (Time.Current - scrollStartTime.Value) / scroll_duration;
|
|
||||||
|
|
||||||
if (scrollOffset < 1)
|
|
||||||
ScrollTo(DrawWidth * (initial_offset + scrollOffset), false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
|
|
||||||
private void onUserStateChanged(MultiplayerRoomUser user, MultiplayerUserState state) => Schedule(updateCount);
|
private void onUserStateChanged(MultiplayerRoomUser user, MultiplayerUserState state) => Schedule(updateCount);
|
||||||
|
|
||||||
private void onUserVotedToSkipIntro(int userId) => Schedule(() =>
|
private void onUserVotedToSkipIntro(int userId, bool voted) => Schedule(() =>
|
||||||
{
|
{
|
||||||
FadingContent.TriggerShow();
|
FadingContent.TriggerShow();
|
||||||
updateCount();
|
updateCount();
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ namespace osu.Game.Screens.Ranking
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Height of the panel when contracted.
|
/// Height of the panel when contracted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float contracted_height = 385;
|
public const float CONTRACTED_HEIGHT = 385;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Width of the panel when expanded.
|
/// Width of the panel when expanded.
|
||||||
@@ -280,7 +280,7 @@ namespace osu.Game.Screens.Ranking
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PanelState.Contracted:
|
case PanelState.Contracted:
|
||||||
Size = new Vector2(CONTRACTED_WIDTH, contracted_height);
|
Size = new Vector2(CONTRACTED_WIDTH, CONTRACTED_HEIGHT);
|
||||||
|
|
||||||
topLayerBackground.FadeColour(getColour(contracted_top_layer_colour), RESIZE_DURATION, Easing.OutQuint);
|
topLayerBackground.FadeColour(getColour(contracted_top_layer_colour), RESIZE_DURATION, Easing.OutQuint);
|
||||||
middleLayerBackground.FadeColour(getColour(contracted_middle_layer_colour), RESIZE_DURATION, Easing.OutQuint);
|
middleLayerBackground.FadeColour(getColour(contracted_middle_layer_colour), RESIZE_DURATION, Easing.OutQuint);
|
||||||
|
|||||||
@@ -568,7 +568,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
|
|
||||||
public async Task UserVoteToSkipIntro(int userId)
|
public async Task UserVoteToSkipIntro(int userId)
|
||||||
{
|
{
|
||||||
await ((IMultiplayerClient)this).UserVotedToSkipIntro(userId).ConfigureAwait(false);
|
await ((IMultiplayerClient)this).UserVotedToSkipIntro(userId, true).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task<MultiplayerRoom> CreateRoomInternal(MultiplayerRoom room)
|
protected override Task<MultiplayerRoom> CreateRoomInternal(MultiplayerRoom room)
|
||||||
|
|||||||
@@ -2,10 +2,14 @@
|
|||||||
// 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.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@@ -17,6 +21,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Models;
|
using osu.Game.Models;
|
||||||
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
@@ -219,20 +224,35 @@ namespace osu.Game.Utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly HashSet<int> ignored_io_exception_hresults =
|
||||||
|
[
|
||||||
|
// see https://stackoverflow.com/a/9294382 for how these are synthesised
|
||||||
|
unchecked((int)0x80070020), // ERROR_SHARING_VIOLATION
|
||||||
|
unchecked((int)0x80070027), // ERROR_HANDLE_DISK_FULL
|
||||||
|
unchecked((int)0x80070070), // ERROR_DISK_FULL
|
||||||
|
];
|
||||||
|
|
||||||
private bool shouldSubmitException(Exception exception)
|
private bool shouldSubmitException(Exception exception)
|
||||||
{
|
{
|
||||||
switch (exception)
|
switch (exception)
|
||||||
{
|
{
|
||||||
case IOException ioe:
|
// disk I/O failures, invalid formats, etc.
|
||||||
// disk full exceptions, see https://stackoverflow.com/a/9294382
|
|
||||||
const int hr_error_handle_disk_full = unchecked((int)0x80070027);
|
|
||||||
const int hr_error_disk_full = unchecked((int)0x80070070);
|
|
||||||
|
|
||||||
if (ioe.HResult == hr_error_handle_disk_full || ioe.HResult == hr_error_disk_full)
|
case IOException ioe:
|
||||||
|
if (ignored_io_exception_hresults.Contains(ioe.HResult))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case UnauthorizedAccessException:
|
||||||
|
case SharpCompress.Common.InvalidFormatException:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// connectivity failures
|
||||||
|
|
||||||
|
case TimeoutException te:
|
||||||
|
return !te.Message.Contains(@"elapsed without receiving a message from the server");
|
||||||
|
|
||||||
case WebException we:
|
case WebException we:
|
||||||
switch (we.Status)
|
switch (we.Status)
|
||||||
{
|
{
|
||||||
@@ -242,6 +262,16 @@ namespace osu.Game.Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WebSocketException:
|
||||||
|
case SocketException:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// stuff that should really never make it to sentry
|
||||||
|
|
||||||
|
case APIAccess.WebRequestFlushedException:
|
||||||
|
case TaskCanceledException:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="20.1.0" />
|
<PackageReference Include="Realm" Version="20.1.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2025.1205.1" />
|
<PackageReference Include="ppy.osu.Framework" Version="2025.1209.0" />
|
||||||
<PackageReference Include="jvnkosu.Resources" Version="2025.1119.0" />
|
<PackageReference Include="jvnkosu.Resources" Version="2025.1119.0" />
|
||||||
<PackageReference Include="Sentry" Version="5.1.1" />
|
<PackageReference Include="Sentry" Version="5.1.1" />
|
||||||
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
|
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
|
||||||
|
|||||||
@@ -17,6 +17,6 @@
|
|||||||
<MtouchInterpreter>-all</MtouchInterpreter>
|
<MtouchInterpreter>-all</MtouchInterpreter>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2025.1205.1" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2025.1209.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user