In-game Mods page: official mods are listed as official, not as deleted folders

They load from Mods/.reskate-official, outside the Mods folder's list, so
the page took the ones in effect for mods whose folder was gone: "Folder
deleted / APPLY TO UNLOAD", a change pending, and an Apply that reloaded
the level for nothing.

- Each official mod has a tile of its own after the installed mods:
  OFFICIAL while in effect, SET ASIDE while Board Break has another deck
  in the game, APPLY TO LOAD for one put in place since the game started.
  No Enable/Disable: the launcher keeps them.
- live_mods::preview leaves built-in and official content out of what
  applying would unload.
This commit is contained in:
AyeZeeBB committed 2026-10-16 20:01:30 -05:00
1 parent a2d6daffbc
commit a14a6f44b5
2 files changed
+41 -1

No files matched your search

+3
View File
@@ -8,6 +8,7 @@
#include "Engine/Core/Platform/path_text.h"
#include "Engine/Game/Build/20260929/live_mods.h"
#include "Engine/Game/Build/addresses.h"
#include "Engine/Vfs/builtin_packs.h"
#include "Engine/Vfs/mod_catalog.h"
#include "Engine/Vfs/mod_scoring.h"
#include "Extension/World/loading_screen.h"
@@ -508,6 +509,8 @@ ApplyEffect preview(const mods::ModList &list) {
// One of a Board Break deck pack's decks stands for the pack (mod_catalog.h).
if (const auto pack = mods::board_break_pack_of(name); named(now, pack.empty() ? name : pack)) continue;
const auto *entry = entry_of(name);
// Content ReSkate keeps itself (builtin_packs.h) is in no mod list, and stays.
if (!entry && (name.starts_with("reskate-builtin-") || mods::kept_by_reskate(list.root, name))) continue;
// A folder deleted since: what it held is unknown, so assume the worst.
if (!entry) {
reload = true;
+38 -1
View File
@@ -1,6 +1,7 @@
#include "modding_menu.h"
#include "skate_menu.h"
#include "skate_menu_internal.h"
#include "Engine/Vfs/builtin_packs.h"
#include "Engine/Vfs/initfs.h"
#include "Engine/Vfs/mod_catalog.h"
#include "Extension/Assets/live_mods.h"
@@ -151,6 +152,7 @@ std::string mod_contents(const mods::Mod& mod) {
// mod folder added since launch.
void draw_installed_mods(SkateMenu& menu) {
static auto* scans = new FolderRead<mods::ModList>; // process lifetime, as the threads that fill it
static auto* officials = new FolderRead<std::vector<mods::BuiltinPack>>;
static std::string action_error;
const auto scanned = scans->get([] { return mods::scan_mods(mods::engine_data_root()); }, 1500);
if (!scanned) {
@@ -160,6 +162,17 @@ void draw_installed_mods(SkateMenu& menu) {
const auto& list = *scanned;
const auto& launch = mods::catalog();
const auto applied = live_mods::applied_mods();
// Official mods (builtin_packs.h) live outside the Mods folder's list: ReSkate keeps them,
// always on. Without them here, the ones in effect read as mods whose folder was deleted.
const auto official_read = officials->get([root = list.root] { return mods::official_packs(root); }, 1500);
const auto official = official_read ? *official_read : std::vector<mods::BuiltinPack>{};
const auto kept = [&](const std::string& name) {
const auto pack = mods::board_break_pack_of(name);
const auto& base = pack.empty() ? name : pack;
return base.starts_with("reskate-builtin-") || std::ranges::any_of(official, [&](const mods::BuiltinPack& other) {
return _stricmp(other.name.c_str(), base.c_str()) == 0;
});
};
// A Board Break deck pack is in effect when one of the decks inside it is (mod_catalog.h).
const auto in_effect = [&](const std::string& name) {
return std::ranges::any_of(applied, [&](const std::string& other) {
@@ -182,10 +195,17 @@ void draw_installed_mods(SkateMenu& menu) {
if (effect) ++loaded;
if (entry.enabled != effect && !excluded(entry.mod.name) && !set_aside(entry)) ++pending;
}
const auto official_aside = [&](const std::string& name) {
return !in_effect(name) && std::ranges::any_of(aside, [&](const std::string& other) { return _stricmp(other.c_str(), name.c_str()) == 0; });
};
for (const auto& pack : official) {
if (in_effect(pack.name)) ++loaded;
else if (!official_aside(pack.name)) ++pending; // put in place since the game started: applying loads it
}
// Mods still in effect whose folder has been deleted: applying unloads them.
std::vector<std::string> removed;
for (const auto& name : applied)
if (mods::board_break_pack_of(name).empty() &&
if (mods::board_break_pack_of(name).empty() && !kept(name) &&
std::ranges::none_of(list.entries, [&](const mods::ModEntry& entry) { return _stricmp(entry.mod.name.c_str(), name.c_str()) == 0; }))
removed.push_back(name);
pending += removed.size();
@@ -300,6 +320,23 @@ void draw_installed_mods(SkateMenu& menu) {
entry.enabled ? "Disable" : "Enable"))
toggle = i;
}
for (const auto& pack : official) {
const auto* known = [&]() -> const mods::Mod* {
for (const auto& mod : launch.mods)
if (_stricmp(mod.name.c_str(), pack.name.c_str()) == 0) return &mod;
return nullptr;
}();
auto title = known && !known->title.empty() ? known->title : pack.name.substr(pack.name.find('-') + 1);
if (!known) std::replace(title.begin(), title.end(), '_', ' ');
auto line = known ? byline(*known) : "by " + pack.name.substr(0, pack.name.find('-'));
line += " - always on";
const bool effect = in_effect(pack.name), set_by_deck = official_aside(pack.name);
tile(index++, title, line, effect ? "OFFICIAL" : set_by_deck ? "SET ASIDE" : "APPLY TO LOAD",
effect ? skate_theme::good : set_by_deck ? skate_theme::tile_light : skate_theme::danger,
known ? mod_contents(*known) : "",
"An official mod: ReSkate keeps it for every player at its latest version, always on. "
"The launcher installs and updates it, so it cannot be disabled here.", nullptr);
}
for (const auto& name : removed)
tile(index++, name, "Folder deleted", "APPLY TO UNLOAD", skate_theme::danger, "",
"This mod's folder is gone, but it stays loaded until you apply.", nullptr);