Skip to content

Commit

Permalink
Merge branch 'master' into unify-game-page-progress-bars
Browse files Browse the repository at this point in the history
  • Loading branch information
luchaos authored Dec 3, 2023
2 parents a1a1db0 + d1663a0 commit 883f175
Show file tree
Hide file tree
Showing 31 changed files with 235 additions and 141 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ FORWARD_PHPMYADMIN_PORT=64080

# Feature Flags

FEATURE_BEAT=true
#FEATURE_EXAMPLE=true

# Application

Expand Down
3 changes: 1 addition & 2 deletions .env.testing
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,4 @@ SCOUT_DRIVER=null
SESSION_DRIVER=array
TELESCOPE_ENABLED=false
DEBUGBAR_ENABLED=false
CSP_ENABLED=false
FEATURE_BEAT=true
CSP_ENABLED=false
10 changes: 0 additions & 10 deletions app/Exceptions/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,16 +171,6 @@ public function render($request, Throwable $e): Response
}
}

if ($e instanceof AuthenticationException) {
// parent::render will call parent::unauthenticated for AuthenticationException,
// which redirects to route('login') unless the exception specifies another target.
// Since we don't define a login route, this causes an exception. If no redirect
// route is provided, just fail with 401 Not Authorized.
if (empty($e->redirectTo())) {
abort(401);
}
}

return parent::render($request, $e);
}
}
6 changes: 6 additions & 0 deletions app/Helpers/database/user-activity.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ function expireRecentlyPlayedGames(string $user): void

function getRecentlyPlayedGames(string $user, int $offset, int $count, ?array &$dataOut): int
{
if ($count < 1) {
$dataOut = [];

return 0;
}

$query = "SELECT pg.last_played_at AS LastPlayed, pg.game_id AS GameID, pg.achievements_total
FROM player_games pg
INNER JOIN UserAccounts ua ON ua.ID = pg.user_id
Expand Down
2 changes: 1 addition & 1 deletion app/Helpers/render/game.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ function RenderGameSort(
// }
echo "<a href='/game/$gameID?$flagParam&s=$sort4'>Points$mark4</a> - ";
echo "<a href='/game/$gameID?$flagParam&s=$sort5'>Title$mark5</a>";
if (config('feature.beat') && $canSortByType) {
if ($canSortByType) {
echo " - ";
echo "<a href='/game/$gameID?$flagParam&s=$sort6'>Type$mark6</a>";
}
Expand Down
4 changes: 1 addition & 3 deletions app/Helpers/render/user.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,7 @@ function RenderCompletedGamesList(
</label>
HTML;

if (config('feature.beat')) {
echo "<a href='" . route('user.completion-progress', $username) . "'>more...</a>";
}
echo "<a href='" . route('user.completion-progress', $username) . "'>more...</a>";
echo "</div>";

echo "<div id='usercompletedgamescomponent'>";
Expand Down
19 changes: 13 additions & 6 deletions app/Platform/Actions/UpdatePlayerStats.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use App\Platform\Enums\UnlockMode;
use App\Platform\Events\PlayerStatsUpdated;
use App\Platform\Models\PlayerStat;
use App\Platform\Models\System;
use App\Site\Models\User;

class UpdatePlayerStats
Expand Down Expand Up @@ -83,7 +84,7 @@ private function calculateAggregatedGameBeatenHardcoreStatValues(mixed $playerBe

foreach ($playerBeatenHardcoreGames as $playerGame) {
$gameConsoleId = $playerGame['game']['ConsoleID'];
$gameKind = $this->deriveGameKindFromTitle($playerGame['game']['Title']);
$gameKind = $this->determineGameKind($playerGame['game']['Title'], $gameConsoleId);
$statTypeKey = $gameKindToStatType[$gameKind] ?? PlayerStatType::GamesBeatenHardcoreRetail;

// Update the overall aggregates.
Expand Down Expand Up @@ -117,7 +118,7 @@ private function clearExistingUntrackedStats(User $user): void
PlayerStat::where('user_id', $user->ID)->delete();
}

private function deriveGameKindFromTitle(string $gameTitle): string
private function determineGameKind(string $gameTitle, int $gameConsoleId): string
{
$sanitizedTitle = mb_strtolower($gameTitle);
$gameKinds = [
Expand All @@ -132,6 +133,12 @@ private function deriveGameKindFromTitle(string $gameTitle): string
if (str_contains($sanitizedTitle, $keyword)) {
return $kind;
}

// Some consoles were never sold in stores and are considered "homebrew".
// Their games fall back to "homebrew" rather than "retail".
if (System::isHomebrewSystem($gameConsoleId)) {
return 'homebrew';
}
}

return 'retail';
Expand All @@ -149,10 +156,10 @@ private function upsertAllPlayerStats(User $user, array $aggregatedPlayerStatVal
// Now, loop through each stat type for this system.
foreach ($systemStats as $statType => $values) {
// Extract the value and most recent game ID.
[$value, $lastGameId, $updatedAt] = $values;
[$value, $lastGameId, $statUpdatedAt] = $values;

if ($value > 0) {
$this->upsertPlayerStat($user, $statType, $value, $systemId, $lastGameId, $updatedAt);
$this->upsertPlayerStat($user, $statType, $value, $systemId, $lastGameId, $statUpdatedAt);
$updatedCount++;
}
}
Expand All @@ -167,7 +174,7 @@ private function upsertPlayerStat(
int $value,
?int $systemId,
?int $lastGameId,
?string $updatedAt
?string $statUpdatedAt
): void {
PlayerStat::updateOrCreate(
[
Expand All @@ -178,7 +185,7 @@ private function upsertPlayerStat(
[
'last_game_id' => $lastGameId,
'value' => $value,
'updated_at' => $updatedAt,
'stat_updated_at' => $statUpdatedAt,
]
);

Expand Down
5 changes: 1 addition & 4 deletions app/Platform/Components/GameCard.php
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,7 @@ private function buildCardUserProgressionData(array $userGameProgressionAwards,
$highestProgressionStatus = null;
$highestProgressionAwardDate = null;

$progressionTypes = ['completed', 'mastered'];
if (config('feature.beat')) {
$progressionTypes = ['beaten-softcore', 'beaten-hardcore', 'completed', 'mastered'];
}
$progressionTypes = ['beaten-softcore', 'beaten-hardcore', 'completed', 'mastered'];

foreach ($progressionTypes as $progressionType) {
if (isset($userGameProgressionAwards[$progressionType])) {
Expand Down
10 changes: 3 additions & 7 deletions app/Platform/Controllers/BeatenGamesLeaderboardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ class BeatenGamesLeaderboardController extends Controller

public function __invoke(Request $request): View
{
if (!config('feature.beat')) {
abort(404);
}

$validatedData = $request->validate([
'page.number' => 'sometimes|integer|min:1',
'filter.system' => 'sometimes|integer',
Expand Down Expand Up @@ -129,7 +125,7 @@ private function buildAggregatedLeaderboardQuery(array $gameKindFilterOptions =
$aggregateSubquery = PlayerStat::selectRaw(
'user_id,
SUM(value) AS total_awards,
MAX(updated_at) AS last_beaten_date'
MAX(stat_updated_at) AS last_beaten_date'
)
->when($targetSystemId, function ($query) use ($targetSystemId) {
return $query->where('system_id', $targetSystemId);
Expand All @@ -142,14 +138,14 @@ private function buildAggregatedLeaderboardQuery(array $gameKindFilterOptions =
$query = PlayerStat::selectRaw(
'sub.user_id,
MAX(CASE WHEN player_stats.type IN (\'' . implode("', '", $includedTypes) . '\') THEN player_stats.last_game_id ELSE NULL END) AS last_game_id,
MAX(CASE WHEN player_stats.type IN (\'' . implode("', '", $includedTypes) . '\') THEN player_stats.updated_at ELSE NULL END) as last_beaten_date,
MAX(CASE WHEN player_stats.type IN (\'' . implode("', '", $includedTypes) . '\') THEN player_stats.stat_updated_at ELSE NULL END) as last_beaten_date,
sub.total_awards,
RANK() OVER (ORDER BY sub.total_awards DESC) as rank_number,
ROW_NUMBER() OVER (ORDER BY sub.total_awards DESC, sub.last_beaten_date ASC) as leaderboard_row_number'
)
->joinSub($aggregateSubquery, 'sub', function ($join) use ($targetSystemId) {
$join->on('sub.user_id', '=', 'player_stats.user_id')
->on('sub.last_beaten_date', '=', 'player_stats.updated_at');
->on('sub.last_beaten_date', '=', 'player_stats.stat_updated_at');

if (isset($targetSystemId) && $targetSystemId > 0) {
$join->where('player_stats.system_id', '=', $targetSystemId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ public function __construct(protected PlayerProgressionService $playerProgressio

public function __invoke(Request $request): View
{
if (!config('feature.beat')) {
abort(404);
}

$targetUsername = $request->route()->parameters['user'];
$validatedData = $request->validate([
'page.number' => 'sometimes|integer|min:1',
Expand Down
3 changes: 2 additions & 1 deletion app/Platform/Models/PlayerStat.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ class PlayerStat extends BaseModel
'last_game_id',
'type',
'value',
'updated_at',
'stat_updated_at',
];

protected $casts = [
'user_id' => 'integer',
'system_id' => 'integer',
'last_game_id' => 'integer',
'stat_updated_at' => 'datetime',
'value' => 'integer',
];

Expand Down
13 changes: 13 additions & 0 deletions app/Platform/Models/System.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,27 @@ protected static function newFactory(): SystemFactory

// == constants

public const Arduboy = 71;
public const WASM4 = 72;
public const Uzebox = 80;
public const Hubs = 100;
public const Events = 101;

public static function getHomebrewSystems(): array
{
return [System::Arduboy, System::WASM4, System::Uzebox];
}

public static function isGameSystem(int $type): bool
{
return $type != System::Hubs && $type != System::Events;
}

public static function isHomebrewSystem(int $type): bool
{
return in_array($type, self::getHomebrewSystems());
}

// == media

public function registerMediaCollections(): void
Expand Down
2 changes: 1 addition & 1 deletion config/envy.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
'REDIS_CACHE_DB',
'MYSQL_ATTR_SSL_CA',
// config/feature.php
'FEATURE_BEAT',
//
// config/filesystem.php
'AWS_ENDPOINT',
'AWS_URL',
Expand Down
9 changes: 3 additions & 6 deletions config/feature.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
<?php

return [

/*
* If true, player-facing beaten games UI/UX is enabled.
*/
'beat' => env('FEATURE_BEAT', false),

/**
* 'example' => env('FEATURE_EXAMPLE', false),
*/
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class() extends Migration {
public function up(): void
{
Schema::table('player_stats', function (Blueprint $table) {
$table->timestampTz('stat_updated_at')->nullable()->after('last_game_id');
});
}

public function down(): void
{
Schema::table('player_stats', function (Blueprint $table) {
$table->dropColumn('stat_updated_at');
});
}
};
14 changes: 11 additions & 3 deletions public/API/API_GetUserSummary.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
/*
* API_GetUserSummary
* u : username
* g : number of recent games to return (default: 5)
* g : number of recent games to return (default: 0)
* a : number of recent achievements to return (default: 10)
* NOTE: Recent achievements are pulled from recent games, so if you ask for
* 1 game and 10 achievements, and the user has only earned 8 achievements in
* the most recent game, you'll only get 8 recent achievements back. Similarly,
* with the default of 0 recent games, no recent achievements will be returned.
*
* int ID unique identifier of the user
* int TotalPoints number of hardcore points the user has
Expand Down Expand Up @@ -84,12 +88,12 @@

$input = Validator::validate(Arr::wrap(request()->query()), [
'u' => ['required', 'min:2', 'max:20', new CtypeAlnum()],
'g' => 'nullable|integer|min:0',
'g' => 'nullable|integer|min:0|max:100',
'a' => 'nullable|integer|min:0',
]);

$user = request()->query('u');
$recentGamesPlayed = (int) request()->query('g', '5');
$recentGamesPlayed = (int) request()->query('g', '0');
$recentAchievementsEarned = (int) request()->query('a', '10');

$retVal = getUserPageInfo($user, $recentGamesPlayed, $recentAchievementsEarned);
Expand All @@ -108,6 +112,10 @@
if (array_key_exists('LastGame', $retVal)) {
unset($retVal['LastGame']['RichPresencePatch']);
unset($retVal['LastGame']['system']);
} elseif ($recentGamesPlayed === 0) {
// if no games were requested, initialize empty arrays for Awarded and RecentAchievements
$retVal['Awarded'] = [];
$retVal['RecentAchievements'] = [];
}

$retVal['LastActivity'] = [
Expand Down
4 changes: 2 additions & 2 deletions public/admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@
}

if ($action === 'copy-unlocks') {
$fromAchievementIds = explode(',', requestInputSanitized('s'));
$fromAchievementIds = separateList(requestInputSanitized('s'));
$fromAchievementCount = count($fromAchievementIds);
$toAchievementIds = explode(',', requestInputSanitized('a'));
$toAchievementIds = separateList(requestInputSanitized('a'));

// determine which players have earned all of the required achievements
$existing = PlayerAchievement::whereIn('achievement_id', $fromAchievementIds)
Expand Down
2 changes: 1 addition & 1 deletion public/gameInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,7 @@ function () {
', [
'beatenGameCreditDialogContext' => $beatenGameCreditDialogContext,
'gameId' => $gameID,
'isBeatable' => $isGameBeatable && config('feature.beat') === true,
'isBeatable' => $isGameBeatable,
'isBeatenHardcore' => $isBeatenHardcore,
'isBeatenSoftcore' => $isBeatenSoftcore,
'isCompleted' => !is_null($userGameProgressionAwards['completed']),
Expand Down
3 changes: 1 addition & 2 deletions public/userInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -485,8 +485,7 @@ function resize() {
}

$canShowProgressionStatusComponent =
config('feature.beat')
&& !empty($userCompletedGamesList)
!empty($userCompletedGamesList)
// Needs at least one non-event game.
&& count(array_filter($userCompletedGamesList, fn ($game) => $game['ConsoleID'] != 101)) > 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@
<div class="grid grid-cols-2 gap-px mb-2">
<x-global-statistics.stat-embed label="Games" :count="$numGames" href="{{ route('game.index', ['s' => 1]) }}" />
<x-global-statistics.stat-embed label="Achievements" :count="$numAchievements" href="/achievementList.php" />
@hasfeature("beat")
<x-global-statistics.stat-embed label="Games Mastered" :count="$numHardcoreMasteryAwards" href="/recentMastery.php?t=1&m=1" />
<x-global-statistics.stat-embed label="Games Beaten" :count="$numHardcoreGameBeatenAwards" href="/recentMastery.php?t=8&m=1" />
@endhasfeature
<x-global-statistics.stat-embed label="Games Mastered" :count="$numHardcoreMasteryAwards" href="/recentMastery.php?t=1&m=1" />
<x-global-statistics.stat-embed label="Games Beaten" :count="$numHardcoreGameBeatenAwards" href="/recentMastery.php?t=8&m=1" />
<x-global-statistics.stat-embed label="Registered Players" :count="$numRegisteredPlayers" href="/userList.php" />
<x-global-statistics.stat-embed label="Achievement Unlocks" :count="$numAwarded" href="/recentMastery.php" />
</div>
Expand All @@ -31,14 +29,12 @@
:timestamp="$lastMasteredTimeAgo"
/>

@hasfeature("beat")
<x-global-statistics.recent-game-progress
headingLabel="Most recent game beaten"
:game="$lastBeatenGame"
:userId="$lastBeatenUserId"
:timestamp="$lastBeatenTimeAgo"
/>
@endhasfeature
<x-global-statistics.recent-game-progress
headingLabel="Most recent game beaten"
:game="$lastBeatenGame"
:userId="$lastBeatenUserId"
:timestamp="$lastBeatenTimeAgo"
/>
</div>

@if ($lastRegisteredUser)
Expand Down
Loading

0 comments on commit 883f175

Please sign in to comment.