mirror of
https://github.com/ppy/osu-tools.git
synced 2025-06-09 09:35:15 +09:00
273 lines
11 KiB
C#
273 lines
11 KiB
C#
// 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.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using osu.Framework;
|
|
using osu.Framework.Allocation;
|
|
using osu.Framework.Bindables;
|
|
using osu.Framework.Graphics;
|
|
using osu.Framework.Graphics.Containers;
|
|
using osu.Game.Graphics.Containers;
|
|
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 osu.Game.Rulesets.Scoring;
|
|
using osu.Game.Scoring;
|
|
using osu.Game.Scoring.Legacy;
|
|
using osuTK.Graphics;
|
|
using PerformanceCalculatorGUI.Components;
|
|
using PerformanceCalculatorGUI.Configuration;
|
|
|
|
namespace PerformanceCalculatorGUI.Screens
|
|
{
|
|
internal class ProfileScreen : PerformanceCalculatorScreen
|
|
{
|
|
[Cached]
|
|
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Plum);
|
|
|
|
private StatefulButton calculationButton;
|
|
private VerboseLoadingLayer loadingLayer;
|
|
|
|
private GridContainer layout;
|
|
|
|
private FillFlowContainer scores;
|
|
|
|
private LabelledTextBox usernameTextBox;
|
|
private Container userPanelContainer;
|
|
private UserPPListPanel userPanel;
|
|
|
|
private string currentUser;
|
|
|
|
[Resolved]
|
|
private APIManager apiManager { get; set; }
|
|
|
|
[Resolved]
|
|
private Bindable<RulesetInfo> ruleset { get; set; }
|
|
|
|
[Resolved]
|
|
private SettingsManager configManager { get; set; }
|
|
|
|
public override bool ShouldShowConfirmationDialogOnSwitch => false;
|
|
|
|
private const float username_container_height = 40;
|
|
|
|
public ProfileScreen()
|
|
{
|
|
RelativeSizeAxes = Axes.Both;
|
|
}
|
|
|
|
[BackgroundDependencyLoader]
|
|
private void load()
|
|
{
|
|
InternalChildren = new Drawable[]
|
|
{
|
|
layout = new GridContainer
|
|
{
|
|
RelativeSizeAxes = Axes.Both,
|
|
ColumnDimensions = new[] { new Dimension() },
|
|
RowDimensions = new[] { new Dimension(GridSizeMode.Absolute, username_container_height), new Dimension(GridSizeMode.Absolute), new Dimension() },
|
|
Content = new[]
|
|
{
|
|
new Drawable[]
|
|
{
|
|
new GridContainer
|
|
{
|
|
Name = "Settings",
|
|
Height = username_container_height,
|
|
RelativeSizeAxes = Axes.X,
|
|
ColumnDimensions = new[]
|
|
{
|
|
new Dimension(),
|
|
new Dimension(GridSizeMode.AutoSize)
|
|
},
|
|
RowDimensions = new[]
|
|
{
|
|
new Dimension(GridSizeMode.AutoSize)
|
|
},
|
|
Content = new[]
|
|
{
|
|
new Drawable[]
|
|
{
|
|
usernameTextBox = new ExtendedLabelledTextBox
|
|
{
|
|
RelativeSizeAxes = Axes.X,
|
|
Anchor = Anchor.TopLeft,
|
|
Label = "Username",
|
|
PlaceholderText = "peppy",
|
|
CommitOnFocusLoss = false
|
|
},
|
|
calculationButton = new StatefulButton("Start calculation")
|
|
{
|
|
Width = 150,
|
|
Height = username_container_height,
|
|
Action = () => { calculateProfile(usernameTextBox.Current.Value); }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
},
|
|
new Drawable[]
|
|
{
|
|
userPanelContainer = new Container
|
|
{
|
|
RelativeSizeAxes = Axes.X,
|
|
AutoSizeAxes = Axes.Y
|
|
}
|
|
},
|
|
new Drawable[]
|
|
{
|
|
new OsuScrollContainer(Direction.Vertical)
|
|
{
|
|
RelativeSizeAxes = Axes.Both,
|
|
Child = scores = new FillFlowContainer
|
|
{
|
|
RelativeSizeAxes = Axes.X,
|
|
AutoSizeAxes = Axes.Y,
|
|
Direction = FillDirection.Vertical
|
|
}
|
|
}
|
|
},
|
|
}
|
|
},
|
|
loadingLayer = new VerboseLoadingLayer(true)
|
|
{
|
|
RelativeSizeAxes = Axes.Both
|
|
}
|
|
};
|
|
|
|
usernameTextBox.OnCommit += (_, _) => { calculateProfile(usernameTextBox.Current.Value); };
|
|
|
|
if (RuntimeInfo.IsDesktop)
|
|
HotReloadCallbackReceiver.CompilationFinished += _ => Schedule(() => { calculateProfile(currentUser); });
|
|
}
|
|
|
|
private void calculateProfile(string username)
|
|
{
|
|
if (string.IsNullOrEmpty(username))
|
|
{
|
|
usernameTextBox.FlashColour(Color4.Red, 1);
|
|
return;
|
|
}
|
|
|
|
loadingLayer.Show();
|
|
calculationButton.State.Value = ButtonState.Loading;
|
|
|
|
scores.Clear();
|
|
|
|
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(() =>
|
|
{
|
|
if (userPanel != null)
|
|
userPanelContainer.Remove(userPanel);
|
|
|
|
userPanelContainer.Add(userPanel = new UserPPListPanel(player)
|
|
{
|
|
RelativeSizeAxes = Axes.X
|
|
});
|
|
|
|
layout.RowDimensions = new[] { new Dimension(GridSizeMode.Absolute, username_container_height), new Dimension(GridSizeMode.AutoSize), new Dimension() };
|
|
});
|
|
|
|
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<APIScore>>($"users/{player.OnlineID}/scores/best?mode={ruleset.Value.ShortName}&limit=100");
|
|
|
|
foreach (var score in apiScores)
|
|
{
|
|
var working = ProcessorWorkingBeatmap.FromFileOrId(score.Beatmap?.OnlineID.ToString(), cachePath: configManager.GetBindable<string>(Settings.CachePath).Value);
|
|
|
|
Schedule(() => loadingLayer.Text.Value = $"Calculating {working.Metadata}");
|
|
|
|
var modsAcronyms = score.Mods.Select(x => x.ToString()).ToArray();
|
|
Mod[] mods = rulesetInstance.CreateAllMods().Where(m => modsAcronyms.Contains(m.Acronym)).ToArray();
|
|
|
|
var scoreInfo = new ScoreInfo(working.BeatmapInfo, ruleset.Value)
|
|
{
|
|
TotalScore = score.TotalScore,
|
|
MaxCombo = score.MaxCombo,
|
|
Mods = mods,
|
|
Statistics = new Dictionary<HitResult, int>()
|
|
};
|
|
|
|
scoreInfo.SetCount300(score.Statistics["count_300"]);
|
|
scoreInfo.SetCountGeki(score.Statistics["count_geki"]);
|
|
scoreInfo.SetCount100(score.Statistics["count_100"]);
|
|
scoreInfo.SetCountKatu(score.Statistics["count_katu"]);
|
|
scoreInfo.SetCount50(score.Statistics["count_50"]);
|
|
scoreInfo.SetCountMiss(score.Statistics["count_miss"]);
|
|
|
|
var parsedScore = new ProcessorScoreDecoder(working).Parse(scoreInfo);
|
|
|
|
var difficultyCalculator = rulesetInstance.CreateDifficultyCalculator(working);
|
|
var difficultyAttributes = difficultyCalculator.Calculate(RulesetHelper.ConvertToLegacyDifficultyAdjustmentMods(rulesetInstance, mods));
|
|
var performanceCalculator = rulesetInstance.CreatePerformanceCalculator();
|
|
|
|
var livePp = score.PP ?? 0.0;
|
|
var perfAttributes = performanceCalculator?.Calculate(parsedScore.ScoreInfo, difficultyAttributes);
|
|
score.PP = perfAttributes?.Total ?? 0.0;
|
|
|
|
var extendedScore = new ExtendedScore(score, livePp, perfAttributes);
|
|
plays.Add(extendedScore);
|
|
|
|
Schedule(() => scores.Add(new ExtendedProfileScore(extendedScore)));
|
|
}
|
|
|
|
var localOrdered = plays.OrderByDescending(x => x.PP).ToList();
|
|
var liveOrdered = plays.OrderByDescending(x => x.LivePP).ToList();
|
|
|
|
Schedule(() =>
|
|
{
|
|
foreach (var play in plays)
|
|
{
|
|
play.PositionChange.Value = liveOrdered.IndexOf(play) - localOrdered.IndexOf(play);
|
|
scores.SetLayoutPosition(scores[liveOrdered.IndexOf(play)], localOrdered.IndexOf(play));
|
|
}
|
|
});
|
|
|
|
int index = 0;
|
|
decimal totalLocalPP = (decimal)localOrdered.Select(x=> x.PP).Sum(play => Math.Pow(0.95, index++) * play);
|
|
decimal totalLivePP = player.Statistics.PP ?? (decimal)0.0;
|
|
|
|
index = 0;
|
|
decimal nonBonusLivePP = (decimal)liveOrdered.Select(x => x.LivePP).Sum(play => Math.Pow(0.95, index++) * play);
|
|
|
|
//todo: implement properly. this is pretty damn wrong.
|
|
var playcountBonusPP = (totalLivePP - nonBonusLivePP);
|
|
totalLocalPP += playcountBonusPP;
|
|
|
|
Schedule(() =>
|
|
{
|
|
userPanel.Data.Value = new UserPPListPanelData
|
|
{
|
|
LivePP = totalLivePP,
|
|
LocalPP = totalLocalPP,
|
|
PlaycountPP = playcountBonusPP
|
|
};
|
|
});
|
|
}).ContinueWith(t =>
|
|
{
|
|
Schedule(() =>
|
|
{
|
|
loadingLayer.Hide();
|
|
calculationButton.State.Value = ButtonState.Done;
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}
|