Skip to content

Commit

Permalink
#2662 Week selection now functioning properly.
Browse files Browse the repository at this point in the history
Added support for epochs which are required by Raider.io API.
  • Loading branch information
Wotuu committed Jan 26, 2025
1 parent 0f01eb1 commit af22e83
Show file tree
Hide file tree
Showing 13 changed files with 149 additions and 43 deletions.
2 changes: 1 addition & 1 deletion app/Http/Controllers/SiteController.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public function benchmark(
$result = $combatLogRouteDungeonRouteService->correctCombatLogRoute(
CombatLogRouteRequestModel::createFromArray($validated)
);
Stopwatch::pause('SiteController::benchmark');
Stopwatch::stop('SiteController::benchmark');

// dump('hey');
return view('misc.credits');
Expand Down
17 changes: 9 additions & 8 deletions app/Http/Requests/Heatmap/AjaxGetDataFormRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ public function authorize(): bool
public function rules(): array
{
return [
'dungeon_id' => ['required', Rule::exists(Dungeon::class, 'id')],
'event_type' => ['required', Rule::in(CombatLogEventEventType::cases())],
'data_type' => ['required', Rule::in(CombatLogEventDataType::cases())],
'level' => ['nullable', 'regex:/^\d*;\d*$/'],
'affixes' => ['nullable', 'array'],
'affixes.*' => ['integer', Rule::exists(Affix::class, 'id')],
'weekly_affix_groups' => ['integer'],
'duration' => ['nullable', 'regex:/^\d*;\d*$/'],
'dungeon_id' => ['required', Rule::exists(Dungeon::class, 'id')],
'event_type' => ['required', Rule::in(CombatLogEventEventType::cases())],
'data_type' => ['required', Rule::in(CombatLogEventDataType::cases())],
'level' => ['nullable', 'regex:/^\d*;\d*$/'],
'affixes' => ['nullable', 'array'],
'affixes.*' => ['integer', Rule::exists(Affix::class, 'id')],
'weekly_affix_groups' => ['nullable', 'array'],
'weekly_affix_groups.*' => ['integer'],
'duration' => ['nullable', 'regex:/^\d*;\d*$/'],
];
}
}
Expand Down
52 changes: 50 additions & 2 deletions app/Models/GameServerRegion.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use App\Service\Cache\CacheServiceInterface;
use Eloquent;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Auth;
Expand All @@ -14,6 +15,7 @@
* @property int $id
* @property string $short
* @property string $name
* @property Carbon $epoch_start
* @property string $timezone
* @property int $reset_day_offset ISO-8601 numeric representation of the day of the week
* @property string $reset_hours_offset
Expand All @@ -25,9 +27,19 @@ class GameServerRegion extends CacheModel
{
use SeederModel;

protected $fillable = ['short', 'name', 'timezone', 'reset_day_offset', 'reset_hours_offset'];
// Blizzard changed the reset time on November 16th:
//
// Weekly Reset Time Changing to 05:00 CET on 16 November
//
// https://eu.forums.blizzard.com/en/wow/t/weekly-reset-time-changing-to-0500-cet-on-16-november/398498
// the date at which the EU epoch change kicks in
const EU_EPOCH_CHANGE_STARTED_AT_DATE = '2022-11-16 04:00:00';

public $timestamps = false;
// The base date we return when requesting a date using the new epoch
const EU_EPOCH_CHANGE_DATE = '2005-12-28 04:00:00';

// The period that the EU epoch change started at
const EU_EPOCH_CHANGE_PERIOD = 881;

public const AMERICAS = 'us';
public const EUROPE = 'eu';
Expand All @@ -45,6 +57,14 @@ class GameServerRegion extends CacheModel
self::KOREA => 5,
];

protected $fillable = ['short', 'name', 'epoch_start', 'timezone', 'reset_day_offset', 'reset_hours_offset'];

protected $casts = [
'epoch_start' => 'datetime',
];

public $timestamps = false;

public function users(): HasMany
{
return $this->hasMany(User::class);
Expand All @@ -69,4 +89,32 @@ public static function getUserOrDefaultRegion(): GameServerRegion
static fn() => GameServerRegion::where('short', self::DEFAULT_REGION)->first()
);
}

/**
* Get the leaderboard period based on the region and a given date.
*
* @param Carbon $dateTime
* @return int
*/
public function getKeystoneLeaderboardPeriod(Carbon $dateTime): int
{
$epoch = self::getRegionEpochByDate($dateTime);

return $epoch->diffInWeeks($dateTime);
}

/**
* Get the epoch date for a region based on the given date.
*
* @param Carbon $dateTime
* @return Carbon|null
*/
public function getRegionEpochByDate(Carbon $dateTime): ?Carbon
{
if ($this->short === self::EUROPE && $dateTime >= Carbon::parse(self::EU_EPOCH_CHANGE_STARTED_AT_DATE)) {
return Carbon::parse(self::EU_EPOCH_CHANGE_DATE);
}

return $this->epoch_start;
}
}
2 changes: 1 addition & 1 deletion app/Providers/KeystoneGuruServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ public function boot(
}

// API requests don't need to load this in at all! We don't use the view cache since there's no view
if (Str::startsWith(request()->getRequestUri(), ['/api', '/benchmark'])) {
if (Str::startsWith(request()->getRequestUri(), ['/ajax', '/api', '/benchmark'])) {
return;
}

Expand Down
33 changes: 18 additions & 15 deletions app/Service/CombatLogEvent/Dtos/CombatLogEventFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace App\Service\CombatLogEvent\Dtos;

use App\Models\Affix;
use App\Models\AffixGroup\AffixGroup;
use App\Models\CombatLog\CombatLogEventDataType;
use App\Models\CombatLog\CombatLogEventEventType;
use App\Models\Dungeon;
Expand All @@ -13,12 +12,10 @@
use App\Service\Season\SeasonServiceInterface;
use Codeart\OpensearchLaravel\Search\Query;
use Codeart\OpensearchLaravel\Search\SearchQueries\BoolQuery;
use Codeart\OpensearchLaravel\Search\SearchQueries\Filter;
use Codeart\OpensearchLaravel\Search\SearchQueries\Must;
use Codeart\OpensearchLaravel\Search\SearchQueries\Should;
use Codeart\OpensearchLaravel\Search\SearchQueries\Types\MatchOne;
use Codeart\OpensearchLaravel\Search\SearchQueries\Types\Range;
use Codeart\OpensearchLaravel\Search\SearchQueries\Types\Term;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Collection;

Expand All @@ -31,7 +28,7 @@ class CombatLogEventFilter implements Arrayable
/** @var Collection<Affix> */
private Collection $affixes;

private ?int $weeklyAffixGroups = null;
private Collection $weeklyAffixGroups;

private ?int $durationMin = null;

Expand All @@ -41,9 +38,10 @@ public function __construct(
private readonly SeasonServiceInterface $seasonService,
private readonly Dungeon $dungeon,
private readonly CombatLogEventEventType $eventType,
private readonly CombatLogEventDataType $dataType
private readonly CombatLogEventDataType $dataType
) {
$this->affixes = collect();
$this->affixes = collect();
$this->weeklyAffixGroups = collect();
}

public function getDungeon(): Dungeon
Expand Down Expand Up @@ -104,12 +102,12 @@ public function setAffixes(Collection $affixes): CombatLogEventFilter
return $this;
}

public function getWeeklyAffixGroups(): ?int
public function getWeeklyAffixGroups(): Collection
{
return $this->weeklyAffixGroups;
}

public function setWeeklyAffixGroups(?int $weeklyAffixGroups): CombatLogEventFilter
public function setWeeklyAffixGroups(Collection $weeklyAffixGroups): CombatLogEventFilter
{
$this->weeklyAffixGroups = $weeklyAffixGroups;

Expand Down Expand Up @@ -207,23 +205,28 @@ public function toOpensearchQuery(array $must = []): array
]);
}

if ($this->weeklyAffixGroups !== null) {
if ($this->weeklyAffixGroups->isNotEmpty()) {
// Add an AffixGroup filter
/** @var Collection<WeeklyAffixGroup> $weeklyAffixGroupsSinceStart */
$weeklyAffixGroupsSinceStart = $this->seasonService->getWeeklyAffixGroupsSinceStart(
$this->seasonService->getMostRecentSeasonForDungeon($dungeon),
GameServerRegion::getUserOrDefaultRegion()
);

/** @var WeeklyAffixGroup $weeklyAffixGroup */
$weeklyAffixGroup = $weeklyAffixGroupsSinceStart->firstWhere(function (WeeklyAffixGroup $weeklyAffixGroup) {
return $weeklyAffixGroup->week === $this->weeklyAffixGroups;
/** @var WeeklyAffixGroup $minWeeklyAffixGroup */
$minWeeklyAffixGroup = $weeklyAffixGroupsSinceStart->firstWhere(function (WeeklyAffixGroup $weeklyAffixGroup) {
return $weeklyAffixGroup->week === $this->weeklyAffixGroups->min();
});
/** @var WeeklyAffixGroup $maxWeeklyAffixGroup */
$maxWeeklyAffixGroup = $weeklyAffixGroupsSinceStart->firstWhere(function (WeeklyAffixGroup $weeklyAffixGroup) {
return $weeklyAffixGroup->week === $this->weeklyAffixGroups->max();
});


// Add a date range filter
$must[] = Range::make('start', [
'gte' => $weeklyAffixGroup->date->getTimestamp(),
'lte' => $weeklyAffixGroup->date->addWeek()->getTimestamp(),
'gte' => $minWeeklyAffixGroup->date->getTimestamp(),
'lte' => $maxWeeklyAffixGroup->date->addWeek()->getTimestamp(),
]);
}

Expand Down Expand Up @@ -262,7 +265,7 @@ public static function fromArray(SeasonServiceInterface $seasonService, array $r
}

if (isset($requestArray['weekly_affix_groups'])) {
$combatLogEventFilter->setWeeklyAffixGroups($requestArray['weekly_affix_groups']);
$combatLogEventFilter->setWeeklyAffixGroups(collect($requestArray['weekly_affix_groups']));
}

return $combatLogEventFilter;
Expand Down
34 changes: 22 additions & 12 deletions app/Service/RaiderIO/Dtos/HeatmapDataFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
use App\Models\CombatLog\CombatLogEventDataType;
use App\Models\CombatLog\CombatLogEventEventType;
use App\Models\Dungeon;
use App\Models\GameServerRegion;
use App\Models\Season;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Collection;

Expand All @@ -19,17 +21,19 @@ class HeatmapDataFilter implements Arrayable
/** @var Collection<Affix> */
private Collection $affixes;

private ?int $weeklyAffixGroups = null;
private ?int $durationMin = null;
private Collection $weeklyAffixGroups;

private ?int $durationMin = null;

private ?int $durationMax = null;

public function __construct(
private readonly Dungeon $dungeon,
private readonly CombatLogEventEventType $eventType,
private readonly CombatLogEventDataType $dataType
private readonly CombatLogEventDataType $dataType
) {
$this->affixes = collect();
$this->affixes = collect();
$this->weeklyAffixGroups = collect();
}

public function getDungeon(): Dungeon
Expand Down Expand Up @@ -90,12 +94,12 @@ public function setAffixes(Collection $affixes): HeatmapDataFilter
return $this;
}

public function getWeeklyAffixGroups(): ?int
public function getWeeklyAffixGroups(): Collection
{
return $this->weeklyAffixGroups;
}

public function setWeeklyAffixGroups(?int $weeklyAffixGroups): HeatmapDataFilter
public function setWeeklyAffixGroups(Collection $weeklyAffixGroups): HeatmapDataFilter
{
$this->weeklyAffixGroups = $weeklyAffixGroups;

Expand Down Expand Up @@ -126,7 +130,7 @@ public function setDurationMax(?int $durationMax): HeatmapDataFilter
return $this;
}

public function toArray(): array
public function toArray(Season $mostRecentSeason = null): array
{
$result = [
'challengeModeId' => $this->dungeon->challenge_mode_id,
Expand All @@ -153,12 +157,18 @@ public function toArray(): array
}

if ($this->getAffixes()->isNotEmpty()) {
$result['includeAffixIds'] = $this->getAffixes()->map(fn(Affix $affix) => $affix->affix_id)->toArray();
$result['includeAffixIds'] = implode(',', $this->getAffixes()->map(fn(Affix $affix) => $affix->affix_id)->toArray());
}

if ($this->getWeeklyAffixGroups() !== null) {
$result['minPeriod'] = $this->getWeeklyAffixGroups();
$result['maxPeriod'] = $this->getWeeklyAffixGroups();
if ($this->getWeeklyAffixGroups()->isNotEmpty()) {
$gameServerRegion = GameServerRegion::getUserOrDefaultRegion();

$periodStart = $mostRecentSeason !== null ? $gameServerRegion->getKeystoneLeaderboardPeriod(
$mostRecentSeason->start
) : 0;

$result['minPeriod'] = $periodStart + $this->getWeeklyAffixGroups()->min();
$result['maxPeriod'] = $periodStart + $this->getWeeklyAffixGroups()->max();
}

return $result;
Expand Down Expand Up @@ -188,7 +198,7 @@ public static function fromArray(array $requestArray): HeatmapDataFilter
}

if (isset($requestArray['weekly_affix_groups'])) {
$heatmapDataFilter->setWeeklyAffixGroups($requestArray['weekly_affix_groups']);
$heatmapDataFilter->setWeeklyAffixGroups(collect($requestArray['weekly_affix_groups']));
}

return $heatmapDataFilter;
Expand Down
2 changes: 1 addition & 1 deletion app/Service/RaiderIO/RaiderIOApiService.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public function getHeatmapData(HeatmapDataFilter $heatmapDataFilter): HeatmapDat
),
];

foreach ($heatmapDataFilter->toArray() as $key => $value) {
foreach ($heatmapDataFilter->toArray($mostRecentSeason) as $key => $value) {
$parameters[] = sprintf('%s=%s', Str::camel($key), $value);
}

Expand Down
8 changes: 6 additions & 2 deletions app/Service/Season/SeasonService.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,12 @@ public function getSeasonAt(Carbon $date, ?Expansion $expansion = null, ?GameSer
$expansion ??= $this->expansionService->getCurrentExpansion($region);

/** @var Season $season */
$season = Season::whereRaw('DATE_ADD(DATE_ADD(`start`, INTERVAL ? day), INTERVAL ? hour) < ?',
[$region->reset_day_offset, $region->reset_hours_offset, $date]
$season = Season::whereRaw('DATE_ADD(DATE_ADD(`start`, INTERVAL ? day), INTERVAL ? hour) <= ?', [
$region->reset_day_offset,
$region->reset_hours_offset,
// Database stores everything in UTC, so we need to convert the date to UTC to compare it properly
$date->copy()->setTimezone('UTC')->toDateTimeString()
]
)
->where('expansion_id', $expansion->id)
->orderBy('start', 'desc')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

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

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('game_server_regions', function (Blueprint $table) {
$table->date('epoch_start')->nullable()->after('name');
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('game_server_regions', function (Blueprint $table) {
$table->dropColumn('epoch_start');
});
}
};
2 changes: 1 addition & 1 deletion database/seeders/ExpansionsSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public function run(): void
'shortname' => Expansion::EXPANSION_MIDNIGHT,
'color' => '#4B0082',
'released_at' => Carbon::make('2028-08-26 00:00:00'),
]), 'expansions.the_last_titan.name' => new Expansion([
]), 'expansions.tlt.name' => new Expansion([
'active' => 0,
'shortname' => Expansion::EXPANSION_TLT,
'color' => '#6D6E5C',
Expand Down
Loading

0 comments on commit af22e83

Please sign in to comment.