From a1fbcc5b816a9f6a42e95bdd003056227ff39bcd Mon Sep 17 00:00:00 2001 From: nanaya Date: Mon, 27 Nov 2023 17:38:56 +0900 Subject: [PATCH] Accept any attributes for score statistics Removes need to synchronise with the client. Only up to 32 attributes are accepted and they must are all ints. --- app/Models/Solo/Score.php | 8 +-- app/Models/Solo/ScoreDataStatistics.php | 96 ++++++------------------- tests/Models/Solo/ScoreTest.php | 2 +- 3 files changed, 25 insertions(+), 81 deletions(-) diff --git a/app/Models/Solo/Score.php b/app/Models/Solo/Score.php index 5a74a042819..65507e4ad8c 100644 --- a/app/Models/Solo/Score.php +++ b/app/Models/Solo/Score.php @@ -215,7 +215,7 @@ public function makeLegacyEntry(): LegacyScore\Model 'enabled_mods' => app('mods')->idsToBitset(array_column($data->mods, 'acronym')), 'maxcombo' => $data->maxCombo, 'pass' => $data->passed, - 'perfect' => $data->passed && $statistics->miss + $statistics->largeTickMiss === 0, + 'perfect' => $data->passed && $statistics->miss + $statistics->large_tick_miss === 0, 'rank' => $data->rank, 'score' => $data->totalScore, 'scorechecksum' => "\0", @@ -234,9 +234,9 @@ public function makeLegacyEntry(): LegacyScore\Model break; case 'fruits': $score->count300 = $statistics->great; - $score->count100 = $statistics->largeTickHit; - $score->countkatu = $statistics->smallTickMiss; - $score->count50 = $statistics->smallTickHit; + $score->count100 = $statistics->large_tick_hit; + $score->countkatu = $statistics->small_tick_miss; + $score->count50 = $statistics->small_tick_hit; break; case 'mania': $score->countgeki = $statistics->perfect; diff --git a/app/Models/Solo/ScoreDataStatistics.php b/app/Models/Solo/ScoreDataStatistics.php index 77680551cf4..7005924537c 100644 --- a/app/Models/Solo/ScoreDataStatistics.php +++ b/app/Models/Solo/ScoreDataStatistics.php @@ -7,103 +7,47 @@ namespace App\Models\Solo; -use JsonSerializable; - -class ScoreDataStatistics implements JsonSerializable +class ScoreDataStatistics implements \JsonSerializable { - public int $good; - public int $great; - public int $ignoreHit; - public int $ignoreMiss; - public int $largeBonus; - public int $largeTickHit; - public int $largeTickMiss; - public int $legacyComboIncrease; - public int $meh; - public int $miss; - public int $ok; - public int $perfect; - public int $smallBonus; - public int $smallTickHit; - public int $smallTickMiss; + public array $attributes = []; public function __construct($inputData) { - $inputData = get_arr($inputData) ?? []; - - foreach (static::fields() as $field => $map) { - $this->$field = get_int($inputData[$map['json']] ?? $inputData[$map['json_old']] ?? 0) ?? 0; - } - } - - private static function fields(): array - { - static $map; + $n = 0; + foreach (get_arr($inputData) ?? [] as $key => $value) { + if ($n >= 32) { + break; + } else { + $n++; + } - if (!isset($map)) { - $map = []; - $fields = [ - 'good', - 'great', - 'ignoreHit', - 'ignoreMiss', - 'largeBonus', - 'largeTickHit', - 'largeTickMiss', - 'legacyComboIncrease', - 'meh', - 'miss', - 'ok', - 'perfect', - 'smallBonus', - 'smallTickHit', - 'smallTickMiss', - ]; + $intValue = get_int($value); - foreach ($fields as $field) { - $map[$field] = [ - 'json' => snake_case($field), - 'json_old' => studly_case($field), - ]; + if ($intValue !== null && $intValue !== 0) { + $this->attributes[snake_case($key)] = $intValue; } } + } - return $map; + public function __get($key) + { + return $this->attributes[$key] ?? 0; } public function isEmpty(): bool { - foreach (static::fields() as $field => $_map) { - if ($this->$field !== 0) { - return false; - } - } - - return true; + return empty($this->attributes); } public function jsonSerialize(): array { - $ret = []; - - $fields = static::fields(); - foreach ($fields as $field => $map) { - $value = $this->$field; - - if ($value !== 0) { - $ret[$map['json']] = $value; - } - } - // This shouldn't be needed but it's to guarantee the return has // at least one thing so php doesn't json encode it as array. // Using stdClass is an alternative but it's a lot of hacks // for what shouldn't be possible in the first place (short of // completely bogus score data). - if (empty($ret)) { - $ret[$fields['miss']['json']] = $this->miss; - } - - return $ret; + return $this->isEmpty() + ? ['miss' => 0] + : $this->attributes; } } diff --git a/tests/Models/Solo/ScoreTest.php b/tests/Models/Solo/ScoreTest.php index 4f3d6c2d677..aff52ab6b38 100644 --- a/tests/Models/Solo/ScoreTest.php +++ b/tests/Models/Solo/ScoreTest.php @@ -31,7 +31,7 @@ public function testStatisticsStoredInCorrectCasing() $score = $score->fresh(); $this->assertSame(1, json_decode($score->getAttributes()['data'], true)['statistics']['small_tick_hit']); - $this->assertSame(1, $score->data->statistics->smallTickHit); + $this->assertSame(1, $score->data->statistics->small_tick_hit); } public function testLegacyPassScoreRetainsRank()