2018-01-05 11:21:19 +00:00
|
|
|
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
2017-03-04 07:37:34 +00:00
|
|
|
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
|
|
|
|
2017-08-13 15:41:13 +00:00
|
|
|
|
using System;
|
2017-03-13 12:33:25 +00:00
|
|
|
|
using System.Collections.Generic;
|
2017-03-14 15:58:22 +00:00
|
|
|
|
using OpenTK;
|
2017-03-15 16:57:41 +00:00
|
|
|
|
using OpenTK.Graphics;
|
2017-08-13 15:41:13 +00:00
|
|
|
|
using osu.Framework.Allocation;
|
2017-03-15 16:57:41 +00:00
|
|
|
|
using osu.Framework.Extensions.Color4Extensions;
|
2017-03-04 07:37:34 +00:00
|
|
|
|
using osu.Framework.Graphics;
|
2017-03-15 16:57:41 +00:00
|
|
|
|
using osu.Framework.Graphics.Colour;
|
2017-03-04 07:37:34 +00:00
|
|
|
|
using osu.Framework.Graphics.Containers;
|
2017-04-24 09:48:28 +00:00
|
|
|
|
using osu.Framework.Threading;
|
2017-07-26 04:22:46 +00:00
|
|
|
|
using osu.Game.Beatmaps;
|
2017-07-12 09:57:44 +00:00
|
|
|
|
using osu.Game.Graphics.Containers;
|
2017-06-12 07:40:53 +00:00
|
|
|
|
using osu.Game.Graphics.UserInterface;
|
2017-04-18 07:05:58 +00:00
|
|
|
|
using osu.Game.Rulesets.Scoring;
|
2017-04-11 04:48:43 +00:00
|
|
|
|
using osu.Game.Online.API;
|
|
|
|
|
using osu.Game.Online.API.Requests;
|
2017-11-16 12:29:07 +00:00
|
|
|
|
using System.Linq;
|
2017-12-21 14:48:35 +00:00
|
|
|
|
using osu.Framework.Configuration;
|
2017-11-24 13:10:52 +00:00
|
|
|
|
using osu.Framework.Logging;
|
2017-11-25 15:53:36 +00:00
|
|
|
|
using osu.Game.Rulesets;
|
2017-03-04 07:37:34 +00:00
|
|
|
|
|
|
|
|
|
namespace osu.Game.Screens.Select.Leaderboards
|
|
|
|
|
{
|
|
|
|
|
public class Leaderboard : Container
|
|
|
|
|
{
|
2017-12-22 13:41:18 +00:00
|
|
|
|
private const double fade_duration = 300;
|
2017-11-20 11:37:41 +00:00
|
|
|
|
|
2017-03-23 04:41:50 +00:00
|
|
|
|
private readonly ScrollContainer scrollContainer;
|
2017-11-26 09:33:49 +00:00
|
|
|
|
private readonly Container placeholderContainer;
|
|
|
|
|
|
2017-11-16 12:29:07 +00:00
|
|
|
|
private FillFlowContainer<LeaderboardScore> scrollFlow;
|
2017-03-04 07:37:34 +00:00
|
|
|
|
|
2017-12-21 14:48:35 +00:00
|
|
|
|
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
|
|
|
|
|
2017-04-11 05:01:47 +00:00
|
|
|
|
public Action<Score> ScoreSelected;
|
|
|
|
|
|
2017-06-13 06:54:26 +00:00
|
|
|
|
private readonly LoadingAnimation loading;
|
2017-06-12 07:40:53 +00:00
|
|
|
|
|
2017-03-15 05:38:38 +00:00
|
|
|
|
private IEnumerable<Score> scores;
|
2017-11-16 12:29:07 +00:00
|
|
|
|
|
2017-03-15 05:38:38 +00:00
|
|
|
|
public IEnumerable<Score> Scores
|
2017-03-04 07:37:34 +00:00
|
|
|
|
{
|
2017-03-04 08:05:31 +00:00
|
|
|
|
get { return scores; }
|
|
|
|
|
set
|
2017-03-04 07:37:34 +00:00
|
|
|
|
{
|
2017-03-04 08:05:31 +00:00
|
|
|
|
scores = value;
|
|
|
|
|
|
2017-12-22 13:41:18 +00:00
|
|
|
|
scrollFlow?.FadeOut(fade_duration, Easing.OutQuint).Expire();
|
2017-11-18 15:53:59 +00:00
|
|
|
|
scrollFlow = null;
|
2017-03-22 00:07:02 +00:00
|
|
|
|
|
2017-11-24 13:10:52 +00:00
|
|
|
|
loading.Hide();
|
|
|
|
|
|
2017-12-21 09:47:37 +00:00
|
|
|
|
if (scores == null || !scores.Any())
|
2017-03-15 05:38:38 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2017-12-21 09:47:37 +00:00
|
|
|
|
// ensure placeholder is hidden when displaying scores
|
|
|
|
|
PlaceholderState = PlaceholderState.Successful;
|
2017-11-20 11:37:41 +00:00
|
|
|
|
|
2017-11-16 12:29:07 +00:00
|
|
|
|
// schedule because we may not be loaded yet (LoadComponentAsync complains).
|
|
|
|
|
Schedule(() =>
|
2017-03-04 07:37:34 +00:00
|
|
|
|
{
|
2017-11-16 12:29:07 +00:00
|
|
|
|
LoadComponentAsync(new FillFlowContainer<LeaderboardScore>
|
2017-03-15 08:07:56 +00:00
|
|
|
|
{
|
2017-11-16 12:29:07 +00:00
|
|
|
|
RelativeSizeAxes = Axes.X,
|
|
|
|
|
AutoSizeAxes = Axes.Y,
|
|
|
|
|
Spacing = new Vector2(0f, 5f),
|
|
|
|
|
Padding = new MarginPadding { Top = 10, Bottom = 5 },
|
2017-11-18 01:46:02 +00:00
|
|
|
|
ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) })
|
2017-11-16 12:29:07 +00:00
|
|
|
|
}, f =>
|
|
|
|
|
{
|
|
|
|
|
scrollContainer.Add(scrollFlow = f);
|
2017-03-04 08:05:31 +00:00
|
|
|
|
|
2017-11-18 15:53:59 +00:00
|
|
|
|
int i = 0;
|
2017-11-16 12:29:07 +00:00
|
|
|
|
foreach (var s in f.Children)
|
|
|
|
|
{
|
|
|
|
|
using (s.BeginDelayedSequence(i++ * 50, true))
|
|
|
|
|
s.Show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scrollContainer.ScrollTo(0f, false);
|
|
|
|
|
});
|
|
|
|
|
});
|
2017-03-04 07:37:34 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-20 11:37:41 +00:00
|
|
|
|
private LeaderboardScope scope;
|
2017-12-22 13:41:18 +00:00
|
|
|
|
|
2017-11-20 05:06:26 +00:00
|
|
|
|
public LeaderboardScope Scope
|
|
|
|
|
{
|
|
|
|
|
get { return scope; }
|
|
|
|
|
set
|
|
|
|
|
{
|
2018-01-05 18:13:54 +00:00
|
|
|
|
if (value == scope)
|
|
|
|
|
return;
|
2017-11-20 05:06:26 +00:00
|
|
|
|
|
|
|
|
|
scope = value;
|
2017-12-21 09:47:37 +00:00
|
|
|
|
updateScores();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private PlaceholderState placeholderState;
|
2017-12-22 13:41:18 +00:00
|
|
|
|
|
2017-12-21 09:47:37 +00:00
|
|
|
|
protected PlaceholderState PlaceholderState
|
|
|
|
|
{
|
|
|
|
|
get { return placeholderState; }
|
|
|
|
|
set
|
|
|
|
|
{
|
2018-01-02 06:30:29 +00:00
|
|
|
|
if (value != PlaceholderState.Successful)
|
|
|
|
|
{
|
|
|
|
|
getScoresRequest?.Cancel();
|
|
|
|
|
getScoresRequest = null;
|
|
|
|
|
Scores = null;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-05 18:13:54 +00:00
|
|
|
|
if (value == placeholderState)
|
|
|
|
|
return;
|
|
|
|
|
|
2017-12-21 09:47:37 +00:00
|
|
|
|
switch (placeholderState = value)
|
|
|
|
|
{
|
|
|
|
|
case PlaceholderState.NetworkFailure:
|
|
|
|
|
replacePlaceholder(new RetrievalFailurePlaceholder
|
|
|
|
|
{
|
|
|
|
|
OnRetry = updateScores,
|
|
|
|
|
});
|
|
|
|
|
break;
|
2017-12-22 13:41:18 +00:00
|
|
|
|
case PlaceholderState.Unavailable:
|
|
|
|
|
replacePlaceholder(new MessagePlaceholder(@"Leaderboards are not available for this beatmap!"));
|
|
|
|
|
break;
|
2017-12-21 09:47:37 +00:00
|
|
|
|
case PlaceholderState.NoScores:
|
|
|
|
|
replacePlaceholder(new MessagePlaceholder(@"No records yet!"));
|
|
|
|
|
break;
|
|
|
|
|
case PlaceholderState.NotLoggedIn:
|
|
|
|
|
replacePlaceholder(new MessagePlaceholder(@"Please login to view online leaderboards!"));
|
|
|
|
|
break;
|
|
|
|
|
case PlaceholderState.NotSupporter:
|
|
|
|
|
replacePlaceholder(new MessagePlaceholder(@"Please invest in a supporter tag to view this leaderboard!"));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
replacePlaceholder(null);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-11-20 05:06:26 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-04 07:37:34 +00:00
|
|
|
|
public Leaderboard()
|
|
|
|
|
{
|
|
|
|
|
Children = new Drawable[]
|
|
|
|
|
{
|
2017-07-12 09:57:44 +00:00
|
|
|
|
scrollContainer = new OsuScrollContainer
|
2017-03-04 07:37:34 +00:00
|
|
|
|
{
|
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
2017-05-30 07:33:26 +00:00
|
|
|
|
ScrollbarVisible = false,
|
2017-03-04 07:37:34 +00:00
|
|
|
|
},
|
2017-11-20 11:37:41 +00:00
|
|
|
|
loading = new LoadingAnimation(),
|
2017-11-26 07:20:20 +00:00
|
|
|
|
placeholderContainer = new Container
|
2017-11-20 11:37:41 +00:00
|
|
|
|
{
|
2017-12-22 13:41:18 +00:00
|
|
|
|
RelativeSizeAxes = Axes.Both
|
2017-11-24 13:10:52 +00:00
|
|
|
|
},
|
2017-03-04 07:37:34 +00:00
|
|
|
|
};
|
|
|
|
|
}
|
2017-03-16 04:15:06 +00:00
|
|
|
|
|
2017-04-11 04:48:43 +00:00
|
|
|
|
private APIAccess api;
|
|
|
|
|
private BeatmapInfo beatmap;
|
2017-11-21 14:14:38 +00:00
|
|
|
|
private OsuGame osuGame;
|
|
|
|
|
|
2017-04-24 09:48:28 +00:00
|
|
|
|
private ScheduledDelegate pendingBeatmapSwitch;
|
|
|
|
|
|
2017-04-11 04:48:43 +00:00
|
|
|
|
public BeatmapInfo Beatmap
|
|
|
|
|
{
|
|
|
|
|
get { return beatmap; }
|
|
|
|
|
set
|
|
|
|
|
{
|
2018-01-05 18:13:54 +00:00
|
|
|
|
if (beatmap == value)
|
|
|
|
|
return;
|
2017-05-17 07:45:17 +00:00
|
|
|
|
|
2017-04-11 04:48:43 +00:00
|
|
|
|
beatmap = value;
|
2017-04-24 09:48:28 +00:00
|
|
|
|
Scores = null;
|
|
|
|
|
|
|
|
|
|
pendingBeatmapSwitch?.Cancel();
|
2017-12-21 09:47:37 +00:00
|
|
|
|
pendingBeatmapSwitch = Schedule(updateScores);
|
2017-04-11 04:48:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[BackgroundDependencyLoader(permitNulls: true)]
|
2017-11-21 14:14:38 +00:00
|
|
|
|
private void load(APIAccess api, OsuGame osuGame)
|
2017-04-11 04:48:43 +00:00
|
|
|
|
{
|
|
|
|
|
this.api = api;
|
2017-11-21 14:14:38 +00:00
|
|
|
|
this.osuGame = osuGame;
|
|
|
|
|
|
2017-11-25 15:29:03 +00:00
|
|
|
|
if (osuGame != null)
|
2017-12-21 14:48:35 +00:00
|
|
|
|
ruleset.BindTo(osuGame.Ruleset);
|
|
|
|
|
|
|
|
|
|
ruleset.ValueChanged += r => updateScores();
|
2017-12-20 14:30:52 +00:00
|
|
|
|
|
|
|
|
|
if (api != null)
|
|
|
|
|
api.OnStateChange += handleApiStateChange;
|
2017-11-25 15:53:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void Dispose(bool isDisposing)
|
|
|
|
|
{
|
|
|
|
|
base.Dispose(isDisposing);
|
|
|
|
|
|
2017-12-21 12:30:10 +00:00
|
|
|
|
if (api != null)
|
|
|
|
|
api.OnStateChange -= handleApiStateChange;
|
2017-04-11 04:48:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private GetScoresRequest getScoresRequest;
|
2017-06-12 07:40:53 +00:00
|
|
|
|
|
2017-12-20 14:30:52 +00:00
|
|
|
|
private void handleApiStateChange(APIState oldState, APIState newState)
|
|
|
|
|
{
|
|
|
|
|
if (Scope == LeaderboardScope.Local)
|
|
|
|
|
// No need to respond to API state change while current scope is local
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (newState == APIState.Online)
|
2017-12-21 09:47:37 +00:00
|
|
|
|
updateScores();
|
2017-12-20 14:30:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-21 09:47:37 +00:00
|
|
|
|
private void updateScores()
|
2017-04-11 04:48:43 +00:00
|
|
|
|
{
|
2017-11-20 05:06:26 +00:00
|
|
|
|
if (Scope == LeaderboardScope.Local)
|
|
|
|
|
{
|
|
|
|
|
// TODO: get local scores from wherever here.
|
2017-12-21 09:47:37 +00:00
|
|
|
|
PlaceholderState = PlaceholderState.NoScores;
|
2017-11-20 11:37:41 +00:00
|
|
|
|
return;
|
2017-11-20 05:06:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-22 13:41:18 +00:00
|
|
|
|
if (Beatmap?.OnlineBeatmapID == null)
|
2017-12-20 14:30:52 +00:00
|
|
|
|
{
|
2017-12-22 13:41:18 +00:00
|
|
|
|
PlaceholderState = PlaceholderState.Unavailable;
|
2017-12-20 14:30:52 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-22 13:41:18 +00:00
|
|
|
|
if (api?.IsLoggedIn != true)
|
2017-12-20 18:11:44 +00:00
|
|
|
|
{
|
2017-12-22 13:41:18 +00:00
|
|
|
|
PlaceholderState = PlaceholderState.NotLoggedIn;
|
2017-12-20 18:11:44 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-20 14:30:52 +00:00
|
|
|
|
if (Scope != LeaderboardScope.Global && !api.LocalUser.Value.IsSupporter)
|
|
|
|
|
{
|
2017-12-21 09:47:37 +00:00
|
|
|
|
PlaceholderState = PlaceholderState.NotSupporter;
|
2017-12-20 14:30:52 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-01 13:25:26 +00:00
|
|
|
|
PlaceholderState = PlaceholderState.Retrieving;
|
|
|
|
|
loading.Show();
|
|
|
|
|
|
2017-12-20 12:06:33 +00:00
|
|
|
|
getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value ?? Beatmap.Ruleset, Scope);
|
2017-06-12 07:40:53 +00:00
|
|
|
|
getScoresRequest.Success += r =>
|
|
|
|
|
{
|
|
|
|
|
Scores = r.Scores;
|
2017-12-21 09:47:37 +00:00
|
|
|
|
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
|
2017-06-12 07:40:53 +00:00
|
|
|
|
};
|
2017-12-21 09:47:37 +00:00
|
|
|
|
getScoresRequest.Failure += onUpdateFailed;
|
2017-11-28 06:27:29 +00:00
|
|
|
|
|
|
|
|
|
api.Queue(getScoresRequest);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-21 09:47:37 +00:00
|
|
|
|
private void onUpdateFailed(Exception e)
|
2017-11-28 06:27:29 +00:00
|
|
|
|
{
|
2018-01-05 18:13:54 +00:00
|
|
|
|
if (e is OperationCanceledException)
|
|
|
|
|
return;
|
2017-11-28 06:27:29 +00:00
|
|
|
|
|
2017-12-21 09:47:37 +00:00
|
|
|
|
PlaceholderState = PlaceholderState.NetworkFailure;
|
2017-11-28 06:27:29 +00:00
|
|
|
|
Logger.Error(e, @"Couldn't fetch beatmap scores!");
|
2017-04-11 04:48:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-21 09:47:37 +00:00
|
|
|
|
private void replacePlaceholder(Placeholder placeholder)
|
2017-12-20 14:30:52 +00:00
|
|
|
|
{
|
2017-12-22 13:41:18 +00:00
|
|
|
|
var existingPlaceholder = placeholderContainer.Children.LastOrDefault() as Placeholder;
|
|
|
|
|
|
|
|
|
|
if (placeholder != null && placeholder.Equals(existingPlaceholder))
|
2017-12-21 09:47:37 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2017-12-22 13:41:18 +00:00
|
|
|
|
existingPlaceholder?.FadeOut(150, Easing.OutQuint).Expire();
|
2017-12-21 09:47:37 +00:00
|
|
|
|
|
2017-12-22 13:41:18 +00:00
|
|
|
|
if (placeholder == null)
|
2017-12-21 09:47:37 +00:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Scores = null;
|
|
|
|
|
|
2017-12-22 13:41:18 +00:00
|
|
|
|
placeholderContainer.Add(placeholder);
|
|
|
|
|
|
|
|
|
|
placeholder.ScaleTo(0.8f).Then().ScaleTo(1, fade_duration * 3, Easing.OutQuint);
|
|
|
|
|
placeholder.FadeInFromZero(fade_duration, Easing.OutQuint);
|
2017-12-20 14:30:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-16 04:15:06 +00:00
|
|
|
|
protected override void Update()
|
|
|
|
|
{
|
|
|
|
|
base.Update();
|
|
|
|
|
|
2017-03-19 12:49:29 +00:00
|
|
|
|
var fadeStart = scrollContainer.Current + scrollContainer.DrawHeight;
|
2017-03-16 04:15:06 +00:00
|
|
|
|
|
2017-03-19 12:49:29 +00:00
|
|
|
|
if (!scrollContainer.IsScrolledToEnd())
|
|
|
|
|
fadeStart -= LeaderboardScore.HEIGHT;
|
|
|
|
|
|
2018-01-05 18:13:54 +00:00
|
|
|
|
if (scrollFlow == null)
|
|
|
|
|
return;
|
2017-11-16 12:29:07 +00:00
|
|
|
|
|
2017-03-19 12:49:29 +00:00
|
|
|
|
foreach (var c in scrollFlow.Children)
|
2017-03-16 04:15:06 +00:00
|
|
|
|
{
|
2017-03-19 12:49:29 +00:00
|
|
|
|
var topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, scrollFlow).Y;
|
2017-03-17 22:07:45 +00:00
|
|
|
|
var bottomY = topY + LeaderboardScore.HEIGHT;
|
2017-03-16 04:15:06 +00:00
|
|
|
|
|
2017-03-19 12:49:29 +00:00
|
|
|
|
if (bottomY < fadeStart)
|
|
|
|
|
c.Colour = Color4.White;
|
|
|
|
|
else if (topY > fadeStart + LeaderboardScore.HEIGHT)
|
|
|
|
|
c.Colour = Color4.Transparent;
|
2017-03-18 04:44:05 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
2017-07-23 05:30:50 +00:00
|
|
|
|
c.Colour = ColourInfo.GradientVertical(
|
2017-03-19 12:49:29 +00:00
|
|
|
|
Color4.White.Opacity(Math.Min(1 - (topY - fadeStart) / LeaderboardScore.HEIGHT, 1)),
|
|
|
|
|
Color4.White.Opacity(Math.Min(1 - (bottomY - fadeStart) / LeaderboardScore.HEIGHT, 1)));
|
2017-03-18 04:44:05 +00:00
|
|
|
|
}
|
2017-03-16 04:15:06 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-03-04 07:37:34 +00:00
|
|
|
|
}
|
2017-11-20 05:06:26 +00:00
|
|
|
|
|
|
|
|
|
public enum LeaderboardScope
|
|
|
|
|
{
|
|
|
|
|
Local,
|
|
|
|
|
Country,
|
|
|
|
|
Global,
|
2017-12-20 11:33:16 +00:00
|
|
|
|
Friend,
|
2017-11-20 05:06:26 +00:00
|
|
|
|
}
|
2017-12-21 09:47:37 +00:00
|
|
|
|
|
|
|
|
|
public enum PlaceholderState
|
|
|
|
|
{
|
|
|
|
|
Successful,
|
|
|
|
|
Retrieving,
|
|
|
|
|
NetworkFailure,
|
2017-12-22 13:41:18 +00:00
|
|
|
|
Unavailable,
|
2017-12-21 09:47:37 +00:00
|
|
|
|
NoScores,
|
|
|
|
|
NotLoggedIn,
|
|
|
|
|
NotSupporter,
|
|
|
|
|
}
|
2017-03-04 07:37:34 +00:00
|
|
|
|
}
|