feat(score): support osu-rx & osu-ap & all beatmap leaderboard like osu.ppy.sb

This commit is contained in:
MingxuanGame
2025-08-10 07:02:55 +00:00
parent f165ae5dc3
commit efc784d903
14 changed files with 262 additions and 19 deletions

View File

@@ -1,6 +1,7 @@
from datetime import datetime
from typing import TYPE_CHECKING
from app.config import settings
from app.models.beatmap import BeatmapRankStatus
from app.models.score import MODE_TO_INT, GameMode
@@ -62,10 +63,6 @@ class Beatmap(BeatmapBase, table=True):
back_populates="beatmaps", sa_relationship_kwargs={"lazy": "joined"}
)
@property
def can_ranked(self) -> bool:
return self.beatmap_status > BeatmapRankStatus.PENDING
@classmethod
async def from_resp(cls, session: AsyncSession, resp: "BeatmapResp") -> "Beatmap":
d = resp.model_dump()
@@ -160,12 +157,20 @@ class BeatmapResp(BeatmapBase):
) -> "BeatmapResp":
from .score import Score
beatmap_status = beatmap.beatmap_status
beatmap_ = beatmap.model_dump()
if query_mode is not None and beatmap.mode != query_mode:
beatmap_["convert"] = True
beatmap_["is_scoreable"] = beatmap.beatmap_status > BeatmapRankStatus.PENDING
beatmap_["status"] = beatmap.beatmap_status.name.lower()
beatmap_["ranked"] = beatmap.beatmap_status.value
beatmap_["is_scoreable"] = beatmap_status.has_leaderboard()
if (
settings.enable_all_beatmap_leaderboard
and not beatmap_status.has_leaderboard()
):
beatmap_["ranked"] = BeatmapRankStatus.APPROVED.value
beatmap_["status"] = BeatmapRankStatus.APPROVED.name.lower()
else:
beatmap_["status"] = beatmap_status.name.lower()
beatmap_["ranked"] = beatmap_status.value
beatmap_["mode_int"] = MODE_TO_INT[beatmap.mode]
if not from_set:
beatmap_["beatmapset"] = await BeatmapsetResp.from_db(

View File

@@ -1,6 +1,7 @@
from datetime import datetime
from typing import TYPE_CHECKING, NotRequired, TypedDict
from app.config import settings
from app.models.beatmap import BeatmapRankStatus, Genre, Language
from app.models.score import GameMode
@@ -228,11 +229,21 @@ class BeatmapsetResp(BeatmapsetBase):
required=beatmapset.nominations_required,
current=beatmapset.nominations_current,
),
"status": beatmapset.beatmap_status.name.lower(),
"ranked": beatmapset.beatmap_status.value,
"is_scoreable": beatmapset.beatmap_status > BeatmapRankStatus.PENDING,
"is_scoreable": beatmapset.beatmap_status.has_leaderboard(),
**beatmapset.model_dump(),
}
beatmap_status = beatmapset.beatmap_status
if (
settings.enable_all_beatmap_leaderboard
and not beatmap_status.has_leaderboard()
):
update["status"] = BeatmapRankStatus.APPROVED.name.lower()
update["ranked"] = BeatmapRankStatus.APPROVED.value
else:
update["status"] = beatmap_status.name.lower()
update["ranked"] = beatmap_status.value
if session and user:
existing_favourite = (
await session.exec(

View File

@@ -13,6 +13,7 @@ from app.calculator import (
calculate_weighted_pp,
clamp,
)
from app.config import settings
from app.database.team import TeamMember
from app.models.model import RespWithCursor, UTCBaseModel
from app.models.mods import APIMod, mods_can_get_pp
@@ -324,6 +325,13 @@ async def get_leaderboard(
user: User | None = None,
limit: int = 50,
) -> tuple[list[Score], Score | None]:
is_rx = "RX" in (mods or [])
is_ap = "AP" in (mods or [])
if settings.enable_osu_rx and is_rx:
mode = GameMode.OSURX
elif settings.enable_osu_ap and is_ap:
mode = GameMode.OSUAP
wheres = await _score_where(type, beatmap, mode, mods, user)
if wheres is None:
return [], None
@@ -637,6 +645,14 @@ async def process_score(
) -> Score:
assert user.id
can_get_pp = info.passed and ranked and mods_can_get_pp(info.ruleset_id, info.mods)
acronyms = [mod["acronym"] for mod in info.mods]
is_rx = "RX" in acronyms
is_ap = "AP" in acronyms
gamemode = INT_TO_MODE[info.ruleset_id]
if settings.enable_osu_rx and is_rx and gamemode == GameMode.OSU:
gamemode = GameMode.OSURX
elif settings.enable_osu_ap and is_ap and gamemode == GameMode.OSU:
gamemode = GameMode.OSUAP
score = Score(
accuracy=info.accuracy,
max_combo=info.max_combo,
@@ -648,7 +664,7 @@ async def process_score(
total_score_without_mods=info.total_score_without_mods,
beatmap_id=beatmap_id,
ended_at=datetime.now(UTC),
gamemode=INT_TO_MODE[info.ruleset_id],
gamemode=gamemode,
started_at=score_token.created_at,
user_id=user.id,
preserve=info.passed,