1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 11:37:28 +08:00

Refactoring

This commit is contained in:
Dan Balasescu 2023-06-28 15:04:13 +09:00
parent e291dff5ad
commit 09bc8e45de
18 changed files with 133 additions and 108 deletions

View File

@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
if (ComputeLegacyScoringValues) if (ComputeLegacyScoringValues)
{ {
CatchScoreV1Processor sv1Processor = new CatchScoreV1Processor(); CatchLegacyScoreProcessor sv1Processor = new CatchLegacyScoreProcessor();
sv1Processor.Simulate(workingBeatmap, beatmap, mods); sv1Processor.Simulate(workingBeatmap, beatmap, mods);
attributes.LegacyAccuracyScore = sv1Processor.AccuracyScore; attributes.LegacyAccuracyScore = sv1Processor.AccuracyScore;
attributes.LegacyComboScore = sv1Processor.ComboScore; attributes.LegacyComboScore = sv1Processor.ComboScore;

View File

@ -14,22 +14,12 @@ using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Catch.Difficulty namespace osu.Game.Rulesets.Catch.Difficulty
{ {
internal class CatchScoreV1Processor : ILegacyScoreProcessor internal class CatchLegacyScoreProcessor : ILegacyScoreProcessor
{ {
/// <summary>
/// The accuracy portion of the legacy (ScoreV1) total score.
/// </summary>
public int AccuracyScore { get; private set; } public int AccuracyScore { get; private set; }
/// <summary>
/// The combo-multiplied portion of the legacy (ScoreV1) total score.
/// </summary>
public int ComboScore { get; private set; } public int ComboScore { get; private set; }
/// <summary>
/// A ratio of <c>new_bonus_score / old_bonus_score</c> for converting the bonus score of legacy scores to the new scoring.
/// This is made up of all judgements that would be <see cref="HitResult.SmallBonus"/> or <see cref="HitResult.LargeBonus"/>.
/// </summary>
public double BonusScoreRatio => legacyBonusScore == 0 ? 0 : (double)modernBonusScore / legacyBonusScore; public double BonusScoreRatio => legacyBonusScore == 0 ? 0 : (double)modernBonusScore / legacyBonusScore;
private int legacyBonusScore; private int legacyBonusScore;

View File

@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
if (ComputeLegacyScoringValues) if (ComputeLegacyScoringValues)
{ {
ManiaScoreV1Processor sv1Processor = new ManiaScoreV1Processor(); ManiaLegacyScoreProcessor sv1Processor = new ManiaLegacyScoreProcessor();
sv1Processor.Simulate(workingBeatmap, beatmap, mods); sv1Processor.Simulate(workingBeatmap, beatmap, mods);
attributes.LegacyAccuracyScore = sv1Processor.AccuracyScore; attributes.LegacyAccuracyScore = sv1Processor.AccuracyScore;
attributes.LegacyComboScore = sv1Processor.ComboScore; attributes.LegacyComboScore = sv1Processor.ComboScore;

View File

@ -10,7 +10,7 @@ using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Difficulty namespace osu.Game.Rulesets.Mania.Difficulty
{ {
internal class ManiaScoreV1Processor : ILegacyScoreProcessor internal class ManiaLegacyScoreProcessor : ILegacyScoreProcessor
{ {
public int AccuracyScore => 0; public int AccuracyScore => 0;
public int ComboScore { get; private set; } public int ComboScore { get; private set; }

View File

@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (ComputeLegacyScoringValues) if (ComputeLegacyScoringValues)
{ {
OsuScoreV1Processor sv1Processor = new OsuScoreV1Processor(); OsuLegacyScoreProcessor sv1Processor = new OsuLegacyScoreProcessor();
sv1Processor.Simulate(workingBeatmap, beatmap, mods); sv1Processor.Simulate(workingBeatmap, beatmap, mods);
attributes.LegacyAccuracyScore = sv1Processor.AccuracyScore; attributes.LegacyAccuracyScore = sv1Processor.AccuracyScore;
attributes.LegacyComboScore = sv1Processor.ComboScore; attributes.LegacyComboScore = sv1Processor.ComboScore;

View File

@ -14,22 +14,12 @@ using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Difficulty namespace osu.Game.Rulesets.Osu.Difficulty
{ {
internal class OsuScoreV1Processor : ILegacyScoreProcessor internal class OsuLegacyScoreProcessor : ILegacyScoreProcessor
{ {
/// <summary>
/// The accuracy portion of the legacy (ScoreV1) total score.
/// </summary>
public int AccuracyScore { get; private set; } public int AccuracyScore { get; private set; }
/// <summary>
/// The combo-multiplied portion of the legacy (ScoreV1) total score.
/// </summary>
public int ComboScore { get; private set; } public int ComboScore { get; private set; }
/// <summary>
/// A ratio of <c>new_bonus_score / old_bonus_score</c> for converting the bonus score of legacy scores to the new scoring.
/// This is made up of all judgements that would be <see cref="HitResult.SmallBonus"/> or <see cref="HitResult.LargeBonus"/>.
/// </summary>
public double BonusScoreRatio => legacyBonusScore == 0 ? 0 : (double)modernBonusScore / legacyBonusScore; public double BonusScoreRatio => legacyBonusScore == 0 ? 0 : (double)modernBonusScore / legacyBonusScore;
private int legacyBonusScore; private int legacyBonusScore;

View File

@ -323,6 +323,6 @@ namespace osu.Game.Rulesets.Osu
public override RulesetSetupSection CreateEditorSetupSection() => new OsuSetupSection(); public override RulesetSetupSection CreateEditorSetupSection() => new OsuSetupSection();
public override ILegacyScoreProcessor CreateLegacyScoreProcessor() => new OsuScoreV1Processor(); public override ILegacyScoreProcessor CreateLegacyScoreProcessor() => new OsuLegacyScoreProcessor();
} }
} }

View File

@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (ComputeLegacyScoringValues) if (ComputeLegacyScoringValues)
{ {
TaikoScoreV1Processor sv1Processor = new TaikoScoreV1Processor(); TaikoLegacyScoreProcessor sv1Processor = new TaikoLegacyScoreProcessor();
sv1Processor.Simulate(workingBeatmap, beatmap, mods); sv1Processor.Simulate(workingBeatmap, beatmap, mods);
attributes.LegacyAccuracyScore = sv1Processor.AccuracyScore; attributes.LegacyAccuracyScore = sv1Processor.AccuracyScore;
attributes.LegacyComboScore = sv1Processor.ComboScore; attributes.LegacyComboScore = sv1Processor.ComboScore;

View File

@ -14,22 +14,12 @@ using osu.Game.Rulesets.Taiko.Objects;
namespace osu.Game.Rulesets.Taiko.Difficulty namespace osu.Game.Rulesets.Taiko.Difficulty
{ {
internal class TaikoScoreV1Processor : ILegacyScoreProcessor internal class TaikoLegacyScoreProcessor : ILegacyScoreProcessor
{ {
/// <summary>
/// The accuracy portion of the legacy (ScoreV1) total score.
/// </summary>
public int AccuracyScore { get; private set; } public int AccuracyScore { get; private set; }
/// <summary>
/// The combo-multiplied portion of the legacy (ScoreV1) total score.
/// </summary>
public int ComboScore { get; private set; } public int ComboScore { get; private set; }
/// <summary>
/// A ratio of <c>new_bonus_score / old_bonus_score</c> for converting the bonus score of legacy scores to the new scoring.
/// This is made up of all judgements that would be <see cref="HitResult.SmallBonus"/> or <see cref="HitResult.LargeBonus"/>.
/// </summary>
public double BonusScoreRatio => legacyBonusScore == 0 ? 0 : (double)modernBonusScore / legacyBonusScore; public double BonusScoreRatio => legacyBonusScore == 0 ? 0 : (double)modernBonusScore / legacyBonusScore;
private int legacyBonusScore; private int legacyBonusScore;

View File

@ -18,6 +18,7 @@ using osu.Game.Overlays;
using osu.Game.Overlays.Notifications; using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Scoring.Legacy;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
namespace osu.Game namespace osu.Game
@ -27,6 +28,9 @@ namespace osu.Game
[Resolved] [Resolved]
private RulesetStore rulesetStore { get; set; } = null!; private RulesetStore rulesetStore { get; set; } = null!;
[Resolved]
private BeatmapManager beatmapManager { get; set; } = null!;
[Resolved] [Resolved]
private ScoreManager scoreManager { get; set; } = null!; private ScoreManager scoreManager { get; set; } = null!;
@ -241,7 +245,7 @@ namespace osu.Game
try try
{ {
var score = scoreManager.Query(s => s.ID == id); var score = scoreManager.Query(s => s.ID == id);
long newTotalScore = scoreManager.ConvertFromLegacyTotalScore(score); long newTotalScore = StandardisedScoreMigrationTools.ConvertFromLegacyTotalScore(score, beatmapManager);
// Can't use async overload because we're not on the update thread. // Can't use async overload because we're not on the update thread.
// ReSharper disable once MethodHasAsyncOverload // ReSharper disable once MethodHasAsyncOverload
@ -249,7 +253,7 @@ namespace osu.Game
{ {
ScoreInfo s = r.Find<ScoreInfo>(id); ScoreInfo s = r.Find<ScoreInfo>(id);
s.TotalScore = newTotalScore; s.TotalScore = newTotalScore;
s.Version = 30000003; s.Version = LegacyScoreEncoder.LATEST_VERSION;
}); });
Logger.Log($"Converted total score for score {id}"); Logger.Log($"Converted total score for score {id}");

View File

@ -78,7 +78,7 @@ namespace osu.Game.Database
/// 28 2023-06-08 Added IsLegacyScore to ScoreInfo, parsed from replay files. /// 28 2023-06-08 Added IsLegacyScore to ScoreInfo, parsed from replay files.
/// 29 2023-06-12 Run migration of old lazer scores to be best-effort in the new scoring number space. No actual realm changes. /// 29 2023-06-12 Run migration of old lazer scores to be best-effort in the new scoring number space. No actual realm changes.
/// 30 2023-06-16 Run migration of old lazer scores again. This time with more correct rounding considerations. /// 30 2023-06-16 Run migration of old lazer scores again. This time with more correct rounding considerations.
/// 31 2023-06-26 Add Version and LegacyTotalScore to ScoreInfo, set Version to 30000002 and move TotalScore into LegacyTotalScore for legacy scores. /// 31 2023-06-26 Add Version and LegacyTotalScore to ScoreInfo, set Version to 30000002 and copy TotalScore into LegacyTotalScore for legacy scores.
/// </summary> /// </summary>
private const int schema_version = 31; private const int schema_version = 31;
@ -974,8 +974,15 @@ namespace osu.Game.Database
foreach (var score in scores) foreach (var score in scores)
{ {
score.LegacyTotalScore = score.TotalScore; if (score.IsLegacyScore)
score.Version = 30000002; // Last version before legacy total score conversion. {
score.LegacyTotalScore = score.TotalScore;
// Scores with this version will trigger the update process in BackgroundBeatmapProcessor.
score.Version = 30000002;
}
else
score.Version = LegacyScoreEncoder.LATEST_VERSION;
} }
break; break;

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -185,6 +186,92 @@ namespace osu.Game.Database
return (long)Math.Round((1000000 * (accuracyPortion * accuracyScore + (1 - accuracyPortion) * comboScore) + bonusScore) * modMultiplier); return (long)Math.Round((1000000 * (accuracyPortion * accuracyScore + (1 - accuracyPortion) * comboScore) + bonusScore) * modMultiplier);
} }
/// <summary>
/// Converts from <see cref="ScoreInfo.LegacyTotalScore"/> to the new standardised scoring of <see cref="ScoreProcessor"/>.
/// </summary>
/// <param name="score">The score to convert the total score of.</param>
/// <param name="beatmaps">A <see cref="BeatmapManager"/> used for <see cref="WorkingBeatmap"/> lookups.</param>
/// <returns>The standardised total score.</returns>
public static long ConvertFromLegacyTotalScore(ScoreInfo score, BeatmapManager beatmaps)
{
if (!score.IsLegacyScore)
return score.TotalScore;
var beatmap = beatmaps.GetWorkingBeatmap(score.BeatmapInfo);
var ruleset = score.Ruleset.CreateInstance();
var sv1Processor = ruleset.CreateLegacyScoreProcessor();
if (sv1Processor == null)
return score.TotalScore;
sv1Processor.Simulate(beatmap, beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, score.Mods), score.Mods);
return ConvertFromLegacyTotalScore(score, new DifficultyAttributes
{
LegacyAccuracyScore = sv1Processor.AccuracyScore,
LegacyComboScore = sv1Processor.ComboScore,
LegacyBonusScoreRatio = sv1Processor.BonusScoreRatio
});
}
/// <summary>
/// Converts from <see cref="ScoreInfo.LegacyTotalScore"/> to the new standardised scoring of <see cref="ScoreProcessor"/>.
/// </summary>
/// <param name="score">The score to convert the total score of.</param>
/// <param name="attributes">Difficulty attributes providing the legacy scoring values
/// (<see cref="DifficultyAttributes.LegacyAccuracyScore"/>, <see cref="DifficultyAttributes.LegacyComboScore"/>, and <see cref="DifficultyAttributes.LegacyBonusScoreRatio"/>)
/// for the beatmap which the score was set on.</param>
/// <returns>The standardised total score.</returns>
public static long ConvertFromLegacyTotalScore(ScoreInfo score, DifficultyAttributes attributes)
{
if (!score.IsLegacyScore)
return score.TotalScore;
int maximumLegacyAccuracyScore = attributes.LegacyAccuracyScore;
int maximumLegacyComboScore = attributes.LegacyComboScore;
double maximumLegacyBonusRatio = attributes.LegacyBonusScoreRatio;
double modMultiplier = score.Mods.Select(m => m.ScoreMultiplier).Aggregate(1.0, (c, n) => c * n);
// The part of total score that doesn't include bonus.
int maximumLegacyBaseScore = maximumLegacyAccuracyScore + maximumLegacyComboScore;
// The combo proportion is calculated as a proportion of maximumLegacyBaseScore.
double comboProportion = Math.Min(1, (double)score.LegacyTotalScore / maximumLegacyBaseScore);
// The bonus proportion makes up the rest of the score that exceeds maximumLegacyBaseScore.
double bonusProportion = Math.Max(0, (score.LegacyTotalScore - maximumLegacyBaseScore) * maximumLegacyBonusRatio);
switch (score.Ruleset.OnlineID)
{
case 0:
return (long)Math.Round((
700000 * comboProportion
+ 300000 * Math.Pow(score.Accuracy, 10)
+ bonusProportion) * modMultiplier);
case 1:
return (long)Math.Round((
250000 * comboProportion
+ 750000 * Math.Pow(score.Accuracy, 3.6)
+ bonusProportion) * modMultiplier);
case 2:
return (long)Math.Round((
600000 * comboProportion
+ 400000 * score.Accuracy
+ bonusProportion) * modMultiplier);
case 3:
return (long)Math.Round((
990000 * comboProportion
+ 10000 * Math.Pow(score.Accuracy, 2 + 2 * score.Accuracy)
+ bonusProportion) * modMultiplier);
default:
return score.TotalScore;
}
}
private class FakeHit : HitObject private class FakeHit : HitObject
{ {
private readonly Judgement judgement; private readonly Judgement judgement;

View File

@ -99,7 +99,7 @@ namespace osu.Game
/// </summary> /// </summary>
private const double global_track_volume_adjust = 0.8; private const double global_track_volume_adjust = 0.8;
public virtual bool UseDevelopmentServer => DebugUtils.IsDebugBuild; public virtual bool UseDevelopmentServer => false;
public virtual EndpointConfiguration CreateEndpoints() => public virtual EndpointConfiguration CreateEndpoints() =>
UseDevelopmentServer ? new DevelopmentEndpointConfiguration() : new ExperimentalEndpointConfiguration(); UseDevelopmentServer ? new DevelopmentEndpointConfiguration() : new ExperimentalEndpointConfiguration();

View File

@ -25,6 +25,13 @@ namespace osu.Game.Rulesets.Scoring
/// </summary> /// </summary>
double BonusScoreRatio { get; } double BonusScoreRatio { get; }
/// <summary>
/// Performs the simulation, computing the maximum <see cref="AccuracyScore"/>, <see cref="ComboScore"/>,
/// and <see cref="BonusScoreRatio"/> achievable for the given beatmap.
/// </summary>
/// <param name="workingBeatmap">The working beatmap.</param>
/// <param name="playableBeatmap">A playable version of the beatmap for the ruleset.</param>
/// <param name="mods">The applied mods.</param>
void Simulate(IWorkingBeatmap workingBeatmap, IBeatmap playableBeatmap, IReadOnlyList<Mod> mods); void Simulate(IWorkingBeatmap workingBeatmap, IBeatmap playableBeatmap, IReadOnlyList<Mod> mods);
} }
} }

View File

@ -29,7 +29,7 @@ namespace osu.Game.Scoring.Legacy
/// <list type="bullet"> /// <list type="bullet">
/// <item><description>30000001: Appends <see cref="LegacyReplaySoloScoreInfo"/> to the end of scores.</description></item> /// <item><description>30000001: Appends <see cref="LegacyReplaySoloScoreInfo"/> to the end of scores.</description></item>
/// <item><description>30000002: Score stored to replay calculated using the Score V2 algorithm.</description></item> /// <item><description>30000002: Score stored to replay calculated using the Score V2 algorithm.</description></item>
/// <item><description>30000003: First version after legacy total score migration.</description></item> /// <item><description>30000003: First version after converting legacy total score to standardised.</description></item>
/// </list> /// </list>
/// </remarks> /// </remarks>
public const int LATEST_VERSION = 30000003; public const int LATEST_VERSION = 30000003;

View File

@ -89,7 +89,7 @@ namespace osu.Game.Scoring
if (StandardisedScoreMigrationTools.ShouldMigrateToNewStandardised(model)) if (StandardisedScoreMigrationTools.ShouldMigrateToNewStandardised(model))
model.TotalScore = StandardisedScoreMigrationTools.GetNewStandardised(model); model.TotalScore = StandardisedScoreMigrationTools.GetNewStandardised(model);
else if (model.IsLegacyScore) else if (model.IsLegacyScore)
model.TotalScore = ConvertFromLegacyTotalScore(model); model.TotalScore = StandardisedScoreMigrationTools.ConvertFromLegacyTotalScore(model, beatmaps());
} }
/// <summary> /// <summary>
@ -153,65 +153,6 @@ namespace osu.Game.Scoring
#pragma warning restore CS0618 #pragma warning restore CS0618
} }
public long ConvertFromLegacyTotalScore(ScoreInfo score)
{
if (!score.IsLegacyScore)
return score.TotalScore;
var beatmap = beatmaps().GetWorkingBeatmap(score.BeatmapInfo);
var ruleset = score.Ruleset.CreateInstance();
var sv1Processor = ruleset.CreateLegacyScoreProcessor();
if (sv1Processor == null)
return score.TotalScore;
sv1Processor.Simulate(beatmap, beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, score.Mods), score.Mods);
int maximumLegacyAccuracyScore = sv1Processor.AccuracyScore;
int maximumLegacyComboScore = sv1Processor.ComboScore;
double maximumLegacyBonusRatio = sv1Processor.BonusScoreRatio;
double modMultiplier = score.Mods.Select(m => m.ScoreMultiplier).Aggregate(1.0, (c, n) => c * n);
// The part of total score that doesn't include bonus.
int maximumLegacyBaseScore = maximumLegacyAccuracyScore + maximumLegacyComboScore;
// The combo proportion is calculated as a proportion of maximumLegacyBaseScore.
double comboProportion = Math.Min(1, (double)score.LegacyTotalScore / maximumLegacyBaseScore);
// The bonus proportion makes up the rest of the score that exceeds maximumLegacyBaseScore.
double bonusProportion = Math.Max(0, (score.LegacyTotalScore - maximumLegacyBaseScore) * maximumLegacyBonusRatio);
switch (ruleset.RulesetInfo.OnlineID)
{
case 0:
return (long)Math.Round((
700000 * comboProportion
+ 300000 * Math.Pow(score.Accuracy, 10)
+ bonusProportion) * modMultiplier);
case 1:
return (long)Math.Round((
250000 * comboProportion
+ 750000 * Math.Pow(score.Accuracy, 3.6)
+ bonusProportion) * modMultiplier);
case 2:
return (long)Math.Round((
600000 * comboProportion
+ 400000 * score.Accuracy
+ bonusProportion) * modMultiplier);
case 3:
return (long)Math.Round((
990000 * comboProportion
+ 10000 * Math.Pow(score.Accuracy, 2 + 2 * score.Accuracy)
+ bonusProportion) * modMultiplier);
default:
return score.TotalScore;
}
}
// Very naive local caching to improve performance of large score imports (where the username is usually the same for most or all scores). // Very naive local caching to improve performance of large score imports (where the username is usually the same for most or all scores).
private readonly Dictionary<string, APIUser> usernameLookupCache = new Dictionary<string, APIUser>(); private readonly Dictionary<string, APIUser> usernameLookupCache = new Dictionary<string, APIUser>();

View File

@ -57,6 +57,9 @@ namespace osu.Game.Scoring
/// <summary> /// <summary>
/// Used to preserve the total score for legacy scores. /// Used to preserve the total score for legacy scores.
/// </summary> /// </summary>
/// <remarks>
/// Not populated if <see cref="IsLegacyScore"/> is <c>false</c>.
/// </remarks>
public long LegacyTotalScore { get; set; } public long LegacyTotalScore { get; set; }
public int MaxCombo { get; set; } public int MaxCombo { get; set; }
@ -69,6 +72,14 @@ namespace osu.Game.Scoring
public double? PP { get; set; } public double? PP { get; set; }
/// <summary>
/// The version of this score as stored in the database.
/// If this does not match <see cref="LegacyScoreEncoder.LATEST_VERSION"/>,
/// then the score has not yet been updated to reflect the current scoring values.
/// </summary>
/// <remarks>
/// This may not match the version stored in the replay files.
/// </remarks>
public int Version { get; set; } = LegacyScoreEncoder.LATEST_VERSION; public int Version { get; set; } = LegacyScoreEncoder.LATEST_VERSION;
[Indexed] [Indexed]

View File

@ -169,8 +169,6 @@ namespace osu.Game.Scoring
/// <param name="score">The score to populate the statistics of.</param> /// <param name="score">The score to populate the statistics of.</param>
public void PopulateMaximumStatistics(ScoreInfo score) => scoreImporter.PopulateMaximumStatistics(score); public void PopulateMaximumStatistics(ScoreInfo score) => scoreImporter.PopulateMaximumStatistics(score);
public long ConvertFromLegacyTotalScore(ScoreInfo score) => scoreImporter.ConvertFromLegacyTotalScore(score);
#region Implementation of IPresentImports<ScoreInfo> #region Implementation of IPresentImports<ScoreInfo>
public Action<IEnumerable<Live<ScoreInfo>>> PresentImport public Action<IEnumerable<Live<ScoreInfo>>> PresentImport