feat(daily-challenge): complete daily-challenge
This commit is contained in:
@@ -1,20 +1,30 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections import defaultdict
|
||||
from collections.abc import Coroutine
|
||||
from datetime import UTC, datetime
|
||||
import math
|
||||
from typing import override
|
||||
|
||||
from app.calculator import clamp
|
||||
from app.database import Relationship, RelationshipType, User
|
||||
from app.database.playlist_best_score import PlaylistBestScore
|
||||
from app.database.playlists import Playlist
|
||||
from app.database.room import Room
|
||||
from app.dependencies.database import engine, get_redis
|
||||
from app.models.metadata_hub import (
|
||||
TOTAL_SCORE_DISTRIBUTION_BINS,
|
||||
DailyChallengeInfo,
|
||||
MetadataClientState,
|
||||
MultiplayerPlaylistItemStats,
|
||||
MultiplayerRoomScoreSetEvent,
|
||||
MultiplayerRoomStats,
|
||||
OnlineStatus,
|
||||
UserActivity,
|
||||
)
|
||||
from app.models.room import RoomCategory
|
||||
from app.service.subscribers.score_processed import ScoreSubscriber
|
||||
|
||||
from .hub import Client, Hub
|
||||
|
||||
@@ -27,11 +37,33 @@ ONLINE_PRESENCE_WATCHERS_GROUP = "metadata:online-presence-watchers"
|
||||
class MetadataHub(Hub[MetadataClientState]):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.subscriber = ScoreSubscriber()
|
||||
self.subscriber.metadata_hub = self
|
||||
self._daily_challenge_stats: MultiplayerRoomStats | None = None
|
||||
self._today = datetime.now(UTC).date()
|
||||
self._lock = asyncio.Lock()
|
||||
|
||||
def get_daily_challenge_stats(
|
||||
self, daily_challenge_room: int
|
||||
) -> MultiplayerRoomStats:
|
||||
if (
|
||||
self._daily_challenge_stats is None
|
||||
or self._today != datetime.now(UTC).date()
|
||||
):
|
||||
self._daily_challenge_stats = MultiplayerRoomStats(
|
||||
room_id=daily_challenge_room,
|
||||
playlist_item_stats={},
|
||||
)
|
||||
return self._daily_challenge_stats
|
||||
|
||||
@staticmethod
|
||||
def online_presence_watchers_group() -> str:
|
||||
return ONLINE_PRESENCE_WATCHERS_GROUP
|
||||
|
||||
@staticmethod
|
||||
def room_watcher_group(room_id: int) -> str:
|
||||
return f"metadata:multiplayer-room-watchers:{room_id}"
|
||||
|
||||
def broadcast_tasks(
|
||||
self, user_id: int, store: MetadataClientState | None
|
||||
) -> set[Coroutine]:
|
||||
@@ -186,3 +218,76 @@ class MetadataHub(Hub[MetadataClientState]):
|
||||
|
||||
async def EndWatchingUserPresence(self, client: Client) -> None:
|
||||
self.remove_from_group(client, self.online_presence_watchers_group())
|
||||
|
||||
async def notify_room_score_processed(self, event: MultiplayerRoomScoreSetEvent):
|
||||
await self.broadcast_group_call(
|
||||
self.room_watcher_group(event.room_id), "MultiplayerRoomScoreSet", event
|
||||
)
|
||||
|
||||
async def BeginWatchingMultiplayerRoom(self, client: Client, room_id: int):
|
||||
self.add_to_group(client, self.room_watcher_group(room_id))
|
||||
await self.subscriber.subscribe_room_score(room_id, client.user_id)
|
||||
stats = self.get_daily_challenge_stats(room_id)
|
||||
await self.update_daily_challenge_stats(stats)
|
||||
return list(stats.playlist_item_stats.values())
|
||||
|
||||
async def update_daily_challenge_stats(self, stats: MultiplayerRoomStats) -> None:
|
||||
async with AsyncSession(engine) as session:
|
||||
playlist_ids = (
|
||||
await session.exec(
|
||||
select(Playlist.id).where(
|
||||
Playlist.room_id == stats.room_id,
|
||||
)
|
||||
)
|
||||
).all()
|
||||
for playlist_id in playlist_ids:
|
||||
item = stats.playlist_item_stats.get(playlist_id, None)
|
||||
if item is None:
|
||||
item = MultiplayerPlaylistItemStats(
|
||||
playlist_item_id=playlist_id,
|
||||
total_score_distribution=[0] * TOTAL_SCORE_DISTRIBUTION_BINS,
|
||||
cumulative_score=0,
|
||||
last_processed_score_id=0,
|
||||
)
|
||||
stats.playlist_item_stats[playlist_id] = item
|
||||
last_processed_score_id = item.last_processed_score_id
|
||||
scores = (
|
||||
await session.exec(
|
||||
select(PlaylistBestScore).where(
|
||||
PlaylistBestScore.room_id == stats.room_id,
|
||||
PlaylistBestScore.playlist_id == playlist_id,
|
||||
PlaylistBestScore.score_id > last_processed_score_id,
|
||||
)
|
||||
)
|
||||
).all()
|
||||
if len(scores) == 0:
|
||||
continue
|
||||
|
||||
async with self._lock:
|
||||
if item.last_processed_score_id == last_processed_score_id:
|
||||
totals = defaultdict(int)
|
||||
for score in scores:
|
||||
bin_index = int(
|
||||
clamp(
|
||||
math.floor(score.total_score / 100000),
|
||||
0,
|
||||
TOTAL_SCORE_DISTRIBUTION_BINS - 1,
|
||||
)
|
||||
)
|
||||
totals[bin_index] += 1
|
||||
|
||||
item.cumulative_score += sum(
|
||||
score.total_score for score in scores
|
||||
)
|
||||
|
||||
for j in range(TOTAL_SCORE_DISTRIBUTION_BINS):
|
||||
item.total_score_distribution[j] += totals.get(j, 0)
|
||||
|
||||
if scores:
|
||||
item.last_processed_score_id = max(
|
||||
score.score_id for score in scores
|
||||
)
|
||||
|
||||
async def EndWatchingMultiplayerRoom(self, client: Client, room_id: int):
|
||||
self.remove_from_group(client, self.room_watcher_group(room_id))
|
||||
await self.subscriber.unsubscribe_room_score(room_id, client.user_id)
|
||||
|
||||
@@ -1082,7 +1082,6 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
|
||||
)
|
||||
|
||||
async def ChangeSettings(self, client: Client, settings: MultiplayerRoomSettings):
|
||||
print(settings)
|
||||
store = self.get_or_create_state(client)
|
||||
if store.room_id == 0:
|
||||
raise InvokeException("You are not in a room")
|
||||
|
||||
Reference in New Issue
Block a user