1
0
Fork 0
mirror of https://github.com/ppy/osu-tools.git synced 2025-06-07 23:07:01 +09:00

Massive simplification

This commit is contained in:
Eloise 2025-01-31 17:10:50 +00:00
parent 25344481aa
commit 5f3a13052c
5 changed files with 375 additions and 615 deletions

View file

@ -1,53 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
using osuTK;
using osu.Game.Users.Drawables;
namespace PerformanceCalculatorGUI.Components
{
public partial class ExtendedCombinedProfileScore : ExtendedProfileScore
{
private APIUser User;
public ExtendedCombinedProfileScore(ExtendedScore score, APIUser user)
: base(score)
{
User = user;
}
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
AddInternal(new UpdateableAvatar(User, false)
{
Size = new Vector2(height)
});
}
protected override ExtendedProfileItemContainer ProfileScoreItems(RulesetStore rulesets)
{
var items = new ExtendedProfileItemContainer {
// This doesn't show the rounded corners on the right hand side but it's the best I could figure out, please feel free to improve it
Position = new Vector2(height, 0),
Padding = new MarginPadding { Right = height },
OnHoverAction = () =>
{
positionChangeText.Text = $"#{Score.Position.Value}";
},
OnUnhoverAction = () =>
{
positionChangeText.Text = $"{Score.PositionChange.Value:+0;-0;-}";
},
Children = ProfileScoreDrawables(rulesets)
};
return items;
}
}
}

View file

@ -25,6 +25,7 @@ using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Utils;
using osu.Game.Users.Drawables;
using osuTK;
using PerformanceCalculatorGUI.Components.TextBoxes;
@ -73,7 +74,7 @@ namespace PerformanceCalculatorGUI.Components
public partial class ExtendedProfileScore : CompositeDrawable
{
protected const int height = 35;
private const int height = 35;
private const int performance_width = 100;
private const int rank_difference_width = 35;
private const int small_text_font_size = 11;
@ -81,6 +82,8 @@ namespace PerformanceCalculatorGUI.Components
private const float performance_background_shear = 0.45f;
public readonly ExtendedScore Score;
public readonly bool ShowAvatar;
[Resolved]
private OsuColour colours { get; set; }
@ -88,11 +91,12 @@ namespace PerformanceCalculatorGUI.Components
[Resolved]
private OverlayColourProvider colourProvider { get; set; }
protected OsuSpriteText positionChangeText;
private OsuSpriteText positionChangeText;
public ExtendedProfileScore(ExtendedScore score)
public ExtendedProfileScore(ExtendedScore score, bool showAvatar = false)
{
Score = score;
ShowAvatar = showAvatar;
RelativeSizeAxes = Axes.X;
Height = height;
@ -101,14 +105,20 @@ namespace PerformanceCalculatorGUI.Components
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
AddInternal(ProfileScoreItems(rulesets));
Score.PositionChange.BindValueChanged(v => { positionChangeText.Text = $"{v.NewValue:+0;-0;-}"; });
}
protected virtual ExtendedProfileItemContainer ProfileScoreItems(RulesetStore rulesets)
{
var items = new ExtendedProfileItemContainer {
if (ShowAvatar)
{
AddInternal(new UpdateableAvatar(Score.SoloScore.User, false)
{
Size = new Vector2(height)
});
}
AddInternal(new ExtendedProfileItemContainer
{
// Resize to make room for avatar if necessary
X = ShowAvatar ? height : 0,
Padding = ShowAvatar ? new MarginPadding { Right = height } : new MarginPadding {},
OnHoverAction = () =>
{
positionChangeText.Text = $"#{Score.Position.Value}";
@ -117,263 +127,257 @@ namespace PerformanceCalculatorGUI.Components
{
positionChangeText.Text = $"{Score.PositionChange.Value:+0;-0;-}";
},
Children = ProfileScoreDrawables(rulesets)
};
return items;
}
protected Drawable[] ProfileScoreDrawables(RulesetStore rulesets)
{
Drawable[] drawables = {
new Container
Children = new Drawable[]
{
Name = "Rank difference",
RelativeSizeAxes = Axes.Y,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Width = rank_difference_width,
Child = positionChangeText = new OsuSpriteText
new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Colour = colourProvider.Light1,
Text = Score.PositionChange.Value.ToString()
}
},
new Container
{
Name = "Score info",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = rank_difference_width, Right = performance_width },
Children = new Drawable[]
{
new FillFlowContainer
Name = "Rank difference",
RelativeSizeAxes = Axes.Y,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Width = rank_difference_width,
Child = positionChangeText = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Colour = colourProvider.Light1,
Text = Score.PositionChange.Value.ToString()
}
},
new Container
{
Name = "Score info",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = rank_difference_width, Right = performance_width },
Children = new Drawable[]
{
new FillFlowContainer
{
new UpdateableRank(Score.SoloScore.Rank)
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(50, 20),
},
new FillFlowContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 0.5f),
Children = new Drawable[]
new UpdateableRank(Score.SoloScore.Rank)
{
new ScoreBeatmapMetadataContainer(Score.SoloScore.Beatmap),
new FillFlowContainer
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(50, 20),
},
new FillFlowContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 0.5f),
Children = new Drawable[]
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(15, 0),
Children = new Drawable[]
new ScoreBeatmapMetadataContainer(Score.SoloScore.Beatmap),
new FillFlowContainer
{
new OsuSpriteText
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(15, 0),
Children = new Drawable[]
{
Text = $"{Score.SoloScore.Beatmap?.DifficultyName}",
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular),
Colour = colours.Yellow
},
new DrawableDate(Score.SoloScore.EndedAt, 12)
{
Colour = colourProvider.Foreground1
new OsuSpriteText
{
Text = $"{Score.SoloScore.Beatmap?.DifficultyName}",
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular),
Colour = colours.Yellow
},
new DrawableDate(Score.SoloScore.EndedAt, 12)
{
Colour = colourProvider.Foreground1
}
}
}
}
}
}
}
},
new FillFlowContainer
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
},
new FillFlowContainer
{
new Container
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = 10, Vertical = 5 },
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Child = new FillFlowContainer
new Container
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Children = new Drawable[]
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = 10, Vertical = 5 },
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Child = new FillFlowContainer
{
new FillFlowContainer
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Children = new Drawable[]
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
new FillFlowContainer
{
new FillFlowContainer
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 110,
RelativeSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
new FillFlowContainer
{
new OsuSpriteText
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 110,
RelativeSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
Text = Score.SoloScore.Accuracy.FormatAccuracy(),
Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true),
Colour = colours.Yellow,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
},
new OsuSpriteText
{
Text = $"{Score.SoloScore.MaxCombo}x {{ {formatStatistics(Score.SoloScore.Statistics)} }}",
Font = OsuFont.GetFont(size: small_text_font_size, weight: FontWeight.Regular),
Colour = colourProvider.Light2,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
},
}
},
new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 60,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new Container
{
AutoSizeAxes = Axes.Y,
Child = new OsuSpriteText
new OsuSpriteText
{
Font = OsuFont.GetFont(weight: FontWeight.Bold),
Text = Score.LivePP != null ? $"{Score.LivePP:0}pp" : "- pp"
Text = Score.SoloScore.Accuracy.FormatAccuracy(),
Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true),
Colour = colours.Yellow,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
},
},
new OsuSpriteText
new OsuSpriteText
{
Text = $"{Score.SoloScore.MaxCombo}x {{ {formatStatistics(Score.SoloScore.Statistics)} }}",
Font = OsuFont.GetFont(size: small_text_font_size, weight: FontWeight.Regular),
Colour = colourProvider.Light2,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
},
}
},
new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 60,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
Font = OsuFont.GetFont(size: small_text_font_size),
Text = "live"
new Container
{
AutoSizeAxes = Axes.Y,
Child = new OsuSpriteText
{
Font = OsuFont.GetFont(weight: FontWeight.Bold),
Text = Score.LivePP != null ? $"{Score.LivePP:0}pp" : "- pp"
},
},
new OsuSpriteText
{
Font = OsuFont.GetFont(size: small_text_font_size),
Text = "live"
}
}
}
}
}
}
}
}
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(2),
Children = Score.SoloScore.Mods.Select(mod =>
},
new FillFlowContainer
{
var ruleset = rulesets.GetRuleset(Score.SoloScore.RulesetID) ?? throw new InvalidOperationException();
return new ModIcon(mod.ToMod(ruleset.CreateInstance()))
AutoSizeAxes = Axes.Both,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(2),
Children = Score.SoloScore.Mods.Select(mod =>
{
Scale = new Vector2(0.35f)
};
}).ToList(),
var ruleset = rulesets.GetRuleset(Score.SoloScore.RulesetID) ?? throw new InvalidOperationException();
return new ModIcon(mod.ToMod(ruleset.CreateInstance()))
{
Scale = new Vector2(0.35f)
};
}).ToList(),
}
}
}
}
}
},
new Container
{
Name = "Performance",
RelativeSizeAxes = Axes.Y,
Width = performance_width,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Children = new Drawable[]
},
new Container
{
new Box
Name = "Performance",
RelativeSizeAxes = Axes.Y,
Width = performance_width,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Children = new Drawable[]
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Colour = colourProvider.Background4,
Shear = new Vector2(-performance_background_shear, 0),
EdgeSmoothness = new Vector2(2, 0),
},
new Box
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Y,
Height = -0.5f,
Position = new Vector2(0, 1),
Colour = colourProvider.Background4,
Shear = new Vector2(performance_background_shear, 0),
EdgeSmoothness = new Vector2(2, 0),
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Padding = new MarginPadding
new Box
{
Vertical = 5,
Left = 30,
Right = 20
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Colour = colourProvider.Background4,
Shear = new Vector2(-performance_background_shear, 0),
EdgeSmoothness = new Vector2(2, 0),
},
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Direction = FillDirection.Vertical,
Children = new Drawable[]
new Box
{
new ExtendedOsuSpriteText
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Y,
Height = -0.5f,
Position = new Vector2(0, 1),
Colour = colourProvider.Background4,
Shear = new Vector2(performance_background_shear, 0),
EdgeSmoothness = new Vector2(2, 0),
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Padding = new MarginPadding
{
Font = OsuFont.GetFont(weight: FontWeight.Bold),
Text = $"{Score.SoloScore.PP:0}pp",
Colour = colourProvider.Highlight1,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
TooltipContent = $"{AttributeConversion.ToReadableString(Score.PerformanceAttributes)}"
Vertical = 5,
Left = 30,
Right = 20
},
new OsuSpriteText
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
Font = OsuFont.GetFont(size: small_text_font_size),
Text = $"{Score.SoloScore.PP - Score.LivePP:+0.0;-0.0;-}",
Colour = colourProvider.Light1,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
new ExtendedOsuSpriteText
{
Font = OsuFont.GetFont(weight: FontWeight.Bold),
Text = $"{Score.SoloScore.PP:0}pp",
Colour = colourProvider.Highlight1,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
TooltipContent = $"{AttributeConversion.ToReadableString(Score.PerformanceAttributes)}"
},
new OsuSpriteText
{
Font = OsuFont.GetFont(size: small_text_font_size),
Text = $"{Score.SoloScore.PP - Score.LivePP:+0.0;-0.0;-}",
Colour = colourProvider.Light1,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
}
}
}
}
}
}
};
});
return drawables;
Score.PositionChange.BindValueChanged(v => { positionChangeText.Text = $"{v.NewValue:+0;-0;-}"; });
}
private static string formatStatistics(Dictionary<HitResult, int> statistics)

View file

@ -102,10 +102,6 @@ namespace PerformanceCalculatorGUI
{
Action = () => setScreen(new ProfileScreen())
},
new ScreenSelectionButton("Combined Profile", FontAwesome.Solid.Users)
{
Action = () => setScreen(new CombinedProfileScreen())
},
new ScreenSelectionButton("Player Leaderboard", FontAwesome.Solid.List)
{
Action = () => setScreen(new LeaderboardScreen())

View file

@ -1,236 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osuTK.Graphics;
using PerformanceCalculatorGUI.Components;
using PerformanceCalculatorGUI.Components.TextBoxes;
using PerformanceCalculatorGUI.Configuration;
namespace PerformanceCalculatorGUI.Screens
{
public partial class CombinedProfileScreen : ProfileScreen
{
[Cached]
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Plum);
[Resolved]
private NotificationDisplay notificationDisplay { get; set; }
[Resolved]
private APIManager apiManager { get; set; }
[Resolved]
private Bindable<RulesetInfo> ruleset { get; set; }
[Resolved]
private SettingsManager configManager { get; set; }
[Resolved]
private RulesetStore rulesets { get; set; }
private SwitchButton onlyShowBest;
public CombinedProfileScreen()
{
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
usernameTextBox.Label = "Usernames";
usernameTextBox.PlaceholderText = "user1, user2, user3";
checkboxContainer.Children = new Drawable[]
{
includePinnedCheckbox = new SwitchButton
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Current = { Value = true },
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.Torus.With(weight: FontWeight.SemiBold, size: 14),
UseFullGlyphHeight = false,
Text = "Include pinned scores"
},
onlyShowBest = new SwitchButton
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Current = { Value = true },
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.Torus.With(weight: FontWeight.SemiBold, size: 14),
UseFullGlyphHeight = false,
Text = "Only display best score on each beatmap"
},
};
}
protected readonly struct ScoreWithUser
{
public readonly ExtendedScore Score;
public readonly APIUser User;
public ScoreWithUser(ExtendedScore score, APIUser user)
{
Score = score;
User = user;
}
}
protected async Task<List<ScoreWithUser>> GetPlaysWithUser(APIUser player, CancellationToken token)
{
var plays = await base.GetPlays(player, token);
var playsWithUser = new List<ScoreWithUser>();
foreach (ExtendedScore play in plays)
{
playsWithUser.Add(new ScoreWithUser(play, player));
}
return playsWithUser;
}
protected override void calculateProfile(string usernames)
{
currentUser = "";
string[] Usernames = usernames.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (Usernames.Count() < 1)
{
usernameTextBox.FlashColour(Color4.Red, 1);
return;
}
calculationCancellatonToken?.Cancel();
calculationCancellatonToken?.Dispose();
loadingLayer.Show();
calculationButton.State.Value = ButtonState.Loading;
scores.Clear();
calculationCancellatonToken = new CancellationTokenSource();
var token = calculationCancellatonToken.Token;
Task.Run(async () =>
{
Schedule(() =>
{
sortingTabControl.Alpha = 1.0f;
sortingTabControl.Current.Value = ProfileSortCriteria.Local;
layout.RowDimensions = new[]
{
new Dimension(GridSizeMode.Absolute, username_container_height),
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.AutoSize),
new Dimension()
};
});
if (token.IsCancellationRequested)
return;
var allPlays = new List<ScoreWithUser>();
Schedule(() => loadingLayer.Text.Value = "Getting user data...");
foreach (string username in Usernames)
{
var player = await apiManager.GetJsonFromApi<APIUser>($"users/{username}/{ruleset.Value.ShortName}");
// Append player username to current user string
currentUser += (currentUser == "") ? player.Username : (", " + player.Username);
System.Console.WriteLine(currentUser);
var plays = await GetPlaysWithUser(player, token);
allPlays = allPlays.Concat(plays).ToList();
}
if (token.IsCancellationRequested)
return;
if (onlyShowBest.Current.Value)
{
Schedule(() => loadingLayer.Text.Value = "Filtering scores");
var filteredPlays = new List<ScoreWithUser>();
// List of every beatmap ID in combined plays without duplicates
List<int> beatmapIDs = allPlays.Select(x => x.Score.SoloScore.BeatmapID).Distinct().ToList();
foreach (int ID in beatmapIDs)
{
List<ScoreWithUser> playsOnBeatmap = allPlays.Where(x => x.Score.SoloScore.BeatmapID == ID).OrderByDescending(x => x.Score.SoloScore.PP).ToList();
ScoreWithUser bestPlayOnBeatmap = playsOnBeatmap.First();
filteredPlays.Add(bestPlayOnBeatmap);
}
allPlays = filteredPlays;
}
if (token.IsCancellationRequested)
return;
var localOrdered = allPlays.OrderByDescending(x => x.Score.SoloScore.PP).ToList();
var liveOrdered = allPlays.OrderByDescending(x => x.Score.LivePP ?? 0).ToList();
Schedule(() =>
{
foreach (var play in allPlays)
{
scores.Add(new ExtendedCombinedProfileScore(play.Score, play.User));
var score = play.Score;
if (score.LivePP != null)
{
score.Position.Value = localOrdered.IndexOf(play) + 1;
score.PositionChange.Value = liveOrdered.IndexOf(play) - localOrdered.IndexOf(play);
}
}
});
}, token).ContinueWith(t =>
{
Logger.Log(t.Exception?.ToString(), level: LogLevel.Error);
notificationDisplay.Display(new Notification(t.Exception?.Flatten().Message));
}, TaskContinuationOptions.OnlyOnFaulted).ContinueWith(t =>
{
Schedule(() =>
{
loadingLayer.Hide();
calculationButton.State.Value = ButtonState.Done;
updateSorting(ProfileSortCriteria.Local);
});
}, TaskContinuationOptions.None);
}
}
}

View file

@ -36,25 +36,24 @@ namespace PerformanceCalculatorGUI.Screens
[Cached]
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Plum);
protected StatefulButton calculationButton;
protected VerboseLoadingLayer loadingLayer;
private StatefulButton calculationButton;
private SwitchButton includePinnedCheckbox;
private SwitchButton onlyDisplayBestCheckbox;
private VerboseLoadingLayer loadingLayer;
protected FillFlowContainer checkboxContainer;
protected SwitchButton includePinnedCheckbox;
private GridContainer layout;
protected GridContainer layout;
private FillFlowContainer<ExtendedProfileScore> scores;
protected FillFlowContainer<ExtendedProfileScore> scores;
protected LabelledTextBox usernameTextBox;
private LabelledTextBox usernameTextBox;
private Container userPanelContainer;
private UserCard userPanel;
protected string currentUser;
private string currentUsers;
protected CancellationTokenSource calculationCancellatonToken;
private CancellationTokenSource calculationCancellatonToken;
protected OverlaySortTabControl<ProfileSortCriteria> sortingTabControl;
private OverlaySortTabControl<ProfileSortCriteria> sortingTabControl;
private readonly Bindable<ProfileSortCriteria> sorting = new Bindable<ProfileSortCriteria>(ProfileSortCriteria.Local);
[Resolved]
@ -74,7 +73,7 @@ namespace PerformanceCalculatorGUI.Screens
public override bool ShouldShowConfirmationDialogOnSwitch => false;
protected const float username_container_height = 40;
private const float username_container_height = 40;
public ProfileScreen()
{
@ -123,8 +122,8 @@ namespace PerformanceCalculatorGUI.Screens
{
RelativeSizeAxes = Axes.X,
Anchor = Anchor.TopLeft,
Label = "Username",
PlaceholderText = "peppy",
Label = "Username(s)",
PlaceholderText = "peppy, rloseise, peppy2",
CommitOnFocusLoss = false
},
calculationButton = new StatefulButton("Start calculation")
@ -153,7 +152,7 @@ namespace PerformanceCalculatorGUI.Screens
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
checkboxContainer = new FillFlowContainer
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
@ -174,6 +173,20 @@ namespace PerformanceCalculatorGUI.Screens
Font = OsuFont.Torus.With(weight: FontWeight.SemiBold, size: 14),
UseFullGlyphHeight = false,
Text = "Include pinned scores"
},
onlyDisplayBestCheckbox = new SwitchButton
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Current = { Value = true },
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.Torus.With(weight: FontWeight.SemiBold, size: 14),
UseFullGlyphHeight = false,
Text = "Only display best score on each beatmap"
}
}
},
@ -211,64 +224,25 @@ namespace PerformanceCalculatorGUI.Screens
usernameTextBox.OnCommit += (_, _) => { calculateProfile(usernameTextBox.Current.Value); };
sorting.ValueChanged += e => { updateSorting(e.NewValue); };
includePinnedCheckbox.Current.ValueChanged += e => { calculateProfile(currentUser); };
includePinnedCheckbox.Current.ValueChanged += e => { calculateProfile(currentUsers); };
onlyDisplayBestCheckbox.Current.ValueChanged += e => { calculateProfile(currentUsers); };
if (RuntimeInfo.IsDesktop)
HotReloadCallbackReceiver.CompilationFinished += _ => Schedule(() => { calculateProfile(currentUser); });
HotReloadCallbackReceiver.CompilationFinished += _ => Schedule(() => { calculateProfile(currentUsers); });
}
protected async Task<List<ExtendedScore>> GetPlays(APIUser player, CancellationToken token)
private void calculateProfile(string usernames)
{
var plays = new List<ExtendedScore>();
var rulesetInstance = ruleset.Value.CreateInstance();
Schedule(() => loadingLayer.Text.Value = $"Calculating {player.Username} top scores...");
var apiScores = await apiManager.GetJsonFromApi<List<SoloScoreInfo>>($"users/{player.OnlineID}/scores/best?mode={ruleset.Value.ShortName}&limit=100");
if (includePinnedCheckbox.Current.Value)
{
var pinnedScores = await apiManager.GetJsonFromApi<List<SoloScoreInfo>>($"users/{player.OnlineID}/scores/pinned?mode={ruleset.Value.ShortName}&limit=100");
apiScores = apiScores.Concat(pinnedScores.Where(p => !apiScores.Any(b => b.ID == p.ID))).ToList();
}
foreach (var score in apiScores)
{
if (token.IsCancellationRequested)
return new List<ExtendedScore>();
var working = ProcessorWorkingBeatmap.FromFileOrId(score.BeatmapID.ToString(), cachePath: configManager.GetBindable<string>(Settings.CachePath).Value);
Schedule(() => loadingLayer.Text.Value = $"Calculating {working.Metadata}");
Mod[] mods = score.Mods.Select(x => x.ToMod(rulesetInstance)).ToArray();
var scoreInfo = score.ToScoreInfo(rulesets, working.BeatmapInfo);
var parsedScore = new ProcessorScoreDecoder(working).Parse(scoreInfo);
var difficultyCalculator = rulesetInstance.CreateDifficultyCalculator(working);
var difficultyAttributes = difficultyCalculator.Calculate(mods);
var performanceCalculator = rulesetInstance.CreatePerformanceCalculator();
double? livePp = score.PP;
var perfAttributes = await performanceCalculator?.CalculateAsync(parsedScore.ScoreInfo, difficultyAttributes, token)!;
score.PP = perfAttributes?.Total ?? 0.0;
var extendedScore = new ExtendedScore(score, livePp, perfAttributes);
plays.Add(extendedScore);
}
return plays;
}
protected virtual void calculateProfile(string username)
{
if (string.IsNullOrEmpty(username))
if (string.IsNullOrEmpty(usernames))
{
usernameTextBox.FlashColour(Color4.Red, 1);
return;
}
currentUsers = "";
string[] Usernames = usernames.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
bool calculatingSingleProfile = Usernames.Count() <= 1;
calculationCancellatonToken?.Cancel();
calculationCancellatonToken?.Dispose();
@ -283,22 +257,11 @@ namespace PerformanceCalculatorGUI.Screens
Task.Run(async () =>
{
Schedule(() => loadingLayer.Text.Value = "Getting user data...");
var player = await apiManager.GetJsonFromApi<APIUser>($"users/{username}/{ruleset.Value.ShortName}");
currentUser = player.Username;
Schedule(() =>
Schedule(() =>
{
if (userPanel != null)
userPanelContainer.Remove(userPanel, true);
userPanelContainer.Add(userPanel = new UserCard(player)
{
RelativeSizeAxes = Axes.X
});
if (userPanel != null)
userPanelContainer.Remove(userPanel, true);
sortingTabControl.Alpha = 1.0f;
sortingTabControl.Current.Value = ProfileSortCriteria.Local;
@ -310,14 +273,95 @@ namespace PerformanceCalculatorGUI.Screens
new Dimension()
};
});
if (token.IsCancellationRequested)
return;
var plays = new List<ExtendedScore>();
var players = new List<APIUser>();
var rulesetInstance = ruleset.Value.CreateInstance();
foreach (string username in Usernames)
{
Schedule(() => loadingLayer.Text.Value = "Getting user data...");
var player = await apiManager.GetJsonFromApi<APIUser>($"users/{username}/{ruleset.Value.ShortName}");
players.Add(player);
// Append player username to current user(s) string
currentUsers += (currentUsers == "") ? player.Username : (", " + player.Username);
// Add user card if only calculating single profile
if (calculatingSingleProfile)
{
Schedule(() =>
{
userPanelContainer.Add(userPanel = new UserCard(player)
{
RelativeSizeAxes = Axes.X
});
});
}
Schedule(() => loadingLayer.Text.Value = $"Calculating {player.Username} top scores...");
var apiScores = await apiManager.GetJsonFromApi<List<SoloScoreInfo>>($"users/{player.OnlineID}/scores/best?mode={ruleset.Value.ShortName}&limit=100");
if (includePinnedCheckbox.Current.Value)
{
var pinnedScores = await apiManager.GetJsonFromApi<List<SoloScoreInfo>>($"users/{player.OnlineID}/scores/pinned?mode={ruleset.Value.ShortName}&limit=100");
apiScores = apiScores.Concat(pinnedScores.Where(p => !apiScores.Any(b => b.ID == p.ID))).ToList();
}
foreach (var score in apiScores)
{
if (token.IsCancellationRequested)
return;
var working = ProcessorWorkingBeatmap.FromFileOrId(score.BeatmapID.ToString(), cachePath: configManager.GetBindable<string>(Settings.CachePath).Value);
Schedule(() => loadingLayer.Text.Value = $"Calculating {working.Metadata}");
Mod[] mods = score.Mods.Select(x => x.ToMod(rulesetInstance)).ToArray();
var scoreInfo = score.ToScoreInfo(rulesets, working.BeatmapInfo);
var parsedScore = new ProcessorScoreDecoder(working).Parse(scoreInfo);
var difficultyCalculator = rulesetInstance.CreateDifficultyCalculator(working);
var difficultyAttributes = difficultyCalculator.Calculate(mods);
var performanceCalculator = rulesetInstance.CreatePerformanceCalculator();
double? livePp = score.PP;
var perfAttributes = await performanceCalculator?.CalculateAsync(parsedScore.ScoreInfo, difficultyAttributes, token)!;
score.PP = perfAttributes?.Total ?? 0.0;
var extendedScore = new ExtendedScore(score, livePp, perfAttributes);
plays.Add(extendedScore);
}
}
if (token.IsCancellationRequested)
return;
// Filter plays if only displaying best score on each beatmap
if (onlyDisplayBestCheckbox.Current.Value)
{
Schedule(() => loadingLayer.Text.Value = $"Filtering plays");
var filteredPlays = new List<ExtendedScore>();
// List of all beatmap IDs in plays without duplicates
var beatmapIDs = plays.Select(x => x.SoloScore.BeatmapID).Distinct().ToList();
var plays = await GetPlays(player, token);
if (token.IsCancellationRequested)
return;
foreach (int ID in beatmapIDs)
{
var bestPlayOnBeatmap = plays.Where(x => x.SoloScore.BeatmapID == ID).OrderByDescending(x => x.SoloScore.PP).First();
filteredPlays.Add(bestPlayOnBeatmap);
}
plays = filteredPlays;
}
var localOrdered = plays.OrderByDescending(x => x.SoloScore.PP).ToList();
var liveOrdered = plays.OrderByDescending(x => x.LivePP ?? 0).ToList();
@ -326,8 +370,8 @@ namespace PerformanceCalculatorGUI.Screens
{
foreach (var play in plays)
{
scores.Add(new ExtendedProfileScore(play));
scores.Add(new ExtendedProfileScore(play, !calculatingSingleProfile));
if (play.LivePP != null)
{
play.Position.Value = localOrdered.IndexOf(play) + 1;
@ -336,29 +380,34 @@ namespace PerformanceCalculatorGUI.Screens
}
});
decimal totalLocalPP = 0;
for (var i = 0; i < localOrdered.Count; i++)
totalLocalPP += (decimal)(Math.Pow(0.95, i) * (localOrdered[i].SoloScore.PP ?? 0));
if (calculatingSingleProfile)
{
var player = players.First();
decimal totalLocalPP = 0;
for (var i = 0; i < localOrdered.Count; i++)
totalLocalPP += (decimal)(Math.Pow(0.95, i) * (localOrdered[i].SoloScore.PP ?? 0));
decimal totalLivePP = player.Statistics.PP ?? (decimal)0.0;
decimal totalLivePP = player.Statistics.PP ?? (decimal)0.0;
decimal nonBonusLivePP = 0;
for (var i = 0; i < liveOrdered.Count; i++)
nonBonusLivePP += (decimal)(Math.Pow(0.95, i) * liveOrdered[i].LivePP ?? 0);
decimal nonBonusLivePP = 0;
for (var i = 0; i < liveOrdered.Count; i++)
nonBonusLivePP += (decimal)(Math.Pow(0.95, i) * liveOrdered[i].LivePP ?? 0);
//todo: implement properly. this is pretty damn wrong.
var playcountBonusPP = (totalLivePP - nonBonusLivePP);
totalLocalPP += playcountBonusPP;
//todo: implement properly. this is pretty damn wrong.
var playcountBonusPP = (totalLivePP - nonBonusLivePP);
totalLocalPP += playcountBonusPP;
Schedule(() =>
{
userPanel.Data.Value = new UserCardData
{
LivePP = totalLivePP,
LocalPP = totalLocalPP,
PlaycountPP = playcountBonusPP
};
});
Schedule(() =>
{
userPanel.Data.Value = new UserCardData
{
LivePP = totalLivePP,
LocalPP = totalLocalPP,
PlaycountPP = playcountBonusPP
};
});
}
}, token).ContinueWith(t =>
{
Logger.Log(t.Exception?.ToString(), level: LogLevel.Error);
@ -393,7 +442,7 @@ namespace PerformanceCalculatorGUI.Screens
return base.OnKeyDown(e);
}
protected void updateSorting(ProfileSortCriteria sortCriteria)
private void updateSorting(ProfileSortCriteria sortCriteria)
{
if (!scores.Children.Any())
return;