Plugins: the merge moves plugins' old files out before it deletes its folder

Plugins' files from before Mods/.reskate_plugin_data were still in Mods/.reskate/data, and the
launcher's merge deletes Mods/.reskate before the game ever looks for a plugin's folder: a saved
Deathrace route went at the next launch that rebuilt the merge. mods::keep_plugin_data moves each
plugin's old folder to Mods/.reskate_plugin_data first (whole, or the files a folder there lacks,
its own kept), at the start of every merge; the game still runs it once for a merge that kept its
patch. Checked in the build stamp test.
This commit is contained in:
AyeZeeBB committed 2026-10-20 14:09:09 -05:00
1 parent 39f322097a
commit e6de1dfd64
5 files changed
+67 -18

No files matched your search

+12 -17
View File
@@ -19,6 +19,7 @@
#include "Extension/Skater/board_pose.h"
#include "Engine/Vfs/mod_catalog.h"
#include "Engine/Core/Log/logging.h"
#include <mutex>
#include "Extension/Assets/live_mods.h"
#include "Extension/Skater/game_speed.h"
#include "Extension/Skater/grind_exit.h"
@@ -482,29 +483,23 @@ public:
}
// ---- files: <game>/Mods/.reskate_plugin_data/<plugin>/ (mod_list.h plugin_data_folder) -------------
static constexpr std::size_t most_file_bytes = 1u << 20, most_files = 1000;
// Before 2026-10-20 the folder was Mods/.reskate/data/<plugin>/, which a full merge deletes: what is
// still there moves across once, the first time the plugin's folder is looked for.
static void move_old_data(const std::filesystem::path& root, std::string_view plugin) {
std::error_code error;
if (std::filesystem::exists(root, error)) return;
const auto old = game_folder() / mods::mods_folder / mods::generated_folder / "data" / std::filesystem::path(std::string(plugin));
if (!std::filesystem::is_directory(old, error)) return;
std::filesystem::create_directories(root.parent_path(), error);
std::filesystem::rename(old, root, error);
if (error) {
// Another volume or a file in use: copy what can be copied, and leave the old folder.
error.clear();
std::filesystem::copy(old, root, std::filesystem::copy_options::recursive | std::filesystem::copy_options::skip_existing, error);
}
logging::write(logging::Level::info, logging::Channel::plugins,
"Plugins: " + std::string(plugin) + "'s data folder moved to Mods/" + mods::plugin_data_folder + (error ? " (partly: " + error.message() + ")" : ""));
// Before 2026-10-20 the folder was Mods/.reskate/data/<plugin>/, which a full merge deletes. The
// merge moves what is still there first (mods::keep_plugin_data); a merge that kept its patch did
// not run it, so the first look for a plugin's folder does.
static void move_old_data() {
static std::once_flag once;
std::call_once(once, [] {
for (const auto& plugin : mods::keep_plugin_data(game_folder() / mods::mods_folder))
logging::write(logging::Level::info, logging::Channel::plugins,
"Plugins: " + plugin + "'s data folder moved to Mods/" + mods::plugin_data_folder);
});
}
// The plugin's folder and the path in it, or why not. A path that resolves outside the folder
// (through a junction someone made in it) is refused.
static std::string data_file(std::string_view plugin, std::string_view path, std::filesystem::path& root, std::filesystem::path& file) {
try {
root = game_folder() / mods::mods_folder / mods::plugin_data_folder / std::filesystem::path(std::string(plugin));
move_old_data(root, plugin);
move_old_data();
file = path.empty() ? root : root / std::filesystem::path(std::string(path));
std::error_code error;
const auto real_root = std::filesystem::weakly_canonical(root, error);