From 5508cf3633cea5971955a8868ebaa9089bb44f94 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 4 Oct 2026 21:38:04 -0400 Subject: [PATCH] Add migrations for presets and drag-and-drops (#7312) --- ...ncements - Curated First Time Vanilla.json | 3 +- .../Enhancements - Curated Randomizer.json | 3 +- ...ancements - Curated Returning Vanilla.json | 3 +- .../Enhancements - Reset to Default.json | 3 +- .../Rando Seed Settings - Advanced.json | 3 +- .../Rando Seed Settings - Beginner.json | 3 +- ...ndo Seed Settings - Demise Difficulty.json | 3 +- .../Rando Seed Settings - Hell Mode.json | 5 +- ...ando Seed Settings - Reset to Default.json | 3 +- .../Rando Seed Settings - Standard.json | 3 +- soh/soh/Enhancements/Presets/Presets.cpp | 19 +++++- soh/soh/OTRGlobals.cpp | 13 ++-- soh/soh/config/ConfigUpdaters.cpp | 60 +++++++++++++++++-- soh/soh/config/ConfigUpdaters.h | 8 +++ 14 files changed, 106 insertions(+), 26 deletions(-) diff --git a/soh/assets/custom/presets/Enhancements - Curated First Time Vanilla.json b/soh/assets/custom/presets/Enhancements - Curated First Time Vanilla.json index c1bad53c3f..639a6067f4 100644 --- a/soh/assets/custom/presets/Enhancements - Curated First Time Vanilla.json +++ b/soh/assets/custom/presets/Enhancements - Curated First Time Vanilla.json @@ -56,5 +56,6 @@ } }, "presetName": "Enhancements - Curated First Time Vanilla", - "isBuiltIn": true + "isBuiltIn": true, + "ConfigVersion": 7 } diff --git a/soh/assets/custom/presets/Enhancements - Curated Randomizer.json b/soh/assets/custom/presets/Enhancements - Curated Randomizer.json index c3532ea4b7..85b3971cbf 100644 --- a/soh/assets/custom/presets/Enhancements - Curated Randomizer.json +++ b/soh/assets/custom/presets/Enhancements - Curated Randomizer.json @@ -137,5 +137,6 @@ } }, "presetName": "Enhancements - Curated Randomizer", - "isBuiltIn": true + "isBuiltIn": true, + "ConfigVersion": 7 } diff --git a/soh/assets/custom/presets/Enhancements - Curated Returning Vanilla.json b/soh/assets/custom/presets/Enhancements - Curated Returning Vanilla.json index ee2fa0b69e..b7ffdadd0f 100644 --- a/soh/assets/custom/presets/Enhancements - Curated Returning Vanilla.json +++ b/soh/assets/custom/presets/Enhancements - Curated Returning Vanilla.json @@ -59,5 +59,6 @@ } }, "presetName": "Enhancements - Curated Returning Vanilla", - "isBuiltIn": true + "isBuiltIn": true, + "ConfigVersion": 7 } diff --git a/soh/assets/custom/presets/Enhancements - Reset to Default.json b/soh/assets/custom/presets/Enhancements - Reset to Default.json index 8d9e279eb1..a46ac3031f 100644 --- a/soh/assets/custom/presets/Enhancements - Reset to Default.json +++ b/soh/assets/custom/presets/Enhancements - Reset to Default.json @@ -7,5 +7,6 @@ } }, "presetName": "Enhancements - Reset to Default", - "isBuiltIn": true + "isBuiltIn": true, + "ConfigVersion": 7 } diff --git a/soh/assets/custom/presets/Rando Seed Settings - Advanced.json b/soh/assets/custom/presets/Rando Seed Settings - Advanced.json index 2ce1824849..61c96e0158 100644 --- a/soh/assets/custom/presets/Rando Seed Settings - Advanced.json +++ b/soh/assets/custom/presets/Rando Seed Settings - Advanced.json @@ -65,5 +65,6 @@ } }, "presetName": "Rando Seed Settings - Advanced", - "isBuiltIn": true + "isBuiltIn": true, + "ConfigVersion": 7 } diff --git a/soh/assets/custom/presets/Rando Seed Settings - Beginner.json b/soh/assets/custom/presets/Rando Seed Settings - Beginner.json index 560b1e8a86..728533a377 100644 --- a/soh/assets/custom/presets/Rando Seed Settings - Beginner.json +++ b/soh/assets/custom/presets/Rando Seed Settings - Beginner.json @@ -48,5 +48,6 @@ } }, "presetName": "Rando Seed Settings - Beginner", - "isBuiltIn": true + "isBuiltIn": true, + "ConfigVersion": 7 } diff --git a/soh/assets/custom/presets/Rando Seed Settings - Demise Difficulty.json b/soh/assets/custom/presets/Rando Seed Settings - Demise Difficulty.json index dea878dd21..5742150628 100644 --- a/soh/assets/custom/presets/Rando Seed Settings - Demise Difficulty.json +++ b/soh/assets/custom/presets/Rando Seed Settings - Demise Difficulty.json @@ -138,5 +138,6 @@ } }, "presetName": "Rando Seed Settings - Demise Difficulty", - "isBuiltIn": true + "isBuiltIn": true, + "ConfigVersion": 7 } diff --git a/soh/assets/custom/presets/Rando Seed Settings - Hell Mode.json b/soh/assets/custom/presets/Rando Seed Settings - Hell Mode.json index 6a0f381992..22177bfddb 100644 --- a/soh/assets/custom/presets/Rando Seed Settings - Hell Mode.json +++ b/soh/assets/custom/presets/Rando Seed Settings - Hell Mode.json @@ -33,7 +33,7 @@ "MixThievesHideout": 1, "MixedEntrances": 1, "ProgressiveGoronSword": 1, - "RainbowBridge": 9, + "RainbowBridge": 8, "ScrubsPrices": 2, "ShopShieldsTunicsGate": 1, "Shopsanity": 1, @@ -114,5 +114,6 @@ } }, "presetName": "Rando Seed Settings - Hell Mode", - "isBuiltIn": true + "isBuiltIn": true, + "ConfigVersion": 7 } diff --git a/soh/assets/custom/presets/Rando Seed Settings - Reset to Default.json b/soh/assets/custom/presets/Rando Seed Settings - Reset to Default.json index f328fb4f98..7229498f76 100644 --- a/soh/assets/custom/presets/Rando Seed Settings - Reset to Default.json +++ b/soh/assets/custom/presets/Rando Seed Settings - Reset to Default.json @@ -5,5 +5,6 @@ } }, "presetName": "Rando Seed Settings - Reset to Default", - "isBuiltIn": true + "isBuiltIn": true, + "ConfigVersion": 7 } diff --git a/soh/assets/custom/presets/Rando Seed Settings - Standard.json b/soh/assets/custom/presets/Rando Seed Settings - Standard.json index d97aaeac98..2f0fca1b05 100644 --- a/soh/assets/custom/presets/Rando Seed Settings - Standard.json +++ b/soh/assets/custom/presets/Rando Seed Settings - Standard.json @@ -59,5 +59,6 @@ } }, "presetName": "Rando Seed Settings - Standard", - "isBuiltIn": true + "isBuiltIn": true, + "ConfigVersion": 7 } diff --git a/soh/soh/Enhancements/Presets/Presets.cpp b/soh/soh/Enhancements/Presets/Presets.cpp index 998fc6b74a..e40f6ca363 100644 --- a/soh/soh/Enhancements/Presets/Presets.cpp +++ b/soh/soh/Enhancements/Presets/Presets.cpp @@ -16,6 +16,7 @@ #include "soh/Enhancements/randomizer/randomizer_item_tracker.h" #include "soh/Enhancements/randomizer/settings.h" #include "soh/ShipInit.hpp" +#include "soh/config/ConfigUpdaters.h" namespace fs = std::filesystem; @@ -107,6 +108,7 @@ std::string FormatPresetPath(std::string name) { void applyPreset(std::string presetName, std::vector includeSections) { auto& info = presets[presetName]; + bool randoApplied = false; for (int i = PRESET_SECTION_SETTINGS; i < PRESET_SECTION_MAX; i++) { if (info.apply[i] && info.presetValues["blocks"].contains(blockInfo[i].names[1])) { if (!includeSections.empty() && !SohUtils::Contains(i, includeSections)) { @@ -150,12 +152,22 @@ void applyPreset(std::string presetName, std::vector includeSecti } } if (i == PRESET_SECTION_RANDOMIZER) { - Rando::Settings::GetInstance()->UpdateAllOptions(); - SohGui::UpdateMenuTricks(); - SohGui::UpdateMenuLocations(); + randoApplied = true; } } } + if (randoApplied) { + // Unversioned presets predate v4. Only migrate when rando came from the preset + uint32_t presetVersion = SOH::GetConfigVersion(info.presetValues, 3); + if (presetVersion < SOH::GetLatestConfigVersion()) { + SOH::RunVersionUpdatesFrom(presetVersion); + // SetBlock already saved the unmigrated values + Ship::Context::GetRawInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + } + Rando::Settings::GetInstance()->UpdateAllOptions(); + SohGui::UpdateMenuTricks(); + SohGui::UpdateMenuLocations(); + } ShipInit::InitAll(); OTRGlobals::Instance->ScaleImGui(); } @@ -284,6 +296,7 @@ void SavePreset(std::string& presetName) { } presets[presetName].presetValues["presetName"] = presetName; presets[presetName].presetValues["fileType"] = FILE_TYPE_PRESET; + presets[presetName].presetValues["ConfigVersion"] = SOH::GetLatestConfigVersion(); std::ofstream file(FormatPresetPath(presetName)); if (!file.is_open()) { diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 501793e479..34642e3c59 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -1532,13 +1532,7 @@ extern "C" void InitOTR(int argc, char* argv[]) { SaveManager::Instance = new SaveManager(); std::shared_ptr conf = OTRGlobals::Instance->context->GetConfig(); - conf->RegisterVersionUpdater(std::make_shared()); - conf->RegisterVersionUpdater(std::make_shared()); - conf->RegisterVersionUpdater(std::make_shared()); - conf->RegisterVersionUpdater(std::make_shared()); - conf->RegisterVersionUpdater(std::make_shared()); - conf->RegisterVersionUpdater(std::make_shared()); - conf->RegisterVersionUpdater(std::make_shared()); + SOH::RegisterVersionUpdaters(conf.get()); conf->RunVersionUpdates(); SohGui::SetupGuiElements(); @@ -2481,6 +2475,8 @@ bool SoH_HandleConfigDrop(char* filePath) { return false; } + uint32_t configVersion = SOH::GetConfigVersion(configJson, 0); + CVarClearBlock(CVAR_PREFIX_ENHANCEMENT); CVarClearBlock(CVAR_PREFIX_CHEAT); CVarClearBlock(CVAR_PREFIX_RANDOMIZER_SETTING); @@ -2506,6 +2502,9 @@ bool SoH_HandleConfigDrop(char* filePath) { } } + // Migrate configs from older versions + SOH::RunVersionUpdatesFrom(configVersion); + gui->GetGuiWindow("Console")->Hide(); gui->GetGuiWindow("Actor Viewer")->Hide(); gui->GetGuiWindow("Collision Viewer")->Hide(); diff --git a/soh/soh/config/ConfigUpdaters.cpp b/soh/soh/config/ConfigUpdaters.cpp index 4df227ab2e..9e127e550f 100644 --- a/soh/soh/config/ConfigUpdaters.cpp +++ b/soh/soh/config/ConfigUpdaters.cpp @@ -1,6 +1,11 @@ #include "ConfigUpdaters.h" +#include +#include +#include #include +#include +#include #include "soh/Enhancements/randomizer/randomizerEnums.h" namespace SOH { @@ -1631,21 +1636,24 @@ void ConfigVersion7Updater::Update(Ship::Config* conf) { } // Triforce Hunt: the Off/Win/GBK combobox was folded into TriforceHuntTotalPieces (0 = off) - // and the required piece count moved to the Wincon/GBK trigger counts + // and the required piece count moved to the Wincon/GBK trigger counts. + // Counts need +1 from previous values switch (CVarGetInteger("gRandoSettings.TriforceHunt", 0)) { case 1: // Win CVarSetInteger("gRandoSettings.ShuffleWincon", RO_WINCON_TRIFORCE_PIECES); CVarSetInteger("gRandoSettings.WinconTriforceCount", - CVarGetInteger("gRandoSettings.TriforceHuntRequiredPieces", 19)); + CVarGetInteger("gRandoSettings.TriforceHuntRequiredPieces", 19) + 1); + CVarSetInteger("gRandoSettings.GbkTriforceCount", + CVarGetInteger("gRandoSettings.TriforceHuntRequiredPieces", 19) + 1); CVarSetInteger("gRandoSettings.TriforceHuntTotalPieces", - CVarGetInteger("gRandoSettings.TriforceHuntTotalPieces", 29)); + CVarGetInteger("gRandoSettings.TriforceHuntTotalPieces", 29) + 1); break; case 2: // Ganon's Boss Key CVarSetInteger("gRandoSettings.ShuffleGanonBossKey", RO_GANON_BOSS_KEY_TRIFORCE_PIECES); CVarSetInteger("gRandoSettings.GbkTriforceCount", - CVarGetInteger("gRandoSettings.TriforceHuntRequiredPieces", 19)); + CVarGetInteger("gRandoSettings.TriforceHuntRequiredPieces", 19) + 1); CVarSetInteger("gRandoSettings.TriforceHuntTotalPieces", - CVarGetInteger("gRandoSettings.TriforceHuntTotalPieces", 29)); + CVarGetInteger("gRandoSettings.TriforceHuntTotalPieces", 29) + 1); break; default: // Off; a leftover total would now silently enable the hunt CVarClear("gRandoSettings.TriforceHuntTotalPieces"); @@ -1693,4 +1701,46 @@ void ConfigVersion7Updater::Update(Ship::Config* conf) { } CVarClear("gRandoSettings.CompleteMaskQuest"); } + +// v4+ also run on dropped configs and presets +static const auto& GetVersionUpdaters() { + static const std::array, 7> updaters = { + std::make_shared(), std::make_shared(), + std::make_shared(), std::make_shared(), + std::make_shared(), std::make_shared(), + std::make_shared(), + }; + return updaters; +} + +void RegisterVersionUpdaters(Ship::Config* conf) { + for (const auto& updater : GetVersionUpdaters()) { + conf->RegisterVersionUpdater(updater); + } +} + +void RunVersionUpdatesFrom(uint32_t fromVersion) { + if (fromVersion < GetLatestConfigVersion()) { + SPDLOG_INFO("Migrating config from ConfigVersion {} to {}", fromVersion, GetLatestConfigVersion()); + } + fromVersion = std::max(fromVersion, 3u); + Ship::Config* conf = Ship::Context::GetRawInstance()->GetConfig().get(); + for (const auto& updater : GetVersionUpdaters()) { + if (updater->GetVersion() > fromVersion) { + updater->Update(conf); + } + } +} + +uint32_t GetLatestConfigVersion() { + return GetVersionUpdaters().back()->GetVersion(); +} + +uint32_t GetConfigVersion(const nlohmann::json& json, uint32_t defaultVersion) { + auto version = json.find("ConfigVersion"); + if (version == json.end() || !version->is_number_unsigned()) { + return defaultVersion; + } + return version->get(); +} } // namespace SOH diff --git a/soh/soh/config/ConfigUpdaters.h b/soh/soh/config/ConfigUpdaters.h index 189bf99d62..5eaac86098 100644 --- a/soh/soh/config/ConfigUpdaters.h +++ b/soh/soh/config/ConfigUpdaters.h @@ -44,4 +44,12 @@ class ConfigVersion7Updater final : public Ship::ConfigVersionUpdater { ConfigVersion7Updater(); void Update(Ship::Config* conf); }; + +void RegisterVersionUpdaters(Ship::Config* conf); + +void RunVersionUpdatesFrom(uint32_t fromVersion); + +uint32_t GetLatestConfigVersion(); + +uint32_t GetConfigVersion(const nlohmann::json& json, uint32_t defaultVersion); } // namespace SOH