Skip to content

Commit

Permalink
Merge branch 'master' into fix-developer-interest-link
Browse files Browse the repository at this point in the history
  • Loading branch information
wescopeland authored Oct 27, 2023
2 parents 12897d6 + 0e52144 commit 18db5c8
Show file tree
Hide file tree
Showing 215 changed files with 7,103 additions and 4,442 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ DB_PORT=3306
DB_DATABASE=retroachievements-web
DB_USERNAME=retroachievements
DB_PASSWORD="${DB_USERNAME}"
# TODO remove after utf8mb4 conversion
#DB_CHARSET=latin1
#DB_COLLATION=latin1_general_ci

#LEGACY_MEDIA_PATH=

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/database/*.sql
/docker/nginx/logs
/docs/dist
/node_modules
Expand Down
2 changes: 2 additions & 0 deletions app/Community/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use App\Community\Commands\SyncTickets;
use App\Community\Commands\SyncUserRelations;
use App\Community\Commands\SyncVotes;
use App\Community\Components\DeveloperGameStatsTable;
use App\Community\Components\GlobalStatistics;
use App\Community\Components\MessageIcon;
use App\Community\Components\UserCard;
Expand Down Expand Up @@ -104,6 +105,7 @@ public function boot(): void
TriggerTicketComment::disableSearchSyncing();
UserComment::disableSearchSyncing();

Blade::component('developer-game-stats-table', DeveloperGameStatsTable::class);
Blade::component('global-statistics', GlobalStatistics::class);
Blade::component('user-card', UserCard::class);
Blade::component('user-progression-status', UserProgressionStatus::class);
Expand Down
116 changes: 116 additions & 0 deletions app/Community/Components/DeveloperGameStatsTable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php

declare(strict_types=1);

namespace App\Community\Components;

use Illuminate\Contracts\View\View;
use Illuminate\View\Component;

class DeveloperGameStatsTable extends Component
{
private array $easiestGame = [];
private array $hardestGame = [];
private array $targetGameIds = [];
private int $numGamesWithLeaderboards = 0;
private int $numGamesWithRichPresence = 0;
private int $numTotalLeaderboards = 0;
private string $statsKind = 'any'; // 'any' | 'majority' | 'sole'
private string $targetDeveloperUsername = '';

public function __construct(
array $easiestGame,
array $hardestGame,
array $targetGameIds,
int $numGamesWithLeaderboards,
int $numGamesWithRichPresence,
int $numTotalLeaderboards,
string $statsKind,
string $targetDeveloperUsername,
) {
$this->easiestGame = $easiestGame;
$this->hardestGame = $hardestGame;
$this->numGamesWithLeaderboards = $numGamesWithLeaderboards;
$this->numGamesWithRichPresence = $numGamesWithRichPresence;
$this->numTotalLeaderboards = $numTotalLeaderboards;
$this->statsKind = $statsKind;
$this->targetDeveloperUsername = $targetDeveloperUsername;
$this->targetGameIds = $targetGameIds;
}

public function render(): View
{
$builtStats = $this->buildStats($this->targetDeveloperUsername, $this->targetGameIds);

return view('community.components.developer.game-stats-table', array_merge(
$builtStats, [
'easiestGame' => $this->easiestGame,
'hardestGame' => $this->hardestGame,
'numGamesWithLeaderboards' => $this->numGamesWithLeaderboards,
'numGamesWithRichPresence' => $this->numGamesWithRichPresence,
'numTotalLeaderboards' => $this->numTotalLeaderboards,
'statsKind' => $this->statsKind,
'targetDeveloperUsername' => $this->targetDeveloperUsername,
'targetGameIds' => $this->targetGameIds,
],
));
}

private function buildStats(string $targetDeveloperUsername, array $targetGameIds): array
{
$ownAwards = [];
$mostBeatenSoftcoreGame = $mostBeatenHardcoreGame = $mostCompletedGame = $mostMasteredGame = [];
$userMostBeatenSoftcore = $userMostBeatenHardcore = $userMostCompleted = $userMostMastered = [];
$beatenSoftcoreAwards = $beatenHardcoreAwards = $completedAwards = $masteredAwards = 0;

$mostAwardedGames = getMostAwardedGames($targetGameIds);
foreach ($mostAwardedGames as $game) {
$mostBeatenSoftcoreGame = $this->findMost($game, 'BeatenSoftcore', $mostBeatenSoftcoreGame);
$mostBeatenHardcoreGame = $this->findMost($game, 'BeatenHardcore', $mostBeatenHardcoreGame);
$mostCompletedGame = $this->findMost($game, 'Completed', $mostCompletedGame);
$mostMasteredGame = $this->findMost($game, 'Mastered', $mostMasteredGame);
}

$mostAwardedUsers = getMostAwardedUsers($targetGameIds);
foreach ($mostAwardedUsers as $userInfo) {
$userMostBeatenSoftcore = $this->findMost($userInfo, 'BeatenSoftcore', $userMostBeatenSoftcore);
$userMostBeatenHardcore = $this->findMost($userInfo, 'BeatenHardcore', $userMostBeatenHardcore);
$userMostCompleted = $this->findMost($userInfo, 'Completed', $userMostCompleted);
$userMostMastered = $this->findMost($userInfo, 'Mastered', $userMostMastered);

if (strcmp($targetDeveloperUsername, $userInfo['User']) == 0) {
$ownAwards = $userInfo;
}

$beatenSoftcoreAwards += $userInfo['BeatenSoftcore'];
$beatenHardcoreAwards += $userInfo['BeatenHardcore'];
$completedAwards += $userInfo['Completed'];
$masteredAwards += $userInfo['Mastered'];
}

return compact(
'mostBeatenSoftcoreGame',
'mostBeatenHardcoreGame',
'mostCompletedGame',
'mostMasteredGame',
'ownAwards',
'beatenSoftcoreAwards',
'beatenHardcoreAwards',
'completedAwards',
'masteredAwards',
'userMostBeatenSoftcore',
'userMostBeatenHardcore',
'userMostCompleted',
'userMostMastered',
);
}

private function findMost(array $record, string $key, array $currentMost): array
{
if (empty($currentMost) && (int) $record[$key] > 0) {
return $record;
}

return isset($currentMost[$key]) && ($currentMost[$key] < (int) $record[$key]) ? $record : $currentMost;
}
}
2 changes: 2 additions & 0 deletions app/Community/Enums/AwardType.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

abstract class AwardType
{
// TODO refactor to AchievementSetCompleted
public const Mastery = 1;

public const AchievementUnlocksYield = 2;
Expand All @@ -20,6 +21,7 @@ abstract class AwardType

public const CertifiedLegend = 7;

// TODO refactor to AchievementSetBeaten
public const GameBeaten = 8;

public static function cases(): array
Expand Down
6 changes: 4 additions & 2 deletions app/Community/Enums/UserActivityType.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ abstract class UserActivityType

public const EditAchievement = 'achievement.update';

public const CompleteGame = 'achievement-set.complete';
public const CompleteAchievementSet = 'achievement-set.complete';
public const BeatAchievementSet = 'achievement-set.beat';

public const NewLeaderboardEntry = 'leaderboard-entry.create';

Expand All @@ -34,7 +35,8 @@ public static function cases(): array
self::StartedPlaying,
self::UploadAchievement,
self::EditAchievement,
self::CompleteGame,
self::CompleteAchievementSet,
self::BeatAchievementSet,
self::NewLeaderboardEntry,
self::ImprovedLeaderboardEntry,
self::OpenedTicket,
Expand Down
18 changes: 2 additions & 16 deletions app/Community/EventServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,12 @@
namespace App\Community;

use App\Community\Listeners\WriteUserActivity;
use App\Platform\Events\AchievementCreated;
use App\Platform\Events\AchievementPublished;
use App\Platform\Events\AchievementSetBeaten;
use App\Platform\Events\AchievementSetCompleted;
use App\Platform\Events\AchievementUpdated;
use App\Platform\Events\LeaderboardEntryCreated;
use App\Platform\Events\LeaderboardEntryUpdated;
use App\Platform\Events\PlayerAchievementUnlocked;
use App\Platform\Events\PlayerGameAttached;
use App\Platform\Events\PlayerSessionResumed;
use App\Platform\Events\PlayerSessionStarted;
use Illuminate\Auth\Events\Login;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

Expand All @@ -31,16 +27,10 @@ class EventServiceProvider extends ServiceProvider
/*
* Platform Events - Account Listeners
*/
AchievementCreated::class => [
WriteUserActivity::class,
],
AchievementPublished::class => [
WriteUserActivity::class,
],
AchievementSetCompleted::class => [
WriteUserActivity::class,
],
AchievementUpdated::class => [
AchievementSetBeaten::class => [
WriteUserActivity::class,
],
LeaderboardEntryCreated::class => [
Expand All @@ -55,10 +45,6 @@ class EventServiceProvider extends ServiceProvider
PlayerGameAttached::class => [
WriteUserActivity::class,
],
PlayerSessionStarted::class => [
],
PlayerSessionResumed::class => [
],
];

public function boot(): void
Expand Down
40 changes: 17 additions & 23 deletions app/Community/Listeners/WriteUserActivity.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
use App\Community\Enums\UserActivityType;
use App\Community\Models\UserActivity;
use App\Community\Models\UserActivityLegacy;
use App\Platform\Events\AchievementCreated;
use App\Platform\Events\AchievementSetBeaten;
use App\Platform\Events\AchievementSetCompleted;
use App\Platform\Events\AchievementUpdated;
use App\Platform\Events\LeaderboardEntryCreated;
use App\Platform\Events\LeaderboardEntryUpdated;
use App\Platform\Events\PlayerAchievementUnlocked;
Expand All @@ -33,6 +32,7 @@ public function handle(object $event): void
$subjectId = null;
$context = null;

$storeLegacyActivity = true;
$legacyActivityType = null;
$data = null;
$data2 = null;
Expand All @@ -47,27 +47,15 @@ public function handle(object $event): void
* ignore login activity within 6 hours after the last login activity
*/
$legacyActivityType = ActivityType::Login;
$storeActivity = $user->legacyActivities()
$storeLegacyActivity = $user->legacyActivities()
->where('activitytype', '=', $legacyActivityType)
->where('timestamp', '>', Carbon::now()->subHours(6))
->doesntExist();
// $userActivityType = UserActivityType::Login;
// $storeActivity = $user->activities()
// ->where('type', '=', $userActivityType)
// ->where('created_at', '>', Carbon::now()->subHours(6))
// ->doesntExist();
break;
case AchievementCreated::class:
$userActivityType = UserActivityType::UploadAchievement;
// TODO: subject_context = create
// TODO: subject_id
$subjectType = 'achievement';
break;
case AchievementUpdated::class:
$userActivityType = UserActivityType::EditAchievement;
// TODO: subject_context = update
// TODO: subject_id
$subjectType = 'achievement';
$userActivityType = UserActivityType::Login;
$storeActivity = $user->activities()
->where('type', '=', $userActivityType)
->where('created_at', '>', Carbon::now()->subHours(6))
->doesntExist();
break;
case LeaderboardEntryCreated::class:
$userActivityType = UserActivityType::NewLeaderboardEntry;
Expand All @@ -89,10 +77,16 @@ public function handle(object $event): void
$context = $event->hardcore ? 1 : null;
break;
case AchievementSetCompleted::class:
$userActivityType = UserActivityType::CompleteGame;
$userActivityType = UserActivityType::CompleteAchievementSet;
// TODO: subject_context = complete
// TODO: subject_id
$subjectType = 'game';
// TODO $subjectType = 'achievement_set';
break;
case AchievementSetBeaten::class:
$userActivityType = UserActivityType::BeatAchievementSet;
// TODO: subject_context = beat
// TODO: subject_id
// TODO $subjectType = 'achievement_set';
break;
case PlayerGameAttached::class:
$userActivityType = UserActivityType::StartedPlaying;
Expand All @@ -103,7 +97,7 @@ public function handle(object $event): void
default:
}

if ($legacyActivityType && $storeActivity) {
if ($legacyActivityType && $storeLegacyActivity) {
$user->legacyActivities()->save(new UserActivityLegacy([
'activitytype' => $legacyActivityType,
'data' => $data,
Expand Down
35 changes: 21 additions & 14 deletions app/Connect/Concerns/AchievementRequests.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@

namespace App\Connect\Concerns;

use App\Platform\Actions\ResumePlayerSessionAction;
use App\Platform\Actions\UnlockPlayerAchievementAction;
use App\Platform\Jobs\UnlockPlayerAchievementJob;
use App\Platform\Models\Achievement;
use App\Platform\Models\Game;
use App\Platform\Models\PlayerAchievement;
use App\Platform\Models\PlayerGame;
use App\Site\Models\User;
use Exception;
use Illuminate\Http\Request;

trait AchievementRequests
Expand Down Expand Up @@ -115,24 +114,32 @@ protected function awardachievementMethod(Request $request): array
$achievement = Achievement::find($achievementId);
abort_if($achievement === null, 404, 'Achievement with ID "' . $achievementId . '" not found');

// TODO "Unofficial achievements cannot be unlocked"

/** @var User $user */
$user = $request->user('connect-token');

// TODO: validate sent hash
// $request->input('v') === $achievement->unlockValidationHash($user, (int) $hardcore);

// resume the player session before unlocking the achievement so they'll get associated together
try {
/** @var ResumePlayerSessionAction $resumePlayerSessionAction */
$resumePlayerSessionAction = app()->make(ResumePlayerSessionAction::class);
$resumePlayerSessionAction->execute($request, $achievement->game);
} catch (Exception) {
// fail silently - might be an unauthenticated request (RetroArch)
}
// fail silently - might be an unauthenticated request (RetroArch)
dispatch(new UnlockPlayerAchievementJob($user->id, $achievement->id, (bool) $hardcore))
->onQueue('player-achievements');

/** @var UnlockPlayerAchievementAction $unlockPlayerAchievementAction */
$unlockPlayerAchievementAction = app()->make(UnlockPlayerAchievementAction::class);
$playerGame = PlayerGame::where('user_id', $user->id)
->where('game_id', $achievement->game_id)
->first();
$remaining = 0;
if ($playerGame) {
$remaining = $playerGame->achievements_total - $playerGame->achievements_unlocked;
}

return $unlockPlayerAchievementAction->execute($user, $achievement, (bool) $hardcore);
// TODO respond with optimistically updated score values
return [
'Score' => $user->points,
'SoftcoreScore' => (int) $user->points_softcore,
'AchievementID' => (int) $achievementId,
'AchievementsRemaining' => $remaining,
];
}
}
2 changes: 1 addition & 1 deletion app/Connect/Concerns/AuthRequests.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ protected function loginMethod(Request $request): array

$response = [
'displayName' => $user->display_name,
'pointsTotal' => $user->points_total,
'pointsTotal' => $user->points,
'unreadMessagesCount' => $user->unread_messages_count,
'username' => $user->username,
'token' => $user->connect_token,
Expand Down
Loading

0 comments on commit 18db5c8

Please sign in to comment.