feat(score): support rx for taiko & catch

This commit is contained in:
MingxuanGame
2025-08-15 05:59:21 +00:00
parent 64ee8a73d5
commit 1251ba31a2
9 changed files with 233 additions and 41 deletions

View File

@@ -3,7 +3,7 @@ from __future__ import annotations
from enum import Enum
from typing import Annotated, Any
from pydantic import Field, HttpUrl, ValidationInfo, field_validator
from pydantic import AliasChoices, Field, HttpUrl, ValidationInfo, field_validator
from pydantic_settings import BaseSettings, NoDecode, SettingsConfigDict
@@ -97,8 +97,12 @@ class Settings(BaseSettings):
sentry_dsn: HttpUrl | None = None
# 游戏设置
enable_osu_rx: bool = False
enable_osu_ap: bool = False
enable_rx: bool = Field(
default=False, validation_alias=AliasChoices("enable_rx", "enable_osu_rx")
)
enable_ap: bool = Field(
default=False, validation_alias=AliasChoices("enable_ap", "enable_osu_ap")
)
enable_all_mods_pp: bool = False
enable_supporter_for_all_users: bool = False
enable_all_beatmap_leaderboard: bool = False

View File

@@ -330,12 +330,8 @@ 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
mods = mods or []
mode = mode.to_special_mode(mods)
wheres = await _score_where(type, beatmap, mode, mods, user)
if wheres is None:
@@ -696,14 +692,7 @@ 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 = GameMode.from_int(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
gamemode = GameMode.from_int(info.ruleset_id).to_special_mode(info.mods)
score = Score(
accuracy=info.accuracy,
max_combo=info.max_combo,

View File

@@ -160,9 +160,13 @@ def mods_can_get_pp(ruleset_id: int, mods: list[APIMod]) -> bool:
return True
ranked_mods = RANKED_MODS[ruleset_id]
for mod in mods:
if app_settings.enable_osu_rx and mod["acronym"] == "RX" and ruleset_id == 0:
if (
app_settings.enable_rx
and mod["acronym"] == "RX"
and ruleset_id in {0, 1, 2}
):
continue
if app_settings.enable_osu_ap and mod["acronym"] == "AP" and ruleset_id == 0:
if app_settings.enable_ap and mod["acronym"] == "AP" and ruleset_id == 0:
continue
mod["settings"] = mod.get("settings", {})

View File

@@ -1,7 +1,9 @@
from __future__ import annotations
from enum import Enum
from typing import TYPE_CHECKING, Literal, TypedDict
from typing import TYPE_CHECKING, Literal, TypedDict, cast
from app.config import settings
from .mods import API_MODS, APIMod, init_mods
@@ -18,6 +20,8 @@ class GameMode(str, Enum):
MANIA = "mania"
OSURX = "osurx"
OSUAP = "osuap"
TAIKORX = "taikorx"
FRUITSRX = "fruitsrx"
def to_rosu(self) -> "rosu.GameMode":
import rosu_pp_py as rosu
@@ -29,6 +33,8 @@ class GameMode(str, Enum):
GameMode.MANIA: rosu.GameMode.Mania,
GameMode.OSURX: rosu.GameMode.Osu,
GameMode.OSUAP: rosu.GameMode.Osu,
GameMode.TAIKORX: rosu.GameMode.Taiko,
GameMode.FRUITSRX: rosu.GameMode.Catch,
}[self]
def __int__(self) -> int:
@@ -39,6 +45,8 @@ class GameMode(str, Enum):
GameMode.MANIA: 3,
GameMode.OSURX: 0,
GameMode.OSUAP: 0,
GameMode.TAIKORX: 1,
GameMode.FRUITSRX: 2,
}[self]
@classmethod
@@ -59,8 +67,27 @@ class GameMode(str, Enum):
3: GameMode.MANIA,
4: GameMode.OSURX,
5: GameMode.OSUAP,
6: GameMode.TAIKORX,
7: GameMode.FRUITSRX,
}[v]
def to_special_mode(self, mods: list[APIMod] | list[str]) -> "GameMode":
if self not in (GameMode.OSU, GameMode.TAIKO, GameMode.FRUITS):
return self
if not settings.enable_rx and not settings.enable_ap:
return self
if len(mods) > 0 and isinstance(mods[0], dict):
mods = [mod["acronym"] for mod in cast(list[APIMod], mods)]
if "AP" in mods and settings.enable_ap:
return GameMode.OSUAP
if "RX" in mods and settings.enable_rx:
return {
GameMode.OSU: GameMode.OSURX,
GameMode.TAIKO: GameMode.TAIKORX,
GameMode.FRUITS: GameMode.FRUITSRX,
}[self]
raise ValueError(f"Unknown game mode: {self}")
class Rank(str, Enum):
X = "X"

View File

@@ -175,10 +175,11 @@ async def register_user(
for i in [GameMode.OSU, GameMode.TAIKO, GameMode.FRUITS, GameMode.MANIA]:
statistics = UserStatistics(mode=i, user_id=new_user.id)
db.add(statistics)
if settings.enable_osu_rx:
statistics_rx = UserStatistics(mode=GameMode.OSURX, user_id=new_user.id)
db.add(statistics_rx)
if settings.enable_osu_ap:
if settings.enable_rx:
for mode in (GameMode.OSURX, GameMode.TAIKORX, GameMode.FRUITSRX):
statistics_rx = UserStatistics(mode=mode, user_id=new_user.id)
db.add(statistics_rx)
if settings.enable_ap:
statistics_ap = UserStatistics(mode=GameMode.OSUAP, user_id=new_user.id)
db.add(statistics_ap)
daily_challenge_user_stats = DailyChallengeStats(user_id=new_user.id)

View File

@@ -15,19 +15,24 @@ async def create_rx_statistics():
async with AsyncSession(engine) as session:
users = (await session.exec(select(User.id))).all()
for i in users:
if settings.enable_osu_rx:
is_exist = (
await session.exec(
select(exists()).where(
UserStatistics.user_id == i,
UserStatistics.mode == GameMode.OSURX,
if settings.enable_rx:
for mode in (
GameMode.OSURX,
GameMode.TAIKORX,
GameMode.FRUITSRX,
):
is_exist = (
await session.exec(
select(exists()).where(
UserStatistics.user_id == i,
UserStatistics.mode == mode,
)
)
)
).first()
if not is_exist:
statistics_rx = UserStatistics(mode=GameMode.OSURX, user_id=i)
session.add(statistics_rx)
if settings.enable_osu_ap:
).first()
if not is_exist:
statistics_rx = UserStatistics(mode=mode, user_id=i)
session.add(statistics_rx)
if settings.enable_ap:
is_exist = (
await session.exec(
select(exists()).where(