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 0aa4b9452..7a5b4bd47 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() { @@ -59,11 +62,18 @@ 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) { 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 +107,24 @@ bool ArchipelagoClient::StartClient() { return true; } +void ArchipelagoClient::GameLoaded() { + 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() { std::set missing_loc_set = apClient->get_missing_locations(); std::set found_loc_set = apClient->get_checked_locations(); @@ -110,6 +138,35 @@ void ArchipelagoClient::StartLocationScouts() { apClient->LocationScouts(location_list); } +void ArchipelagoClient::SynchItems() { + // 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()) { + 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); +} + +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. + } +} + bool ArchipelagoClient::IsConnected() { return apClient->get_state() == APClient::State::SLOT_CONNECTED; } @@ -130,16 +187,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 +221,7 @@ void ArchipelagoClient::Poll() { apClient->poll(); } -const std::string& ArchipelagoClient::GetSlotName() const { +const std::string ArchipelagoClient::GetSlotName() const { if(apClient == NULL) { return ""; } @@ -302,15 +364,11 @@ 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) { 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..6967d7e14 100644 --- a/soh/soh/Network/Archipelago/Archipelago.h +++ b/soh/soh/Network/Archipelago/Archipelago.h @@ -30,10 +30,14 @@ class ArchipelagoClient{ bool StartClient(); bool StopClient(); + void GameLoaded(); void StartLocationScouts(); + void SynchItems(); + void SynchSentLocations(); + void SynchRecievedLocations(); // getters - const std::string& GetSlotName() const; + const std::string GetSlotName() const; const char* GetConnectionStatus(); const std::map& GetSlotData(); @@ -42,12 +46,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 +71,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();