Files
ReSkate/Engine/Vfs/mod_catalog.h
T
AyeZeeBB af83281b8d Mods: variants, one of several takes on the same game data; Board Break's decks leave the SDK
A mod's reskate-plugin.json can declare "variants" {folder, order, displaces}: each <folder>/<name>/
is a variant, the one chosen (else the first of its order) loads, and every other mod whose layout
lists a displaced bundle is set aside for the run. Choices are kept in
%LOCALAPPDATA%\ReSkate\variants.json. Plugins read and choose their own mod's variants with
reskate.package.variants and reskate.package.choose (permission "variants"), and fit every board
for a deck that is not the game's own with reskate.board.fit {plates, axle}.

The board-break plugin, which ships inside the Board Break deck pack, declares the pack's decks as
variants, picks the deck on its own page and with `boardbreak deck`, reads each deck.ini and fits
the boards. Gone from the SDK: breakable_deck, the deck catalog, the MODS page's deck card, the
boarddeck command and the Board_Break_<Name> folder naming.
2026-10-20 01:31:50 -05:00

102 lines
5.4 KiB
C++

#pragma once
#include "mod_list.h"
#include "mod_merge.h"
#include <array>
#include <cstddef>
#include <filesystem>
#include <string>
#include <string_view>
#include <vector>
namespace dingosdk::mods {
struct Catalog {
std::filesystem::path data_root; // what the engine mounts as /native_data
std::filesystem::path root; // <data_root>/Mods
bool present{}; // the Mods folder exists
// Enabled mods, highest priority first. A later mod never shadows an
// earlier one.
std::vector<Mod> mods;
// Folders present on disk that mods.json disables, in folder-name order.
std::vector<std::string> disabled;
// Disabled mods that ship a layout. Their archives are placed in the
// launch's merge too, so one can be enabled while the game runs.
std::vector<Mod> inactive;
// True once Mods/.reskate holds the merged patch every mod was combined
// into. Empty catalogues never generate one.
bool merged{};
// Non-empty when mods.json was rejected; nothing is loaded in that case.
std::string issue;
// Advisory messages worth logging, such as skipped or missing folders.
std::vector<std::string> notes;
// Enabled mods left out entirely because they could not be merged cleanly
// (their problems say why). A partly merged mod would list maps that never
// finish loading, so a mod either merges whole or not at all.
std::vector<Mod> excluded;
// Mods left out, one line each, for the warning log.
std::vector<std::string> warnings;
// Mods left out because they were built for another game version (Mod::outdated), enabled or
// not: they need updating. Also in `excluded` when they were enabled.
std::vector<Mod> outdated;
// Mods with variants (mod_list.h Variants): the variant of each that is the one in the game, by
// the declaring mod's folder name (none listed: none of its variants loads); and the enabled
// mods set aside for them, their other variants and other mods that bring what a variant
// displaces. Those are not loaded and nothing about them waits to be applied.
std::map<std::string, std::string, std::less<>> variants_in_play;
std::vector<std::string> set_aside;
};
// Variants (mod_list.h). For the run each variant is a mod of its own, listed after the mod that
// holds it as <mod's folder>#<variant>, with that mod's build stamp; mods.json and the mod list only
// know the mod. One variant of each such mod loads, the one the player chose (else the first of its
// "order", else the first there is), first in the load order whatever mods.json says: what would
// fight it is set aside, whole, and its archives are placed like a disabled mod's, so another variant
// can be chosen while the game runs. The variant a mod's entry stands for, and the mod that holds
// it, or nothing (no folder has a # in its name):
std::string variant_of(std::string_view mod);
std::string variant_owner(std::string_view mod);
// The variants a mod folder holds, its "order" first, then the others by name; and the folder with a
// variant's own files (empty when it has no such variant).
std::vector<std::string> installed_variants(const Mod& mod);
std::filesystem::path variant_folder(const Mod& mod, std::string_view variant);
// The variant chosen for the mod with this folder name, empty when none was: read whenever the mods
// are put together, at launch and at every live apply. Saving a choice makes it this run's at once.
std::string chosen_variant(std::string_view mod) noexcept;
bool save_variant(std::string_view mod, std::string_view variant) noexcept;
// What the mods in effect give the game: the launch's catalogue, or the one the last live apply
// read (live_mods tells with variants_applied). Empty: none of that mod's variants is in the game.
std::string variant_in_play(std::string_view mod) noexcept;
std::vector<std::string> set_aside_for_variants();
void variants_applied(const Catalog& catalog) noexcept;
// The engine's own data root: the -dataPath argument when present, otherwise
// the executable's directory. Mirrors the game so mod paths resolve the same.
std::filesystem::path engine_data_root() noexcept;
// Reads <data_root>/Mods plus its mods.json and, when any mod adds a
// superbundle, writes the merged manifest. Never throws; problems land in
// `issue` and `notes`. `observe` hears how a merge that is rebuilding goes.
// `run_merge` false only reads the folder and mods.json, for a merge the caller runs itself (a live one).
Catalog load_catalog(const std::filesystem::path& data_root, const MergeObserver& observe = {},
bool run_merge = true) noexcept;
// Process-wide catalogue, loaded once from engine_data_root() on first use.
// Only that first call's `observe` is ever used.
const Catalog& catalog(const MergeObserver& observe = {}) noexcept;
// reskate-levels.json for every enabled mod, highest priority first.
std::vector<std::filesystem::path> level_manifest_paths(const Catalog& catalog);
// The enabled mod whose reskate-levels.json registers a level named inside
// `destination` (a level path or a loader's "root + level @ start" line),
// ignoring ASCII case; null when no mod does.
const Mod* mod_registering(const Catalog& catalog, std::string_view destination) noexcept;
// One line describing a mod for the log: what built it, what it ships, and any
// problem the merge had with it.
std::string describe(const Mod& mod);
} // namespace dingosdk::mods