From b2c2be034ed2f82b5dc2612f0e94b299c11886cf Mon Sep 17 00:00:00 2001 From: Jerom Venneker Date: Fri, 23 May 2025 18:04:08 +0200 Subject: [PATCH 1/3] Added Item Synch on game load --- soh/soh/Network/Archipelago/Archipelago.cpp | 69 +++++++++++++++++---- soh/soh/Network/Archipelago/Archipelago.h | 18 ++---- 2 files changed, 60 insertions(+), 27 deletions(-) diff --git a/soh/soh/Network/Archipelago/Archipelago.cpp b/soh/soh/Network/Archipelago/Archipelago.cpp index 0aa4b9452..8294e5542 100644 --- a/soh/soh/Network/Archipelago/Archipelago.cpp +++ b/soh/soh/Network/Archipelago/Archipelago.cpp @@ -6,10 +6,12 @@ #include #include #include +#include #include "soh/Network/Archipelago/ArchipelagoConsoleWindow.h" #include "soh/Enhancements/randomizer/randomizerTypes.h" #include "soh/Enhancements/randomizer/static_data.h" +#include "soh/Enhancements/randomizer/context.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/ShipInit.hpp" @@ -24,14 +26,15 @@ extern PlayState* gPlayState; ArchipelagoClient::ArchipelagoClient() { std::string uuid = ap_get_uuid("uuid"); - ItemRecievedCallback = nullptr; gameWon = false; namespace apc = AP_Client_consts; CVarSetInteger(CVAR_REMOTE_ARCHIPELAGO("Connected"), 0); // call poll every frame - COND_HOOK(GameInteractor::OnGameFrameUpdate, true, [](){ArchipelagoClient::GetInstance().Poll();}) + COND_HOOK(GameInteractor::OnGameFrameUpdate, true, [](){ArchipelagoClient::GetInstance().Poll();}); + COND_HOOK(GameInteractor::OnLoadGame, true, [](int32_t file_id){ArchipelagoClient::GetInstance().GameLoaded();}); + } ArchipelagoClient& ArchipelagoClient::GetInstance() { @@ -63,7 +66,7 @@ bool ArchipelagoClient::StartClient() { apClient->set_items_received_handler([&](const std::list& items) { for(const APClient::NetworkItem& item : items) { - OnItemReceived(item.item, false); // todo get rid of notify, since it doesn't work for us right now anyway + OnItemReceived(item.item, item.index); } }); @@ -97,6 +100,15 @@ bool ArchipelagoClient::StartClient() { return true; } +void ArchipelagoClient::GameLoaded() { + if(!IS_ARCHIPELAGO) { + // apClient->reset(); + // return; + } + + SynchItems(); +} + void ArchipelagoClient::StartLocationScouts() { std::set missing_loc_set = apClient->get_missing_locations(); std::set found_loc_set = apClient->get_checked_locations(); @@ -110,6 +122,32 @@ void ArchipelagoClient::StartLocationScouts() { apClient->LocationScouts(location_list); } +void ArchipelagoClient::SynchItems() { + ArchipelagoConsole_SendMessage("[LOG] Synching Items and Locations."); + + // send already checked locations + std::list checkedLocations; + for(const auto& loc : Rando::StaticData::GetLocationTable()) { + const RandomizerCheck rc = loc.GetRandomizerCheck(); + if(Rando::Context::GetInstance()->GetItemLocation(rc)->HasObtained()) { + const int64_t apLocation = apClient->get_location_id(loc.GetName()); + checkedLocations.emplace_back(apLocation); + } + } + std::string locationLog = "[LOG] Synching " + std::to_string(checkedLocations.size())+ " checks already found in game"; + ArchipelagoConsole_SendMessage(locationLog.c_str()); + + apClient->LocationChecks(checkedLocations); + // Open checks that have been found previously but went unsaved + for(const int64_t apLoc : apClient->get_checked_locations()) { + // TODO call location checked function to open any unopened checks. + } + + // Send a Synch request to get any items we may have missed + ArchipelagoConsole_SendMessage("[LOG] Sending synch request"); + apClient->Sync(); +} + bool ArchipelagoClient::IsConnected() { return apClient->get_state() == APClient::State::SLOT_CONNECTED; } @@ -130,16 +168,21 @@ void ArchipelagoClient::CheckLocation(RandomizerCheck sohCheckId) { apClient->LocationChecks({ apItemId }); } -void ArchipelagoClient::AddItemRecievedCallback(std::function callback) { - ItemRecievedCallback = callback; -} +void ArchipelagoClient::OnItemReceived(int64_t apItemId, int64_t itemIndex) { + if(!GameInteractor::IsSaveLoaded(true)) { + // Don't queue up any items when we aren't in game + // Any Items missed this way will get synched when we load the save + return; + } -void ArchipelagoClient::RemoveItemRecievedCallback(std::function old_callback) { - ItemRecievedCallback = nullptr; -} + if(itemIndex < gSaveContext.ship.quest.data.archipelago.lastReceivedItemIndex) { + // Skip recieving any items we already have + return; + } -void ArchipelagoClient::OnItemReceived(int64_t recieved_item_id, bool notify_player) { - const std::string item_name = apClient->get_item_name(recieved_item_id, AP_Client_consts::AP_GAME_NAME); + const std::string item_name = apClient->get_item_name(apItemId, AP_Client_consts::AP_GAME_NAME); + std::string logMessage = "[Log] Recieved " + item_name; + ArchipelagoConsole_SendMessage(logMessage.c_str()); const RandomizerGet item = Rando::StaticData::itemNameToEnum[item_name]; GameInteractor_ExecuteOnArchipelagoItemRecieved(static_cast(item)); } @@ -159,7 +202,7 @@ void ArchipelagoClient::Poll() { apClient->poll(); } -const std::string& ArchipelagoClient::GetSlotName() const { +const std::string ArchipelagoClient::GetSlotName() const { if(apClient == NULL) { return ""; } @@ -310,7 +353,7 @@ void RegisterArchipelago() { COND_HOOK(GameInteractor::OnRandomizerItemGivenHooks, IS_ARCHIPELAGO, [](uint32_t rc) { if (rc == RC_ARCHIPELAGO_RECIEVED_ITEM) { - // Update lastReceivedIndex + gSaveContext.ship.quest.data.archipelago.lastReceivedItemIndex++; } else { ArchipelagoClient::GetInstance().CheckLocation((RandomizerCheck)rc); } diff --git a/soh/soh/Network/Archipelago/Archipelago.h b/soh/soh/Network/Archipelago/Archipelago.h index 35acb6b5a..cb05fe849 100644 --- a/soh/soh/Network/Archipelago/Archipelago.h +++ b/soh/soh/Network/Archipelago/Archipelago.h @@ -30,10 +30,12 @@ class ArchipelagoClient{ bool StartClient(); bool StopClient(); + void GameLoaded(); void StartLocationScouts(); + void SynchItems(); // getters - const std::string& GetSlotName() const; + const std::string GetSlotName() const; const char* GetConnectionStatus(); const std::map& GetSlotData(); @@ -42,12 +44,8 @@ class ArchipelagoClient{ bool IsConnected(); void CheckLocation(RandomizerCheck SoH_check_id); - // callback slots - void AddItemRecievedCallback(std::function callback); - void RemoveItemRecievedCallback(std::function old_callback); - // todo move me back down when done testing - void OnItemReceived(int64_t recieved_item_id, bool notify_player); + void OnItemReceived(int64_t apItemId, int64_t itemIndex); void SendGameWon(); @@ -71,14 +69,6 @@ class ArchipelagoClient{ std::map slotData; std::set locations; std::vector scoutedItems; - - // Callback Functions - void OnLocationChecked(int64_t location_id); - void OnDeathLinkReceived() { }; // TODO: implement me - - // Callbacks - std::function ItemRecievedCallback; - }; void LoadArchipelagoData(); From a9e99e400cb1d4a4f862069270aeecf70074f7f4 Mon Sep 17 00:00:00 2001 From: Jerom Venneker Date: Fri, 23 May 2025 18:22:50 +0200 Subject: [PATCH 2/3] Removed IS_ARCHIPELAGO defines --- soh/include/z64save.h | 2 +- soh/soh/Enhancements/randomizer/hook_handlers.cpp | 1 - soh/soh/Network/Archipelago/Archipelago.cpp | 11 +++++------ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/soh/include/z64save.h b/soh/include/z64save.h index 84f50ded2..ffd413455 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -339,7 +339,7 @@ typedef enum { #define IS_MASTER_QUEST (gSaveContext.ship.quest.id == QUEST_MASTER) #define IS_RANDO (gSaveContext.ship.quest.id == QUEST_RANDOMIZER) #define IS_BOSS_RUSH (gSaveContext.ship.quest.id == QUEST_BOSSRUSH) -#define IS_ARCHIPELAGO (gSaveContext.ship.quest.id == QUEST_ARCHIPELAGO) +#define IS_ARCHIPELAGO (gSaveContext.ship.quest.data.archipelago.isArchipelago == 1) typedef enum { /* 0x00 */ BTN_ENABLED, diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index 25f0d6a46..a9b209b7b 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -2527,7 +2527,6 @@ void RandomizerRegisterHooks() { // TODO Implement propeerly when we can read what kind of game we're playing from the save file - #define IS_ARCHIPELAGO true COND_HOOK(GameInteractor::OnArchipelagoItemRecieved, IS_ARCHIPELAGO, ArchipelagoOnRecieveItem); if (RAND_GET_OPTION(RSK_FISHSANITY) != RO_FISHSANITY_OFF) { diff --git a/soh/soh/Network/Archipelago/Archipelago.cpp b/soh/soh/Network/Archipelago/Archipelago.cpp index 8294e5542..fc8252e59 100644 --- a/soh/soh/Network/Archipelago/Archipelago.cpp +++ b/soh/soh/Network/Archipelago/Archipelago.cpp @@ -101,9 +101,12 @@ bool ArchipelagoClient::StartClient() { } void ArchipelagoClient::GameLoaded() { + // if its not an AP save, disconnect if(!IS_ARCHIPELAGO) { - // apClient->reset(); - // return; + if(apClient != nullptr) { + apClient->reset(); + } + return; } SynchItems(); @@ -345,10 +348,6 @@ void InitArchipelagoData(bool isDebug) { } } -// Implement this properly once we have some kind of indication within a save file wether the player is in a normal -// rando save or an archipelago one. -#define IS_ARCHIPELAGO true - void RegisterArchipelago() { COND_HOOK(GameInteractor::OnRandomizerItemGivenHooks, IS_ARCHIPELAGO, [](uint32_t rc) { From ea95ca0a9a0747ba83efcfa53af08c2a17ea6933 Mon Sep 17 00:00:00 2001 From: Jerom Venneker Date: Fri, 23 May 2025 19:28:42 +0200 Subject: [PATCH 3/3] Enabled Connecting and check synching when already in game --- soh/soh/Network/Archipelago/Archipelago.cpp | 36 +++++++++++++++------ soh/soh/Network/Archipelago/Archipelago.h | 2 ++ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/soh/soh/Network/Archipelago/Archipelago.cpp b/soh/soh/Network/Archipelago/Archipelago.cpp index fc8252e59..7a5b4bd47 100644 --- a/soh/soh/Network/Archipelago/Archipelago.cpp +++ b/soh/soh/Network/Archipelago/Archipelago.cpp @@ -62,6 +62,13 @@ bool ArchipelagoClient::StartClient() { apClient->set_slot_connected_handler([&](const nlohmann::json) { ArchipelagoConsole_SendMessage("[LOG] Connected.", false); ArchipelagoClient::StartLocationScouts(); + + // if we are already in game when we connect + // we won't have to request an itemSynch + if(GameInteractor::IsSaveLoaded(true)) { + SynchSentLocations(); + SynchRecievedLocations(); + } }); apClient->set_items_received_handler([&](const std::list& items) { @@ -101,15 +108,21 @@ bool ArchipelagoClient::StartClient() { } void ArchipelagoClient::GameLoaded() { - // if its not an AP save, disconnect - if(!IS_ARCHIPELAGO) { - if(apClient != nullptr) { - apClient->reset(); - } + if(apClient == nullptr) { return; } + // if its not an AP save, disconnect + if(!IS_ARCHIPELAGO) { + apClient->reset(); + return; + } + + ArchipelagoConsole_SendMessage("[LOG] Synching Items and Locations."); + SynchItems(); + SynchSentLocations(); + SynchRecievedLocations(); } void ArchipelagoClient::StartLocationScouts() { @@ -126,8 +139,12 @@ void ArchipelagoClient::StartLocationScouts() { } void ArchipelagoClient::SynchItems() { - ArchipelagoConsole_SendMessage("[LOG] Synching Items and Locations."); + // Send a Synch request to get any items we may have missed + ArchipelagoConsole_SendMessage("[LOG] Sending synch request"); + apClient->Sync(); +} +void ArchipelagoClient::SynchSentLocations() { // send already checked locations std::list checkedLocations; for(const auto& loc : Rando::StaticData::GetLocationTable()) { @@ -141,14 +158,13 @@ void ArchipelagoClient::SynchItems() { ArchipelagoConsole_SendMessage(locationLog.c_str()); apClient->LocationChecks(checkedLocations); +} + +void ArchipelagoClient::SynchRecievedLocations() { // Open checks that have been found previously but went unsaved for(const int64_t apLoc : apClient->get_checked_locations()) { // TODO call location checked function to open any unopened checks. } - - // Send a Synch request to get any items we may have missed - ArchipelagoConsole_SendMessage("[LOG] Sending synch request"); - apClient->Sync(); } bool ArchipelagoClient::IsConnected() { diff --git a/soh/soh/Network/Archipelago/Archipelago.h b/soh/soh/Network/Archipelago/Archipelago.h index cb05fe849..6967d7e14 100644 --- a/soh/soh/Network/Archipelago/Archipelago.h +++ b/soh/soh/Network/Archipelago/Archipelago.h @@ -33,6 +33,8 @@ class ArchipelagoClient{ void GameLoaded(); void StartLocationScouts(); void SynchItems(); + void SynchSentLocations(); + void SynchRecievedLocations(); // getters const std::string GetSlotName() const;