Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(UpdatePlayerBeatenGamesStatsAction): handle resets to zero correctly #3202

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 49 additions & 20 deletions app/Platform/Actions/UpdatePlayerBeatenGamesStatsAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use App\Platform\Enums\PlayerStatType;
use App\Platform\Enums\UnlockMode;
use App\Platform\Events\PlayerBeatenGamesStatsUpdated;
use Illuminate\Support\Collection;

class UpdatePlayerBeatenGamesStatsAction
{
Expand All @@ -25,6 +26,9 @@ public function execute(User $user): void
return;
}

// Get existing stats to ensure we maintain entries for all previously tracked systems.
$existingStats = PlayerStat::where('user_id', $user->id)->get();

$playerBeatenHardcoreGames = $user
->playerBadges()
->where('AwardType', AwardType::GameBeaten)
Expand Down Expand Up @@ -54,25 +58,41 @@ public function execute(User $user): void
];
});

$aggregatedPlayerStatValues = $this->calculateAggregatedGameBeatenHardcoreStatValues($playerBeatenHardcoreGames);
$this->upsertAllPlayerStats($user, $aggregatedPlayerStatValues);
$aggregatedPlayerStatValues = $this->calculateAggregatedGameBeatenHardcoreStatValues($playerBeatenHardcoreGames, $existingStats);
$this->upsertAllPlayerStats($user, $aggregatedPlayerStatValues, $existingStats);
}

private function calculateAggregatedGameBeatenHardcoreStatValues(mixed $playerBeatenHardcoreGames): array
{
// We'll hold overall and per-console stat values.
// type => [value, most recent hardcore beaten game id, beaten at timestamp]
$statValues = [
'overall' => [
/**
* @param Collection<int, PlayerStat> $existingStats
*/
private function calculateAggregatedGameBeatenHardcoreStatValues(
mixed $playerBeatenHardcoreGames,
Collection $existingStats,
): array {
$getInitializedStats = function () {
return [
PlayerStatType::GamesBeatenHardcoreDemos => [0, null, null],
PlayerStatType::GamesBeatenHardcoreHacks => [0, null, null],
PlayerStatType::GamesBeatenHardcoreHomebrew => [0, null, null],
PlayerStatType::GamesBeatenHardcorePrototypes => [0, null, null],
PlayerStatType::GamesBeatenHardcoreRetail => [0, null, null],
PlayerStatType::GamesBeatenHardcoreUnlicensed => [0, null, null],
],
];
};

// We'll hold overall and per-console stat values.
// type => [value, most recent hardcore beaten game id, beaten at timestamp]
$statValues = [
'overall' => $getInitializedStats(),
];

// Initialize entries for all systems that previously had stats.
foreach ($existingStats as $stat) {
if ($stat->system_id !== null && !isset($statValues[$stat->system_id])) {
$statValues[$stat->system_id] = $getInitializedStats();
}
}

$gameKindToStatType = [
'demo' => PlayerStatType::GamesBeatenHardcoreDemos,
'hack' => PlayerStatType::GamesBeatenHardcoreHacks,
Expand All @@ -94,14 +114,7 @@ private function calculateAggregatedGameBeatenHardcoreStatValues(mixed $playerBe

// Ensure there's an array entry for the console aggregates.
if (!isset($statValues[$gameConsoleId])) {
$statValues[$gameConsoleId] = [
PlayerStatType::GamesBeatenHardcoreDemos => [0, null, null],
PlayerStatType::GamesBeatenHardcoreHacks => [0, null, null],
PlayerStatType::GamesBeatenHardcoreHomebrew => [0, null, null],
PlayerStatType::GamesBeatenHardcorePrototypes => [0, null, null],
PlayerStatType::GamesBeatenHardcoreRetail => [0, null, null],
PlayerStatType::GamesBeatenHardcoreUnlicensed => [0, null, null],
];
$statValues[$gameConsoleId] = $getInitializedStats();
}

// Update the individual console aggregates.
Expand Down Expand Up @@ -144,10 +157,23 @@ private function determineGameKind(string $gameTitle, int $gameConsoleId): strin
return 'retail';
}

private function upsertAllPlayerStats(User $user, array $aggregatedPlayerStatValues): int
{
/**
* @param Collection<int, PlayerStat> $existingStats
*/
private function upsertAllPlayerStats(
User $user,
array $aggregatedPlayerStatValues,
Collection $existingStats,
): int {
$updatedCount = 0;

// Create a map for quick lookups using system_id and type as the key.
$existingStatsMap = [];
foreach ($existingStats as $stat) {
$key = ($stat->system_id ?? 'overall') . '|' . $stat->type;
$existingStatsMap[$key] = true;
}

// Loop through each console ID in the aggregated values (including 'overall').
foreach ($aggregatedPlayerStatValues as $aggregateSystemId => $systemStats) {
// Check if it's the 'overall' key or a specific console ID.
Expand All @@ -158,7 +184,10 @@ private function upsertAllPlayerStats(User $user, array $aggregatedPlayerStatVal
// Extract the value and most recent game ID.
[$value, $lastGameId, $statUpdatedAt] = $values;

if ($value > 0) {
// Check if this stat combination exists in our map.
$key = ($systemId ?? 'overall') . '|' . $statType;

if ($value > 0 || isset($existingStatsMap[$key])) {
$this->upsertPlayerStat($user, $statType, $value, $systemId, $lastGameId, $statUpdatedAt);
$updatedCount++;
}
Expand Down