diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index a318deeea6..73e447999f 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -71,4 +71,5 @@ DEFINE_HOOK(OnAssetAltChange, ()); DEFINE_HOOK(OnKaleidoUpdate, ()); DEFINE_HOOK(OnRandomizerItemGivenHooks, (uint32_t rc, GetItemEntry gi, uint8_t isGiSkipped)); -DEFINE_HOOK(OnArchipelagoItemRecieved, (uint32_t rc)); +DEFINE_HOOK(OnArchipelagoItemRecieved, (uint32_t rg)); +DEFINE_HOOK(OnRandomizerExternalCheck, (uint32_t rc)); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index f34e004211..238cff3b04 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -309,6 +309,10 @@ void GameInteractor_ExecuteOnRandomizerItemGivenHooks(uint32_t rc, GetItemEntry } // MARK: Archipelago -void GameInteractor_ExecuteOnArchipelagoItemRecieved(uint32_t rc) { - GameInteractor::Instance->ExecuteHooks(rc); +void GameInteractor_ExecuteOnArchipelagoItemRecieved(uint32_t rg) { + GameInteractor::Instance->ExecuteHooks(rg); +} + +void GameInteractor_ExecuteOnRandomizerExternalCheck(uint32_t rc) { + GameInteractor::Instance->ExecuteHooks(rc); } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index bb55cb6d81..17de14e5f9 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -85,7 +85,8 @@ void GameInteractor_ExecuteOnKaleidoUpdate(); void GameInteractor_ExecuteOnRandomizerItemGivenHooks(uint32_t rc, GetItemEntry gi, uint8_t isGiSkipped); // Mark: - Archipelago -void GameInteractor_ExecuteOnArchipelagoItemRecieved(uint32_t rc); +void GameInteractor_ExecuteOnArchipelagoItemRecieved(uint32_t rg); +void GameInteractor_ExecuteOnRandomizerExternalCheck(uint32_t rc); #ifdef __cplusplus } diff --git a/soh/soh/Enhancements/randomizer/context.cpp b/soh/soh/Enhancements/randomizer/context.cpp index 67502ff5ed..c9cc8e746a 100644 --- a/soh/soh/Enhancements/randomizer/context.cpp +++ b/soh/soh/Enhancements/randomizer/context.cpp @@ -349,11 +349,11 @@ void Context::SetSpoilerLoaded(const bool spoilerLoaded) { void Context::AddRecievedArchipelagoItem(const RandomizerGet item) { mAPrecieveQueue.emplace(item); std::string logMessage = "[LOG] Item Pushed: " + item; - ArchipelagoConsole_SendMessage(logMessage.c_str()); + ArchipelagoConsole_SendMessage(logMessage.c_str(), true); } GetItemEntry Context::GetArchipelagoGIEntry() { - ArchipelagoConsole_SendMessage("[LOG] Trying to get Item Entry"); + ArchipelagoConsole_SendMessage("[LOG] Trying to get Item Entry", true); if(mAPrecieveQueue.empty()) { // something must have gone wrong here, just give a rupee return ItemTableManager::Instance->RetrieveItemEntry(MOD_NONE, GI_HEART); diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index e678e0dae8..601b2be0db 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -286,6 +286,10 @@ void RandomizerOnSceneFlagSetHandler(int16_t sceneNum, int16_t flagType, int16_t randomizerQueuedChecks.push(rc); } +void RandomizerOnExternalCheckHandler(uint32_t randomizerCheck) { + randomizerQueuedChecks.push(static_cast(randomizerCheck)); +} + static Vec3f spawnPos = { 0.0f, -999.0f, 0.0f }; void RandomizerOnPlayerUpdateForRCQueueHandler() { @@ -2536,9 +2540,8 @@ void RandomizerRegisterHooks() { onCuccoOrChickenHatchHook = GameInteractor::Instance->RegisterGameHook( RandomizerOnCuccoOrChickenHatch); - // TODO Implement propeerly when we can read what kind of game we're playing from the save file - COND_HOOK(GameInteractor::OnArchipelagoItemRecieved, IS_ARCHIPELAGO, ArchipelagoOnRecieveItem); + COND_HOOK(GameInteractor::OnRandomizerExternalCheck, IS_ARCHIPELAGO, RandomizerOnExternalCheckHandler) if (RAND_GET_OPTION(RSK_FISHSANITY) != RO_FISHSANITY_OFF) { OTRGlobals::Instance->gRandoContext->GetFishsanity()->InitializeFromSave(); diff --git a/soh/soh/Network/Archipelago/Archipelago.cpp b/soh/soh/Network/Archipelago/Archipelago.cpp index f079ed5d02..775944fe33 100644 --- a/soh/soh/Network/Archipelago/Archipelago.cpp +++ b/soh/soh/Network/Archipelago/Archipelago.cpp @@ -25,17 +25,14 @@ extern PlayState* gPlayState; } ArchipelagoClient::ArchipelagoClient() { - std::string uuid = ap_get_uuid("uuid"); + uuid = ap_get_uuid("uuid"); gameWon = false; - - namespace apc = AP_Client_consts; - CVarSetInteger(CVAR_REMOTE_ARCHIPELAGO("Connected"), 0); + itemQueued = false; // call poll every frame COND_HOOK(GameInteractor::OnGameFrameUpdate, true, [](){ArchipelagoClient::GetInstance().Poll();}); COND_HOOK(GameInteractor::OnLoadGame, true, [](int32_t file_id){ArchipelagoClient::GetInstance().GameLoaded();}); - } ArchipelagoClient& ArchipelagoClient::GetInstance() { @@ -61,7 +58,7 @@ bool ArchipelagoClient::StartClient() { }); apClient->set_slot_connected_handler([&](const nlohmann::json data) { - ArchipelagoConsole_SendMessage("[LOG] Connected.", false); + ArchipelagoConsole_SendMessage("[LOG] Connected.", true); ArchipelagoClient::StartLocationScouts(); slotData = data; @@ -76,7 +73,14 @@ bool ArchipelagoClient::StartClient() { apClient->set_items_received_handler([&](const std::list& items) { for(const APClient::NetworkItem& item : items) { - OnItemReceived(item.item, item.index); + ApItem apItem; + const std::string game = apClient->get_player_game(item.player); + apItem.itemName = apClient->get_item_name(item.item, game); + apItem.locationName = apClient->get_location_name(item.location, game); + apItem.playerName = apClient->get_player_alias(item.player); + apItem.flags = item.flags; + apItem.index = item.index; + OnItemReceived(apItem); } }); @@ -97,14 +101,21 @@ bool ArchipelagoClient::StartClient() { const std::string playerName = apItem.playerName; const std::string locationName = apItem.locationName; std::string logMessage = "[LOG] Location scouted: " + itemName + " for " + playerName + " in location " + locationName; - ArchipelagoConsole_SendMessage(logMessage.c_str()); + ArchipelagoConsole_SendMessage(logMessage.c_str(), true); } - ArchipelagoConsole_SendMessage("[LOG] Scouting finished."); + ArchipelagoConsole_SendMessage("[LOG] Scouting finished.", true); }); // todo maybe move these functions to a lambda, since they don't have to be static anymore apClient->set_location_checked_handler([&](const std::list locations) { - // todo implement me + for(const int64_t apLoc : locations) { + QueueExternalCheck(apLoc); + } + }); + + apClient->set_print_json_handler([&](const std::list& nodes) { + std::string text = apClient->render_json(nodes, APClient::RenderFormat::TEXT); + ArchipelagoConsole_SendMessage(text.c_str(), false); }); return true; @@ -121,7 +132,7 @@ void ArchipelagoClient::GameLoaded() { return; } - ArchipelagoConsole_SendMessage("[LOG] Synching Items and Locations."); + ArchipelagoConsole_SendMessage("[LOG] Synching Items and Locations.", true); SynchItems(); SynchSentLocations(); @@ -143,7 +154,7 @@ void ArchipelagoClient::StartLocationScouts() { void ArchipelagoClient::SynchItems() { // Send a Synch request to get any items we may have missed - ArchipelagoConsole_SendMessage("[LOG] Sending synch request"); + ArchipelagoConsole_SendMessage("[LOG] Sending synch request", true); apClient->Sync(); } @@ -158,7 +169,7 @@ void ArchipelagoClient::SynchSentLocations() { } } std::string locationLog = "[LOG] Synching " + std::to_string(checkedLocations.size())+ " checks already found in game"; - ArchipelagoConsole_SendMessage(locationLog.c_str()); + ArchipelagoConsole_SendMessage(locationLog.c_str(), true); apClient->LocationChecks(checkedLocations); } @@ -166,10 +177,16 @@ void ArchipelagoClient::SynchSentLocations() { 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. + QueueExternalCheck(apLoc); } } +void ArchipelagoClient::QueueExternalCheck(const int64_t apLocation) { + const std::string checkName = apClient->get_location_name(apLocation, AP_Client_consts::AP_GAME_NAME); + const uint32_t RC = static_cast(Rando::StaticData::locationNameToEnum[checkName]); + GameInteractor_ExecuteOnRandomizerExternalCheck(RC); +} + bool ArchipelagoClient::IsConnected() { return apClient->get_state() == APClient::State::SLOT_CONNECTED; } @@ -182,7 +199,7 @@ void ArchipelagoClient::CheckLocation(RandomizerCheck sohCheckId) { int64_t apItemId = apClient->get_location_id(std::string(apName)); std::string logMessage = "[LOG] Checked: " + apName + "(" + std::to_string(apItemId) + "), sending to AP server"; - ArchipelagoConsole_SendMessage(logMessage.c_str()); + ArchipelagoConsole_SendMessage(logMessage.c_str(), true); if(!IsConnected()) { return; @@ -190,23 +207,37 @@ void ArchipelagoClient::CheckLocation(RandomizerCheck sohCheckId) { apClient->LocationChecks({ apItemId }); } -void ArchipelagoClient::OnItemReceived(int64_t apItemId, int64_t itemIndex) { +void ArchipelagoClient::OnItemReceived(const ApItem apItem) { 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; } - if(itemIndex < gSaveContext.ship.quest.data.archipelago.lastReceivedItemIndex) { - // Skip recieving any items we already have + std::string logMessage = "[Log] Recieved " + apItem.itemName; + ArchipelagoConsole_SendMessage(logMessage.c_str(), true); + + // add item to the queue + recieveQueue.push(apItem); +} + +void ArchipelagoClient::QueueItem(const ApItem item) { + if(item.index < gSaveContext.ship.quest.data.archipelago.lastReceivedItemIndex) { + // Skip queueing any items we already have + std::string logMessage = "[Log] Skipping giving " + item.itemName + ". We recieved this previously."; + ArchipelagoConsole_SendMessage(logMessage.c_str(), true); return; } - 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)); + std::string logMessage = "[Log] Giving " + item.itemName; + ArchipelagoConsole_SendMessage(logMessage.c_str(), true); + const RandomizerGet RG = Rando::StaticData::itemNameToEnum[item.itemName]; + if(RG == RG_NONE) { + return; + } + + itemQueued = true; + GameInteractor_ExecuteOnArchipelagoItemRecieved(static_cast(RG)); } void ArchipelagoClient::SendGameWon() { @@ -220,6 +251,14 @@ void ArchipelagoClient::Poll() { if(apClient == nullptr) { return; } + + // queue another item to be recieved + if(!itemQueued && recieveQueue.size() > 0) { + + const ApItem item = recieveQueue.front(); + recieveQueue.pop(); + QueueItem(item); + } apClient->poll(); } @@ -352,10 +391,13 @@ void InitArchipelagoData(bool isDebug) { } void RegisterArchipelago() { + CVarSetInteger(CVAR_REMOTE_ARCHIPELAGO("Connected"), 0); + COND_HOOK(GameInteractor::OnRandomizerItemGivenHooks, IS_ARCHIPELAGO, [](uint32_t rc, GetItemEntry gi, uint8_t isGiSkipped) { if (rc == RC_ARCHIPELAGO_RECIEVED_ITEM) { gSaveContext.ship.quest.data.archipelago.lastReceivedItemIndex++; + ArchipelagoClient::GetInstance().itemQueued = false; } else { ArchipelagoClient::GetInstance().CheckLocation((RandomizerCheck)rc); diff --git a/soh/soh/Network/Archipelago/Archipelago.h b/soh/soh/Network/Archipelago/Archipelago.h index 8875aa838b..314c0a34a1 100644 --- a/soh/soh/Network/Archipelago/Archipelago.h +++ b/soh/soh/Network/Archipelago/Archipelago.h @@ -4,6 +4,7 @@ #include "soh/Enhancements/randomizer/static_data.h" #include #include +#include // Forward declaration class APClient; @@ -23,7 +24,7 @@ class ArchipelagoClient{ std::string locationName; std::string playerName; unsigned int flags; - int index; + uint64_t index; }; static ArchipelagoClient& GetInstance(); @@ -47,14 +48,16 @@ class ArchipelagoClient{ bool IsConnected(); void CheckLocation(RandomizerCheck SoH_check_id); - // todo move me back down when done testing - void OnItemReceived(int64_t apItemId, int64_t itemIndex); + void OnItemReceived(const ApItem apItem); + void QueueItem(const ApItem item); + void QueueExternalCheck(int64_t apLocation); void SendGameWon(); void Poll(); std::unique_ptr apClient; + bool itemQueued; protected: ArchipelagoClient(); @@ -72,6 +75,7 @@ class ArchipelagoClient{ nlohmann::json slotData; std::set locations; std::vector scoutedItems; + std::queue recieveQueue; }; void LoadArchipelagoData(); diff --git a/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.cpp b/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.cpp index 75e359839f..af5bf1ce82 100644 --- a/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.cpp +++ b/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.cpp @@ -10,15 +10,16 @@ bool autoScroll = true; using namespace UIWidgets; void ArchipelagoConsole_SendMessage(const char* fmt, bool debugMessage, ...) { - if (!debugMessage || CVarGetInteger(CVAR_REMOTE_ARCHIPELAGO("DebugEnabled"), 0)) { - char buf[1024]; - va_list args; - va_start(args, fmt); - vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args); - buf[IM_ARRAYSIZE(buf) - 1] = 0; - va_end(args); - Items.push_back(strdup(buf)); + if (debugMessage && CVarGetInteger(CVAR_REMOTE_ARCHIPELAGO("DebugEnabled"), 0) == 0) { + return; } + char buf[1024]; + va_list args; + va_start(args, fmt); + vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args); + buf[IM_ARRAYSIZE(buf) - 1] = 0; + va_end(args); + Items.push_back(strdup(buf)); } void ArchipelagoConsoleWindow::DrawElement() { diff --git a/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.h b/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.h index 9856969df9..46cc3332e2 100644 --- a/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.h +++ b/soh/soh/Network/Archipelago/ArchipelagoConsoleWindow.h @@ -15,6 +15,6 @@ class ArchipelagoConsoleWindow final : public Ship::GuiWindow { void UpdateElement() override {}; }; -void ArchipelagoConsole_SendMessage(const char* fmt, bool debugMessage = true, ...); +void ArchipelagoConsole_SendMessage(const char* fmt, bool debugMessage = false, ...); #endif // ARCHIPELAGO_CONSOLE_WINDOW_H \ No newline at end of file diff --git a/soh/soh/Network/Archipelago/ArchipelagoSettingsWindow.cpp b/soh/soh/Network/Archipelago/ArchipelagoSettingsWindow.cpp index e41b0bf78f..162045a980 100644 --- a/soh/soh/Network/Archipelago/ArchipelagoSettingsWindow.cpp +++ b/soh/soh/Network/Archipelago/ArchipelagoSettingsWindow.cpp @@ -39,7 +39,7 @@ void ArchipelagoSettingsWindow::DrawElement() { if (UIWidgets::Button("Connect", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.0, 0.0)))) { bool success = AP_client.StartClient(); - ArchipelagoConsole_SendMessage("[LOG] Trying to connect...", false); + ArchipelagoConsole_SendMessage("[LOG] Trying to connect...", true); } ImGui::SameLine(); @@ -63,7 +63,13 @@ void ArchipelagoSettingsWindow::DrawElement() { ImGui::SameLine(); if (UIWidgets::Button("Give Blue Rupee", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(ImVec2(0.0, 0.0)))) { - ArchipelagoClient::GetInstance().OnItemReceived(16711816, true); + ArchipelagoClient::ApItem apItem; + apItem.itemName = "Blue Rupee"; + apItem.locationName = "Nowhere"; + apItem.playerName = "Nobody"; + apItem.flags = 0b001; + apItem.index = 999999; + ArchipelagoClient::GetInstance().OnItemReceived(apItem); } }