From d53afb534d4aa86c6a82d898174e3fc975e62584 Mon Sep 17 00:00:00 2001 From: Jerom Venneker Date: Mon, 7 Jul 2025 19:22:17 +0200 Subject: [PATCH 1/9] Changed AP console text colors to enums --- soh/soh/Network/Archipelago/Archipelago.cpp | 46 +++++----- soh/soh/Network/Archipelago/Archipelago.h | 7 +- .../Archipelago/ArchipelagoConsoleWindow.cpp | 89 ++++++++++--------- .../Archipelago/ArchipelagoConsoleWindow.h | 7 +- .../Network/Archipelago/ArchipelagoTypes.h | 30 +++++++ 5 files changed, 107 insertions(+), 72 deletions(-) create mode 100644 soh/soh/Network/Archipelago/ArchipelagoTypes.h diff --git a/soh/soh/Network/Archipelago/Archipelago.cpp b/soh/soh/Network/Archipelago/Archipelago.cpp index 4421a90ba..264e91090 100644 --- a/soh/soh/Network/Archipelago/Archipelago.cpp +++ b/soh/soh/Network/Archipelago/Archipelago.cpp @@ -146,64 +146,64 @@ bool ArchipelagoClient::StartClient() { return; } - std::vector coloredNodes; + std::vector coloredNodes; for (const APClient::TextNode& node : arg.data) { APClient* client = apClient.get(); - std::string color; + AP_Text::TextColor color = AP_Text::TextColor::COLOR_DEFAULT; std::string text; if (node.type == "player_id") { int id = std::stoi(node.text); - if (color.empty() && id == client->get_player_number()) - color = "magenta"; - else if (color.empty()) - color = "yellow"; + if (color == AP_Text::TextColor::COLOR_DEFAULT && id == client->get_player_number()) + color = AP_Text::TextColor::COLOR_MAGENTA; + else if (color == AP_Text::TextColor::COLOR_DEFAULT) + color = AP_Text::TextColor::COLOR_YELLOW; text = client->get_player_alias(id); } else if (node.type == "item_id") { int64_t id = std::stoll(node.text); - if (color.empty()) { + if (color == AP_Text::TextColor::COLOR_DEFAULT) { if (node.flags & APClient::ItemFlags::FLAG_ADVANCEMENT) - color = "plum"; + color = AP_Text::TextColor::COLOR_PLUM; else if (node.flags & APClient::ItemFlags::FLAG_NEVER_EXCLUDE) - color = "slateblue"; + color = AP_Text::TextColor::COLOR_SLATEBLUE; else if (node.flags & APClient::ItemFlags::FLAG_TRAP) - color = "salmon"; + color = AP_Text::TextColor::COLOR_SALMON; else - color = "cyan"; + color = AP_Text::TextColor::COLOR_CYAN; } text = client->get_item_name(id, client->get_player_game(node.player)); } else if (node.type == "location_id") { int64_t id = std::stoll(node.text); - if (color.empty()) - color = "blue"; + if (color == AP_Text::TextColor::COLOR_DEFAULT) + color = AP_Text::TextColor::COLOR_BLUE; text = client->get_location_name(id, client->get_player_game(node.player)); } else if (node.type == "hint_status") { text = node.text; if (node.hintStatus == APClient::HINT_FOUND) - color = "green"; + color = AP_Text::TextColor::COLOR_GREEN; else if (node.hintStatus == APClient::HINT_UNSPECIFIED) - color = "grey"; + color = AP_Text::TextColor::COLOR_GRAY; else if (node.hintStatus == APClient::HINT_NO_PRIORITY) - color = "slateblue"; + color = AP_Text::TextColor::COLOR_SLATEBLUE; else if (node.hintStatus == APClient::HINT_AVOID) - color = "salmon"; + color = AP_Text::TextColor::COLOR_SALMON; else if (node.hintStatus == APClient::HINT_PRIORITY) - color = "plum"; + color = AP_Text::TextColor::COLOR_PLUM; else - color = "red"; // unknown status -> red + color = AP_Text::TextColor::COLOR_RED; // unknown status -> red } else if (node.type == "ERROR") { - color = "ERROR"; + color = AP_Text::TextColor::COLOR_ERROR; text = node.text; } else if (node.type == "LOG") { - color = "LOG"; + color = AP_Text::TextColor::COLOR_LOG; text = node.text; } else { - color = "white"; + color = AP_Text::TextColor::COLOR_WHITE; text = node.text; } - ColoredTextNode Colornode; + AP_Text::ColoredTextNode Colornode; Colornode.color = color; Colornode.text = text; coloredNodes.push_back(Colornode); diff --git a/soh/soh/Network/Archipelago/Archipelago.h b/soh/soh/Network/Archipelago/Archipelago.h index ed77fb5ee..d71d5466d 100644 --- a/soh/soh/Network/Archipelago/Archipelago.h +++ b/soh/soh/Network/Archipelago/Archipelago.h @@ -5,6 +5,8 @@ #include #include #include +#include "ArchipelagoConsoleWindow.h" +#include "ArchipelagoTypes.h" // Forward declaration class APClient; @@ -28,11 +30,6 @@ class ArchipelagoClient { uint64_t index; }; - struct ColoredTextNode { - std::string text; - std::string color; - }; - static ArchipelagoClient& GetInstance(); bool StartClient(); diff --git a/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.cpp b/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.cpp index 4ee7b1e06..f5b687169 100644 --- a/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.cpp +++ b/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.cpp @@ -3,8 +3,9 @@ #include "soh/SohGui/UIWidgets.hpp" #include "soh/SohGui/SohGui.hpp" #include "soh/OTRGlobals.h" +#include "ArchipelagoTypes.h" -std::vector> Items; +std::vector> Items; bool autoScroll = true; using namespace UIWidgets; @@ -16,15 +17,15 @@ void ArchipelagoConsole_SendMessage(const char* fmt, ...) { vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args); buf[IM_ARRAYSIZE(buf) - 1] = 0; va_end(args); - ArchipelagoClient::ColoredTextNode node; + AP_Text::ColoredTextNode node; node.text = std::string(buf); - node.color = "white"; + node.color = AP_Text::TextColor::COLOR_WHITE; if (strstr(buf, "[ERROR]")) { - node.color = "ERROR"; + node.color = AP_Text::TextColor::COLOR_ERROR; } else if (strstr(buf, "[LOG]")) { - node.color = "LOG"; + node.color = AP_Text::TextColor::COLOR_LOG; } - std::vector line; + std::vector line; line.push_back(node); Items.push_back(line); if (Items.size() > 50) { @@ -32,7 +33,7 @@ void ArchipelagoConsole_SendMessage(const char* fmt, ...) { } } -void ArchipelagoConsole_PrintJson(const std::vector nodes) { +void ArchipelagoConsole_PrintJson(const std::vector nodes) { Items.push_back(nodes); if (Items.size() > 50) { Items.erase(Items.begin()); @@ -50,8 +51,8 @@ void ArchipelagoConsoleWindow::DrawElement() { if (ImGui::BeginChild("ScrollingRegion", ImVec2(0, 400), ImGuiChildFlags_AlwaysUseWindowPadding, ImGuiWindowFlags_HorizontalScrollbar)) { - for (const std::vector& line : Items) { - for (const ArchipelagoClient::ColoredTextNode& node : line) { + for (const std::vector& line : Items) { + for (const AP_Text::ColoredTextNode& node : line) { ImGui::PushStyleColor(ImGuiCol_Text, getColorVal(node.color)); ImGui::TextUnformatted(node.text.c_str()); ImGui::SameLine(); @@ -99,35 +100,41 @@ void ArchipelagoConsoleWindow::DrawElement() { ImGui::PopStyleVar(4); }; -ImVec4 getColorVal(const std::string& color) { // TODO change color strings to an enum - if (color == "ERROR") { - return ImVec4(1.0f, 0.4f, 0.4f, 1.0f); - } else if (color == "LOG") { - return ImVec4(0.7f, 0.7f, 1.0f, 1.0f); - } else if (color == "black") { - return ImVec4(0.000f, 0.000f, 0.000f, 1.00f); - } else if (color == "red") { - return ImVec4(0.933f, 0.000f, 0.000f, 1.00f); - } else if (color == "green") { - return ImVec4(0.000f, 1.000f, 0.498f, 1.00f); - } else if (color == "yellow") { - return ImVec4(0.980f, 0.980f, 0.824f, 1.00f); - } else if (color == "blue") { - return ImVec4(0.392f, 0.584f, 0.929f, 1.00f); - } else if (color == "cyan") { - return ImVec4(0.000f, 0.933f, 0.933f, 1.00f); - } else if (color == "magenta") { - return ImVec4(0.933f, 0.000f, 0.933f, 1.00f); - } else if (color == "slateblue") { - return ImVec4(0.427f, 0.545f, 0.910f, 1.00f); - } else if (color == "plum") { - return ImVec4(0.686f, 0.600f, 0.937f, 1.00f); - } else if (color == "salmon") { - return ImVec4(0.980f, 0.502f, 0.447f, 1.00f); - } else if (color == "white") { - return ImVec4(0.93f, 0.93f, 0.93f, 1.00f); - } else if (color == "orange") { - return ImVec4(1.000, 0.467f, 0.000f, 1.000f); - } - return ImVec4(0.93f, 0.93f, 0.93f, 1.00f); -} \ No newline at end of file +ImVec4 ArchipelagoConsoleWindow::getColorVal(const AP_Text::TextColor color) { + using apt = AP_Text::TextColor; + switch(color) { + case apt::COLOR_ERROR: + return ImVec4(1.0f, 0.4f, 0.4f, 1.0f); + case apt::COLOR_LOG: + return ImVec4(0.7f, 0.7f, 1.0f, 1.0f); + case apt::COLOR_BLACK: + return ImVec4(0.000f, 0.000f, 0.000f, 1.00f); + case apt::COLOR_RED: + return ImVec4(0.933f, 0.000f, 0.000f, 1.00f); + case apt::COLOR_GREEN: + return ImVec4(0.000f, 1.000f, 0.498f, 1.00f); + case apt::COLOR_YELLOW: + return ImVec4(0.980f, 0.980f, 0.824f, 1.00f); + case apt::COLOR_BLUE: + return ImVec4(0.392f, 0.584f, 0.929f, 1.00f); + case apt::COLOR_CYAN: + return ImVec4(0.000f, 0.933f, 0.933f, 1.00f); + case apt::COLOR_MAGENTA: + return ImVec4(0.933f, 0.000f, 0.933f, 1.00f); + case apt::COLOR_SLATEBLUE: + return ImVec4(0.427f, 0.545f, 0.910f, 1.00f); + case apt::COLOR_PLUM: + return ImVec4(0.686f, 0.600f, 0.937f, 1.00f); + case apt::COLOR_SALMON: + return ImVec4(0.980f, 0.502f, 0.447f, 1.00f); + case apt::COLOR_ORANGE: + return ImVec4(1.000, 0.467f, 0.000f, 1.000f); + case apt::COLOR_GRAY: + return ImVec4(0.53f, 0.53f, 0.53f, 1.00f); + case apt::COLOR_WHITE: + case apt::COLOR_DEFAULT: + default: + return ImVec4(0.93f, 0.93f, 0.93f, 1.00f); + }; +} + \ No newline at end of file diff --git a/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.h b/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.h index cd782ce86..92f975fb2 100644 --- a/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.h +++ b/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.h @@ -3,15 +3,17 @@ #define ARCHIPELAGO_CONSOLE_WINDOW_H #include -#include "Archipelago.h" #include #include +#include "ArchipelagoTypes.h" class ArchipelagoConsoleWindow final : public Ship::GuiWindow { public: using GuiWindow::GuiWindow; ~ArchipelagoConsoleWindow(){}; + static ImVec4 getColorVal(const AP_Text::TextColor color); + protected: void InitElement() override{}; void DrawElement() override; @@ -19,7 +21,6 @@ class ArchipelagoConsoleWindow final : public Ship::GuiWindow { }; void ArchipelagoConsole_SendMessage(const char* fmt, ...); -void ArchipelagoConsole_PrintJson(const std::vector nodes); -ImVec4 getColorVal(const std::string& color); +void ArchipelagoConsole_PrintJson(const std::vector nodes); #endif // ARCHIPELAGO_CONSOLE_WINDOW_H \ No newline at end of file diff --git a/soh/soh/Network/Archipelago/ArchipelagoTypes.h b/soh/soh/Network/Archipelago/ArchipelagoTypes.h new file mode 100644 index 000000000..e2da978b1 --- /dev/null +++ b/soh/soh/Network/Archipelago/ArchipelagoTypes.h @@ -0,0 +1,30 @@ +#pragma once +#ifdef __cplusplus + +namespace AP_Text { + enum class TextColor : char { + COLOR_DEFAULT = 0, + COLOR_ERROR, + COLOR_LOG, + COLOR_BLACK, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW, + COLOR_BLUE, + COLOR_CYAN, + COLOR_MAGENTA, + COLOR_SLATEBLUE, + COLOR_PLUM, + COLOR_SALMON, + COLOR_WHITE, + COLOR_ORANGE, + COLOR_GRAY + }; + + struct ColoredTextNode { + std::string text; + AP_Text::TextColor color; + }; +} + +#endif \ No newline at end of file From 6df2fd9b8a06c8e7f130d7a74d4b2e7938c754b4 Mon Sep 17 00:00:00 2001 From: Jerom Venneker Date: Wed, 9 Jul 2025 19:21:40 +0200 Subject: [PATCH 2/9] Fixed crash on game completion without being connected --- soh/soh/Network/Archipelago/Archipelago.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/soh/soh/Network/Archipelago/Archipelago.cpp b/soh/soh/Network/Archipelago/Archipelago.cpp index 264e91090..033d8d94c 100644 --- a/soh/soh/Network/Archipelago/Archipelago.cpp +++ b/soh/soh/Network/Archipelago/Archipelago.cpp @@ -39,7 +39,7 @@ ArchipelagoClient& ArchipelagoClient::GetInstance() { } bool ArchipelagoClient::StartClient() { - if (apClient != NULL) { + if (apClient != nullptr) { apClient.reset(); } @@ -372,6 +372,10 @@ void ArchipelagoClient::QueueItem(const ApItem item) { } void ArchipelagoClient::SendGameWon() { + if(apClient == nullptr) { + return; + } + if (!gameWon) { apClient->StatusUpdate(APClient::ClientStatus::GOAL); gameWon = true; @@ -425,7 +429,7 @@ bool ArchipelagoClient::isRightSaveLoaded() const { } const std::string ArchipelagoClient::GetSlotName() const { - if (apClient == NULL) { + if (apClient == nullptr) { return ""; } @@ -482,7 +486,7 @@ void ArchipelagoClient::OnItemGiven(uint32_t rc, GetItemEntry gi, uint8_t isGiSk } void ArchipelagoClient::SendDeathLink() { - if (apClient && CVarGetInteger(CVAR_REMOTE_ARCHIPELAGO("DeathLink"), 0) && !isDeathLinkedDeath) { + if (apClient != nullptr && CVarGetInteger(CVAR_REMOTE_ARCHIPELAGO("DeathLink"), 0) && !isDeathLinkedDeath) { nlohmann::json data{ { "time", apClient->get_server_time() }, { "cause", "Shipwrecked by King Harkinian." }, { "source", apClient->get_slot() } }; From e272300245f2e0516fcaf7f638c7c413c2b0cc6b Mon Sep 17 00:00:00 2001 From: Jerom Venneker Date: Thu, 10 Jul 2025 20:23:49 +0200 Subject: [PATCH 3/9] Fixed crash when recieving a bounce without any tags --- soh/soh/Network/Archipelago/Archipelago.cpp | 28 +++++++++++---------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/soh/soh/Network/Archipelago/Archipelago.cpp b/soh/soh/Network/Archipelago/Archipelago.cpp index 033d8d94c..a73b30d8a 100644 --- a/soh/soh/Network/Archipelago/Archipelago.cpp +++ b/soh/soh/Network/Archipelago/Archipelago.cpp @@ -213,19 +213,21 @@ bool ArchipelagoClient::StartClient() { }); apClient->set_bounced_handler([&](const nlohmann::json data) { - std::list tags = data["tags"]; - bool deathLink = (std::find(tags.begin(), tags.end(), "DeathLink") != tags.end()); - - if (deathLink && data["data"]["source"] != apClient->get_slot()) { - if (GameInteractor::IsSaveLoaded()) { - gSaveContext.health = 0; - std::string prefixText = std::string(data["data"]["source"]) + " died."; - Notification::Emit({ .prefix = prefixText, .message = "Cause:", .suffix = data["data"]["cause"] }); - std::string deathLinkMessage = "[LOG] Received death link from " + std::string(data["data"]["source"]) + - ". Cause: " + std::string(data["data"]["cause"]); - ArchipelagoConsole_SendMessage(deathLinkMessage.c_str()); - - isDeathLinkedDeath = true; + if(data.contains("tags")) { + std::list tags = data["tags"]; + bool deathLink = (std::find(tags.begin(), tags.end(), "DeathLink") != tags.end()); + + if (deathLink && data["data"]["source"] != apClient->get_slot()) { + if (GameInteractor::IsSaveLoaded()) { + gSaveContext.health = 0; + std::string prefixText = std::string(data["data"]["source"]) + " died."; + Notification::Emit({ .prefix = prefixText, .message = "Cause:", .suffix = data["data"]["cause"] }); + std::string deathLinkMessage = "[LOG] Received death link from " + std::string(data["data"]["source"]) + + ". Cause: " + std::string(data["data"]["cause"]); + ArchipelagoConsole_SendMessage(deathLinkMessage.c_str()); + + isDeathLinkedDeath = true; + } } } }); From 9fbf67fc9413d9c914f522fa3827b7bce173433c Mon Sep 17 00:00:00 2001 From: Jerom Venneker Date: Tue, 15 Jul 2025 19:07:19 +0200 Subject: [PATCH 4/9] Added connection status to archi file select --- soh/include/z64save.h | 1 + .../Enhancements/FileSelectEnhancements.cpp | 18 +++++ soh/soh/Enhancements/FileSelectEnhancements.h | 3 + soh/soh/Network/Archipelago/Archipelago.cpp | 27 +++++++- soh/soh/Network/Archipelago/Archipelago.h | 3 + soh/soh/OTRGlobals.cpp | 6 ++ soh/soh/OTRGlobals.h | 1 + soh/soh/SaveManager.cpp | 10 +++ soh/soh/SaveManager.h | 4 ++ .../ovl_file_choose/z_file_choose.c | 66 ++++++++++++++++--- 10 files changed, 130 insertions(+), 9 deletions(-) diff --git a/soh/include/z64save.h b/soh/include/z64save.h index 6a0e0b720..3aa867891 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -178,6 +178,7 @@ typedef struct ShipArchipelagoSaveContextData { u32 lastReceivedItemIndex; char roomHash[100]; char slotName[17]; + char archiUri[50]; ArchipelagoLocationData locations[RC_MAX]; } ShipArchipelagoSaveContextData; diff --git a/soh/soh/Enhancements/FileSelectEnhancements.cpp b/soh/soh/Enhancements/FileSelectEnhancements.cpp index 4d8208733..b75e7d81c 100644 --- a/soh/soh/Enhancements/FileSelectEnhancements.cpp +++ b/soh/soh/Enhancements/FileSelectEnhancements.cpp @@ -111,6 +111,24 @@ std::array ArchipelagoSettingsMenuText[ASM_MAX]{ "Todo", "Todo", }, + //ASM_CHAR_START_TO_CONNECT + { + "Start to automatically connect to this slot", + "Todo", + "Todo", + }, + //ASM_CHAR_SELECT_CONNECTED_TO_OTHER_SLOT + { + "Connected to a different slot", + "Todo", + "Todo", + }, + // ASM_CHAR_SELECT_CHANGE_CONNECTION_INFO + { + "Z-Connection Settings", + "Z-Todo", + "Z-Todo", + } }; const char* SohFileSelect_GetRandomizerSettingText(uint8_t optionIndex, uint8_t language) { diff --git a/soh/soh/Enhancements/FileSelectEnhancements.h b/soh/soh/Enhancements/FileSelectEnhancements.h index 680c7be1e..b76f728e7 100644 --- a/soh/soh/Enhancements/FileSelectEnhancements.h +++ b/soh/soh/Enhancements/FileSelectEnhancements.h @@ -30,6 +30,9 @@ typedef enum { ASM_CONNECTING, ASM_CONNECTED, ASM_STATUS, + ASM_CHAR_START_TO_CONNECT, + ASM_CHAR_SELECT_CONNECTED_TO_OTHER_SLOT, + ASM_CHAR_SELECT_CHANGE_CONNECTION_INFO, ASM_MAX } ArchipelagoSettingsMenuEnums; diff --git a/soh/soh/Network/Archipelago/Archipelago.cpp b/soh/soh/Network/Archipelago/Archipelago.cpp index a73b30d8a..d5abd4e88 100644 --- a/soh/soh/Network/Archipelago/Archipelago.cpp +++ b/soh/soh/Network/Archipelago/Archipelago.cpp @@ -45,9 +45,10 @@ bool ArchipelagoClient::StartClient() { disconnecting = false; retries = 0; + uri = CVarGetString(CVAR_REMOTE_ARCHIPELAGO("ServerAddress"), "localhost:38281"); apClient = std::unique_ptr( new APClient(uuid, AP_Client_consts::AP_GAME_NAME, - CVarGetString(CVAR_REMOTE_ARCHIPELAGO("ServerAddress"), "localhost:38281"), "cacert.pem")); + uri, "cacert.pem")); CVarSetInteger(CVAR_REMOTE_ARCHIPELAGO("ConnectionStatus"), 1); // Connecting @@ -424,6 +425,23 @@ void ArchipelagoClient::Poll() { apClient->poll(); } +bool ArchipelagoClient::slotMatch(const std::string& slotName, const std::string& roomHash) { + if (apClient == nullptr) { + return false; + } + + if(disconnecting) { + return false; + } + + const std::string seed = apClient->get_seed(); + const std::string slot = GetSlotName(); + + const bool seedMatch = apClient->get_seed().compare(roomHash) == 0; + const bool slotMatch = GetSlotName().compare(slotName) == 0; + return seedMatch && slotMatch; +} + bool ArchipelagoClient::isRightSaveLoaded() const { const bool seedMatch = apClient->get_seed().compare(gSaveContext.ship.quest.data.archipelago.roomHash) == 0; const bool slotMatch = GetSlotName().compare(gSaveContext.ship.quest.data.archipelago.slotName) == 0; @@ -524,6 +542,8 @@ extern "C" void Archipelago_InitSaveFile() { ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.roomHash)); SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.slotName, client.apClient->get_slot(), ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.slotName)); + SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.archiUri, client.uri, + ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.archiUri)); for (uint32_t i = 0; i < scoutedItems.size(); i++) { RandomizerCheck rc = Rando::StaticData::locationNameToEnum[scoutedItems[i].locationName]; @@ -546,6 +566,8 @@ void LoadArchipelagoData() { ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.roomHash)); SaveManager::Instance->LoadCharArray("slotName", gSaveContext.ship.quest.data.archipelago.slotName, ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.slotName)); + SaveManager::Instance->LoadCharArray("archiUri", gSaveContext.ship.quest.data.archipelago.archiUri, + ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.archiUri)); SaveManager::Instance->LoadArray( "locations", ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.locations), [](size_t i) { @@ -567,6 +589,7 @@ void SaveArchipelagoData(SaveContext* saveContext, int sectionID, bool fullSave) SaveManager::Instance->SaveData("roomHash", saveContext->ship.quest.data.archipelago.roomHash); SaveManager::Instance->SaveData("slotName", saveContext->ship.quest.data.archipelago.slotName); + SaveManager::Instance->SaveData("archiUri", saveContext->ship.quest.data.archipelago.archiUri); SaveManager::Instance->SaveArray( "locations", ARRAY_COUNT(saveContext->ship.quest.data.archipelago.locations), [&](size_t i) { @@ -587,6 +610,8 @@ void InitArchipelagoData(bool isDebug) { ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.roomHash)); SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.slotName, "", ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.slotName)); + SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.archiUri, "", + ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.archiUri)); for (uint32_t i = 0; i < ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.locations); i++) { SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.locations[i].itemName, "", diff --git a/soh/soh/Network/Archipelago/Archipelago.h b/soh/soh/Network/Archipelago/Archipelago.h index d71d5466d..17adb3747 100644 --- a/soh/soh/Network/Archipelago/Archipelago.h +++ b/soh/soh/Network/Archipelago/Archipelago.h @@ -62,11 +62,14 @@ class ArchipelagoClient { void SendMessageToConsole(const std::string message); void Poll(); + bool slotMatch(const std::string& slotName, const std::string& roomHash); + std::unique_ptr apClient; bool itemQueued; bool disconnecting; bool isDeathLinkedDeath; int retries; + std::string uri; protected: ArchipelagoClient(); diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 53b65a848..76e9afeee 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -2728,6 +2728,12 @@ extern "C" void ParseArchipelago() { OTRGlobals::Instance->gRandoContext->ParseArchipelago(); } +extern "C" bool checkArchipelagoSlotInfo(const char* slotName, const char* roomHash) { + const std::string slot = std::string(slotName); + const std::string room = std::string(roomHash); + return ArchipelagoClient::GetInstance().slotMatch(slot, room); +} + extern "C" void CheckTracker_RecalculateAvailableChecks() { CheckTracker::RecalculateAvailableChecks(); } diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index e3d4e9048..74029ce59 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -173,6 +173,7 @@ void CheckTracker_RecalculateAvailableChecks(); GetItemID RetrieveGetItemIDFromItemID(ItemID itemID); RandomizerGet RetrieveRandomizerGetFromItemID(ItemID itemID); void ParseArchipelago(); +bool checkArchipelagoSlotInfo(const char* slotName, const char* roomHash); void Messagebox_ShowErrorBox(char* title, char* body); #endif diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index c7eb49950..c9fbb7582 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -154,6 +154,9 @@ SaveManager::SaveManager() { info.buildVersionMinor = 0; info.buildVersionPatch = 0; memset(&info.buildVersion, 0, sizeof(info.buildVersion)); + + memset(&info.archiUri, 0, sizeof(info.archiUri)); + memset(&info.slotName, 0, sizeof(info.slotName)); } } @@ -509,6 +512,13 @@ void SaveManager::InitMeta(int fileNum) { fileMetaInfo[fileNum].buildVersionPatch = gSaveContext.ship.stats.buildVersionPatch; SohUtils::CopyStringToCharArray(fileMetaInfo[fileNum].buildVersion, gSaveContext.ship.stats.buildVersion, ARRAY_COUNT(fileMetaInfo[fileNum].buildVersion)); + + SohUtils::CopyStringToCharArray(fileMetaInfo[fileNum].archiUri, gSaveContext.ship.quest.data.archipelago.archiUri, + ARRAY_COUNT(fileMetaInfo[fileNum].archiUri)); + SohUtils::CopyStringToCharArray(fileMetaInfo[fileNum].slotName, gSaveContext.ship.quest.data.archipelago.slotName, + ARRAY_COUNT(fileMetaInfo[fileNum].slotName)); + SohUtils::CopyStringToCharArray(fileMetaInfo[fileNum].archiRoomSeed, gSaveContext.ship.quest.data.archipelago.roomHash, + ARRAY_COUNT(fileMetaInfo[fileNum].archiRoomSeed)); } void SaveManager::InitFile(bool isDebug) { diff --git a/soh/soh/SaveManager.h b/soh/soh/SaveManager.h index 8cdb16a91..7baad1dac 100644 --- a/soh/soh/SaveManager.h +++ b/soh/soh/SaveManager.h @@ -33,6 +33,10 @@ typedef struct { s32 filenameLanguage; s32 gregFound; s32 hasWallet; + + char archiRoomSeed[100]; + char slotName[17]; + char archiUri[50]; } SaveFileMetaInfo; typedef enum { diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index 0e323d565..6ace3962e 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -2478,7 +2478,8 @@ void FileChoose_DrawFileInfo(GameState* thisx, s16 fileIndex, s16 isActive) { &deathCountSplit[2]); // draw death count - if (CVarGetInteger(CVAR_ENHANCEMENT("FileSelectMoreInfo"), 0) == 0 || this->menuMode != FS_MENU_MODE_SELECT) { + if (CVarGetInteger(CVAR_ENHANCEMENT("FileSelectMoreInfo"), 0) == 0 || this->menuMode != FS_MENU_MODE_SELECT || + Save_GetSaveMetaInfo(this->selectedFileIndex)->archiSave) { for (i = 0, vtxOffset = 0; i < 3; i++, vtxOffset += 4) { FileChoose_DrawCharacter(this->state.gfxCtx, sp54->fontBuf + deathCountSplit[i] * FONT_CHAR_TEX_SIZE, vtxOffset); @@ -2504,7 +2505,8 @@ void FileChoose_DrawFileInfo(GameState* thisx, s16 fileIndex, s16 isActive) { i = Save_GetSaveMetaInfo(fileIndex)->healthCapacity / 0x10; - if (CVarGetInteger(CVAR_ENHANCEMENT("FileSelectMoreInfo"), 0) == 0 || this->menuMode != FS_MENU_MODE_SELECT) { + if (CVarGetInteger(CVAR_ENHANCEMENT("FileSelectMoreInfo"), 0) == 0 || this->menuMode != FS_MENU_MODE_SELECT || + Save_GetSaveMetaInfo(this->selectedFileIndex)->archiSave) { // draw hearts for (vtxOffset = 0, j = 0; j < i; j++, vtxOffset += 4) { gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[D_8081284C[fileIndex] + vtxOffset] + 0x30, 4, 0); @@ -2521,7 +2523,8 @@ void FileChoose_DrawFileInfo(GameState* thisx, s16 fileIndex, s16 isActive) { textAlpha = 255; } - if (CVarGetInteger(CVAR_ENHANCEMENT("FileSelectMoreInfo"), 0) != 0 && this->menuMode == FS_MENU_MODE_SELECT) { + if (CVarGetInteger(CVAR_ENHANCEMENT("FileSelectMoreInfo"), 0) != 0 && this->menuMode == FS_MENU_MODE_SELECT && + Save_GetSaveMetaInfo(this->selectedFileIndex)->archiSave == 0) { DrawMoreInfo(this, fileIndex, textAlpha); } else { // draw quest items @@ -2545,6 +2548,53 @@ void FileChoose_DrawFileInfo(GameState* thisx, s16 fileIndex, s16 isActive) { } } } + + if(Save_GetSaveMetaInfo(this->selectedFileIndex)->archiSave) { + uint8_t language = (gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language; + + // Connection status text + int statusPos = 61 + Interface_DrawTextLine(this->state.gfxCtx, SohFileSelect_GetArchipelagoSettingText(ASM_STATUS, language), + 58, 133, 200, 200, 200, textAlpha, 0.8f, true); + + const bool connectedToThisSlot = checkArchipelagoSlotInfo(Save_GetSaveMetaInfo(this->selectedFileIndex)->slotName, + Save_GetSaveMetaInfo(this->selectedFileIndex)->archiRoomSeed); + + switch(CVarGetInteger(CVAR_REMOTE_ARCHIPELAGO("ConnectionStatus"), 0)) { + case 0: // Not Connected + Interface_DrawTextLine(this->state.gfxCtx, + SohFileSelect_GetArchipelagoSettingText(ASM_NOT_CONNECTED, language), + statusPos, 133, 255, 120, 120, textAlpha, 0.8f, true); + break; + case 1: // Connecting + case 2: // Connection error, retrying + case 3: // Connected + Interface_DrawTextLine(this->state.gfxCtx, + SohFileSelect_GetArchipelagoSettingText(ASM_CONNECTING, language), + statusPos, 133, 185, 185, 185, textAlpha, 0.8f, true); + break; + case 4: // Connected + Locations Scouted + if(connectedToThisSlot) { + Interface_DrawTextLine(this->state.gfxCtx, + SohFileSelect_GetArchipelagoSettingText(ASM_CONNECTED, language), + statusPos, 133, 120, 255, 120, textAlpha, 0.8f, true); + } else { + Interface_DrawTextLine(this->state.gfxCtx, + SohFileSelect_GetArchipelagoSettingText(ASM_CHAR_SELECT_CONNECTED_TO_OTHER_SLOT, language), + statusPos, 133, 255, 255, 120, textAlpha, 0.8f, true); + } + + break; + } + + if(!connectedToThisSlot) { + Interface_DrawTextLine(this->state.gfxCtx, SohFileSelect_GetArchipelagoSettingText(ASM_CHAR_START_TO_CONNECT, language), + 58, 144, 200, 200, 200, textAlpha, 0.8f, true); + } + + //Interface_DrawTextLine(this->state.gfxCtx, + // SohFileSelect_GetArchipelagoSettingText(ASM_CHAR_SELECT_CHANGE_CONNECTION_INFO, language), 95, 220, + // 100, 250, 255, textAlpha, 1.0f, true); + } } CLOSE_DISPS(this->state.gfxCtx); @@ -2944,25 +2994,25 @@ void FileChoose_DrawWindowContents(GameState* thisx) { 155, 185, 185, 185, textAlpha, 0.8f, true); // Connection status text - Interface_DrawTextLine(this->state.gfxCtx, SohFileSelect_GetArchipelagoSettingText(ASM_STATUS, language), 70, + int statusPos = 75 + Interface_DrawTextLine(this->state.gfxCtx, SohFileSelect_GetArchipelagoSettingText(ASM_STATUS, language), 70, 175, 255, 255, 255, textAlpha, 0.8f, true); switch (CVarGetInteger(CVAR_REMOTE_ARCHIPELAGO("ConnectionStatus"), 0)) { case 0: // Not Connected Interface_DrawTextLine(this->state.gfxCtx, - SohFileSelect_GetArchipelagoSettingText(ASM_NOT_CONNECTED, language), 110, 175, + SohFileSelect_GetArchipelagoSettingText(ASM_NOT_CONNECTED, language), statusPos, 175, 255, 120, 120, textAlpha, 0.8f, true); break; case 1: // Connecting case 2: // Connection error, retrying case 3: // Connected Interface_DrawTextLine(this->state.gfxCtx, - SohFileSelect_GetArchipelagoSettingText(ASM_CONNECTING, language), 110, 175, 185, + SohFileSelect_GetArchipelagoSettingText(ASM_CONNECTING, language), statusPos, 175, 185, 185, 185, textAlpha, 0.8f, true); break; case 4: // Connected + Locations Scouted Interface_DrawTextLine(this->state.gfxCtx, - SohFileSelect_GetArchipelagoSettingText(ASM_CONNECTED, language), 110, 175, 120, + SohFileSelect_GetArchipelagoSettingText(ASM_CONNECTED, language), statusPos, 175, 120, 255, 120, textAlpha, 0.8f, true); break; } @@ -3005,7 +3055,7 @@ void FileChoose_DrawWindowContents(GameState* thisx) { // Draw the small file name box instead when more meta info is enabled if (CVarGetInteger(CVAR_ENHANCEMENT("FileSelectMoreInfo"), 0) != 0 && - this->menuMode == FS_MENU_MODE_SELECT) { + this->menuMode == FS_MENU_MODE_SELECT && Save_GetSaveMetaInfo(this->selectedFileIndex)->archiSave == 0) { // Location of file 1 small name box vertices gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[68], 4, 0); From 79dd481116f8783039a4268a83ea2d9cc425bf94 Mon Sep 17 00:00:00 2001 From: Jerom Venneker Date: Wed, 16 Jul 2025 18:50:32 +0200 Subject: [PATCH 5/9] Added autoconnect on file load --- soh/include/z64save.h | 1 + soh/soh/Network/Archipelago/Archipelago.cpp | 38 ++++++++++++++++++--- soh/soh/Network/Archipelago/Archipelago.h | 1 + 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/soh/include/z64save.h b/soh/include/z64save.h index 3aa867891..15e068fb4 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -179,6 +179,7 @@ typedef struct ShipArchipelagoSaveContextData { char roomHash[100]; char slotName[17]; char archiUri[50]; + char roomPass[50]; ArchipelagoLocationData locations[RC_MAX]; } ShipArchipelagoSaveContextData; diff --git a/soh/soh/Network/Archipelago/Archipelago.cpp b/soh/soh/Network/Archipelago/Archipelago.cpp index d5abd4e88..10d494726 100644 --- a/soh/soh/Network/Archipelago/Archipelago.cpp +++ b/soh/soh/Network/Archipelago/Archipelago.cpp @@ -17,6 +17,7 @@ #include "soh/Notification/Notification.h" #include "soh/ShipInit.hpp" #include "soh/SaveManager.h" +#include "soh/util.h" extern "C" { #include "variables.h" @@ -31,6 +32,8 @@ ArchipelagoClient::ArchipelagoClient() { itemQueued = false; disconnecting = false; isDeathLinkedDeath = false; + uri = ""; + password = ""; } ArchipelagoClient& ArchipelagoClient::GetInstance() { @@ -46,6 +49,8 @@ bool ArchipelagoClient::StartClient() { disconnecting = false; retries = 0; uri = CVarGetString(CVAR_REMOTE_ARCHIPELAGO("ServerAddress"), "localhost:38281"); + password = CVarGetString(CVAR_REMOTE_ARCHIPELAGO("Password"), ""); + apClient = std::unique_ptr( new APClient(uuid, AP_Client_consts::AP_GAME_NAME, uri, "cacert.pem")); @@ -70,7 +75,7 @@ bool ArchipelagoClient::StartClient() { tags.push_back("DeathLink"); } apClient->ConnectSlot(CVarGetString(CVAR_REMOTE_ARCHIPELAGO("SlotName"), ""), - CVarGetString(CVAR_REMOTE_ARCHIPELAGO("Password"), ""), 0b001, tags); + password, 0b001, tags); }); apClient->set_slot_connected_handler([&](const nlohmann::json data) { @@ -89,6 +94,14 @@ bool ArchipelagoClient::StartClient() { return; } + // save the connection details in case they changed + SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.archiUri, uri, + ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.archiUri)); + SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.slotName, GetSlotName(), + ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.slotName)); + SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.roomPass, password, + ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.roomPass)); + SynchSentLocations(); SynchReceivedLocations(); } @@ -243,6 +256,12 @@ bool ArchipelagoClient::StopClient() { void ArchipelagoClient::GameLoaded() { if (apClient == nullptr) { + if(IS_ARCHIPELAGO) { + CVarSetString(CVAR_REMOTE_ARCHIPELAGO("ServerAddress"), gSaveContext.ship.quest.data.archipelago.archiUri); + CVarSetString(CVAR_REMOTE_ARCHIPELAGO("SlotName"), gSaveContext.ship.quest.data.archipelago.slotName); + CVarSetString(CVAR_REMOTE_ARCHIPELAGO("Password"), gSaveContext.ship.quest.data.archipelago.roomPass); + StartClient(); + } return; } @@ -254,8 +273,12 @@ void ArchipelagoClient::GameLoaded() { } if (!isRightSaveLoaded()) { - ArchipelagoConsole_SendMessage("[ERROR] Loaded save is not associated with connected slot, disconnecting..."); - disconnecting = true; + ArchipelagoConsole_SendMessage("Connec"); + //disconnecting = true; + CVarSetString(CVAR_REMOTE_ARCHIPELAGO("ServerAddress"), gSaveContext.ship.quest.data.archipelago.archiUri); + CVarSetString(CVAR_REMOTE_ARCHIPELAGO("SlotName"), gSaveContext.ship.quest.data.archipelago.slotName); + CVarSetString(CVAR_REMOTE_ARCHIPELAGO("Password"), gSaveContext.ship.quest.data.archipelago.roomPass); + StartClient(); return; } @@ -543,7 +566,9 @@ extern "C" void Archipelago_InitSaveFile() { SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.slotName, client.apClient->get_slot(), ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.slotName)); SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.archiUri, client.uri, - ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.archiUri)); + ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.archiUri)); + SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.roomPass, client.password, + ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.roomPass)); for (uint32_t i = 0; i < scoutedItems.size(); i++) { RandomizerCheck rc = Rando::StaticData::locationNameToEnum[scoutedItems[i].locationName]; @@ -568,6 +593,8 @@ void LoadArchipelagoData() { ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.slotName)); SaveManager::Instance->LoadCharArray("archiUri", gSaveContext.ship.quest.data.archipelago.archiUri, ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.archiUri)); + SaveManager::Instance->LoadCharArray("roomPass", gSaveContext.ship.quest.data.archipelago.roomPass, + ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.roomPass)); SaveManager::Instance->LoadArray( "locations", ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.locations), [](size_t i) { @@ -590,6 +617,7 @@ void SaveArchipelagoData(SaveContext* saveContext, int sectionID, bool fullSave) SaveManager::Instance->SaveData("roomHash", saveContext->ship.quest.data.archipelago.roomHash); SaveManager::Instance->SaveData("slotName", saveContext->ship.quest.data.archipelago.slotName); SaveManager::Instance->SaveData("archiUri", saveContext->ship.quest.data.archipelago.archiUri); + SaveManager::Instance->SaveData("roomPass", gSaveContext.ship.quest.data.archipelago.roomPass); SaveManager::Instance->SaveArray( "locations", ARRAY_COUNT(saveContext->ship.quest.data.archipelago.locations), [&](size_t i) { @@ -612,6 +640,8 @@ void InitArchipelagoData(bool isDebug) { ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.slotName)); SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.archiUri, "", ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.archiUri)); + SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.roomHash, "", + ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.roomPass)); for (uint32_t i = 0; i < ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.locations); i++) { SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.locations[i].itemName, "", diff --git a/soh/soh/Network/Archipelago/Archipelago.h b/soh/soh/Network/Archipelago/Archipelago.h index 17adb3747..96cf8a261 100644 --- a/soh/soh/Network/Archipelago/Archipelago.h +++ b/soh/soh/Network/Archipelago/Archipelago.h @@ -70,6 +70,7 @@ class ArchipelagoClient { bool isDeathLinkedDeath; int retries; std::string uri; + std::string password; protected: ArchipelagoClient(); From 70241aba0c92477876fc987d32483e8dcf8e8158 Mon Sep 17 00:00:00 2001 From: Jerom Venneker Date: Wed, 16 Jul 2025 19:04:34 +0200 Subject: [PATCH 6/9] Attempt at buildfix for windows runner --- soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.h | 1 + 1 file changed, 1 insertion(+) diff --git a/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.h b/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.h index 92f975fb2..e0631fcb6 100644 --- a/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.h +++ b/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.h @@ -5,6 +5,7 @@ #include #include #include +#include "window/gui/GuiWindow.h" #include "ArchipelagoTypes.h" class ArchipelagoConsoleWindow final : public Ship::GuiWindow { From 0101a4d8423e95748c52b2b2b4c30a2653568230 Mon Sep 17 00:00:00 2001 From: jerom Date: Thu, 17 Jul 2025 21:21:12 +0200 Subject: [PATCH 7/9] Another attempted buildfix for windows runner --- soh/soh/Network/Archipelago/Archipelago.h | 1 - soh/soh/Network/Archipelago/ArchipelagoSettingsWindow.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Network/Archipelago/Archipelago.h b/soh/soh/Network/Archipelago/Archipelago.h index 96cf8a261..00973536f 100644 --- a/soh/soh/Network/Archipelago/Archipelago.h +++ b/soh/soh/Network/Archipelago/Archipelago.h @@ -5,7 +5,6 @@ #include #include #include -#include "ArchipelagoConsoleWindow.h" #include "ArchipelagoTypes.h" // Forward declaration diff --git a/soh/soh/Network/Archipelago/ArchipelagoSettingsWindow.h b/soh/soh/Network/Archipelago/ArchipelagoSettingsWindow.h index f7c530314..2a393c081 100644 --- a/soh/soh/Network/Archipelago/ArchipelagoSettingsWindow.h +++ b/soh/soh/Network/Archipelago/ArchipelagoSettingsWindow.h @@ -3,6 +3,7 @@ #define ARCHIPELAGO_SETTINGS_WINDOW_H #include +#include "window/gui/GuiWindow.h" class ArchipelagoSettingsWindow final : public Ship::GuiWindow { public: From 4f1ebbc3d56dae5044564bc3ab7de47abbb65d62 Mon Sep 17 00:00:00 2001 From: Jerom Venneker Date: Fri, 18 Jul 2025 13:13:39 +0200 Subject: [PATCH 8/9] Automatically open the connection settings when failing to autoconnect --- soh/soh/Network/Archipelago/Archipelago.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/soh/soh/Network/Archipelago/Archipelago.cpp b/soh/soh/Network/Archipelago/Archipelago.cpp index 10d494726..067973f4f 100644 --- a/soh/soh/Network/Archipelago/Archipelago.cpp +++ b/soh/soh/Network/Archipelago/Archipelago.cpp @@ -18,6 +18,7 @@ #include "soh/ShipInit.hpp" #include "soh/SaveManager.h" #include "soh/util.h" +#include "soh/SohGui/SohGui.hpp" extern "C" { #include "variables.h" @@ -64,6 +65,10 @@ bool ArchipelagoClient::StartClient() { "server address and slot name correct?"); CVarSetInteger(CVAR_REMOTE_ARCHIPELAGO("ConnectionStatus"), 2); // Connection error disconnecting = true; + + if(GameInteractor::IsSaveLoaded) { + SohGui::ShowArchipelagoSettingsMenu(); + } return; } ArchipelagoConsole_SendMessage("[ERROR] Could not connect to server, retrying..."); From 7859b548bed3a64140bf62f229a296dcd36e6c9b Mon Sep 17 00:00:00 2001 From: Jerom Venneker Date: Tue, 22 Jul 2025 21:54:59 +0200 Subject: [PATCH 9/9] Added Archipelago connection status in game, todo refactor and clean up --- .../gArchipelagoOutline.rgba32.png | Bin 0 -> 3795 bytes soh/assets/soh_assets.h | 4 + soh/include/functions.h | 1 + soh/src/code/z_parameter.c | 110 ++++++++++++++++++ 4 files changed, 115 insertions(+) create mode 100644 soh/assets/custom/textures/parameter_static/gArchipelagoOutline.rgba32.png diff --git a/soh/assets/custom/textures/parameter_static/gArchipelagoOutline.rgba32.png b/soh/assets/custom/textures/parameter_static/gArchipelagoOutline.rgba32.png new file mode 100644 index 0000000000000000000000000000000000000000..ff2bddbcc367426bffce03ecb56a33274d7e91f8 GIT binary patch literal 3795 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 JPDHLkV1g?aK8gSU literal 0 HcmV?d00001 diff --git a/soh/assets/soh_assets.h b/soh/assets/soh_assets.h index 26799f8e7..aa50cb4a3 100644 --- a/soh/assets/soh_assets.h +++ b/soh/assets/soh_assets.h @@ -466,6 +466,10 @@ static const ALIGN_ASSET(2) char gShipLogoDL[] = dgShipLogoDL; #define dnintendo_rogo_static_Tex_LUS_000000 "__OTR__textures/nintendo_rogo_static/nintendo_rogo_static_Tex_LUS_000000" static const ALIGN_ASSET(2) char nintendo_rogo_static_Tex_LUS_000000[] = dnintendo_rogo_static_Tex_LUS_000000; +// Archipelago Item Icons +#define dgArchipelagoItemTex "__OTR__textures/parameter_static/gArchipelagoOutline" +static const ALIGN_ASSET(2) char gArchipelagoItemTex[] = dgArchipelagoItemTex; + // Archipelago Item Models #define dgArchipelagoItemDL "__OTR__objects/object_archipelago_item/gArchipelagoItemDL" static const ALIGN_ASSET(2) char gArchipelagoItemDL[] = dgArchipelagoItemDL; diff --git a/soh/include/functions.h b/soh/include/functions.h index 8deb36953..826c336e6 100644 --- a/soh/include/functions.h +++ b/soh/include/functions.h @@ -1069,6 +1069,7 @@ void Interface_LoadItemIcon1(PlayState* play, u16 button); void Interface_LoadItemIcon2(PlayState* play, u16 button); void func_80084BF4(PlayState* play, u16 flag); uint16_t Interface_DrawTextLine(GraphicsContext* gfx, char text[], int16_t x, int16_t y, uint16_t colorR, uint16_t colorG, uint16_t colorB, uint16_t colorA, float textScale, uint8_t textShadow); +uint16_t Interface_DrawTextLine_overlay(GraphicsContext* gfx, char text[], int16_t x, int16_t y, uint16_t colorR, uint16_t colorG, uint16_t colorB, uint16_t colorA, float textScale, uint8_t textShadow); u8 Item_Give(PlayState* play, u8 item); u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry); u8 Item_CheckObtainability(u8 item); diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 0fc923070..3459d7d80 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -12,6 +12,7 @@ #include "soh/Enhancements/custom-message/CustomMessageInterfaceAddon.h" #include "soh/Enhancements/cosmetics/cosmeticsTypes.h" #include "soh/Enhancements/enhancementTypes.h" +#include "soh/Enhancements/FileSelectEnhancements.h" #include "soh/ShipUtils.h" #include @@ -3424,6 +3425,45 @@ void Interface_DrawLineupTick(PlayState* play) { CLOSE_DISPS(play->state.gfxCtx); } +void Interface_DrawArchipelagoStatusString(PlayState* play) { + s16 posX = OTRGetRectDimensionFromLeftEdge(28); + s16 posY = SCREEN_HEIGHT - 15; + + int32_t scale = R_TEXT_CHAR_SCALE * 0.2f; + int32_t sTexSize = (scale / 100.0f) * 64.0f; + int32_t sTexScale = 1024.0f / (scale / 100.0f); + + gDPLoadTextureBlock(play->state.gfxCtx->overlay.p++, gArchipelagoItemTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 64, 64, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 0, 0, G_TX_NOLOD, G_TX_NOLOD); + + gSPWideTextureRectangle(play->state.gfxCtx->overlay.p++, posX << 2, posY << 2, (posX + sTexSize) << 2, + (posY + sTexSize) << 2, G_TX_RENDERTILE, 0, 0, sTexScale, sTexScale); + + //SPWideTextureRectangle(OVERLAY_DISP++, ((rMagicBarX + gSaveContext.magicCapacity) + 8) << 2, magicBarY << 2, + // ((rMagicBarX + gSaveContext.magicCapacity) + 16) << 2, (magicBarY + 16) << 2, + // G_TX_RENDERTILE, 256, 0, 1 << 10, 1 << 10); + posX += 13; + + uint8_t language = (gSaveContext.language == LANGUAGE_JPN) ? LANGUAGE_ENG : gSaveContext.language; + char* statusText = SohFileSelect_GetArchipelagoSettingText(ASM_NOT_CONNECTED, language); + switch(CVarGetInteger(CVAR_REMOTE_ARCHIPELAGO("ConnectionStatus"), 0)) { + case 0: // Not Connected + statusText = SohFileSelect_GetArchipelagoSettingText(ASM_NOT_CONNECTED, language); + break; + case 1: // Connecting + case 2: // Connection error, retrying + case 3: // Connected + statusText = SohFileSelect_GetArchipelagoSettingText(ASM_CONNECTING, language); + break; + case 4: // Connected + Locations Scouted + statusText = SohFileSelect_GetArchipelagoSettingText(ASM_CONNECTED, language); + break; + } + + + Interface_DrawTextLine_overlay(play->state.gfxCtx, statusText, posX, posY, 255, 255, 255, 255, 0.8f, true); +} + void Interface_DrawMagicBar(PlayState* play) { InterfaceContext* interfaceCtx = &play->interfaceCtx; s16 magicDrop = R_MAGIC_BAR_LARGE_Y - R_MAGIC_BAR_SMALL_Y + 2; @@ -5397,6 +5437,10 @@ void Interface_Draw(PlayState* play) { Interface_DrawLineupTick(play); } + if(IS_ARCHIPELAGO) { + Interface_DrawArchipelagoStatusString(play); + } + if (fullUi || gSaveContext.magicState > MAGIC_STATE_IDLE) { Interface_DrawMagicBar(play); } @@ -6936,6 +6980,41 @@ void Interface_DrawTextCharacter(GraphicsContext* gfx, int16_t x, int16_t y, voi CLOSE_DISPS(gfx); } +void Interface_DrawTextCharacter_overlay(GraphicsContext* gfx, int16_t x, int16_t y, void* texture, uint16_t colorR, + uint16_t colorG, uint16_t colorB, uint16_t colorA, float textScale, + uint8_t textShadow) { + + int32_t scale = R_TEXT_CHAR_SCALE * textScale; + int32_t sCharTexSize = (scale / 100.0f) * 16.0f; + int32_t sCharTexScale = 1024.0f / (scale / 100.0f); + + OPEN_DISPS(gfx); + + gDPPipeSync(OVERLAY_DISP++); + + gDPLoadTextureBlock_4b(OVERLAY_DISP++, texture, G_IM_FMT_I, FONT_CHAR_TEX_WIDTH, FONT_CHAR_TEX_HEIGHT, 0, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + if (textShadow) { + // Draw drop shadow + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 0, 0, colorA); + gSPWideTextureRectangle(OVERLAY_DISP++, (x + R_TEXT_DROP_SHADOW_OFFSET) << 2, (y + R_TEXT_DROP_SHADOW_OFFSET) << 2, + (x + R_TEXT_DROP_SHADOW_OFFSET + sCharTexSize) << 2, + (y + R_TEXT_DROP_SHADOW_OFFSET + sCharTexSize) << 2, G_TX_RENDERTILE, 0, 0, sCharTexScale, + sCharTexScale); + } + + gDPPipeSync(OVERLAY_DISP++); + + // Draw normal text + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, colorR, colorG, colorB, colorA); + gSPWideTextureRectangle(OVERLAY_DISP++, x << 2, y << 2, (x + sCharTexSize) << 2, (y + sCharTexSize) << 2, + G_TX_RENDERTILE, 0, 0, sCharTexScale, sCharTexScale); + + CLOSE_DISPS(gfx); +} + uint16_t Interface_DrawTextLine(GraphicsContext* gfx, char text[], int16_t x, int16_t y, uint16_t colorR, uint16_t colorG, uint16_t colorB, uint16_t colorA, float textScale, uint8_t textShadow) { @@ -6966,3 +7045,34 @@ uint16_t Interface_DrawTextLine(GraphicsContext* gfx, char text[], int16_t x, in return kerningOffset; } + +uint16_t Interface_DrawTextLine_overlay(GraphicsContext* gfx, char text[], int16_t x, int16_t y, uint16_t colorR, + uint16_t colorG, uint16_t colorB, uint16_t colorA, float textScale, + uint8_t textShadow) { + + uint16_t textureIndex; + uint16_t kerningOffset = 0; + uint16_t lineOffset = 0; + void* texture; + const char* processedText = Interface_ReplaceSpecialCharacters(text); + uint8_t textLength = strlen(processedText); + + for (uint16_t i = 0; i < textLength; i++) { + if (processedText[i] == '\n') { + lineOffset += 15 * textScale; + kerningOffset = 0; + } else { + textureIndex = processedText[i] - 32; + + if (textureIndex != 0) { + texture = Ship_GetCharFontTexture(processedText[i]); + Interface_DrawTextCharacter_overlay(gfx, (int16_t)(x + kerningOffset), (int16_t)(y + lineOffset), texture, colorR, colorG, colorB, + colorA, textScale, textShadow); + } + kerningOffset += + (uint16_t)(Ship_GetCharFontWidth(processedText[i]) * (R_TEXT_CHAR_SCALE / 100.0f) * textScale); + } + } + + return kerningOffset; +}