Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion PerformanceCalculatorGUI/Components/ExtendedProfileScore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,16 @@ public class ExtendedScore
public PerformanceAttributes? PerformanceAttributes { get; }
public DifficultyAttributes DifficultyAttributes { get; }

public ExtendedScore(SoloScoreInfo score, DifficultyAttributes difficultyAttributes, PerformanceAttributes? performanceAttributes)
public string? StoredScoreId { get; }
public bool IsStoredScore => StoredScoreId != null;

public ExtendedScore(SoloScoreInfo score, DifficultyAttributes difficultyAttributes, PerformanceAttributes? performanceAttributes, string? storedScoreId = null)
{
SoloScore = score;
PerformanceAttributes = performanceAttributes;
DifficultyAttributes = difficultyAttributes;
LivePP = score.PP;
StoredScoreId = storedScoreId;
}
}

Expand Down
1 change: 1 addition & 0 deletions PerformanceCalculatorGUI/Screens/Collections/Collection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ public class Collection
public required string FileName { get; set; }
public required string Name { get; set; }
public required long[] Scores { get; set; }
public StoredScore[]? StoredScores { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public partial class ScoreContainer : Container

private readonly IconButton deleteButton;

public delegate void OnDeleteHandler(long scoreId);
public delegate void OnDeleteHandler(ExtendedScore score);

public event OnDeleteHandler? OnDelete;

Expand All @@ -43,7 +43,7 @@ public ScoreContainer(ExtendedScore score)
Icon = FontAwesome.Regular.TrashAlt,
Action = () =>
{
OnDelete?.Invoke((long)score.SoloScore.ID!);
OnDelete?.Invoke(score);
}
},
new ExtendedProfileScore(score, true)
Expand Down
70 changes: 70 additions & 0 deletions PerformanceCalculatorGUI/Screens/Collections/StoredScore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// 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 osu.Game.Database;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Scoring;

namespace PerformanceCalculatorGUI.Screens.Collections
{
public class StoredScore
{
public string Id { get; set; } = Guid.NewGuid().ToString();

public int BeatmapID { get; set; }

public int RulesetID { get; set; }

public double Accuracy { get; set; }

public int MaxCombo { get; set; }

public Dictionary<HitResult, int> Statistics { get; set; } = new Dictionary<HitResult, int>();

public APIMod[] Mods { get; set; } = Array.Empty<APIMod>();

public long TotalScore { get; set; }

public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.Now;

public SoloScoreInfo ToSoloScoreInfo(ProcessorWorkingBeatmap working)
{
var metadata = working.BeatmapInfo.Metadata;
var rulesetInstance = RulesetHelper.GetRulesetFromLegacyID(RulesetID);
var mods = Mods.Select(m => m.ToMod(rulesetInstance)).ToArray();
var rank = StandardisedScoreMigrationTools.ComputeRank(Accuracy, Statistics, mods, rulesetInstance.CreateScoreProcessor());

return new SoloScoreInfo
{
BeatmapID = BeatmapID,
RulesetID = RulesetID,
Accuracy = Accuracy,
MaxCombo = MaxCombo,
Statistics = new Dictionary<HitResult, int>(Statistics),
Mods = Mods,
TotalScore = TotalScore,
Passed = true,
Rank = rank,
EndedAt = CreatedAt,
Beatmap = new APIBeatmap
{
OnlineID = working.BeatmapInfo.OnlineID,
DifficultyName = working.BeatmapInfo.DifficultyName,
RulesetID = RulesetID,
BeatmapSet = new APIBeatmapSet
{
OnlineID = working.BeatmapInfo.BeatmapSet?.OnlineID ?? 0,
Title = metadata.Title,
TitleUnicode = metadata.TitleUnicode,
Artist = metadata.Artist,
ArtistUnicode = metadata.ArtistUnicode
}
}
};
}
}
}
61 changes: 57 additions & 4 deletions PerformanceCalculatorGUI/Screens/CollectionsScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,21 @@ private void onScoreAdd(long scoreId)
return;
}

currentCollection.Value.Scores = [..currentCollection.Value.Scores, scoreId];
currentCollection.Value.Scores = [.. currentCollection.Value.Scores, scoreId];

saveCurrentCollection();
}

private void onScoreRemove(long scoreId)
private void onScoreRemove(ExtendedScore score)
{
currentCollection.Value!.Scores = currentCollection.Value.Scores.Where(x => x != scoreId).ToArray();
if (score.IsStoredScore)
{
currentCollection.Value!.StoredScores = currentCollection.Value.StoredScores?.Where(x => x.Id != score.StoredScoreId).ToArray();
}
else
{
currentCollection.Value!.Scores = currentCollection.Value.Scores.Where(x => x != (long)score.SoloScore.ID!).ToArray();
}

saveCurrentCollection();
}
Expand Down Expand Up @@ -297,6 +304,39 @@ private void calculateScores()
scoresList.Add(scoreContainer);
});
}

if (currentCollection.Value.StoredScores != null)
{
foreach (var storedScore in currentCollection.Value.StoredScores)
{
var rulesetInstance = rulesets.GetRuleset(storedScore.RulesetID)!.CreateInstance();

var working = ProcessorWorkingBeatmap.FromFileOrId(storedScore.BeatmapID.ToString(), cachePath: configManager.GetBindable<string>(Settings.CachePath).Value);

var soloScore = storedScore.ToSoloScoreInfo(working);

Mod[] mods = soloScore.Mods.Select(x => x.ToMod(rulesetInstance)).ToArray();

var scoreInfo = soloScore.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();
if (performanceCalculator == null)
continue;

var perfAttributes = performanceCalculator.Calculate(parsedScore.ScoreInfo, difficultyAttributes);
Schedule(() =>
{
var scoreContainer = new ScoreContainer(new ExtendedScore(soloScore, difficultyAttributes, perfAttributes, storedScore.Id));
scoreContainer.OnDelete += onScoreRemove;

scoresList.Add(scoreContainer);
});
}
}
}).ContinueWith(t =>
{
Logger.Log(t.Exception?.ToString(), level: LogLevel.Error);
Expand Down Expand Up @@ -386,9 +426,22 @@ private void updateSorting(CollectionSortCriteria sortCriteria)

if (sortCriteria == CollectionSortCriteria.None)
{
int onlineCount = currentCollection.Value!.Scores.Length;

for (int i = 0; i < scoresList.Count; i++)
{
scoresList.SetLayoutPosition(scoresList[i], Array.IndexOf(currentCollection.Value!.Scores, scoresList[i].Score.SoloScore.ID));
var container = scoresList[i];

if (container.Score.IsStoredScore)
{
var storedScores = currentCollection.Value.StoredScores;
int storedIndex = storedScores != null ? Array.FindIndex(storedScores, s => s.Id == container.Score.StoredScoreId) : 0;
scoresList.SetLayoutPosition(container, onlineCount + storedIndex);
}
else
{
scoresList.SetLayoutPosition(container, Array.IndexOf(currentCollection.Value.Scores, (long)container.Score.SoloScore.ID!));
}
}

return;
Expand Down
138 changes: 138 additions & 0 deletions PerformanceCalculatorGUI/Screens/Simulate/AddToCollectionButton.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// 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.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.UserInterfaceV2;
using PerformanceCalculatorGUI.Screens.Collections;

namespace PerformanceCalculatorGUI.Screens.Simulate
{
public partial class AddToCollectionButton : Container
{
private readonly RoundedButton addButton;
private readonly FillFlowContainer collectionListContainer;
private readonly FillFlowContainer collectionList;
private readonly CreateCollectionButton createCollectionButton;

public delegate void OnAddHandler(Collection collection);

public event OnAddHandler? OnAdd;

private const int button_height = 40;
private const int fade_duration = 200;
private const string collections_directory = "collections";

private readonly Bindable<Collection?> selectedCollection = new Bindable<Collection?>();

public AddToCollectionButton()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;

Children = new Drawable[]
{
addButton = new RoundedButton
{
RelativeSizeAxes = Axes.X,
Text = "Add to Collection",
Height = button_height,
Action = showSelection
},
collectionListContainer = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new osuTK.Vector2(0, 2f),
Alpha = 0,
Children = new Drawable[]
{
collectionList = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
},
createCollectionButton = new CreateCollectionButton()
}
}
};

selectedCollection.ValueChanged += e =>
{
if (e.NewValue == null)
return;

collectionListContainer.FadeOut(fade_duration);
addButton.FadeIn(fade_duration);

OnAdd?.Invoke(e.NewValue);

selectedCollection.Value = null;
};

createCollectionButton.OnSave += onCollectionCreated;
}

private void showSelection()
{
loadCollectionList();

collectionListContainer.FadeIn(fade_duration);
addButton.FadeOut(fade_duration);
}

private void loadCollectionList()
{
collectionList.Clear();

if (!Directory.Exists(collections_directory))
{
Directory.CreateDirectory(collections_directory);
return;
}

var collections = new List<Collection>();

foreach (string collectionFile in Directory.EnumerateFiles(collections_directory))
{
var collection = JsonConvert.DeserializeObject<Collection>(File.ReadAllText(collectionFile));

if (collection != null)
collections.Add(collection);
}

foreach (var collection in collections.OrderBy(x => x.Name))
{
collectionList.Add(new CollectionButton(collection, selectedCollection));
}
}

private void onCollectionCreated(string name)
{
string fileName = RandomNumberGenerator.GetString(choices: "abcdefghijklmnopqrstuvwxyz0123456789", length: 16) + ".json";

var collection = new Collection
{
Name = name,
FileName = fileName,
Scores = []
};

if (!Directory.Exists(collections_directory))
Directory.CreateDirectory(collections_directory);

string path = Path.Combine(collections_directory, fileName);
File.WriteAllText(path, JsonConvert.SerializeObject(collection));

loadCollectionList();
}
}
}
Loading
Loading