mirror of
https://github.com/ppy/osu
synced 2024-12-14 02:46:27 +00:00
Merge branch 'master' into beatmap-serialization
This commit is contained in:
commit
a3d947301b
@ -93,6 +93,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||
float approxFollowCircleRadius = (float)(slider.Radius * 3);
|
||||
var computeVertex = new Action<double>(t =>
|
||||
{
|
||||
// ReSharper disable once PossibleInvalidOperationException (bugged in current r# version)
|
||||
var diff = slider.PositionAt(t) - slider.LazyEndPosition.Value;
|
||||
float dist = diff.Length;
|
||||
|
||||
|
@ -6,14 +6,44 @@ using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Select.Leaderboards;
|
||||
using osu.Game.Users;
|
||||
using osu.Framework.Allocation;
|
||||
using OpenTK;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[Description("PlaySongSelect leaderboard")]
|
||||
public class TestCaseLeaderboard : OsuTestCase
|
||||
{
|
||||
private readonly Leaderboard leaderboard;
|
||||
private RulesetStore rulesets;
|
||||
|
||||
private readonly FailableLeaderboard leaderboard;
|
||||
|
||||
public TestCaseLeaderboard()
|
||||
{
|
||||
Add(leaderboard = new FailableLeaderboard
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Size = new Vector2(550f, 450f),
|
||||
Scope = LeaderboardScope.Global,
|
||||
});
|
||||
|
||||
AddStep(@"New Scores", newScores);
|
||||
AddStep(@"Empty Scores", () => leaderboard.SetRetrievalState(PlaceholderState.NoScores));
|
||||
AddStep(@"Network failure", () => leaderboard.SetRetrievalState(PlaceholderState.NetworkFailure));
|
||||
AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter));
|
||||
AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn));
|
||||
AddStep(@"Real beatmap", realBeatmap);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets)
|
||||
{
|
||||
this.rulesets = rulesets;
|
||||
}
|
||||
|
||||
private void newScores()
|
||||
{
|
||||
@ -204,17 +234,44 @@ namespace osu.Game.Tests.Visual
|
||||
leaderboard.Scores = scores;
|
||||
}
|
||||
|
||||
public TestCaseLeaderboard()
|
||||
private void realBeatmap()
|
||||
{
|
||||
Add(leaderboard = new Leaderboard
|
||||
leaderboard.Beatmap = new BeatmapInfo
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Size = new Vector2(550f, 450f),
|
||||
});
|
||||
StarDifficulty = 1.36,
|
||||
Version = @"BASIC",
|
||||
OnlineBeatmapID = 1113057,
|
||||
Ruleset = rulesets.GetRuleset(0),
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 4,
|
||||
DrainRate = 6.5f,
|
||||
OverallDifficulty = 6.5f,
|
||||
ApproachRate = 5,
|
||||
},
|
||||
OnlineInfo = new BeatmapOnlineInfo
|
||||
{
|
||||
Length = 115000,
|
||||
CircleCount = 265,
|
||||
SliderCount = 71,
|
||||
PlayCount = 47906,
|
||||
PassCount = 19899,
|
||||
},
|
||||
Metrics = new BeatmapMetrics
|
||||
{
|
||||
Ratings = Enumerable.Range(0, 11),
|
||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
AddStep(@"New Scores", newScores);
|
||||
newScores();
|
||||
private class FailableLeaderboard : Leaderboard
|
||||
{
|
||||
public void SetRetrievalState(PlaceholderState state)
|
||||
{
|
||||
PlaceholderState = state;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
@ -8,16 +10,30 @@ namespace osu.Game.Tests.Visual
|
||||
public class TestCaseSettings : OsuTestCase
|
||||
{
|
||||
private readonly SettingsOverlay settings;
|
||||
private readonly DialogOverlay dialogOverlay;
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent);
|
||||
|
||||
public TestCaseSettings()
|
||||
{
|
||||
Children = new[] { settings = new MainSettings() };
|
||||
settings = new MainSettings
|
||||
{
|
||||
State = Visibility.Visible
|
||||
};
|
||||
Add(dialogOverlay = new DialogOverlay
|
||||
{
|
||||
Depth = -1
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
base.LoadComplete();
|
||||
settings.ToggleVisibility();
|
||||
dependencies.Cache(dialogOverlay);
|
||||
|
||||
Add(settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -341,6 +341,61 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
public void UndeleteAll()
|
||||
{
|
||||
var deleteMaps = QueryBeatmapSets(bs => bs.DeletePending).ToList();
|
||||
|
||||
if (!deleteMaps.Any()) return;
|
||||
|
||||
var notification = new ProgressNotification
|
||||
{
|
||||
CompletionText = "Restored all deleted beatmaps!",
|
||||
Progress = 0,
|
||||
State = ProgressNotificationState.Active,
|
||||
};
|
||||
|
||||
PostNotification?.Invoke(notification);
|
||||
|
||||
int i = 0;
|
||||
|
||||
foreach (var bs in deleteMaps)
|
||||
{
|
||||
if (notification.State == ProgressNotificationState.Cancelled)
|
||||
// user requested abort
|
||||
return;
|
||||
|
||||
notification.Text = $"Restoring ({i} of {deleteMaps.Count})";
|
||||
notification.Progress = (float)++i / deleteMaps.Count;
|
||||
Undelete(bs);
|
||||
}
|
||||
|
||||
notification.State = ProgressNotificationState.Completed;
|
||||
}
|
||||
|
||||
public void Undelete(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
if (beatmapSet.Protected)
|
||||
return;
|
||||
|
||||
lock (importContext)
|
||||
{
|
||||
var context = importContext.Value;
|
||||
|
||||
using (var transaction = context.BeginTransaction())
|
||||
{
|
||||
context.ChangeTracker.AutoDetectChangesEnabled = false;
|
||||
|
||||
var iFiles = new FileStore(() => context, storage);
|
||||
var iBeatmaps = createBeatmapStore(() => context);
|
||||
|
||||
undelete(iBeatmaps, iFiles, beatmapSet);
|
||||
|
||||
context.ChangeTracker.AutoDetectChangesEnabled = true;
|
||||
context.SaveChanges(transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete a beatmap difficulty.
|
||||
/// </summary>
|
||||
|
@ -10,19 +10,28 @@ using osu.Game.Rulesets;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Select.Leaderboards;
|
||||
using osu.Framework.IO.Network;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class GetScoresRequest : APIRequest<GetScoresResponse>
|
||||
{
|
||||
private readonly BeatmapInfo beatmap;
|
||||
private readonly LeaderboardScope scope;
|
||||
private readonly RulesetInfo ruleset;
|
||||
|
||||
public GetScoresRequest(BeatmapInfo beatmap)
|
||||
public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, LeaderboardScope scope = LeaderboardScope.Global)
|
||||
{
|
||||
if (!beatmap.OnlineBeatmapID.HasValue)
|
||||
throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}.");
|
||||
|
||||
if (scope == LeaderboardScope.Local)
|
||||
throw new InvalidOperationException("Should not attempt to request online scores for a local scoped leaderboard");
|
||||
|
||||
this.beatmap = beatmap;
|
||||
this.scope = scope;
|
||||
this.ruleset = ruleset ?? throw new ArgumentNullException(nameof(ruleset));
|
||||
|
||||
Success += onSuccess;
|
||||
}
|
||||
@ -33,6 +42,17 @@ namespace osu.Game.Online.API.Requests
|
||||
score.ApplyBeatmap(beatmap);
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
var req = base.CreateWebRequest();
|
||||
|
||||
req.Timeout = 30000;
|
||||
req.AddParameter(@"type", scope.ToString().ToLowerInvariant());
|
||||
req.AddParameter(@"mode", ruleset.ShortName);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores";
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
scores.IsLoading = true;
|
||||
|
||||
getScoresRequest = new GetScoresRequest(beatmap);
|
||||
getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset);
|
||||
getScoresRequest.Success += r =>
|
||||
{
|
||||
scores.Scores = r.Scores;
|
||||
|
@ -31,6 +31,8 @@ namespace osu.Game.Overlays.Mods
|
||||
protected readonly OsuSpriteText MultiplierLabel;
|
||||
private readonly FillFlowContainer footerContainer;
|
||||
|
||||
protected override bool BlockPassThroughKeyboard => false;
|
||||
|
||||
protected readonly FillFlowContainer<ModSection> ModSectionsContainer;
|
||||
|
||||
public readonly Bindable<IEnumerable<Mod>> SelectedMods = new Bindable<IEnumerable<Mod>>();
|
||||
|
@ -13,7 +13,6 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using System.Threading;
|
||||
|
||||
namespace osu.Game.Overlays.Music
|
||||
{
|
||||
@ -153,11 +152,6 @@ namespace osu.Game.Overlays.Music
|
||||
var track = beatmapBacking.Value.Track;
|
||||
|
||||
track.Restart();
|
||||
|
||||
// this is temporary until we have blocking (async.Wait()) audio component methods.
|
||||
// then we can call RestartAsync().Wait() or the blocking version above.
|
||||
while (!track.IsRunning)
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
23
osu.Game/Overlays/Settings/DangerousSettingsButton.cs
Normal file
23
osu.Game/Overlays/Settings/DangerousSettingsButton.cs
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="SettingsButton"/> with pink colours to mark dangerous/destructive actions.
|
||||
/// </summary>
|
||||
public class DangerousSettingsButton : SettingsButton
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
BackgroundColour = colours.Pink;
|
||||
|
||||
Triangles.ColourDark = colours.PinkDark;
|
||||
Triangles.ColourLight = colours.PinkLight;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
{
|
||||
public class DeleteAllBeatmapsDialog : PopupDialog
|
||||
{
|
||||
public DeleteAllBeatmapsDialog(Action deleteAction)
|
||||
{
|
||||
BodyText = "Everything?";
|
||||
|
||||
Icon = FontAwesome.fa_trash_o;
|
||||
HeaderText = @"Confirm deletion of";
|
||||
Buttons = new PopupDialogButton[]
|
||||
{
|
||||
new PopupDialogOkButton
|
||||
{
|
||||
Text = @"Yes. Go for it.",
|
||||
Action = deleteAction
|
||||
},
|
||||
new PopupDialogCancelButton
|
||||
{
|
||||
Text = @"No! Abort mission!",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -15,12 +15,12 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
private TriangleButton importButton;
|
||||
private TriangleButton deleteButton;
|
||||
private TriangleButton restoreButton;
|
||||
private TriangleButton migrateButton;
|
||||
private TriangleButton undeleteButton;
|
||||
|
||||
protected override string Header => "General";
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BeatmapManager beatmaps)
|
||||
private void load(BeatmapManager beatmaps, DialogOverlay dialogOverlay)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -34,13 +34,16 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
.ContinueWith(t => Schedule(() => importButton.Enabled.Value = true), TaskContinuationOptions.LongRunning);
|
||||
}
|
||||
},
|
||||
deleteButton = new SettingsButton
|
||||
deleteButton = new DangerousSettingsButton
|
||||
{
|
||||
Text = "Delete ALL beatmaps",
|
||||
Action = () =>
|
||||
{
|
||||
deleteButton.Enabled.Value = false;
|
||||
Task.Run(() => beatmaps.DeleteAll()).ContinueWith(t => Schedule(() => deleteButton.Enabled.Value = true));
|
||||
dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() =>
|
||||
{
|
||||
deleteButton.Enabled.Value = false;
|
||||
Task.Run(() => beatmaps.DeleteAll()).ContinueWith(t => Schedule(() => deleteButton.Enabled.Value = true));
|
||||
}));
|
||||
}
|
||||
},
|
||||
restoreButton = new SettingsButton
|
||||
@ -55,7 +58,16 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
beatmaps.Restore(b);
|
||||
}).ContinueWith(t => Schedule(() => restoreButton.Enabled.Value = true));
|
||||
}
|
||||
}
|
||||
},
|
||||
undeleteButton = new SettingsButton
|
||||
{
|
||||
Text = "Restore all recently deleted beatmaps",
|
||||
Action = () =>
|
||||
{
|
||||
undeleteButton.Enabled.Value = false;
|
||||
Task.Run(() => beatmaps.UndeleteAll()).ContinueWith(t => Schedule(() => undeleteButton.Enabled.Value = true));
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ namespace osu.Game.Overlays
|
||||
|
||||
private readonly Container contentContainer;
|
||||
|
||||
protected override bool BlockPassThroughKeyboard => true;
|
||||
|
||||
protected override Container<Drawable> Content => contentContainer;
|
||||
|
||||
protected Color4 FirstWaveColour
|
||||
|
@ -17,7 +17,6 @@ using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -327,11 +326,6 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
adjustableSourceClock.Reset();
|
||||
|
||||
// this is temporary until we have blocking (async.Wait()) audio component methods.
|
||||
// then we can call ResetAsync().Wait() or the blocking version above.
|
||||
while (adjustableSourceClock.IsRunning)
|
||||
Thread.Sleep(1);
|
||||
|
||||
Schedule(() =>
|
||||
{
|
||||
decoupledClock.ChangeSource(adjustableSourceClock);
|
||||
|
@ -52,6 +52,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
default:
|
||||
Details.Hide();
|
||||
Leaderboard.Scope = (LeaderboardScope)tab - 1;
|
||||
Leaderboard.Show();
|
||||
break;
|
||||
}
|
||||
|
@ -18,14 +18,26 @@ using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using System.Linq;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Framework.Input;
|
||||
|
||||
namespace osu.Game.Screens.Select.Leaderboards
|
||||
{
|
||||
public class Leaderboard : Container
|
||||
{
|
||||
private const double fade_duration = 200;
|
||||
|
||||
private readonly ScrollContainer scrollContainer;
|
||||
private readonly Container placeholderContainer;
|
||||
|
||||
private FillFlowContainer<LeaderboardScore> scrollFlow;
|
||||
|
||||
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
||||
|
||||
public Action<Score> ScoreSelected;
|
||||
|
||||
private readonly LoadingAnimation loading;
|
||||
@ -38,15 +50,18 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
set
|
||||
{
|
||||
scores = value;
|
||||
getScoresRequest?.Cancel();
|
||||
|
||||
scrollFlow?.FadeOut(200);
|
||||
scrollFlow?.Expire();
|
||||
scrollFlow?.FadeOut(fade_duration).Expire();
|
||||
scrollFlow = null;
|
||||
|
||||
if (scores == null)
|
||||
loading.Hide();
|
||||
|
||||
if (scores == null || !scores.Any())
|
||||
return;
|
||||
|
||||
// ensure placeholder is hidden when displaying scores
|
||||
PlaceholderState = PlaceholderState.Successful;
|
||||
|
||||
// schedule because we may not be loaded yet (LoadComponentAsync complains).
|
||||
Schedule(() =>
|
||||
{
|
||||
@ -74,6 +89,55 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
}
|
||||
}
|
||||
|
||||
private LeaderboardScope scope;
|
||||
public LeaderboardScope Scope
|
||||
{
|
||||
get { return scope; }
|
||||
set
|
||||
{
|
||||
if (value == scope) return;
|
||||
|
||||
scope = value;
|
||||
updateScores();
|
||||
}
|
||||
}
|
||||
|
||||
private PlaceholderState placeholderState;
|
||||
protected PlaceholderState PlaceholderState
|
||||
{
|
||||
get { return placeholderState; }
|
||||
set
|
||||
{
|
||||
if (value == placeholderState) return;
|
||||
|
||||
switch (placeholderState = value)
|
||||
{
|
||||
case PlaceholderState.NetworkFailure:
|
||||
replacePlaceholder(new RetrievalFailurePlaceholder
|
||||
{
|
||||
OnRetry = updateScores,
|
||||
});
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Leaderboard()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
@ -83,7 +147,14 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ScrollbarVisible = false,
|
||||
},
|
||||
loading = new LoadingAnimation()
|
||||
loading = new LoadingAnimation(),
|
||||
placeholderContainer = new Container
|
||||
{
|
||||
Alpha = 0,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -91,6 +162,8 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
|
||||
private BeatmapInfo beatmap;
|
||||
|
||||
private OsuGame osuGame;
|
||||
|
||||
private ScheduledDelegate pendingBeatmapSwitch;
|
||||
|
||||
public BeatmapInfo Beatmap
|
||||
@ -109,33 +182,115 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(APIAccess api)
|
||||
private void load(APIAccess api, OsuGame osuGame)
|
||||
{
|
||||
this.api = api;
|
||||
this.osuGame = osuGame;
|
||||
|
||||
if (osuGame != null)
|
||||
ruleset.BindTo(osuGame.Ruleset);
|
||||
|
||||
ruleset.ValueChanged += r => updateScores();
|
||||
|
||||
if (api != null)
|
||||
api.OnStateChange += handleApiStateChange;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (api != null)
|
||||
api.OnStateChange -= handleApiStateChange;
|
||||
}
|
||||
|
||||
private GetScoresRequest getScoresRequest;
|
||||
|
||||
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)
|
||||
updateScores();
|
||||
}
|
||||
|
||||
private void updateScores()
|
||||
{
|
||||
if (!IsLoaded) return;
|
||||
|
||||
Scores = null;
|
||||
getScoresRequest?.Cancel();
|
||||
getScoresRequest = null;
|
||||
Scores = null;
|
||||
|
||||
if (api == null || Beatmap?.OnlineBeatmapID == null) return;
|
||||
if (Scope == LeaderboardScope.Local)
|
||||
{
|
||||
// TODO: get local scores from wherever here.
|
||||
PlaceholderState = PlaceholderState.NoScores;
|
||||
return;
|
||||
}
|
||||
|
||||
if (api?.IsLoggedIn != true)
|
||||
{
|
||||
PlaceholderState = PlaceholderState.NotLoggedIn;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Beatmap?.OnlineBeatmapID == null)
|
||||
{
|
||||
PlaceholderState = PlaceholderState.NetworkFailure;
|
||||
return;
|
||||
}
|
||||
|
||||
PlaceholderState = PlaceholderState.Retrieving;
|
||||
loading.Show();
|
||||
|
||||
getScoresRequest = new GetScoresRequest(Beatmap);
|
||||
if (Scope != LeaderboardScope.Global && !api.LocalUser.Value.IsSupporter)
|
||||
{
|
||||
loading.Hide();
|
||||
PlaceholderState = PlaceholderState.NotSupporter;
|
||||
return;
|
||||
}
|
||||
|
||||
getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value ?? Beatmap.Ruleset, Scope);
|
||||
getScoresRequest.Success += r =>
|
||||
{
|
||||
Scores = r.Scores;
|
||||
loading.Hide();
|
||||
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
|
||||
};
|
||||
getScoresRequest.Failure += onUpdateFailed;
|
||||
|
||||
api.Queue(getScoresRequest);
|
||||
}
|
||||
|
||||
private void onUpdateFailed(Exception e)
|
||||
{
|
||||
if (e is OperationCanceledException) return;
|
||||
|
||||
PlaceholderState = PlaceholderState.NetworkFailure;
|
||||
Logger.Error(e, @"Couldn't fetch beatmap scores!");
|
||||
}
|
||||
|
||||
private void replacePlaceholder(Placeholder placeholder)
|
||||
{
|
||||
if (placeholder == null)
|
||||
{
|
||||
placeholderContainer.FadeOutFromOne(fade_duration, Easing.OutQuint);
|
||||
placeholderContainer.Clear(true);
|
||||
return;
|
||||
}
|
||||
|
||||
var existingPlaceholder = placeholderContainer.Children.FirstOrDefault() as Placeholder;
|
||||
|
||||
if (placeholder.Equals(existingPlaceholder))
|
||||
return;
|
||||
|
||||
Scores = null;
|
||||
|
||||
placeholderContainer.Clear(true);
|
||||
placeholderContainer.Child = placeholder;
|
||||
placeholderContainer.FadeInFromZero(fade_duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@ -164,5 +319,118 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class Placeholder : FillFlowContainer, IEquatable<Placeholder>
|
||||
{
|
||||
public virtual bool Equals(Placeholder other) => GetType() == other?.GetType();
|
||||
}
|
||||
|
||||
private class MessagePlaceholder : Placeholder
|
||||
{
|
||||
private readonly string message;
|
||||
|
||||
public MessagePlaceholder(string message)
|
||||
{
|
||||
Direction = FillDirection.Horizontal;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SpriteIcon
|
||||
{
|
||||
Icon = FontAwesome.fa_exclamation_circle,
|
||||
Size = new Vector2(26),
|
||||
Margin = new MarginPadding { Right = 10 },
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = this.message = message,
|
||||
TextSize = 22,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public override bool Equals(Placeholder other) => (other as MessagePlaceholder)?.message == message;
|
||||
}
|
||||
|
||||
private class RetrievalFailurePlaceholder : Placeholder
|
||||
{
|
||||
public Action OnRetry;
|
||||
|
||||
public RetrievalFailurePlaceholder()
|
||||
{
|
||||
Direction = FillDirection.Horizontal;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new RetryButton
|
||||
{
|
||||
Action = () => OnRetry?.Invoke(),
|
||||
Margin = new MarginPadding { Right = 10 },
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Text = @"Couldn't retrieve scores!",
|
||||
TextSize = 22,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private class RetryButton : OsuHoverContainer
|
||||
{
|
||||
private readonly SpriteIcon icon;
|
||||
|
||||
public Action Action;
|
||||
|
||||
public RetryButton()
|
||||
{
|
||||
Height = 26;
|
||||
Width = 26;
|
||||
Child = new OsuClickableContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Action = () => Action?.Invoke(),
|
||||
Child = icon = new SpriteIcon
|
||||
{
|
||||
Icon = FontAwesome.fa_refresh,
|
||||
Size = new Vector2(26),
|
||||
Shadow = true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
icon.ScaleTo(0.8f, 4000, Easing.OutQuint);
|
||||
return base.OnMouseDown(state, args);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
{
|
||||
icon.ScaleTo(1, 1000, Easing.OutElastic);
|
||||
return base.OnMouseUp(state, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum LeaderboardScope
|
||||
{
|
||||
Local,
|
||||
Country,
|
||||
Global,
|
||||
Friend,
|
||||
}
|
||||
|
||||
public enum PlaceholderState
|
||||
{
|
||||
Successful,
|
||||
Retrieving,
|
||||
NetworkFailure,
|
||||
NoScores,
|
||||
NotLoggedIn,
|
||||
NotSupporter,
|
||||
}
|
||||
}
|
||||
|
@ -275,7 +275,7 @@ namespace osu.Game.Screens.Select
|
||||
if (beatmap == Beatmap.Value.BeatmapInfo)
|
||||
performLoad();
|
||||
else
|
||||
selectionChangedDebounce = Scheduler.AddDelayed(performLoad, 100);
|
||||
selectionChangedDebounce = Scheduler.AddDelayed(performLoad, 200);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,7 +244,7 @@ namespace osu.Game.Tests.Visual
|
||||
if (!api.IsLoggedIn)
|
||||
return;
|
||||
|
||||
lastRequest = new GetScoresRequest(newBeatmap.BeatmapInfo);
|
||||
lastRequest = new GetScoresRequest(newBeatmap.BeatmapInfo, newBeatmap.BeatmapInfo.Ruleset);
|
||||
lastRequest.Success += res => res.Scores.ForEach(s => scores.Add(new PerformanceDisplay(s, newBeatmap.Beatmap)));
|
||||
api.Queue(lastRequest);
|
||||
}
|
||||
|
@ -268,6 +268,7 @@
|
||||
<Compile Include="Beatmaps\Formats\LegacyStoryboardDecoder.cs" />
|
||||
<Compile Include="Database\DatabaseContextFactory.cs" />
|
||||
<Compile Include="Database\IHasPrimaryKey.cs" />
|
||||
<Compile Include="Overlays\Settings\DangerousSettingsButton.cs" />
|
||||
<Compile Include="Graphics\UserInterface\HoverClickSounds.cs" />
|
||||
<Compile Include="Graphics\UserInterface\HoverSounds.cs" />
|
||||
<Compile Include="Graphics\UserInterface\OsuButton.cs" />
|
||||
@ -308,6 +309,7 @@
|
||||
<Compile Include="Overlays\Profile\Sections\Ranks\PaginatedScoreContainer.cs" />
|
||||
<Compile Include="Overlays\Profile\Sections\Ranks\DrawableTotalScore.cs" />
|
||||
<Compile Include="Overlays\Profile\Sections\Ranks\ScoreModsContainer.cs" />
|
||||
<Compile Include="Overlays\Settings\Sections\Maintenance\DeleteAllBeatmapsDialog.cs" />
|
||||
<Compile Include="Overlays\Settings\SettingsButton.cs" />
|
||||
<Compile Include="Rulesets\Edit\Layers\Selection\OriginHandle.cs" />
|
||||
<Compile Include="Rulesets\Edit\Layers\Selection\HitObjectSelectionBox.cs" />
|
||||
|
Loading…
Reference in New Issue
Block a user