window) { Window::PersonalBest => static::handlePersonalBestWindow($request), Window::Lobby => static::handleLobbyWindow($request), }; } public static function handlePersonalBestWindow(GetScoresRequest $request) { $response = new GetScoresResponse(); $topScores = []; $startDate = static::getCurrentLeaderboardStartDate(); static::getLeaderboardTopScores(5, $request->faction, $startDate) ->each(function (ArchivedPlayerProgression $item, int $rank) use (&$topScores) { $topScores[] = new LeaderboardEntry( $item->user, $item->gained_experience, $rank + 1, ); }); foreach ($request->playerIds as $playerId) { $entries = static::getLeaderboardEntriesForPlayer($playerId, $request->faction, $startDate); $response->playerScores[$playerId] = []; $entries->each(function (ArchivedPlayerProgression $item) use (&$response, $playerId) { $response->playerScores[$playerId][] = new LeaderboardEntry( $item->user, $item->gained_experience, $item->rank, ); }); } $response->topScores = $topScores; $response->leaderboardSize = static::getLeaderboardSize($request->faction, $startDate); $response->leaderboardReset = static::getCurrentLeaderboardEndDate(); return $response; } public static function handleLobbyWindow(GetScoresRequest $request) { $response = new GetScoresResponse(); $startDate = static::getCurrentLeaderboardStartDate(); static::getLeaderboardEntriesForPlayer($request->playerIds, $request->faction, $startDate) ->each(function (ArchivedPlayerProgression $item) use (&$response, &$lobbyScores) { $response->playerScores[$item->user_id][] = new LeaderboardEntry( $item->user, $item->gained_experience, $item->rank, ); }); $response->leaderboardSize = static::getLeaderboardSize($request->faction, $startDate); $response->leaderboardReset = static::getCurrentLeaderboardEndDate(); return $response; } /** * @param string|array $userId Select the given user id. if its an array it only selects them and no one around. * @param Faction $faction * @param Carbon $startDate Player scores before this Time won't get selected. * @param ?Carbon $endDate scores after this time won't be selected, defaults to now when null. * @return Collection */ public static function getLeaderboardEntriesForPlayer(string|array $userId, Faction $faction, Carbon $startDate, ?Carbon $endDate = null): Collection { $endDate ??= Carbon::now(); $subSubQuery = static::getBaseSelectQuery($faction, $startDate, $endDate); $query = ArchivedPlayerProgression::query(); $query->orderBy('gained_experience', 'desc'); $query->select(['user_id', 'gained_experience']) ->addSelect(DB::raw('ROW_NUMBER() OVER (ORDER BY gained_experience DESC) AS "rank"')); $query->fromSub($subSubQuery, 'a'); $mainQuery = ArchivedPlayerProgression::query(); $mainQuery->fromSub($query, 'b'); if (is_array($userId)) { $mainQuery->whereIn('user_id', $userId); } else { $mainQuery->where('rank', '<=', function (\Illuminate\Database\Query\Builder $whereQuery) use ($userId, $query) { $whereQuery->where('user_id', $userId); $whereQuery->fromSub($query, 'c'); $whereQuery->selectRaw('(c.rank + (CASE c.rank WHEN 1 THEN 4 WHEN 2 THEN 3 WHEN 3 THEN 2 ELSE 1 END))'); }); } $mainQuery->orderByDesc('rank'); $mainQuery->limit(is_array($userId) ? count($userId) : 5); return $mainQuery->get(); } /** * @param int $count * @param Faction $faction * @param Carbon $startDate Player scores before this Time won't get selected. * @param ?Carbon $endDate scores after this time won't be selected, defaults to now when null. * @return Collection */ public static function getLeaderboardTopScores(int $count, Faction $faction, Carbon $startDate, ?Carbon $endDate = null): Collection { $endDate ??= Carbon::now(); $query = static::getBaseSelectQuery($faction, $startDate, $endDate); $query->limit($count); return $query->get(); } public static function getCurrentLeaderboardStartDate(): Carbon { return Carbon::today()->firstOfMonth()->addHours(20); } public static function getCurrentLeaderboardEndDate(): Carbon { return Carbon::today()->addMonth()->firstOfMonth()->addHours(20); } /** * @param Faction $faction * @param Carbon $startDate Player scores before this Time won't get selected. * @param ?Carbon $endDate scores after this time won't be selected, defaults to now when null. * @return int */ public static function getLeaderboardSize(Faction $faction, Carbon $startDate, ?Carbon $endDate = null): int { $endDate ??= Carbon::now(); $query = ArchivedPlayerProgression::query(); $query->whereDate('created_at', '>=', $startDate); $query->whereDate('created_at', '<=', $endDate); $query->whereIn('played_character', $faction->getCharacterList()); $query->distinct('user_id'); $query->selectRaw('user_id'); return $query->count('user_id'); } protected static function getBaseSelectQuery(Faction $faction, Carbon $startDate, Carbon $endDate): Builder { $subSubQuery = ArchivedPlayerProgression::query(); $subSubQuery->select(['user_id'])->addSelect(DB::raw('MAX(gained_experience) as gained_experience')); $subSubQuery->whereDate('created_at', '>=', $startDate); $subSubQuery->whereDate('created_at', '<=', $endDate); $subSubQuery->whereIn('played_character', $faction->getCharacterList()); $subSubQuery->groupBy('user_id'); $subSubQuery->orderByDesc('gained_experience'); return $subSubQuery; } }