Music: skip the Mods shelf when no mod declares a playlist

Build the Mods shelf only when an enabled mod declares at least one playlist with songs, read from each mod's reskate-music.json. The shelf must be inserted before the native widgets bind, so it cannot wait for Featured rows to hydrate, and it should not publish an empty shelf when there is nothing to show. With no declared mod playlist the native three shelves are left as they are and a music_mods_shelf_skipped event is logged.
This commit is contained in:
ReSkate local committed 2026-10-05 06:51:05 +01:00
1 parent d4d3549799
commit 88e033b33f
2 files changed
+40 -16

No files matched your search

+34 -16
View File
@@ -1,4 +1,5 @@
#include "Extension/Music/local_music_shelf.h"
#include "Extension/Music/local_music_assets.h"
#include "Engine/Core/Log/logging.h"
#include "Engine/Game/Abi/native_data.h"
#include "Engine/Game/Build/addresses.h"
@@ -198,22 +199,39 @@ std::uint64_t music_model_construct_hook(std::uintptr_t manager, std::uint8_t mo
try {
if (!handle || !type || md::read<std::uint32_t>(md::read<std::uintptr_t>(type)) != 0x48455d84U)
return handle;
game::ModelWriteLock lock(manager);
md::Context context{local_runtime().base, manager};
const auto items = context.field(md::Value{handle, type}, 0x67223da7U);
unsigned count{}, stride{};
const auto bytes = context.array(items, 1024, count, stride);
if (count != 3 || stride != 16) return handle;
constexpr const char* records[] = {
"MusicPlaylistManager_Base_ContentResources/Featured_AnchoredContent_VM",
"MusicPlaylistManager_Base_ContentResources/Liked_AnchoredContent_VM",
"MusicPlaylistManager_Base_ContentResources/NewlyDiscovered_AnchoredContent_VM"};
for (unsigned item = 0; item < 3; ++item) {
std::uintptr_t item_record{};
std::memcpy(&item_record, bytes.data() + item * stride, sizeof(item_record));
// Named record header +0x28 is also used by the native UI dumper.
if (!item_record || md::string(md::read<std::uintptr_t>(item_record + 0x28), 256) != records[item])
return handle;
bool is_music_shelf = false;
{
game::ModelWriteLock lock(manager);
md::Context context{local_runtime().base, manager};
const auto items = context.field(md::Value{handle, type}, 0x67223da7U);
unsigned count{}, stride{};
const auto bytes = context.array(items, 1024, count, stride);
if (count != 3 || stride != 16) return handle;
constexpr const char* records[] = {
"MusicPlaylistManager_Base_ContentResources/Featured_AnchoredContent_VM",
"MusicPlaylistManager_Base_ContentResources/Liked_AnchoredContent_VM",
"MusicPlaylistManager_Base_ContentResources/NewlyDiscovered_AnchoredContent_VM"};
is_music_shelf = true;
for (unsigned item = 0; item < 3; ++item) {
std::uintptr_t item_record{};
std::memcpy(&item_record, bytes.data() + item * stride, sizeof(item_record));
// Named record header +0x28 is also used by the native UI dumper.
if (!item_record || md::string(md::read<std::uintptr_t>(item_record + 0x28), 256) != records[item]) {
is_music_shelf = false;
break;
}
}
}
if (!is_music_shelf) return handle;
// Only add our shelf when an enabled mod actually declares a playlist with songs. Otherwise
// leave the native three shelves untouched instead of publishing an empty Mods shelf.
bool has_mod_playlists = false;
for (const auto& playlist : mod_music_playlists())
if (!playlist.songs.empty()) { has_mod_playlists = true; break; }
if (!has_mod_playlists) {
mods_shelf = {};
dingosdk::logging::event(dingosdk::logging::Channel::music, "{\"event\":\"music_mods_shelf_skipped\"}");
return handle;
}
const auto report = music_shelf_append_one(local_runtime().base, manager, handle);
dingosdk::logging::event(dingosdk::logging::Channel::music,
+6
View File
@@ -198,6 +198,12 @@ The Mods implementation performs these steps under the native model write lock:
**Mods, Featured, Liked, Newly Discovered**.
7. Populate the owned tile list from mod playlist rows in Featured.
The shelf is only built when an enabled mod declares a playlist with songs, read from
each mod's `reskate-music.json`. The insertion has to happen before the widgets bind,
so its rows cannot be awaited; the enabled mods' declarations are the signal that does
not depend on row hydration. With none, the native three shelves are left untouched
rather than publishing an empty Mods shelf.
The native template supplies layout and presentation defaults. Creating both
models separately supplies independent state. Changing the label on the original
Liked tile list would rename the original shelf too.