diff --git a/osu.Game/Database/BeatmapMetrics.cs b/osu.Game/Database/BeatmapMetrics.cs
index 91320110d0..25de0f0a8d 100644
--- a/osu.Game/Database/BeatmapMetrics.cs
+++ b/osu.Game/Database/BeatmapMetrics.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
+using Newtonsoft.Json;
namespace osu.Game.Database
{
@@ -18,11 +19,13 @@ namespace osu.Game.Database
///
/// Points of failure on a relative time scale (usually 0..100).
///
+ [JsonProperty(@"fail")]
public IEnumerable Fails { get; set; }
///
/// Points of retry on a relative time scale (usually 0..100).
///
+ [JsonProperty(@"exit")]
public IEnumerable Retries { get; set; }
}
}
diff --git a/osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs
new file mode 100644
index 0000000000..a3b99331c6
--- /dev/null
+++ b/osu.Game/Online/API/Requests/GetBeatmapDetailsRequest.cs
@@ -0,0 +1,61 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using Newtonsoft.Json;
+using osu.Framework.IO.Network;
+using osu.Game.Database;
+using osu.Game.Rulesets.Scoring;
+
+namespace osu.Game.Online.API.Requests
+{
+ public class GetBeatmapDeatilsRequest : APIRequest
+ {
+ private readonly BeatmapInfo beatmap;
+
+ private string lookupString;
+
+ public GetBeatmapDeatilsRequest(BeatmapInfo beatmap)
+ {
+ this.beatmap = beatmap;
+ }
+
+ protected override WebRequest CreateWebRequest()
+ {
+ if (beatmap.OnlineBeatmapID > 0)
+ lookupString = beatmap.OnlineBeatmapID.ToString();
+ else
+ lookupString = $@"lookup?checksum={beatmap.Hash}&filename={beatmap.Path}";
+
+ var req = base.CreateWebRequest();
+
+ return req;
+ }
+
+ protected override string Target => $@"beatmaps/{lookupString}";
+ }
+
+ public class GetBeatmapDeatilsResponse : BeatmapMetrics
+ {
+ //the online API returns some metrics as a nested object.
+ [JsonProperty(@"failtimes")]
+ private BeatmapMetrics failTimes
+ {
+ set
+ {
+ this.Fails = value.Fails;
+ this.Retries = value.Retries;
+ }
+ }
+
+ //and other metrics in the beatmap set.
+ [JsonProperty(@"beatmapset")]
+ private BeatmapMetrics beatmapSet
+ {
+ set
+ {
+ this.Ratings = value.Ratings;
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Select/BeatmapDetailArea.cs b/osu.Game/Screens/Select/BeatmapDetailArea.cs
index 26ec74e299..cc22cca8bf 100644
--- a/osu.Game/Screens/Select/BeatmapDetailArea.cs
+++ b/osu.Game/Screens/Select/BeatmapDetailArea.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Screens.Select
{
beatmap = value;
Leaderboard.Beatmap = beatmap?.BeatmapInfo;
- Details.Beatmap = beatmap?.Beatmap.BeatmapInfo;
+ Details.Beatmap = beatmap?.BeatmapInfo;
}
}
diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs
index 462f741dae..c5fb5eafaa 100644
--- a/osu.Game/Screens/Select/BeatmapDetails.cs
+++ b/osu.Game/Screens/Select/BeatmapDetails.cs
@@ -14,6 +14,10 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using System.Globalization;
using System.Linq;
+using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
+using osu.Framework.Threading;
+using System;
namespace osu.Game.Screens.Select
{
@@ -39,61 +43,101 @@ namespace osu.Game.Screens.Select
private readonly BarGraph retryGraph;
private readonly BarGraph failGraph;
+ private ScheduledDelegate pendingBeatmapSwitch;
private BeatmapInfo beatmap;
+
public BeatmapInfo Beatmap
{
- get
- {
- return beatmap;
- }
+ get { return beatmap; }
set
{
beatmap = value;
- if (beatmap == null) return;
-
- description.Text = beatmap.Version;
- source.Text = beatmap.Metadata.Source;
- tags.Text = beatmap.Metadata.Tags;
-
- circleSize.Value = beatmap.Difficulty.CircleSize;
- drainRate.Value = beatmap.Difficulty.DrainRate;
- overallDifficulty.Value = beatmap.Difficulty.OverallDifficulty;
- approachRate.Value = beatmap.Difficulty.ApproachRate;
- stars.Value = (float)beatmap.StarDifficulty;
-
- if (beatmap.Metrics?.Ratings.Any() ?? false)
- {
- var ratings = beatmap.Metrics.Ratings.ToList();
- ratingsContainer.Show();
-
- negativeRatings.Text = ratings.GetRange(0, ratings.Count / 2).Sum().ToString();
- positiveRatings.Text = ratings.GetRange(ratings.Count / 2, ratings.Count / 2).Sum().ToString();
- ratingsBar.Length = (float)ratings.GetRange(0, ratings.Count / 2).Sum() / ratings.Sum();
-
- ratingsGraph.Values = ratings.Select(rating => (float)rating);
- }
- else
- ratingsContainer.Hide();
-
- if ((beatmap.Metrics?.Retries.Any() ?? false) && beatmap.Metrics.Fails.Any())
- {
- var retries = beatmap.Metrics.Retries;
- var fails = beatmap.Metrics.Fails;
- retryFailContainer.Show();
-
- float maxValue = fails.Zip(retries, (fail, retry) => fail + retry).Max();
- failGraph.MaxValue = maxValue;
- retryGraph.MaxValue = maxValue;
-
- failGraph.Values = fails.Select(fail => (float)fail);
- retryGraph.Values = retries.Zip(fails, (retry, fail) => retry + MathHelper.Clamp(fail, 0, maxValue));
- }
- else
- retryFailContainer.Hide();
+ pendingBeatmapSwitch?.Cancel();
+ pendingBeatmapSwitch = Schedule(updateStats);
}
}
+ private void updateStats()
+ {
+ description.Text = beatmap.Version;
+ source.Text = beatmap.Metadata.Source;
+ tags.Text = beatmap.Metadata.Tags;
+
+ circleSize.Value = beatmap.Difficulty.CircleSize;
+ drainRate.Value = beatmap.Difficulty.DrainRate;
+ overallDifficulty.Value = beatmap.Difficulty.OverallDifficulty;
+ approachRate.Value = beatmap.Difficulty.ApproachRate;
+ stars.Value = (float)beatmap.StarDifficulty;
+
+ var requestedBeatmap = beatmap;
+ if (requestedBeatmap.Metrics == null)
+ {
+ var lookup = new GetBeatmapDeatilsRequest(requestedBeatmap);
+ lookup.Success += res =>
+ {
+ if (beatmap != requestedBeatmap)
+ //the beatmap has been changed since we started the lookup.
+ return;
+
+ requestedBeatmap.Metrics = res;
+ Schedule(() => updateMetrics(res, true));
+ };
+ lookup.Failure += e => updateMetrics(null, true);
+
+ api.Queue(lookup);
+ }
+
+ updateMetrics(requestedBeatmap.Metrics, false);
+ }
+
+ private void updateMetrics(BeatmapMetrics metrics, bool failOnMissing = true)
+ {
+ var hasRatings = metrics?.Ratings.Any() ?? false;
+ var hasRetriesFails = (metrics?.Retries.Any() ?? false) && metrics.Fails.Any();
+
+ if (hasRatings)
+ {
+ var ratings = metrics.Ratings.ToList();
+ ratingsContainer.Show();
+
+ negativeRatings.Text = ratings.GetRange(0, ratings.Count / 2).Sum().ToString();
+ positiveRatings.Text = ratings.GetRange(ratings.Count / 2, ratings.Count / 2).Sum().ToString();
+ ratingsBar.Length = (float)ratings.GetRange(0, ratings.Count / 2).Sum() / ratings.Sum();
+
+ ratingsGraph.Values = ratings.Select(rating => (float)rating);
+
+ ratingsContainer.FadeColour(Color4.White, 500, EasingTypes.Out);
+ }
+ else if (failOnMissing)
+ ratingsGraph.Values = new float[10];
+ else
+ ratingsContainer.FadeColour(Color4.Gray, 500, EasingTypes.Out);
+
+ if (hasRetriesFails)
+ {
+ var retries = metrics.Retries;
+ var fails = metrics.Fails;
+ retryFailContainer.Show();
+
+ float maxValue = fails.Zip(retries, (fail, retry) => fail + retry).Max();
+ failGraph.MaxValue = maxValue;
+ retryGraph.MaxValue = maxValue;
+
+ failGraph.Values = fails.Select(fail => (float)fail);
+ retryGraph.Values = retries.Zip(fails, (retry, fail) => retry + MathHelper.Clamp(fail, 0, maxValue));
+
+ retryFailContainer.FadeColour(Color4.White, 500, EasingTypes.Out);
+ }
+ else if (failOnMissing)
+ {
+ failGraph.Values = new float[100];
+ retryGraph.Values = new float[100];
+ }
+ else
+ retryFailContainer.FadeColour(Color4.Gray, 500, EasingTypes.Out);
+ }
+
public BeatmapDetails()
{
Children = new Drawable[]
@@ -272,9 +316,13 @@ namespace osu.Game.Screens.Select
};
}
+ private APIAccess api;
+
[BackgroundDependencyLoader]
- private void load(OsuColour colour)
+ private void load(OsuColour colour, APIAccess api)
{
+ this.api = api;
+
description.AccentColour = colour.GrayB;
source.AccentColour = colour.GrayB;
tags.AccentColour = colour.YellowLight;
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index c9a3b08713..c32c5112cf 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -179,6 +179,7 @@
+