mirror of
https://github.com/ppy/osu.git
synced 2026-05-23 10:20:07 +08:00
Merge branch 'master' into show-hud-while-editing-skin-layout
This commit is contained in:
@@ -29,17 +29,17 @@ namespace osu.Game.Tests.Online.Matchmaking
|
||||
new SoloScoreInfo { UserID = 3, TotalScore = 750 },
|
||||
], placement_points);
|
||||
|
||||
Assert.AreEqual(8, state.Users[1].Points);
|
||||
Assert.AreEqual(1, state.Users[1].Placement);
|
||||
Assert.AreEqual(1, state.Users[1].Rounds[1].Placement);
|
||||
Assert.AreEqual(8, state.Users.GetOrAdd(1).Points);
|
||||
Assert.AreEqual(1, state.Users.GetOrAdd(1).Placement);
|
||||
Assert.AreEqual(1, state.Users.GetOrAdd(1).Rounds.GetOrAdd(1).Placement);
|
||||
|
||||
Assert.AreEqual(6, state.Users[2].Points);
|
||||
Assert.AreEqual(3, state.Users[2].Placement);
|
||||
Assert.AreEqual(3, state.Users[2].Rounds[1].Placement);
|
||||
Assert.AreEqual(6, state.Users.GetOrAdd(2).Points);
|
||||
Assert.AreEqual(3, state.Users.GetOrAdd(2).Placement);
|
||||
Assert.AreEqual(3, state.Users.GetOrAdd(2).Rounds.GetOrAdd(1).Placement);
|
||||
|
||||
Assert.AreEqual(7, state.Users[3].Points);
|
||||
Assert.AreEqual(2, state.Users[3].Placement);
|
||||
Assert.AreEqual(2, state.Users[3].Rounds[1].Placement);
|
||||
Assert.AreEqual(7, state.Users.GetOrAdd(3).Points);
|
||||
Assert.AreEqual(2, state.Users.GetOrAdd(3).Placement);
|
||||
Assert.AreEqual(2, state.Users.GetOrAdd(3).Rounds.GetOrAdd(1).Placement);
|
||||
|
||||
// 2 -> 1 -> 3
|
||||
|
||||
@@ -51,17 +51,17 @@ namespace osu.Game.Tests.Online.Matchmaking
|
||||
new SoloScoreInfo { UserID = 3, TotalScore = 500 },
|
||||
], placement_points);
|
||||
|
||||
Assert.AreEqual(15, state.Users[1].Points);
|
||||
Assert.AreEqual(1, state.Users[1].Placement);
|
||||
Assert.AreEqual(2, state.Users[1].Rounds[2].Placement);
|
||||
Assert.AreEqual(15, state.Users.GetOrAdd(1).Points);
|
||||
Assert.AreEqual(1, state.Users.GetOrAdd(1).Placement);
|
||||
Assert.AreEqual(2, state.Users.GetOrAdd(1).Rounds.GetOrAdd(2).Placement);
|
||||
|
||||
Assert.AreEqual(14, state.Users[2].Points);
|
||||
Assert.AreEqual(2, state.Users[2].Placement);
|
||||
Assert.AreEqual(1, state.Users[2].Rounds[2].Placement);
|
||||
Assert.AreEqual(14, state.Users.GetOrAdd(2).Points);
|
||||
Assert.AreEqual(2, state.Users.GetOrAdd(2).Placement);
|
||||
Assert.AreEqual(1, state.Users.GetOrAdd(2).Rounds.GetOrAdd(2).Placement);
|
||||
|
||||
Assert.AreEqual(13, state.Users[3].Points);
|
||||
Assert.AreEqual(3, state.Users[3].Placement);
|
||||
Assert.AreEqual(3, state.Users[3].Rounds[2].Placement);
|
||||
Assert.AreEqual(13, state.Users.GetOrAdd(3).Points);
|
||||
Assert.AreEqual(3, state.Users.GetOrAdd(3).Placement);
|
||||
Assert.AreEqual(3, state.Users.GetOrAdd(3).Rounds.GetOrAdd(2).Placement);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -80,21 +80,21 @@ namespace osu.Game.Tests.Online.Matchmaking
|
||||
new SoloScoreInfo { UserID = 4, TotalScore = 500 },
|
||||
], placement_points);
|
||||
|
||||
Assert.AreEqual(7, state.Users[1].Points);
|
||||
Assert.AreEqual(1, state.Users[1].Placement);
|
||||
Assert.AreEqual(2, state.Users[1].Rounds[1].Placement);
|
||||
Assert.AreEqual(7, state.Users.GetOrAdd(1).Points);
|
||||
Assert.AreEqual(1, state.Users.GetOrAdd(1).Placement);
|
||||
Assert.AreEqual(2, state.Users.GetOrAdd(1).Rounds.GetOrAdd(1).Placement);
|
||||
|
||||
Assert.AreEqual(7, state.Users[2].Points);
|
||||
Assert.AreEqual(2, state.Users[2].Placement);
|
||||
Assert.AreEqual(2, state.Users[2].Rounds[1].Placement);
|
||||
Assert.AreEqual(7, state.Users.GetOrAdd(2).Points);
|
||||
Assert.AreEqual(2, state.Users.GetOrAdd(2).Placement);
|
||||
Assert.AreEqual(2, state.Users.GetOrAdd(2).Rounds.GetOrAdd(1).Placement);
|
||||
|
||||
Assert.AreEqual(5, state.Users[3].Points);
|
||||
Assert.AreEqual(3, state.Users[3].Placement);
|
||||
Assert.AreEqual(4, state.Users[3].Rounds[1].Placement);
|
||||
Assert.AreEqual(5, state.Users.GetOrAdd(3).Points);
|
||||
Assert.AreEqual(3, state.Users.GetOrAdd(3).Placement);
|
||||
Assert.AreEqual(4, state.Users.GetOrAdd(3).Rounds.GetOrAdd(1).Placement);
|
||||
|
||||
Assert.AreEqual(5, state.Users[4].Points);
|
||||
Assert.AreEqual(4, state.Users[4].Placement);
|
||||
Assert.AreEqual(4, state.Users[4].Rounds[1].Placement);
|
||||
Assert.AreEqual(5, state.Users.GetOrAdd(4).Points);
|
||||
Assert.AreEqual(4, state.Users.GetOrAdd(4).Placement);
|
||||
Assert.AreEqual(4, state.Users.GetOrAdd(4).Rounds.GetOrAdd(1).Placement);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -120,8 +120,8 @@ namespace osu.Game.Tests.Online.Matchmaking
|
||||
new SoloScoreInfo { UserID = 2, TotalScore = 1000 },
|
||||
], placement_points);
|
||||
|
||||
Assert.AreEqual(1, state.Users[1].Placement);
|
||||
Assert.AreEqual(2, state.Users[2].Placement);
|
||||
Assert.AreEqual(1, state.Users.GetOrAdd(1).Placement);
|
||||
Assert.AreEqual(2, state.Users.GetOrAdd(2).Placement);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -142,12 +142,12 @@ namespace osu.Game.Tests.Online.Matchmaking
|
||||
new SoloScoreInfo { UserID = 5, TotalScore = 1000 },
|
||||
], placement_points);
|
||||
|
||||
Assert.AreEqual(1, state.Users[1].Placement);
|
||||
Assert.AreEqual(2, state.Users[2].Placement);
|
||||
Assert.AreEqual(3, state.Users[3].Placement);
|
||||
Assert.AreEqual(4, state.Users[4].Placement);
|
||||
Assert.AreEqual(5, state.Users[5].Placement);
|
||||
Assert.AreEqual(6, state.Users[6].Placement);
|
||||
Assert.AreEqual(1, state.Users.GetOrAdd(1).Placement);
|
||||
Assert.AreEqual(2, state.Users.GetOrAdd(2).Placement);
|
||||
Assert.AreEqual(3, state.Users.GetOrAdd(3).Placement);
|
||||
Assert.AreEqual(4, state.Users.GetOrAdd(4).Placement);
|
||||
Assert.AreEqual(5, state.Users.GetOrAdd(5).Placement);
|
||||
Assert.AreEqual(6, state.Users.GetOrAdd(6).Placement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||
|
||||
protected override Drawable CreateDefaultImplementation() => new ArgonKeyCounterDisplay();
|
||||
protected override Drawable CreateArgonImplementation() => new ArgonKeyCounterDisplay();
|
||||
|
||||
protected override Drawable CreateDefaultImplementation() => new DefaultKeyCounterDisplay();
|
||||
|
||||
protected override Drawable CreateLegacyImplementation() => new LegacyKeyCounterDisplay();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
// 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.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
@@ -62,5 +64,41 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
||||
panel.AllowSelection = value;
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFailedBeatmapLookup()
|
||||
{
|
||||
AddStep("setup request handle", () =>
|
||||
{
|
||||
var api = (DummyAPIAccess)API;
|
||||
var handler = api.HandleRequest;
|
||||
api.HandleRequest = req =>
|
||||
{
|
||||
switch (req)
|
||||
{
|
||||
case GetBeatmapRequest:
|
||||
case GetBeatmapsRequest:
|
||||
req.TriggerFailure(new InvalidOperationException());
|
||||
return false;
|
||||
|
||||
default:
|
||||
return handler?.Invoke(req) ?? false;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("add panel", () =>
|
||||
{
|
||||
Child = new OsuContextMenuContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new BeatmapSelectPanel(new MultiplayerPlaylistItem())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,11 +124,11 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
||||
|
||||
foreach (var user in MultiplayerClient.ServerRoom!.Users.OrderBy(_ => RNG.Next()))
|
||||
{
|
||||
state.Users[user.UserID].Placement = i++;
|
||||
state.Users[user.UserID].Points = (8 - i) * 7;
|
||||
state.Users[user.UserID].Rounds[1].Placement = 1;
|
||||
state.Users[user.UserID].Rounds[1].TotalScore = 1;
|
||||
state.Users[user.UserID].Rounds[1].Statistics[HitResult.LargeBonus] = 1;
|
||||
state.Users.GetOrAdd(user.UserID).Placement = i++;
|
||||
state.Users.GetOrAdd(user.UserID).Points = (8 - i) * 7;
|
||||
state.Users.GetOrAdd(user.UserID).Rounds.GetOrAdd(1).Placement = 1;
|
||||
state.Users.GetOrAdd(user.UserID).Rounds.GetOrAdd(1).TotalScore = 1;
|
||||
state.Users.GetOrAdd(user.UserID).Rounds.GetOrAdd(1).Statistics[HitResult.LargeBonus] = 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
||||
MatchmakingRoomState state = new MatchmakingRoomState();
|
||||
|
||||
for (int i = 0; i < room.Users.Count; i++)
|
||||
state.Users[room.Users[i].UserID].Placement = placements[i];
|
||||
state.Users.GetOrAdd(room.Users[i].UserID).Placement = placements[i];
|
||||
|
||||
MultiplayerClient.ChangeMatchRoomState(state).WaitSafely();
|
||||
});
|
||||
|
||||
@@ -36,28 +36,28 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
||||
int localUserId = API.LocalUser.Value.OnlineID;
|
||||
|
||||
// Overall state.
|
||||
state.Users[localUserId].Placement = 1;
|
||||
state.Users[localUserId].Points = 8;
|
||||
state.Users.GetOrAdd(localUserId).Placement = 1;
|
||||
state.Users.GetOrAdd(localUserId).Points = 8;
|
||||
for (int round = 1; round <= state.CurrentRound; round++)
|
||||
state.Users[localUserId].Rounds[round].Placement = round;
|
||||
state.Users.GetOrAdd(localUserId).Rounds.GetOrAdd(round).Placement = round;
|
||||
|
||||
// Highest score.
|
||||
state.Users[localUserId].Rounds[1].TotalScore = 1000;
|
||||
state.Users.GetOrAdd(localUserId).Rounds.GetOrAdd(1).TotalScore = 1000;
|
||||
|
||||
// Highest accuracy.
|
||||
state.Users[localUserId].Rounds[2].Accuracy = 0.9995;
|
||||
state.Users.GetOrAdd(localUserId).Rounds.GetOrAdd(2).Accuracy = 0.9995;
|
||||
|
||||
// Highest combo.
|
||||
state.Users[localUserId].Rounds[3].MaxCombo = 100;
|
||||
state.Users.GetOrAdd(localUserId).Rounds.GetOrAdd(3).MaxCombo = 100;
|
||||
|
||||
// Most bonus score.
|
||||
state.Users[localUserId].Rounds[4].Statistics[HitResult.LargeBonus] = 50;
|
||||
state.Users.GetOrAdd(localUserId).Rounds.GetOrAdd(4).Statistics[HitResult.LargeBonus] = 50;
|
||||
|
||||
// Smallest score difference.
|
||||
state.Users[localUserId].Rounds[5].TotalScore = 1000;
|
||||
state.Users.GetOrAdd(localUserId).Rounds.GetOrAdd(5).TotalScore = 1000;
|
||||
|
||||
// Largest score difference.
|
||||
state.Users[localUserId].Rounds[6].TotalScore = 1000;
|
||||
state.Users.GetOrAdd(localUserId).Rounds.GetOrAdd(6).TotalScore = 1000;
|
||||
|
||||
MultiplayerClient.ChangeMatchRoomState(state).WaitSafely();
|
||||
});
|
||||
@@ -103,36 +103,51 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
||||
int localUserId = API.LocalUser.Value.OnlineID;
|
||||
|
||||
// Overall state.
|
||||
state.Users[localUserId].Placement = 1;
|
||||
state.Users[localUserId].Points = 8;
|
||||
state.Users[invalid_user_id].Placement = 2;
|
||||
state.Users[invalid_user_id].Points = 7;
|
||||
state.Users.GetOrAdd(localUserId).Placement = 1;
|
||||
state.Users.GetOrAdd(localUserId).Points = 8;
|
||||
state.Users.GetOrAdd(invalid_user_id).Placement = 2;
|
||||
state.Users.GetOrAdd(invalid_user_id).Points = 7;
|
||||
for (int round = 1; round <= state.CurrentRound; round++)
|
||||
state.Users[localUserId].Rounds[round].Placement = round;
|
||||
state.Users.GetOrAdd(localUserId).Rounds.GetOrAdd(round).Placement = round;
|
||||
|
||||
// Highest score.
|
||||
state.Users[localUserId].Rounds[1].TotalScore = 1000;
|
||||
state.Users[invalid_user_id].Rounds[1].TotalScore = 990;
|
||||
state.Users.GetOrAdd(localUserId).Rounds.GetOrAdd(1).TotalScore = 1000;
|
||||
state.Users.GetOrAdd(invalid_user_id).Rounds.GetOrAdd(1).TotalScore = 990;
|
||||
|
||||
// Highest accuracy.
|
||||
state.Users[localUserId].Rounds[2].Accuracy = 0.9995;
|
||||
state.Users[invalid_user_id].Rounds[2].Accuracy = 0.5;
|
||||
state.Users.GetOrAdd(localUserId).Rounds.GetOrAdd(2).Accuracy = 0.9995;
|
||||
state.Users.GetOrAdd(invalid_user_id).Rounds.GetOrAdd(2).Accuracy = 0.5;
|
||||
|
||||
// Highest combo.
|
||||
state.Users[localUserId].Rounds[3].MaxCombo = 100;
|
||||
state.Users[invalid_user_id].Rounds[3].MaxCombo = 10;
|
||||
state.Users.GetOrAdd(localUserId).Rounds.GetOrAdd(3).MaxCombo = 100;
|
||||
state.Users.GetOrAdd(invalid_user_id).Rounds.GetOrAdd(3).MaxCombo = 10;
|
||||
|
||||
// Most bonus score.
|
||||
state.Users[localUserId].Rounds[4].Statistics[HitResult.LargeBonus] = 50;
|
||||
state.Users[invalid_user_id].Rounds[4].Statistics[HitResult.LargeBonus] = 25;
|
||||
state.Users.GetOrAdd(localUserId).Rounds.GetOrAdd(4).Statistics[HitResult.LargeBonus] = 50;
|
||||
state.Users.GetOrAdd(invalid_user_id).Rounds.GetOrAdd(4).Statistics[HitResult.LargeBonus] = 25;
|
||||
|
||||
// Smallest score difference.
|
||||
state.Users[localUserId].Rounds[5].TotalScore = 1000;
|
||||
state.Users[invalid_user_id].Rounds[5].TotalScore = 999;
|
||||
state.Users.GetOrAdd(localUserId).Rounds.GetOrAdd(5).TotalScore = 1000;
|
||||
state.Users.GetOrAdd(invalid_user_id).Rounds.GetOrAdd(5).TotalScore = 999;
|
||||
|
||||
// Largest score difference.
|
||||
state.Users[localUserId].Rounds[6].TotalScore = 1000;
|
||||
state.Users[invalid_user_id].Rounds[6].TotalScore = 0;
|
||||
state.Users.GetOrAdd(localUserId).Rounds.GetOrAdd(6).TotalScore = 1000;
|
||||
state.Users.GetOrAdd(invalid_user_id).Rounds.GetOrAdd(6).TotalScore = 0;
|
||||
|
||||
MultiplayerClient.ChangeMatchRoomState(state).WaitSafely();
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNoUsers()
|
||||
{
|
||||
AddStep("show results with no users", () =>
|
||||
{
|
||||
var state = new MatchmakingRoomState
|
||||
{
|
||||
CurrentRound = 6,
|
||||
Stage = MatchmakingStage.Ended
|
||||
};
|
||||
|
||||
MultiplayerClient.ChangeMatchRoomState(state).WaitSafely();
|
||||
});
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
@@ -13,25 +16,35 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
public partial class TestSceneDrawableDate : OsuTestScene
|
||||
{
|
||||
public TestSceneDrawableDate()
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
Child = new FillFlowContainer
|
||||
AddStep("Create 7 dates", () =>
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
new PokeyDrawableDate(DateTimeOffset.Now.Subtract(TimeSpan.FromSeconds(60))),
|
||||
new PokeyDrawableDate(DateTimeOffset.Now.Subtract(TimeSpan.FromSeconds(55))),
|
||||
new PokeyDrawableDate(DateTimeOffset.Now.Subtract(TimeSpan.FromSeconds(50))),
|
||||
new PokeyDrawableDate(DateTimeOffset.Now),
|
||||
new PokeyDrawableDate(DateTimeOffset.Now.Add(TimeSpan.FromSeconds(60))),
|
||||
new PokeyDrawableDate(DateTimeOffset.Now.Add(TimeSpan.FromSeconds(65))),
|
||||
new PokeyDrawableDate(DateTimeOffset.Now.Add(TimeSpan.FromSeconds(70))),
|
||||
}
|
||||
};
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new PokeyDrawableDate(DateTimeOffset.Now.Subtract(TimeSpan.FromSeconds(60))),
|
||||
new PokeyDrawableDate(DateTimeOffset.Now.Subtract(TimeSpan.FromSeconds(55))),
|
||||
new PokeyDrawableDate(DateTimeOffset.Now.Subtract(TimeSpan.FromSeconds(50))),
|
||||
new PokeyDrawableDate(DateTimeOffset.Now),
|
||||
new PokeyDrawableDate(DateTimeOffset.Now.Add(TimeSpan.FromSeconds(60))),
|
||||
new PokeyDrawableDate(DateTimeOffset.Now.Add(TimeSpan.FromSeconds(65))),
|
||||
new PokeyDrawableDate(DateTimeOffset.Now.Add(TimeSpan.FromSeconds(70))),
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSecondsUpdate()
|
||||
{
|
||||
AddUntilStep("4th date says \"2 seconds ago\"", () => this.ChildrenOfType<DrawableDate>().ElementAt(3).Current.Value == "2 seconds ago");
|
||||
}
|
||||
|
||||
private partial class PokeyDrawableDate : CompositeDrawable
|
||||
|
||||
@@ -5,6 +5,7 @@ using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Utils;
|
||||
@@ -80,7 +81,7 @@ namespace osu.Game.Graphics
|
||||
|
||||
public DateTimeOffset TooltipContent => Date;
|
||||
|
||||
private class HumanisedDate : IEquatable<HumanisedDate>, ILocalisableStringData
|
||||
private class HumanisedDate : ILocalisableStringData
|
||||
{
|
||||
public readonly DateTimeOffset Date;
|
||||
|
||||
@@ -89,11 +90,18 @@ namespace osu.Game.Graphics
|
||||
Date = date;
|
||||
}
|
||||
|
||||
public bool Equals(HumanisedDate? other)
|
||||
=> other?.Date != null && Date.Equals(other.Date);
|
||||
|
||||
public bool Equals(ILocalisableStringData? other)
|
||||
=> other is HumanisedDate otherDate && Equals(otherDate);
|
||||
/// <remarks>
|
||||
/// Humanizer formats the <see cref="Date"/> relative to the local computer time.
|
||||
/// Therefore, replacing a <see cref="HumanisedDate"/> instance with another instance of the class with the same <see cref="Date"/>
|
||||
/// should have the effect of replacing and re-formatting the text.
|
||||
/// Including <see cref="Date"/> in equality members would stop this from happening, as <see cref="SpriteText.Text"/>
|
||||
/// has equality-based early guards to prevent redundant text replaces.
|
||||
/// Thus, instances of these class just compare <see langword="false"/> to any <see cref="ILocalisableStringData"/> to ensure re-formatting happens correctly.
|
||||
/// There are "technically" more "correct" ways to do this (like also including the current time into equality checks),
|
||||
/// but they are simultaneously functionally equivalent to this and overly convoluted.
|
||||
/// This is a private hack-job of a wrapper around humanizer anyway.
|
||||
/// </remarks>
|
||||
public bool Equals(ILocalisableStringData? other) => false;
|
||||
|
||||
public string GetLocalised(LocalisationParameters parameters) => HumanizerUtils.Humanize(Date);
|
||||
|
||||
|
||||
@@ -89,13 +89,13 @@ namespace osu.Game.Online.Metadata
|
||||
userStatus.BindValueChanged(status =>
|
||||
{
|
||||
if (localUser.Value is not GuestUser)
|
||||
UpdateStatus(status.NewValue);
|
||||
UpdateStatus(status.NewValue).FireAndForget();
|
||||
}, true);
|
||||
|
||||
userActivity.BindValueChanged(activity =>
|
||||
{
|
||||
if (localUser.Value is not GuestUser)
|
||||
UpdateActivity(activity.NewValue);
|
||||
UpdateActivity(activity.NewValue).FireAndForget();
|
||||
}, true);
|
||||
}
|
||||
|
||||
@@ -121,8 +121,8 @@ namespace osu.Game.Online.Metadata
|
||||
|
||||
if (localUser.Value is not GuestUser)
|
||||
{
|
||||
UpdateActivity(userActivity.Value);
|
||||
UpdateStatus(userStatus.Value);
|
||||
UpdateActivity(userActivity.Value).FireAndForget();
|
||||
UpdateStatus(userStatus.Value).FireAndForget();
|
||||
}
|
||||
|
||||
if (lastQueueId.Value >= 0)
|
||||
|
||||
@@ -81,10 +81,10 @@ namespace osu.Game.Online.Multiplayer.MatchTypes.Matchmaking
|
||||
|
||||
foreach (var score in scoreGroup)
|
||||
{
|
||||
MatchmakingUser mmUser = Users[score.UserID];
|
||||
MatchmakingUser mmUser = Users.GetOrAdd(score.UserID);
|
||||
mmUser.Points += placementPoints[placement - 1];
|
||||
|
||||
MatchmakingRound mmRound = mmUser.Rounds[CurrentRound];
|
||||
MatchmakingRound mmRound = mmUser.Rounds.GetOrAdd(CurrentRound);
|
||||
mmRound.Placement = placement;
|
||||
mmRound.TotalScore = score.TotalScore;
|
||||
mmRound.Accuracy = score.Accuracy;
|
||||
|
||||
@@ -21,27 +21,24 @@ namespace osu.Game.Online.Multiplayer.MatchTypes.Matchmaking
|
||||
[Key(0)]
|
||||
public IDictionary<int, MatchmakingRound> RoundsDictionary { get; set; } = new Dictionary<int, MatchmakingRound>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates or retrieves the score for the given round.
|
||||
/// </summary>
|
||||
/// <param name="round">The round.</param>
|
||||
public MatchmakingRound this[int round]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (RoundsDictionary.TryGetValue(round, out MatchmakingRound? score))
|
||||
return score;
|
||||
|
||||
return RoundsDictionary[round] = new MatchmakingRound { Round = round };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The total number of rounds.
|
||||
/// </summary>
|
||||
[IgnoreMember]
|
||||
public int Count => RoundsDictionary.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves or adds a <see cref="MatchmakingRound"/> entry to this list.
|
||||
/// </summary>
|
||||
/// <param name="round">The round.</param>
|
||||
public MatchmakingRound GetOrAdd(int round)
|
||||
{
|
||||
if (RoundsDictionary.TryGetValue(round, out MatchmakingRound? score))
|
||||
return score;
|
||||
|
||||
return RoundsDictionary[round] = new MatchmakingRound { Round = round };
|
||||
}
|
||||
|
||||
public IEnumerator<MatchmakingRound> GetEnumerator() => RoundsDictionary.Values.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace osu.Game.Online.Multiplayer.MatchTypes.Matchmaking
|
||||
/// The aggregate room placement (1-based).
|
||||
/// </summary>
|
||||
[Key(1)]
|
||||
public int Placement { get; set; }
|
||||
public int? Placement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The aggregate points.
|
||||
|
||||
@@ -21,27 +21,24 @@ namespace osu.Game.Online.Multiplayer.MatchTypes.Matchmaking
|
||||
[Key(0)]
|
||||
public IDictionary<int, MatchmakingUser> UserDictionary { get; set; } = new Dictionary<int, MatchmakingUser>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates or retrieves the user for the given id.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user id.</param>
|
||||
public MatchmakingUser this[int userId]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (UserDictionary.TryGetValue(userId, out MatchmakingUser? user))
|
||||
return user;
|
||||
|
||||
return UserDictionary[userId] = new MatchmakingUser { UserId = userId };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The total number of users.
|
||||
/// </summary>
|
||||
[IgnoreMember]
|
||||
public int Count => UserDictionary.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves or adds a <see cref="MatchmakingUser"/> entry to this list.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user ID.</param>
|
||||
public MatchmakingUser GetOrAdd(int userId)
|
||||
{
|
||||
if (UserDictionary.TryGetValue(userId, out MatchmakingUser? user))
|
||||
return user;
|
||||
|
||||
return UserDictionary[userId] = new MatchmakingUser { UserId = userId };
|
||||
}
|
||||
|
||||
public IEnumerator<MatchmakingUser> GetEnumerator() => UserDictionary.Values.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
if (!connected.NewValue)
|
||||
{
|
||||
if (Room != null)
|
||||
LeaveRoom();
|
||||
LeaveRoom().FireAndForget();
|
||||
|
||||
MatchmakingQueueLeft?.Invoke();
|
||||
}
|
||||
@@ -560,7 +560,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
return;
|
||||
|
||||
if (user.Equals(LocalUser))
|
||||
LeaveRoom();
|
||||
LeaveRoom().FireAndForget();
|
||||
|
||||
handleUserLeft(user, UserKicked);
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Replays.Legacy;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
@@ -203,7 +204,7 @@ namespace osu.Game.Online.Spectator
|
||||
|
||||
Task IStatefulUserHubClient.DisconnectRequested()
|
||||
{
|
||||
Schedule(() => DisconnectInternal());
|
||||
Schedule(() => DisconnectInternal().FireAndForget());
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -290,7 +291,7 @@ namespace osu.Game.Online.Spectator
|
||||
else
|
||||
currentState.State = SpectatedUserState.Quit;
|
||||
|
||||
EndPlayingInternal(currentState);
|
||||
EndPlayingInternal(currentState).FireAndForget();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -304,7 +305,7 @@ namespace osu.Game.Online.Spectator
|
||||
return;
|
||||
}
|
||||
|
||||
WatchUserInternal(userId);
|
||||
WatchUserInternal(userId).FireAndForget();
|
||||
}
|
||||
|
||||
public void StopWatchingUser(int userId)
|
||||
@@ -321,7 +322,7 @@ namespace osu.Game.Online.Spectator
|
||||
|
||||
watchedUsersRefCounts.Remove(userId);
|
||||
watchedUserStates.Remove(userId);
|
||||
StopWatchingUserInternal(userId);
|
||||
StopWatchingUserInternal(userId).FireAndForget();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -162,16 +162,17 @@ namespace osu.Game.Overlays
|
||||
private int runningDepth;
|
||||
|
||||
private readonly Scheduler postScheduler = new Scheduler();
|
||||
private readonly Scheduler criticalPostScheduler = new Scheduler();
|
||||
|
||||
public override bool IsPresent =>
|
||||
// Delegate presence as we need to consider the toast tray in addition to the main overlay.
|
||||
State.Value == Visibility.Visible || mainContent.IsPresent || toastTray.IsPresent || postScheduler.HasPendingTasks;
|
||||
State.Value == Visibility.Visible || mainContent.IsPresent || toastTray.IsPresent || postScheduler.HasPendingTasks || criticalPostScheduler.HasPendingTasks;
|
||||
|
||||
private bool processingPosts = true;
|
||||
|
||||
private double? lastSamplePlayback;
|
||||
|
||||
public void Post(Notification notification) => postScheduler.Add(() =>
|
||||
public void Post(Notification notification) => (notification.IsCritical ? criticalPostScheduler : postScheduler).Add(() =>
|
||||
{
|
||||
++runningDepth;
|
||||
|
||||
@@ -220,6 +221,8 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
base.Update();
|
||||
|
||||
criticalPostScheduler.Update();
|
||||
|
||||
if (processingPosts)
|
||||
postScheduler.Update();
|
||||
}
|
||||
|
||||
@@ -91,7 +91,12 @@ namespace osu.Game.Overlays
|
||||
public void FlushAllToasts()
|
||||
{
|
||||
foreach (var notification in toastFlow.ToArray())
|
||||
{
|
||||
if (notification.IsCritical)
|
||||
continue;
|
||||
|
||||
forwardNotification(notification);
|
||||
}
|
||||
}
|
||||
|
||||
public void Post(Notification notification)
|
||||
|
||||
@@ -39,6 +39,11 @@ namespace osu.Game.Overlays.Notifications
|
||||
/// </summary>
|
||||
public bool IsImportant { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Critical notifications show even during gameplay or other scenarios where notifications would usually be suppressed.
|
||||
/// </summary>
|
||||
public bool IsCritical { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Transient notifications only show as a toast, and do not linger in notification history.
|
||||
/// </summary>
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace osu.Game.Screens.Footer
|
||||
|
||||
private Box background = null!;
|
||||
private FillFlowContainer<ScreenFooterButton> buttonsFlow = null!;
|
||||
private Container footerContentContainer = null!;
|
||||
private Container overlayContentContainer = null!;
|
||||
private Container<ScreenFooterButton> hiddenButtonsContainer = null!;
|
||||
|
||||
private LogoTrackingContainer logoTrackingContainer = null!;
|
||||
@@ -102,6 +102,7 @@ namespace osu.Game.Screens.Footer
|
||||
{
|
||||
buttonsFlow = new FillFlowContainer<ScreenFooterButton>
|
||||
{
|
||||
Name = "Visible footer buttons",
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Y = ScreenFooterButton.CORNER_RADIUS,
|
||||
@@ -109,8 +110,9 @@ namespace osu.Game.Screens.Footer
|
||||
Spacing = new Vector2(7, 0),
|
||||
AutoSizeAxes = Axes.Both,
|
||||
},
|
||||
footerContentContainer = new Container
|
||||
overlayContentContainer = new Container
|
||||
{
|
||||
Name = "Overlay-provided extra content",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Y = -OsuGame.SCREEN_EDGE_MARGIN,
|
||||
},
|
||||
@@ -126,6 +128,7 @@ namespace osu.Game.Screens.Footer
|
||||
},
|
||||
hiddenButtonsContainer = new Container<ScreenFooterButton>
|
||||
{
|
||||
Name = "Hidden footer buttons",
|
||||
Margin = new MarginPadding { Left = OsuGame.SCREEN_EDGE_MARGIN + ScreenBackButton.BUTTON_WIDTH + padding },
|
||||
Y = ScreenFooterButton.CORNER_RADIUS,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
@@ -234,11 +237,11 @@ namespace osu.Game.Screens.Footer
|
||||
|
||||
public ShearedOverlayContainer? ActiveOverlay { get; private set; }
|
||||
|
||||
private VisibilityContainer? activeFooterContent;
|
||||
private VisibilityContainer? activeOverlayContent;
|
||||
|
||||
private readonly List<ScreenFooterButton> temporarilyHiddenButtons = new List<ScreenFooterButton>();
|
||||
|
||||
public IDisposable RegisterActiveOverlayContainer(ShearedOverlayContainer overlay, out VisibilityContainer? footerContent)
|
||||
public IDisposable RegisterActiveOverlayContainer(ShearedOverlayContainer overlay, out VisibilityContainer? overlayContent)
|
||||
{
|
||||
if (ActiveOverlay != null)
|
||||
{
|
||||
@@ -267,12 +270,12 @@ namespace osu.Game.Screens.Footer
|
||||
|
||||
updateColourScheme(overlay.ColourProvider.Hue);
|
||||
|
||||
footerContent = overlay.CreateFooterContent();
|
||||
activeFooterContent = footerContent;
|
||||
var content = footerContent;
|
||||
overlayContent = overlay.CreateFooterContent();
|
||||
activeOverlayContent = overlayContent;
|
||||
var content = overlayContent;
|
||||
|
||||
if (content != null)
|
||||
footerContentContainer.Child = content;
|
||||
overlayContentContainer.Child = content;
|
||||
|
||||
if (temporarilyHiddenButtons.Count > 0)
|
||||
this.Delay(60).Schedule(() => content?.Show());
|
||||
@@ -287,15 +290,19 @@ namespace osu.Game.Screens.Footer
|
||||
if (ActiveOverlay == null)
|
||||
return;
|
||||
|
||||
Debug.Assert(activeFooterContent != null);
|
||||
activeFooterContent.Hide();
|
||||
Debug.Assert(activeOverlayContent != null);
|
||||
activeOverlayContent.Hide();
|
||||
|
||||
double timeUntilRun = activeFooterContent.LatestTransformEndTime - Time.Current;
|
||||
double timeUntilRun = activeOverlayContent.LatestTransformEndTime - Time.Current;
|
||||
|
||||
for (int i = 0; i < temporarilyHiddenButtons.Count; i++)
|
||||
{
|
||||
var button = temporarilyHiddenButtons[i];
|
||||
hiddenButtonsContainer.Remove(button, false);
|
||||
// temporarily bypass autosize on the X axis to prevent the buttons taking space
|
||||
// immediately upon being moved back to the flow.
|
||||
// this prevents the overlay content jumping to the right during its fade-out.
|
||||
button.BypassAutoSizeAxes = Axes.X;
|
||||
buttonsFlow.Add(button);
|
||||
|
||||
makeButtonAppearFromBottom(button, 0);
|
||||
@@ -305,8 +312,13 @@ namespace osu.Game.Screens.Footer
|
||||
|
||||
updateColourScheme(OverlayColourScheme.Aquamarine.GetHue());
|
||||
|
||||
activeFooterContent.Delay(timeUntilRun).Expire();
|
||||
activeFooterContent = null;
|
||||
activeOverlayContent.Delay(timeUntilRun).Schedule(() =>
|
||||
{
|
||||
// overlay content is done displaying, re-enable autosize on all active buttons
|
||||
foreach (var button in buttonsFlow)
|
||||
button.BypassAutoSizeAxes = Axes.None;
|
||||
}).Expire();
|
||||
activeOverlayContent = null;
|
||||
ActiveOverlay = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,17 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
{
|
||||
Debug.Assert(card == null);
|
||||
|
||||
var beatmap = b.GetResultSafely()!;
|
||||
APIBeatmap beatmap = b.GetResultSafely() ?? new APIBeatmap
|
||||
{
|
||||
BeatmapSet = new APIBeatmapSet
|
||||
{
|
||||
Title = "unknown beatmap",
|
||||
TitleUnicode = "unknown beatmap",
|
||||
Artist = "unknown artist",
|
||||
ArtistUnicode = "unknown artist",
|
||||
}
|
||||
};
|
||||
|
||||
beatmap.StarRating = Item.StarRating;
|
||||
|
||||
mainContent.Add(card = new BeatmapCardMatchmaking(beatmap)
|
||||
|
||||
@@ -414,8 +414,11 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match
|
||||
if (!matchmakingState.Users.UserDictionary.TryGetValue(User.Id, out MatchmakingUser? userScore))
|
||||
return;
|
||||
|
||||
rankText.Text = userScore.Placement.Ordinalize(CultureInfo.CurrentCulture);
|
||||
rankText.FadeColour(SubScreenResults.ColourForPlacement(userScore.Placement));
|
||||
if (userScore.Placement == null)
|
||||
return;
|
||||
|
||||
rankText.Text = userScore.Placement.Value.Ordinalize(CultureInfo.CurrentCulture);
|
||||
rankText.FadeColour(SubScreenResults.ColourForPlacement(userScore.Placement.Value));
|
||||
scoreText.Text = $"{userScore.Points} pts";
|
||||
});
|
||||
|
||||
|
||||
@@ -239,8 +239,8 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match
|
||||
if (client.Room?.MatchState is not MatchmakingRoomState matchmakingState)
|
||||
continue;
|
||||
|
||||
if (matchmakingState.Users.UserDictionary.TryGetValue(panels[i].User.Id, out MatchmakingUser? user))
|
||||
SetLayoutPosition(Children[i], user.Placement);
|
||||
if (matchmakingState.Users.UserDictionary.TryGetValue(panels[i].User.Id, out MatchmakingUser? user) && user.Placement != null)
|
||||
SetLayoutPosition(Children[i], user.Placement.Value);
|
||||
else
|
||||
SetLayoutPosition(Children[i], float.MaxValue);
|
||||
}
|
||||
|
||||
@@ -194,20 +194,25 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.Results
|
||||
{
|
||||
userStatistics.Clear();
|
||||
|
||||
if (state.Users[client.LocalUser!.UserID].Rounds.Count == 0)
|
||||
var localUserState = state.Users.GetOrAdd(client.LocalUser!.UserID);
|
||||
|
||||
if (localUserState.Rounds.Count == 0)
|
||||
{
|
||||
placementText.Text = "-";
|
||||
placementText.Colour = OsuColour.Gray(1f);
|
||||
return;
|
||||
}
|
||||
|
||||
int overallPlacement = state.Users[client.LocalUser!.UserID].Placement;
|
||||
int? overallPlacement = localUserState.Placement;
|
||||
|
||||
placementText.Text = overallPlacement.Ordinalize(CultureInfo.CurrentCulture);
|
||||
placementText.Colour = ColourForPlacement(overallPlacement);
|
||||
if (overallPlacement != null)
|
||||
{
|
||||
placementText.Text = overallPlacement.Value.Ordinalize(CultureInfo.CurrentCulture);
|
||||
placementText.Colour = ColourForPlacement(overallPlacement.Value);
|
||||
|
||||
int overallPoints = state.Users[client.LocalUser!.UserID].Points;
|
||||
addStatistic(overallPlacement, $"Overall position ({overallPoints} points)");
|
||||
int overallPoints = localUserState.Points;
|
||||
addStatistic(overallPlacement.Value, $"Overall position ({overallPoints} points)");
|
||||
}
|
||||
|
||||
var accuracyOrderedUsers = state.Users.Select(u => (user: u, avgAcc: u.Rounds.Select(r => r.Accuracy).DefaultIfEmpty(0).Average()))
|
||||
.OrderByDescending(t => t.avgAcc)
|
||||
@@ -223,7 +228,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.Results
|
||||
int maxComboPlacement = maxComboOrderedUsers.index + 1;
|
||||
addStatistic(maxComboPlacement, $"Best max combo ({maxComboOrderedUsers.info.maxCombo}x)");
|
||||
|
||||
var bestPlacement = state.Users[client.LocalUser!.UserID].Rounds.MinBy(r => r.Placement);
|
||||
var bestPlacement = localUserState.Rounds.MinBy(r => r.Placement);
|
||||
addStatistic(bestPlacement!.Placement, $"Best round placement (round {bestPlacement.Round})");
|
||||
|
||||
void addStatistic(int position, string text) => userStatistics.Add(new PanelUserStatistic(position, text));
|
||||
@@ -255,27 +260,27 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.Results
|
||||
roomAwards.Clear();
|
||||
|
||||
long maxScore = long.MinValue;
|
||||
int maxScoreUserId = 0;
|
||||
int maxScoreUserId = -1;
|
||||
|
||||
double maxAccuracy = double.MinValue;
|
||||
int maxAccuracyUserId = 0;
|
||||
int maxAccuracyUserId = -1;
|
||||
|
||||
int maxCombo = int.MinValue;
|
||||
int maxComboUserId = 0;
|
||||
int maxComboUserId = -1;
|
||||
|
||||
long maxBonusScore = 0;
|
||||
int maxBonusScoreUserId = 0;
|
||||
int maxBonusScoreUserId = -1;
|
||||
|
||||
long largestScoreDifference = long.MinValue;
|
||||
int largestScoreDifferenceUserId = 0;
|
||||
int largestScoreDifferenceUserId = -1;
|
||||
|
||||
long smallestScoreDifference = long.MaxValue;
|
||||
int smallestScoreDifferenceUserId = 0;
|
||||
int smallestScoreDifferenceUserId = -1;
|
||||
|
||||
for (int round = 1; round <= state.CurrentRound; round++)
|
||||
{
|
||||
long roundHighestScore = long.MinValue;
|
||||
int roundHighestScoreUserId = 0;
|
||||
int roundHighestScoreUserId = -1;
|
||||
|
||||
long roundLowestScore = long.MaxValue;
|
||||
|
||||
@@ -344,11 +349,14 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.Results
|
||||
}
|
||||
}
|
||||
|
||||
addAward(maxScoreUserId, "Score champ", "Highest score in a single round");
|
||||
if (maxScoreUserId > 0)
|
||||
addAward(maxScoreUserId, "Score champ", "Highest score in a single round");
|
||||
|
||||
addAward(maxAccuracyUserId, "Most accurate", "Highest accuracy in a single round");
|
||||
if (maxAccuracyUserId > 0)
|
||||
addAward(maxAccuracyUserId, "Most accurate", "Highest accuracy in a single round");
|
||||
|
||||
addAward(maxComboUserId, "Top combo", "Highest combo in a single round");
|
||||
if (maxComboUserId > 0)
|
||||
addAward(maxComboUserId, "Top combo", "Highest combo in a single round");
|
||||
|
||||
if (maxBonusScoreUserId > 0)
|
||||
addAward(maxBonusScoreUserId, "Biggest bonus", "Biggest bonus score across all rounds");
|
||||
|
||||
@@ -392,7 +392,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match
|
||||
return;
|
||||
}
|
||||
|
||||
client.ChangeState(MultiplayerUserState.Idle);
|
||||
client.ChangeState(MultiplayerUserState.Idle).FireAndForget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,10 +2,15 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
@@ -32,11 +37,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue
|
||||
[Resolved]
|
||||
private INotificationOverlay? notifications { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IPerformFromScreenRunner? performer { get; set; }
|
||||
|
||||
private ProgressNotification? backgroundNotification;
|
||||
private Notification? readyNotification;
|
||||
private BackgroundQueueNotification? backgroundNotification;
|
||||
private bool isBackgrounded;
|
||||
|
||||
protected override void LoadComplete()
|
||||
@@ -118,27 +119,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue
|
||||
if (backgroundNotification != null)
|
||||
return;
|
||||
|
||||
notifications?.Post(backgroundNotification = new ProgressNotification
|
||||
{
|
||||
Text = "Searching for opponents...",
|
||||
CompletionTarget = n => notifications.Post(readyNotification = n),
|
||||
CompletionText = "Your match is ready! Click to join.",
|
||||
CompletionClickAction = () =>
|
||||
{
|
||||
client.MatchmakingAcceptInvitation().FireAndForget();
|
||||
performer?.PerformFromScreen(s => s.Push(new IntroScreen()));
|
||||
|
||||
closeNotifications();
|
||||
return true;
|
||||
},
|
||||
CancelRequested = () =>
|
||||
{
|
||||
client.MatchmakingLeaveQueue().FireAndForget();
|
||||
|
||||
closeNotifications();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
notifications?.Post(backgroundNotification = new BackgroundQueueNotification());
|
||||
}
|
||||
|
||||
private void closeNotifications()
|
||||
@@ -146,13 +127,9 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue
|
||||
if (backgroundNotification != null)
|
||||
{
|
||||
backgroundNotification.State = ProgressNotificationState.Cancelled;
|
||||
backgroundNotification.Close(false);
|
||||
backgroundNotification.CloseAll();
|
||||
backgroundNotification = null;
|
||||
}
|
||||
|
||||
readyNotification?.Close(false);
|
||||
|
||||
backgroundNotification = null;
|
||||
readyNotification = null;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
@@ -168,5 +145,78 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue
|
||||
client.MatchmakingRoomReady -= onMatchmakingRoomReady;
|
||||
}
|
||||
}
|
||||
|
||||
private partial class BackgroundQueueNotification : ProgressNotification
|
||||
{
|
||||
[Resolved]
|
||||
private IPerformFromScreenRunner? performer { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private MultiplayerClient client { get; set; } = null!;
|
||||
|
||||
private Notification? foundNotification;
|
||||
private Sample? matchFoundSample;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
{
|
||||
Text = "Searching for opponents...";
|
||||
|
||||
CompletionClickAction = () =>
|
||||
{
|
||||
client.MatchmakingAcceptInvitation().FireAndForget();
|
||||
performer?.PerformFromScreen(s => s.Push(new IntroScreen()));
|
||||
|
||||
Close(false);
|
||||
return true;
|
||||
};
|
||||
|
||||
CancelRequested = () =>
|
||||
{
|
||||
client.MatchmakingLeaveQueue().FireAndForget();
|
||||
return true;
|
||||
};
|
||||
|
||||
matchFoundSample = audio.Samples.Get(@"Multiplayer/Matchmaking/match-found");
|
||||
}
|
||||
|
||||
protected override Notification CreateCompletionNotification()
|
||||
{
|
||||
// Playing here means it will play even if notification overlay is hidden.
|
||||
//
|
||||
// If we add support for the completion notification to be processed during gameplay,
|
||||
// this can be moved inside the `MatchFoundNotification` implementation.
|
||||
matchFoundSample?.Play();
|
||||
|
||||
return foundNotification = new MatchFoundNotification
|
||||
{
|
||||
Activated = CompletionClickAction,
|
||||
Text = "Your match is ready! Click to join.",
|
||||
};
|
||||
}
|
||||
|
||||
public void CloseAll()
|
||||
{
|
||||
foundNotification?.Close(false);
|
||||
Close(false);
|
||||
}
|
||||
|
||||
public partial class MatchFoundNotification : ProgressCompletionNotification
|
||||
{
|
||||
protected override IconUsage CloseButtonIcon => FontAwesome.Solid.Times;
|
||||
|
||||
public MatchFoundNotification()
|
||||
{
|
||||
IsCritical = true;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Icon = FontAwesome.Solid.Bolt;
|
||||
IconContent.Colour = ColourInfo.GradientVertical(colours.YellowDark, colours.YellowLight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
Debug.Assert(client.LocalUser != null);
|
||||
|
||||
if (client.LocalUser.State == MultiplayerUserState.Results)
|
||||
client.ChangeState(MultiplayerUserState.Idle);
|
||||
client.ChangeState(MultiplayerUserState.Idle).FireAndForget();
|
||||
}
|
||||
|
||||
protected override string ScreenTitle => "Multiplayer";
|
||||
|
||||
@@ -618,7 +618,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
updateGameplayState();
|
||||
|
||||
if (client.LocalUser.State == MultiplayerUserState.Ready)
|
||||
client.ChangeState(MultiplayerUserState.Idle);
|
||||
client.ChangeState(MultiplayerUserState.Idle).FireAndForget();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
if (client.LocalUser?.State == MultiplayerUserState.Loaded)
|
||||
{
|
||||
loadingDisplay.Show();
|
||||
client.ChangeState(MultiplayerUserState.ReadyForGameplay);
|
||||
client.ChangeState(MultiplayerUserState.ReadyForGameplay).FireAndForget();
|
||||
}
|
||||
|
||||
// This will pause the clock, pending the gameplay started callback from the server.
|
||||
|
||||
@@ -296,7 +296,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
// On a manual exit, set the player back to idle unless gameplay has finished.
|
||||
// Of note, this doesn't cover exiting using alt-f4 or menu home option.
|
||||
if (multiplayerClient.Room.State != MultiplayerRoomState.Open)
|
||||
multiplayerClient.ChangeState(MultiplayerUserState.Idle);
|
||||
multiplayerClient.ChangeState(MultiplayerUserState.Idle).FireAndForget();
|
||||
|
||||
return base.OnBackButton();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user