From 3900babe3d9f4bf0154486957a7e5e171ba17c77 Mon Sep 17 00:00:00 2001 From: MingxuanGame Date: Wed, 30 Jul 2025 10:05:28 +0000 Subject: [PATCH] feat(solo): return overall ranking --- app/database/score.py | 5 +- app/signalr/hub/spectator.py | 109 ++++++++++++++++++----------------- 2 files changed, 59 insertions(+), 55 deletions(-) diff --git a/app/database/score.py b/app/database/score.py index 694395b..046f83c 100644 --- a/app/database/score.py +++ b/app/database/score.py @@ -582,6 +582,7 @@ async def process_score( session: AsyncSession, redis: Redis, ) -> Score: + can_get_pp = info.passed and ranked and mods_can_get_pp(info.ruleset_id, info.mods) score = Score( accuracy=info.accuracy, max_combo=info.max_combo, @@ -611,7 +612,7 @@ async def process_score( nlarge_tick_hit=info.statistics.get(HitResult.LARGE_TICK_HIT, 0), nslider_tail_hit=info.statistics.get(HitResult.SLIDER_TAIL_HIT, 0), ) - if info.passed and ranked and mods_can_get_pp(info.ruleset_id, info.mods): + if can_get_pp: beatmap_raw = await fetcher.get_or_fetch_beatmap_raw(redis, beatmap_id) pp = await asyncio.get_event_loop().run_in_executor( None, calculate_pp, score, beatmap_raw @@ -621,7 +622,7 @@ async def process_score( user_id = user.id await session.commit() await session.refresh(score) - if score.passed and ranked: + if can_get_pp: previous_pp_best = await get_user_best_pp_in_beatmap( session, beatmap_id, user_id, score.gamemode ) diff --git a/app/signalr/hub/spectator.py b/app/signalr/hub/spectator.py index 7efcc89..0d0899e 100644 --- a/app/signalr/hub/spectator.py +++ b/app/signalr/hub/spectator.py @@ -241,39 +241,63 @@ class SpectatorHub(Hub[StoreClientState]): user_id = int(client.connection_id) store = self.get_or_create_state(client) score = store.score + assert store.beatmap_status is not None + assert store.state is not None + assert store.score is not None if not score or not store.score_token: return + if ( + BeatmapRankStatus.PENDING < store.beatmap_status <= BeatmapRankStatus.LOVED + ) and any( + k.is_hit() and v > 0 for k, v in store.score.score_info.statistics.items() + ): + await self._process_score(store, client) + store.state = None + store.beatmap_status = None + store.checksum = None + store.ruleset_id = None + store.score_token = None + store.score = None + await self._end_session(user_id, state) - assert store.beatmap_status is not None - - async def _save_replay(): - assert store.checksum is not None - assert store.ruleset_id is not None - assert store.state is not None - assert store.score is not None - async with AsyncSession(engine) as session: - async with session: - start_time = time.time() - score_record = None - while time.time() - start_time < READ_SCORE_TIMEOUT: - sub_query = select(ScoreToken.score_id).where( - ScoreToken.id == store.score_token, + async def _process_score(self, store: StoreClientState, client: Client) -> None: + user_id = int(client.connection_id) + assert store.state is not None + assert store.score_token is not None + assert store.checksum is not None + assert store.ruleset_id is not None + assert store.score is not None + async with AsyncSession(engine) as session: + async with session: + start_time = time.time() + score_record = None + while time.time() - start_time < READ_SCORE_TIMEOUT: + sub_query = select(ScoreToken.score_id).where( + ScoreToken.id == store.score_token, + ) + result = await session.exec( + select(Score) + .options(joinedload(Score.beatmap)) # pyright: ignore[reportArgumentType] + .where( + Score.id == sub_query, + Score.user_id == user_id, ) - result = await session.exec( - select(Score) - .options(joinedload(Score.beatmap)) # pyright: ignore[reportArgumentType] - .where( - Score.id == sub_query, - Score.user_id == user_id, - ) - ) - score_record = result.first() - if score_record: - break - if not score_record: - return - if not score_record.passed: - return + ) + score_record = result.first() + if score_record: + break + if not score_record: + return + if not score_record.passed: + return + await self.call_noblock( + client, + "UserScoreProcessed", + user_id, + score_record.id, + ) + # save replay + if store.state.state == SpectatedUserState.Passed: score_record.has_replay = True await session.commit() await session.refresh(score_record) @@ -282,32 +306,11 @@ class SpectatorHub(Hub[StoreClientState]): md5=store.checksum, username=store.score.score_info.user.name, score=score_record, - statistics=score.score_info.statistics, - maximum_statistics=score.score_info.maximum_statistics, - frames=score.replay_frames, + statistics=store.score.score_info.statistics, + maximum_statistics=store.score.score_info.maximum_statistics, + frames=store.score.replay_frames, ) - if ( - ( - BeatmapRankStatus.PENDING - < store.beatmap_status - <= BeatmapRankStatus.LOVED - ) - and any( - k.is_hit() and v > 0 for k, v in score.score_info.statistics.items() - ) - and state.state != SpectatedUserState.Failed - ): - # save replay - await _save_replay() - store.state = None - store.beatmap_status = None - store.checksum = None - store.ruleset_id = None - store.score_token = None - store.score = None - await self._end_session(user_id, state) - async def _end_session(self, user_id: int, state: SpectatorState) -> None: if state.state == SpectatedUserState.Playing: state.state = SpectatedUserState.Quit