summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarrett Cox <garrettjcox@gmail.com>2026-01-20 20:37:14 -0600
committerGitHub <noreply@github.com>2026-01-20 20:37:14 -0600
commit77cbefd28023d21f73fcd169f57015a21ad87bac (patch)
treefddb1b91ff4c6a8d40f5fd5350f9452da3ad2cd7
parent31f902da390b38dcae0cc7f3ee2d81dfa796ba1a (diff)
Emit notification on seed generation failure (#1478)
-rw-r--r--mm/2s2h/Rando/Logic/GlitchlessLogic.cpp6
-rw-r--r--mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp25
-rw-r--r--mm/2s2h/Rando/Spoiler/Apply.cpp13
-rw-r--r--mm/2s2h/Rando/Spoiler/File.cpp4
4 files changed, 36 insertions, 12 deletions
diff --git a/mm/2s2h/Rando/Logic/GlitchlessLogic.cpp b/mm/2s2h/Rando/Logic/GlitchlessLogic.cpp
index f92c6528c..af266198f 100644
--- a/mm/2s2h/Rando/Logic/GlitchlessLogic.cpp
+++ b/mm/2s2h/Rando/Logic/GlitchlessLogic.cpp
@@ -61,7 +61,7 @@ void ApplyGlitchlessLogicToSaveContext(std::vector<RandoCheckId>& checkPool, std
while (true) {
// Break if we've been running for too long
if (GetUnixTimestamp() - tick > 10000) {
- handleError("Logic Generation Timeout");
+ handleError("Generation took too long, aborting");
}
bool regionsInLogicChanged = false;
@@ -168,7 +168,7 @@ void ApplyGlitchlessLogicToSaveContext(std::vector<RandoCheckId>& checkPool, std
if (!regionsInLogicChanged && !checksInLogicChanged && !eventsInLogicChanged) {
if (checkWithJunk == RC_UNKNOWN) {
if (checksWithJunk.empty()) {
- handleError("No checks with junk, not sure what to do");
+ handleError("Out of checks to replace, cannot place remaining items");
}
if (checksWithJunk.size() == 1) {
@@ -202,7 +202,7 @@ void ApplyGlitchlessLogicToSaveContext(std::vector<RandoCheckId>& checkPool, std
}
if (!anyNonJunkItemsLeft) {
- handleError("No non-junk items left");
+ handleError("No non-junk items left to place, progression is impossible");
}
if (nonJunkItemsThatWeHaveNotTried.empty()) {
diff --git a/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp b/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp
index 86188a056..be39bacd9 100644
--- a/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp
+++ b/mm/2s2h/Rando/MiscBehavior/OnFileCreate.cpp
@@ -6,6 +6,7 @@
#include <libultraship/bridge/consolevariablebridge.h>
#include "ClockShuffle.h"
#include <spdlog/spdlog.h>
+#include "2s2h/BenGui/Notification.h"
extern "C" {
#include "functions.h"
@@ -76,10 +77,10 @@ void Rando::MiscBehavior::OnFileCreate(s16 fileNum) {
Rando::Logic::GeneratePools(gSaveContext.save.shipSaveInfo.rando, checkPool, itemPool);
if (checkPool.empty()) {
- throw std::runtime_error("No checks in logic");
+ throw std::runtime_error("Check pool is empty");
}
if (itemPool.empty()) {
- throw std::runtime_error("No items in logic");
+ throw std::runtime_error("Item pool is empty");
}
// Balance pools
@@ -124,15 +125,24 @@ void Rando::MiscBehavior::OnFileCreate(s16 fileNum) {
continue;
}
- SPDLOG_ERROR("Could not match item pool size to check pool size {}/{}", itemPool.size(),
+ SPDLOG_ERROR("Could not balance item/check pools. Too many items. {}/{}", itemPool.size(),
checkPool.size());
- throw std::runtime_error("Could not match item pool size to check pool size");
+ throw std::runtime_error("Could not balance item/check pools. Too many items.");
}
}
// Grant the starting stuff
Rando::GrantStartingItems();
+ // Run prelim compatibility/validation checks before attempting to place items
+
+ // Verify we have at least one time item if clock shuffle is enabled
+ if (RANDO_SAVE_OPTIONS[RO_CLOCK_SHUFFLE] != 0) {
+ if (Rando::Logic::ClockCount() == 0) {
+ throw std::runtime_error("Shuffle Time is enabled but no starting time was given");
+ }
+ }
+
if (RANDO_SAVE_OPTIONS[RO_LOGIC] == RO_LOGIC_VANILLA) {
GiveItem(RI_SWORD_KOKIRI);
GiveItem(RI_SHIELD_HERO);
@@ -185,8 +195,13 @@ void Rando::MiscBehavior::OnFileCreate(s16 fileNum) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnRandoSeedGeneration>();
} catch (const std::exception& e) {
- SPDLOG_ERROR("Error with randomizer save creation: {}", e.what());
+ SPDLOG_ERROR("Seed Failure: {}", e.what());
Audio_PlaySfx(NA_SE_SY_QUIZ_INCORRECT);
+ Notification::Emit({
+ .prefix = "Seed Failure:",
+ .prefixColor = ImVec4(1.0f, 0.2f, 0.2f, 1.0f),
+ .message = e.what(),
+ });
gSaveContext.save.shipSaveInfo.saveType = SAVETYPE_VANILLA;
char invalidName[8] = { 18, 23, 31, 10, 21, 18, 13, 62 };
memcpy(gSaveContext.save.saveInfo.playerData.playerName, invalidName, sizeof(invalidName));
diff --git a/mm/2s2h/Rando/Spoiler/Apply.cpp b/mm/2s2h/Rando/Spoiler/Apply.cpp
index cb9cb43c9..724e37804 100644
--- a/mm/2s2h/Rando/Spoiler/Apply.cpp
+++ b/mm/2s2h/Rando/Spoiler/Apply.cpp
@@ -15,7 +15,8 @@ void ApplyToSaveContext(nlohmann::json spoiler) {
gSaveContext.save.shipSaveInfo.rando.finalSeed = spoiler["finalSeed"].get<uint32_t>();
for (auto& [randoOptionId, randoStaticOption] : Rando::StaticData::Options) {
- RANDO_SAVE_OPTIONS[randoOptionId] = spoiler["options"][randoStaticOption.name].get<uint32_t>();
+ RANDO_SAVE_OPTIONS[randoOptionId] =
+ spoiler["options"].value(randoStaticOption.name, (uint32_t)randoStaticOption.defaultValue);
}
if (!RANDO_SAVE_OPTIONS[RO_SHUFFLE_GOLD_SKULLTULAS]) {
@@ -41,6 +42,11 @@ void ApplyToSaveContext(nlohmann::json spoiler) {
std::string itemName = spoiler["checks"][randoStaticCheck.name]["randoItemId"].get<std::string>();
RandoItemId randoItemId = Rando::StaticData::GetItemIdFromName(itemName.c_str());
+ if (randoItemId == RI_UNKNOWN) {
+ SPDLOG_ERROR("Unknown item in spoiler for check {}: {}", randoStaticCheck.name, itemName);
+ throw std::runtime_error("Unknown item in spoiler: " + itemName);
+ }
+
RANDO_SAVE_CHECKS[randoCheckId].randoItemId = randoItemId;
RANDO_SAVE_CHECKS[randoCheckId].shuffled = true;
@@ -53,6 +59,11 @@ void ApplyToSaveContext(nlohmann::json spoiler) {
std::string itemName = spoiler["checks"][randoStaticCheck.name].get<std::string>();
RandoItemId randoItemId = Rando::StaticData::GetItemIdFromName(itemName.c_str());
+ if (randoItemId == RI_UNKNOWN) {
+ SPDLOG_ERROR("Unknown item in spoiler for check {}: {}", randoStaticCheck.name, itemName);
+ throw std::runtime_error("Unknown item in spoiler: " + itemName);
+ }
+
RANDO_SAVE_CHECKS[randoCheckId].randoItemId = randoItemId;
RANDO_SAVE_CHECKS[randoCheckId].shuffled = true;
}
diff --git a/mm/2s2h/Rando/Spoiler/File.cpp b/mm/2s2h/Rando/Spoiler/File.cpp
index c54f6ac3b..e2f610dfe 100644
--- a/mm/2s2h/Rando/Spoiler/File.cpp
+++ b/mm/2s2h/Rando/Spoiler/File.cpp
@@ -26,9 +26,7 @@ nlohmann::json LoadFromFile(const std::string& fileName) {
nlohmann::json spoiler;
try {
fileStream >> spoiler;
- } catch (nlohmann::json::exception& e) {
- throw std::runtime_error("Failed to parse spoiler file: " + std::string(e.what()));
- }
+ } catch (nlohmann::json::exception& e) { throw std::runtime_error("Failed to parse spoiler file"); }
if (!spoiler.contains("type") || spoiler["type"] != "2S2H_RANDO_SPOILER") {
throw std::runtime_error("Spoiler file is not a valid spoiler file");