1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-17 21:13:01 +08:00

Merge pull request #35923 from smoogipoo/qp-abandoned-at

Consider abandon time for user placements
This commit is contained in:
Dean Herbert
2025-12-08 19:27:56 +09:00
committed by GitHub
Unverified
3 changed files with 72 additions and 26 deletions
@@ -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 NUnit.Framework;
using osu.Game.Online.API.Requests.Responses;
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(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);
}
}
}
@@ -36,5 +36,11 @@ namespace osu.Game.Online.Multiplayer.MatchTypes.Matchmaking
/// </summary>
[Key(3)]
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(y);
// X appears earlier in the list if it has more points.
if (x.Points > y.Points)
return -1;
int compare = compareAbandonedAt(x, y);
if (compare != 0)
return compare;
// Y appears earlier in the list if it has more points.
if (y.Points > x.Points)
return 1;
compare = comparePoints(x, y);
if (compare != 0)
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++)
{
MatchmakingRound? xRound;
x.Rounds.RoundsDictionary.TryGetValue(r, out xRound);
x.Rounds.RoundsDictionary.TryGetValue(r, out var xRound);
y.Rounds.RoundsDictionary.TryGetValue(r, out var yRound);
MatchmakingRound? yRound;
y.Rounds.RoundsDictionary.TryGetValue(r, out yRound);
int xPlacement = xRound?.Placement ?? int.MaxValue;
int yPlacement = yRound?.Placement ?? int.MaxValue;
// Nothing to do if both players haven't played this round.
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);
int compare = xPlacement.CompareTo(yPlacement);
if (compare != 0)
return compare;
}
// Tiebreaker 2 (unlikely): User ID.
return 0;
}
private int compareUserIds(MatchmakingUser x, MatchmakingUser y)
{
return x.UserId.CompareTo(y.UserId);
}
}