From 85ecfa5560a8c7b4e9a78cf5a30448db58eaebe8 Mon Sep 17 00:00:00 2001 From: Vari Date: Sat, 6 Sep 2025 20:23:52 +0200 Subject: [PATCH] Leaderboard finished --- dist/app/Enums/Game/Faction.php | 23 ++ .../Controllers/Api/LeaderboardController.php | 209 +++++++++--------- .../Api/Leaderboard/LeaderboardEntry.php | 12 +- 3 files changed, 130 insertions(+), 114 deletions(-) diff --git a/dist/app/Enums/Game/Faction.php b/dist/app/Enums/Game/Faction.php index 4eb4345..25944f3 100644 --- a/dist/app/Enums/Game/Faction.php +++ b/dist/app/Enums/Game/Faction.php @@ -8,4 +8,27 @@ enum Faction: string case Hunter = 'Hunter'; case Runner = 'Runner'; case Emcee = 'Emcee'; + + /** + * @return array + */ + public function getCharacterList(): array { + return match ($this) { + self::Hunter => [ + Hunter::Inquisitor, + Hunter::Stalker, + Hunter::Poacher, + Hunter::Mass + ], + self::Runner => [ + Runner::Smoke, + Runner::Dash, + Runner::Switch, + Runner::Ghost, + Runner::Sawbones, + Runner::Ink, + ], + default => [], + }; + } } diff --git a/dist/app/Http/Controllers/Api/LeaderboardController.php b/dist/app/Http/Controllers/Api/LeaderboardController.php index 79fd456..bd6b938 100644 --- a/dist/app/Http/Controllers/Api/LeaderboardController.php +++ b/dist/app/Http/Controllers/Api/LeaderboardController.php @@ -6,37 +6,65 @@ use App\Enums\Game\Hunter; use App\Http\Controllers\Controller; use App\Http\Requests\Api\Leaderboard\GetScoresRequest; +use App\Http\Responses\Api\Leaderboard\GetScoresResponse; +use App\Http\Responses\Api\Leaderboard\LeaderboardEntry; use App\Models\Admin\Archive\ArchivedPlayerProgression; -use App\Models\User\User; -use Carbon\Carbon; -use Illuminate\Database\Query\Builder; -use Illuminate\Http\Request; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Support\Carbon; +use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; -use stdClass; class LeaderboardController extends Controller { - const HUNTER_CHARACTER_LIST = [ - Hunter::Inquisitor, - Hunter::Stalker, - Hunter::Poacher, - Hunter::Mass - ]; - public function getScores(GetScoresRequest $request) { - $user = \Auth::user(); + $response = new GetScoresResponse(); - $dateFilter = Carbon::now()->startOfMonth(); + $topScores = []; + $startDate = static::getCurrentLeaderboardStartDate(); - dump($request->playerIds); + static::getLeaderboardTopScores(5, $request->faction, $startDate) + ->each(function (ArchivedPlayerProgression $item, int $rank) use (&$topScores) { + $topScores[] = new LeaderboardEntry( + $item->user, + $item->gained_experience, + $rank + 1, + ); + }); - $subSubQuery = ArchivedPlayerProgression::query(); - $subSubQuery->select(['user_id'])->addSelect(DB::raw('MAX(gained_experience) as gained_experience')); - $subSubQuery->whereDate('created_at', '>=', $dateFilter); - $subSubQuery->groupBy('user_id'); - $subSubQuery->orderByDesc('gained_experience'); + foreach ($request->playerIds as $playerId) { + $entries = static::getLeaderboardEntriesForPlayer($playerId, $request->faction, $startDate); + $response->playerScores[$playerId] = []; + + $entries->each(function (ArchivedPlayerProgression $item, int $key) 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 = $startDate->addMonth()->firstOfMonth(); + + return $response; + } + + /** + * @param string $userId + * @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 $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'); @@ -45,103 +73,74 @@ public function getScores(GetScoresRequest $request) $query->fromSub($subSubQuery, 'a'); $whereQuery = DB::query(); - $whereQuery->where('user_id', $request->playerIds[0]); + $whereQuery->where('user_id', $userId); $whereQuery->fromSub($query, 'c'); $whereQuery->selectRaw('c.rank + 1 AS "user_rank"'); $mainQuery = ArchivedPlayerProgression::query(); $mainQuery->fromSub($query, 'b'); - $mainQuery->where('rank', '<=', function (Builder $whereQuery) use ($request, $query) { - $whereQuery->where('user_id', $request->playerIds[0]); + $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 + 1)'); }); $mainQuery->orderByDesc('rank'); $mainQuery->limit(5); - dd($mainQuery->toRawSql()); - - - - $username = $user->last_known_username . '_' . Str::substr($user->id, 0, 8); - return <<get(); } - ], - "playerScores": { - "$user->id": [ - { - "id": "13d86f42-8446-49b5-a0c4-077ba23a43e4", - "score": 12130, - "rank": 40, - "playerName": "Clonebones_13d86f42" - }, - { - "id": "1f58fbae-23b2-481f-b679-0f1f7ab4f31a", - "score": 12120, - "rank": 41, - "playerName": "Inked_1f58fbae" - }, - { - "id": "cdd7bea9-f20a-4cc0-ae82-3297614933b7", - "score": 12085, - "rank": 42, - "playerName": "Fog_cdd7bea9" - }, - { - "id": "7c6c1e40-4e6c-47a7-a2a3-480f8b774994", - "score": 12085, - "rank": 43, - "playerName": "Dash_7c6c1e40" - }, - { - "id": "$user->id", - "score": 12015, - "rank": 44, - "playerName": "$username" - }, - { - "id": "0e38856d-97a5-4222-b546-f3f9360102b4", - "score": 12010, - "rank": 45, - "playerName": "Ghost_0e38856d" - } - ] - }, - "leaderboardSize": 8497, - "leaderboardReset": 1753210800 -} -JSON; + /** + * @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(); + } + + /** + * @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->groupBy('user_id'); + + return $query->count(); + } + + 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; } } \ No newline at end of file diff --git a/dist/app/Http/Responses/Api/Leaderboard/LeaderboardEntry.php b/dist/app/Http/Responses/Api/Leaderboard/LeaderboardEntry.php index 00480f0..ebfd815 100644 --- a/dist/app/Http/Responses/Api/Leaderboard/LeaderboardEntry.php +++ b/dist/app/Http/Responses/Api/Leaderboard/LeaderboardEntry.php @@ -6,16 +6,10 @@ class LeaderboardEntry implements \JsonSerializable { - public User $user; - - public int $score; - - public int $rank; - public function __construct( - User $user, - int $score, - int $rank + public User $user, + public int $score, + public int $rank, ) {}