1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-18 10:43:22 +08:00

Ensure API starts up with LocalUser in correct state

I noticed in passing that in a very edge case scenario where the API's
`run` thread doesn't run before it is loaded into the game, something
could access it and get a guest `LocalUser` when the local user actually
has a valid login.

Put another way, the `protected HasLogin` could be `true` while
`LocalUser` is `Guest`.

I think we want to avoid this, so I've moved the initial set of the
local user earlier in the initialisation process.

If this is controversial in any way, the PR can be closed and we can
assume no one is ever going to run into this scenario (or that it
doesn't matter enough even if they did).
This commit is contained in:
Dean Herbert 2025-01-14 17:43:29 +09:00
parent 75d1fab6d0
commit f6073d4ac0
No known key found for this signature in database

View File

@ -13,6 +13,7 @@ using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using osu.Framework.Bindables;
using osu.Framework.Development;
using osu.Framework.Extensions.ExceptionExtensions;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
@ -110,6 +111,9 @@ namespace osu.Game.Online.API
config.BindWith(OsuSetting.UserOnlineStatus, configStatus);
// Early call to ensure the local user / "logged in" state is correct immediately.
setPlaceholderLocalUser();
localUser.BindValueChanged(u =>
{
u.OldValue?.Activity.UnbindFrom(activity);
@ -193,7 +197,7 @@ namespace osu.Game.Online.API
Debug.Assert(HasLogin);
// Ensure that we are in an online state. If not, attempt a connect.
// Ensure that we are in an online state. If not, attempt to connect.
if (state.Value != APIState.Online)
{
attemptConnect();
@ -247,17 +251,7 @@ namespace osu.Game.Online.API
/// <returns>Whether the connection attempt was successful.</returns>
private void attemptConnect()
{
if (localUser.IsDefault)
{
// Show a placeholder user if saved credentials are available.
// This is useful for storing local scores and showing a placeholder username after starting the game,
// until a valid connection has been established.
setLocalUser(new APIUser
{
Username = ProvidedUsername,
Status = { Value = configStatus.Value ?? UserStatus.Online }
});
}
Scheduler.Add(setPlaceholderLocalUser, false);
// save the username at this point, if the user requested for it to be.
config.SetValue(OsuSetting.Username, config.Get<bool>(OsuSetting.SaveUsername) ? ProvidedUsername : string.Empty);
@ -339,9 +333,11 @@ namespace osu.Game.Online.API
userReq.Success += me =>
{
Debug.Assert(ThreadSafety.IsUpdateThread);
me.Status.Value = configStatus.Value ?? UserStatus.Online;
setLocalUser(me);
localUser.Value = me;
state.Value = me.SessionVerified ? APIState.Online : APIState.RequiresSecondFactorAuth;
failureCount = 0;
@ -366,6 +362,23 @@ namespace osu.Game.Online.API
Thread.Sleep(500);
}
/// <summary>
/// Show a placeholder user if saved credentials are available.
/// This is useful for storing local scores and showing a placeholder username after starting the game,
/// until a valid connection has been established.
/// </summary>
private void setPlaceholderLocalUser()
{
if (!localUser.IsDefault)
return;
localUser.Value = new APIUser
{
Username = ProvidedUsername,
Status = { Value = configStatus.Value ?? UserStatus.Online }
};
}
public void Perform(APIRequest request)
{
try
@ -593,7 +606,7 @@ namespace osu.Game.Online.API
// Scheduled prior to state change such that the state changed event is invoked with the correct user and their friends present
Schedule(() =>
{
setLocalUser(createGuestUser());
localUser.Value = createGuestUser();
friends.Clear();
});
@ -619,8 +632,6 @@ namespace osu.Game.Online.API
private static APIUser createGuestUser() => new GuestUser();
private void setLocalUser(APIUser user) => Scheduler.Add(() => localUser.Value = user, false);
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);