feat(calculator): support generate PerformanceAttributes & DifficultyAttributes from JSON Schema (#59)

Prepare for custom rulesets.

Schema Genetator: https://github.com/GooGuTeam/custom-rulesets/tree/main/CustomRulesetMetadataGenerator

```bash
dotnet -- schemas path/to/rulesets -o schema.json
```

```bash
python scripts/generate_ruleset_attributes.py schema.json 
```
This commit is contained in:
MingxuanGame
2025-10-25 19:10:53 +08:00
committed by GitHub
parent f792d146b5
commit 2c81e22749
12 changed files with 370 additions and 85 deletions

View File

@@ -1,13 +1,28 @@
from app.models.score import GameMode
# Version: 2025.10.19
# Auto-generated by scripts/generate_ruleset_attributes.py.
# Schema generated by https://github.com/GooGuTeam/custom-rulesets
# Do not edit this file directly.
# ruff: noqa: E501
from pydantic import BaseModel
from typing import Any
from pydantic import BaseModel, Field, RootModel
class OsuAttributeModels(RootModel[Any]):
root: Any = Field(..., title="osu! Attribute models")
class PerformanceAttributes(BaseModel):
pp: float
pp: float = Field(..., description="Calculated score performance points.")
class DifficultyAttributes(BaseModel):
star_rating: float = Field(..., description="The combined star rating of all skills.")
max_combo: int = Field(..., description="The maximum achievable combo.")
# https://github.com/ppy/osu/blob/9ebc5b0a35452e50bd408af1db62cfc22a57b1f4/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceAttributes.cs
class OsuPerformanceAttributes(PerformanceAttributes):
aim: float
speed: float
@@ -15,67 +30,111 @@ class OsuPerformanceAttributes(PerformanceAttributes):
flashlight: float
effective_miss_count: float
speed_deviation: float | None = None
# 2025 Q3 update
# combo_based_estimated_miss_count: int
# score_based_estimated_miss_count: int | None = None
# aim_estimated_slider_breaks: int
# speed_estimated_slider_breaks: int
combo_based_estimated_miss_count: float
score_based_estimated_miss_count: float | None = None
aim_estimated_slider_breaks: float
speed_estimated_slider_breaks: float
class OsuDifficultyAttributes(DifficultyAttributes):
aim_difficulty: float = Field(..., description="The difficulty corresponding to the aim skill.")
aim_difficult_slider_count: float = Field(..., description="The number of Sliders weighted by difficulty.")
speed_difficulty: float = Field(..., description="The difficulty corresponding to the speed skill.")
speed_note_count: float = Field(
..., description="The number of clickable objects weighted by difficulty.\nRelated to SpeedDifficulty"
)
flashlight_difficulty: float = Field(..., description="The difficulty corresponding to the flashlight skill.")
slider_factor: float = Field(
...,
description="Describes how much of AimDifficulty is contributed to by hitcircles or sliders.\nA value closer to 1.0 indicates most of AimDifficulty is contributed by hitcircles.\nA value closer to 0.0 indicates most of AimDifficulty is contributed by sliders.",
)
aim_top_weighted_slider_factor: float = Field(
...,
description="Describes how much of AimDifficultStrainCount is contributed to by hitcircles or sliders\nA value closer to 0.0 indicates most of AimDifficultStrainCount is contributed by hitcircles\nA value closer to Infinity indicates most of AimDifficultStrainCount is contributed by sliders",
)
speed_top_weighted_slider_factor: float = Field(
...,
description="Describes how much of SpeedDifficultStrainCount is contributed to by hitcircles or sliders\nA value closer to 0.0 indicates most of SpeedDifficultStrainCount is contributed by hitcircles\nA value closer to Infinity indicates most of SpeedDifficultStrainCount is contributed by sliders",
)
aim_difficult_strain_count: float
speed_difficult_strain_count: float
nested_score_per_object: float
legacy_score_base_multiplier: float
maximum_legacy_combo_score: float
# https://github.com/ppy/osu/blob/9ebc5b0a35452e50bd408af1db62cfc22a57b1f4/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceAttributes.cs
class TaikoPerformanceAttributes(PerformanceAttributes):
difficulty: float
accuracy: float
estimated_unstable_rate: float | None = None
# https://github.com/ppy/osu/blob/9ebc5b0a35452e50bd408af1db62cfc22a57b1f4/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceAttributes.cs
class TaikoDifficultyAttributes(DifficultyAttributes):
rhythm_difficulty: float = Field(..., description="The difficulty corresponding to the rhythm skill.")
mono_stamina_factor: float = Field(
...,
description="The ratio of stamina difficulty from mono-color (single colour) streams to total stamina difficulty.",
)
consistency_factor: float = Field(..., description="The factor corresponding to the consistency of a map.")
CatchPerformanceAttributes = PerformanceAttributes
CatchDifficultyAttributes = DifficultyAttributes
class ManiaPerformanceAttributes(PerformanceAttributes):
difficulty: float
PERFORMANCE_CLASS: dict[GameMode, type[PerformanceAttributes]] = {
GameMode.OSU: OsuPerformanceAttributes,
GameMode.MANIA: ManiaPerformanceAttributes,
GameMode.TAIKO: TaikoPerformanceAttributes,
}
ManiaDifficultyAttributes = DifficultyAttributes
class BeatmapAttributes(BaseModel):
star_rating: float
max_combo: int
HishigataPerformanceAttributes = PerformanceAttributes
# https://github.com/ppy/osu/blob/9ebc5b0a35452e50bd408af1db62cfc22a57b1f4/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
class OsuBeatmapAttributes(BeatmapAttributes):
HishigataDifficultyAttributes = DifficultyAttributes
RushPerformanceAttributes = PerformanceAttributes
RushDifficultyAttributes = DifficultyAttributes
SentakkiPerformanceAttributes = PerformanceAttributes
SentakkiDifficultyAttributes = DifficultyAttributes
SoyokazePerformanceAttributes = PerformanceAttributes
SoyokazeDifficultyAttributes = DifficultyAttributes
class TauPerformanceAttribute(PerformanceAttributes):
aim: float
speed: float
accuracy: float
complexity: float
effective_miss_count: float
class TauDifficultyAttributes(DifficultyAttributes):
aim_difficulty: float
aim_difficult_slider_count: float
speed_difficulty: float
speed_note_count: float
flashlight_difficulty: float | None = None
complexity_difficulty: float
approach_rate: float
slider_factor: float
aim_difficult_strain_count: float
speed_difficult_strain_count: float
# 2025 Q3 update
# aim_top_weighted_slider_factor: float
# speed_top_weighted_slider_factor: float
# nested_score_per_object: float
# legacy_score_base_multiplier: float
# maximum_legacy_combo_score: float
overall_difficulty: float
# https://github.com/ppy/osu/blob/9ebc5b0a35452e50bd408af1db62cfc22a57b1f4/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs
class TaikoBeatmapAttributes(BeatmapAttributes):
rhythm_difficulty: float
mono_stamina_factor: float
# 2025 Q3 update
# consistency_factor: float
DIFFICULTY_CLASS: dict[GameMode, type[BeatmapAttributes]] = {
GameMode.OSU: OsuBeatmapAttributes,
GameMode.TAIKO: TaikoBeatmapAttributes,
}
PerformanceAttributesUnion = (
OsuPerformanceAttributes | TaikoPerformanceAttributes | ManiaPerformanceAttributes | PerformanceAttributes
)
DifficultyAttributesUnion = (
OsuDifficultyAttributes | TaikoDifficultyAttributes | TauDifficultyAttributes | DifficultyAttributes
)