";
}
public function parseBoxHelperSuffix()
diff --git a/app/Libraries/CleanHTML.php b/app/Libraries/CleanHTML.php
index c7a5b2cd4e9..0a7ead84c8c 100644
--- a/app/Libraries/CleanHTML.php
+++ b/app/Libraries/CleanHTML.php
@@ -47,14 +47,6 @@ public function __construct()
]
);
- $def->addElement(
- 'button',
- 'Formctrl',
- 'Optional: #PCDATA | Heading | List | Block | Inline',
- 'Common',
- ['type' => 'Enum#button'],
- );
-
$def->addAttribute('audio', 'preload', 'Text');
$def->addAttribute('img', 'loading', 'Text');
diff --git a/app/Libraries/Search/ScoreSearch.php b/app/Libraries/Search/ScoreSearch.php
index 1d1c138395b..79b3da52651 100644
--- a/app/Libraries/Search/ScoreSearch.php
+++ b/app/Libraries/Search/ScoreSearch.php
@@ -119,11 +119,13 @@ public function queueForIndex(?array $schemas, array $ids): void
$schemas ??= $this->getActiveSchemas();
+ $values = array_map(
+ static fn (int $id): string => json_encode(['ScoreId' => $id]),
+ $ids,
+ );
+
foreach ($schemas as $schema) {
- LaravelRedis::lpush("osu-queue:score-index-{$schema}", ...array_map(
- fn (int $id): string => json_encode(['ScoreId' => $id]),
- $ids,
- ));
+ LaravelRedis::lpush("osu-queue:score-index-{$schema}", ...$values);
}
}
diff --git a/app/Models/Solo/Score.php b/app/Models/Solo/Score.php
index 24d865f027d..4bd02dc6857 100644
--- a/app/Models/Solo/Score.php
+++ b/app/Models/Solo/Score.php
@@ -24,12 +24,14 @@
/**
* @property int $beatmap_id
* @property \Carbon\Carbon|null $created_at
- * @property \stdClass $data
- * @property \Carbon\Carbon|null $deleted_at
+ * @property string|null $created_at_json
+ * @property ScoreData $data
+ * @property bool $has_replay
* @property int $id
* @property bool $preserve
+ * @property bool $ranked
* @property int $ruleset_id
- * @property \Carbon\Carbon|null $updated_at
+ * @property int $unix_updated_at
* @property User $user
* @property int $user_id
*/
@@ -39,6 +41,8 @@ class Score extends Model implements Traits\ReportableInterface
const PROCESSING_QUEUE = 'osu-queue:score-statistics';
+ public $timestamps = false;
+
protected $casts = [
'data' => ScoreData::class,
'has_replay' => 'boolean',
@@ -160,18 +164,17 @@ public function getAttribute($key)
'beatmap_id',
'id',
'ruleset_id',
+ 'unix_updated_at',
'user_id' => $this->getRawAttribute($key),
'data' => $this->getClassCastableAttributeValue($key, $this->getRawAttribute($key)),
'has_replay',
- 'preserve' => (bool) $this->getRawAttribute($key),
-
- 'created_at',
- 'updated_at' => $this->getTimeFast($key),
+ 'preserve',
+ 'ranked' => (bool) $this->getRawAttribute($key),
- 'created_at_json',
- 'updated_at_json' => $this->getJsonTimeFast($key),
+ 'created_at' => $this->getTimeFast($key),
+ 'created_at_json' => $this->getJsonTimeFast($key),
'pp' => $this->performance?->pp,
diff --git a/database/migrations/2023_12_18_085034_update_scores_table.php b/database/migrations/2023_12_18_085034_update_scores_table.php
new file mode 100644
index 00000000000..d67902fb822
--- /dev/null
+++ b/database/migrations/2023_12_18_085034_update_scores_table.php
@@ -0,0 +1,69 @@
+. Licensed under the GNU Affero General Public License v3.0.
+// See the LICENCE file in the repository root for full licence text.
+
+declare(strict_types=1);
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+ private static function resetView(): void
+ {
+ DB::statement('DROP VIEW scores');
+ DB::statement('CREATE VIEW scores AS SELECT * FROM solo_scores');
+ }
+
+ public function up(): void
+ {
+ Schema::table('solo_scores', function (Blueprint $table) {
+ $table->dropColumn('updated_at');
+
+ $table->dropIndex('solo_scores_beatmap_id_index');
+ $table->dropIndex('solo_scores_preserve_index');
+ $table->dropIndex('user_ruleset_id_index');
+
+ $table->boolean('ranked')->default(true)->after('preserve');
+ $table->unsignedInteger('unix_updated_at')->default(DB::raw('(unix_timestamp())'));
+ $table->timestamp('created_at')->useCurrent()->change();
+
+ $table->index(['user_id', 'ruleset_id'], 'user_ruleset_index');
+ $table->index(['beatmap_id', 'user_id'], 'beatmap_user_index');
+ });
+
+ DB::statement('ALTER TABLE solo_scores MODIFY id bigint unsigned NOT NULL');
+ DB::statement('ALTER TABLE solo_scores DROP PRIMARY KEY');
+ DB::statement('ALTER TABLE solo_scores ADD PRIMARY KEY (id, preserve, unix_updated_at)');
+ DB::statement('ALTER TABLE solo_scores MODIFY id bigint unsigned NOT NULL AUTO_INCREMENT');
+
+ static::resetView();
+ }
+
+ public function down(): void
+ {
+ Schema::table('solo_scores', function (Blueprint $table) {
+ $table->dropColumn('unix_updated_at');
+ $table->dropColumn('ranked');
+
+ $table->dropIndex('user_ruleset_index');
+ $table->dropIndex('beatmap_user_index');
+
+ $table->datetime('created_at')->change();
+ $table->timestamp('updated_at')->nullable(true);
+
+ $table->index('preserve', 'solo_scores_preserve_index');
+ $table->index('beatmap_id', 'solo_scores_beatmap_id_index');
+ $table->index(['user_id', 'ruleset_id', DB::raw('id DESC')], 'user_ruleset_id_index');
+ });
+
+ DB::statement('ALTER TABLE solo_scores MODIFY id bigint unsigned NOT NULL');
+ DB::statement('ALTER TABLE solo_scores DROP PRIMARY KEY');
+ DB::statement('ALTER TABLE solo_scores ADD PRIMARY KEY (id)');
+ DB::statement('ALTER TABLE solo_scores MODIFY id bigint unsigned NOT NULL AUTO_INCREMENT');
+
+ static::resetView();
+ }
+};
diff --git a/database/mods.json b/database/mods.json
index bf82cf1b2f5..0fb0df78b26 100644
--- a/database/mods.json
+++ b/database/mods.json
@@ -37,7 +37,6 @@
"SD",
"PF",
"AC",
- "RX",
"AP"
],
"RequiresConfiguration": false,
@@ -142,7 +141,6 @@
"NF",
"PF",
"TP",
- "RX",
"AP"
],
"RequiresConfiguration": false,
@@ -168,7 +166,6 @@
"NF",
"SD",
"AC",
- "RX",
"AP"
],
"RequiresConfiguration": false,
@@ -357,7 +354,6 @@
"EZ",
"NF",
"PF",
- "RX",
"AP"
],
"RequiresConfiguration": false,
@@ -634,10 +630,6 @@
"Type": "Automation",
"Settings": [],
"IncompatibleMods": [
- "NF",
- "SD",
- "PF",
- "AC",
"AL",
"SG",
"AT",
@@ -1222,8 +1214,7 @@
"IncompatibleMods": [
"SD",
"PF",
- "AC",
- "RX"
+ "AC"
],
"RequiresConfiguration": false,
"UserPlayable": true,
@@ -1324,8 +1315,7 @@
],
"IncompatibleMods": [
"NF",
- "PF",
- "RX"
+ "PF"
],
"RequiresConfiguration": false,
"UserPlayable": true,
@@ -1349,8 +1339,7 @@
"IncompatibleMods": [
"NF",
"SD",
- "AC",
- "RX"
+ "AC"
],
"RequiresConfiguration": false,
"UserPlayable": true,
@@ -1486,8 +1475,7 @@
],
"IncompatibleMods": [
"NF",
- "PF",
- "RX"
+ "PF"
],
"RequiresConfiguration": false,
"UserPlayable": true,
@@ -1647,10 +1635,6 @@
"Type": "Automation",
"Settings": [],
"IncompatibleMods": [
- "NF",
- "SD",
- "PF",
- "AC",
"SG",
"AT",
"CN"
@@ -1864,8 +1848,7 @@
"IncompatibleMods": [
"SD",
"PF",
- "AC",
- "RX"
+ "AC"
],
"RequiresConfiguration": false,
"UserPlayable": true,
@@ -1964,8 +1947,7 @@
],
"IncompatibleMods": [
"NF",
- "PF",
- "RX"
+ "PF"
],
"RequiresConfiguration": false,
"UserPlayable": true,
@@ -1989,8 +1971,7 @@
"IncompatibleMods": [
"NF",
"SD",
- "AC",
- "RX"
+ "AC"
],
"RequiresConfiguration": false,
"UserPlayable": true,
@@ -2125,8 +2106,7 @@
"IncompatibleMods": [
"EZ",
"NF",
- "PF",
- "RX"
+ "PF"
],
"RequiresConfiguration": false,
"UserPlayable": true,
@@ -2253,10 +2233,6 @@
"Type": "Automation",
"Settings": [],
"IncompatibleMods": [
- "NF",
- "SD",
- "PF",
- "AC",
"AT",
"CN"
],
diff --git a/resources/css/bem/profile-info.less b/resources/css/bem/profile-info.less
index ce11d01d982..00fe84197d5 100644
--- a/resources/css/bem/profile-info.less
+++ b/resources/css/bem/profile-info.less
@@ -130,6 +130,8 @@
&--supporter {
.center-content();
+ .link-plain();
+ .link-white();
border-radius: 10000px;
background-color: @osu-colour-h1;
padding: 0 10px;
diff --git a/resources/js/beatmapsets-show/header.tsx b/resources/js/beatmapsets-show/header.tsx
index b3124acbda5..8dbc6034909 100644
--- a/resources/js/beatmapsets-show/header.tsx
+++ b/resources/js/beatmapsets-show/header.tsx
@@ -352,13 +352,23 @@ export default class Header extends React.Component
{