// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Configuration; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Users; namespace osu.Game.Online.API { public partial class LocalUserState : Component, ILocalUserState { public IBindable<APIUser> User => localUser; public IBindableList<APIRelation> Friends => friends; public IBindableList<APIRelation> Blocks => blocks; public IBindableList<int> FavouriteBeatmapSets => favouriteBeatmapSets; private readonly IAPIProvider api; private readonly Bindable<APIUser> localUser = new Bindable<APIUser>(createGuestUser()); private readonly BindableList<APIRelation> friends = new BindableList<APIRelation>(); private readonly BindableList<APIRelation> blocks = new BindableList<APIRelation>(); private readonly BindableList<int> favouriteBeatmapSets = new BindableList<int>(); private readonly Bindable<UserStatus> configStatus = new Bindable<UserStatus>(); private readonly Bindable<bool> configSupporter = new Bindable<bool>(); public LocalUserState(IAPIProvider api, OsuConfigManager config) { this.api = api; config.BindWith(OsuSetting.UserOnlineStatus, configStatus); config.BindWith(OsuSetting.WasSupporter, configSupporter); } #region Logging in / out private static APIUser createGuestUser() => new GuestUser(); /// <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> public void SetPlaceholderLocalUser(string username) { if (!localUser.IsDefault) return; localUser.Value = new APIUser { Username = username, IsSupporter = configSupporter.Value, }; } public void SetLocalUser(APIMe me) { localUser.Value = me; configSupporter.Value = me.IsSupporter; // `last_visit` is assumed to be `null` if and only if the web-side "hide online presence toggle" is enabled if (me.LastVisit == null) configStatus.Value = UserStatus.Offline; UpdateFriends(); UpdateBlocks(); UpdateFavouriteBeatmapSets(); } public void ClearLocalUser() { // Reset the status to be broadcast on the next login, in case multiple players share the same system. configStatus.Value = UserStatus.Online; // Scheduled prior to state change such that the state changed event is invoked with the correct user and their friends present Schedule(() => { localUser.Value = createGuestUser(); configSupporter.Value = false; friends.Clear(); blocks.Clear(); favouriteBeatmapSets.Clear(); }); } #endregion public void UpdateFriends() { if (!api.IsLoggedIn) return; var friendsReq = new GetFriendsRequest(); friendsReq.Success += res => { var existingFriends = friends.Select(f => f.TargetID).ToHashSet(); var updatedFriends = res.Select(f => f.TargetID).ToHashSet(); // Add new friends into local list. friends.AddRange(res.Where(r => !existingFriends.Contains(r.TargetID))); // Remove non-friends from local list. friends.RemoveAll(f => !updatedFriends.Contains(f.TargetID)); }; api.Queue(friendsReq); } public void UpdateBlocks() { if (!api.IsLoggedIn) return; var blocksReq = new GetBlocksRequest(); blocksReq.Success += res => { var existingBlocks = blocks.Select(f => f.TargetID).ToHashSet(); var updatedBlocks = res.Select(f => f.TargetID).ToHashSet(); // Add new blocked users to local list. blocks.AddRange(res.Where(r => !existingBlocks.Contains(r.TargetID))); // Remove non-blocked users from local list. blocks.RemoveAll(b => !updatedBlocks.Contains(b.TargetID)); // Remove friends who got blocked since last check. friends.RemoveAll(f => updatedBlocks.Contains(f.TargetID)); }; api.Queue(blocksReq); } public void UpdateFavouriteBeatmapSets() { if (!api.IsLoggedIn) return; var favouritesReq = new GetMyFavouriteBeatmapSetsRequest(); favouritesReq.Success += res => { var existingBeatmapSets = favouriteBeatmapSets.ToHashSet(); var updatedBeatmapSets = res.BeatmapSetIds.ToHashSet(); favouriteBeatmapSets.AddRange(updatedBeatmapSets.Except(existingBeatmapSets)); favouriteBeatmapSets.RemoveAll(b => !updatedBeatmapSets.Contains(b)); }; api.Queue(favouritesReq); } } }