From ea2e718c06fd5e7e72cd957d396ffd9e8b301d5e Mon Sep 17 00:00:00 2001 From: Jerom Venneker Date: Mon, 10 Feb 2025 20:58:38 +0100 Subject: [PATCH] Added some basic Item recieving from server, not thoroughly tested yet --- APCpp | 2 +- .../Enhancements/randomizer/archipelago.cpp | 61 ++++++++++++++++--- soh/soh/Enhancements/randomizer/archipelago.h | 21 ++++++- soh/soh/Enhancements/randomizer/context.cpp | 29 ++++++++- soh/soh/Enhancements/randomizer/context.h | 3 + .../Enhancements/randomizer/hook_handlers.cpp | 33 +++++++++- soh/soh/Enhancements/randomizer/item_list.cpp | 2 +- .../Enhancements/randomizer/item_location.cpp | 2 + .../Enhancements/randomizer/randomizer.cpp | 8 ++- .../Enhancements/randomizer/randomizerTypes.h | 4 +- 10 files changed, 145 insertions(+), 20 deletions(-) diff --git a/APCpp b/APCpp index c671d9355..38d5cf079 160000 --- a/APCpp +++ b/APCpp @@ -1 +1 @@ -Subproject commit c671d935530a544e83e65836e79499f54b734b95 +Subproject commit 38d5cf0798fb4134b0414ad680a0c2798d0bc311 diff --git a/soh/soh/Enhancements/randomizer/archipelago.cpp b/soh/soh/Enhancements/randomizer/archipelago.cpp index 07d763fc4..bcbc615e6 100644 --- a/soh/soh/Enhancements/randomizer/archipelago.cpp +++ b/soh/soh/Enhancements/randomizer/archipelago.cpp @@ -8,6 +8,8 @@ #include #include "fixed_string.hpp" +#include "randomizerTypes.h" +#include "static_data.h" //extern "C" { // #include "include/z64item.h" @@ -33,6 +35,8 @@ auto SubscribeToSlotData() { } ArchipelagoClient::ArchipelagoClient() { + ItemRecievedCallback = nullptr; + namespace apc = AP_Client_consts; CVarSetInteger("archipelago_connected", 0); strncpy(server_address, CVarGetString(apc::SETTING_ADDRESS, apc::DEFAULT_SERVER_NAME), apc::MAX_ADDRESS_LENGTH); @@ -183,6 +187,30 @@ void ArchipelagoClient::save_data() { CVarSetString(AP_Client_consts::SETTING_NAME, slot_name); } +bool ArchipelagoClient::isConnected() { + return AP_GetConnectionStatus() == AP_ConnectionStatus::Authenticated; +} + +void ArchipelagoClient::check_location(RandomizerCheck SoH_check_id) { + std::string_view ap_name = Rando::StaticData::SohCheckToAP[SoH_check_id]; + int64_t ap_item_id = CheckNameToId(std::string(ap_name)); + SPDLOG_TRACE("Checked: {}({}), sending to AP server", ap_name, ap_item_id); + +// currently not sending, because i only get so many real chances + if(!isConnected()) { + return; + } + AP_SendItem(ap_item_id); +} + +void ArchipelagoClient::addItemRecievedCallback(std::function callback) { + ItemRecievedCallback = callback; +} + +void ArchipelagoClient::removeItemRecievedCallback(std::function old_callback) { + ItemRecievedCallback = nullptr; +} + void ArchipelagoClient::on_connected() { // todo implement me SPDLOG_TRACE("AP Connected!!"); @@ -197,7 +225,14 @@ void ArchipelagoClient::on_clear_items() { } void ArchipelagoClient::on_item_recieved(int64_t recieved_item_id, bool notify_player) { - // todo implement me + // call each callback + SPDLOG_TRACE("Trying to give rupie..."); + std::string item_name = getAPitemName(recieved_item_id); + ArchipelagoClient& ap_client = ArchipelagoClient::getInstance(); + if(ap_client.ItemRecievedCallback) { + SPDLOG_TRACE("Giving Rupie! {}", item_name); + ap_client.ItemRecievedCallback.operator()(item_name); // somehow passing it through the itemname breaks it???? + } } void ArchipelagoClient::on_location_checked(int64_t location_id) { @@ -237,21 +272,23 @@ void ArchipelagoWindow::ArchipelagoDrawConnectPage() { ImGui::InputText("Slot Name", AP_client.get_slot_name_buff(), AP_Client_consts::MAX_PLAYER_NAME_LENGHT); ImGui::InputText("Password (leave blank for no password)", AP_client.get_password_buff(), AP_Client_consts::MAX_PASSWORD_LENGTH, ImGuiInputTextFlags_Password); - char connected_text[25] = "Disconnected"; + static char connected_text[25] = "Disconnected"; if(ImGui::Button("Connect")) { bool success = AP_client.start_client(); - if(success) { - strncpy(connected_text, "Connected!", 25); - SPDLOG_TRACE("Connected!!"); - } - else { - strncpy(connected_text, "Connection failed!", 25); - SPDLOG_TRACE("Connection failed :("); - } } ImGui::SameLine(); ImGui::Text(connected_text); + AP_ConnectionStatus con_status = AP_GetConnectionStatus(); + if(con_status == AP_ConnectionStatus::Connected) { + strncpy(connected_text, "Connected!", 25); + } else if(con_status == AP_ConnectionStatus::Authenticated) { + strncpy(connected_text, "Authenticated!", 25); + } + else { + strncpy(connected_text, "Not Connected", 25); + } + if(ImGui::Button("scout")) { AP_client.start_location_scouts(); } @@ -265,4 +302,8 @@ void ArchipelagoWindow::ArchipelagoDrawConnectPage() { void ArchipelagoWindow::DrawElement() { ArchipelagoDrawConnectPage(); UIWidgets::PaddedSeparator(); + + if(ImGui::Button("give blue ruppie")) { + ArchipelagoClient::getInstance().on_item_recieved(66077, true); + } }; \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/archipelago.h b/soh/soh/Enhancements/randomizer/archipelago.h index cbaad2348..9310b656d 100644 --- a/soh/soh/Enhancements/randomizer/archipelago.h +++ b/soh/soh/Enhancements/randomizer/archipelago.h @@ -3,6 +3,11 @@ #include "fixed_string.hpp" +#include "randomizerTypes.h" +#include "static_data.h" +#include + + namespace AP_Client_consts { static constexpr int MAX_ADDRESS_LENGTH = 64; static constexpr int MAX_PLAYER_NAME_LENGHT = 17; @@ -30,6 +35,19 @@ class ArchipelagoClient { const std::vector& get_scouted_items(); void add_slot_data(std::string_view key, int id); + + //void add_slot_data(std::string_view key, int id); + + bool isConnected(); + void check_location(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 + static void on_item_recieved(int64_t recieved_item_id, bool notify_player); protected: ArchipelagoClient(); @@ -56,12 +74,13 @@ class ArchipelagoClient { static void on_connected(); static void on_couldntConnect(AP_ConnectionStatus connection_status); static void on_clear_items(); - static void on_item_recieved(int64_t recieved_item_id, bool notify_player); + static void on_location_checked(int64_t location_id); static void on_deathlink_recieved() { }; // TODO: implement me static void on_location_scouted(std::vector network_items); // callbacks + std::function ItemRecievedCallback; }; diff --git a/soh/soh/Enhancements/randomizer/context.cpp b/soh/soh/Enhancements/randomizer/context.cpp index 9d4aba779..fa5f083b6 100644 --- a/soh/soh/Enhancements/randomizer/context.cpp +++ b/soh/soh/Enhancements/randomizer/context.cpp @@ -271,6 +271,30 @@ void Context::SetSpoilerLoaded(const bool spoilerLoaded) { mSpoilerLoaded = spoilerLoaded; } +void Context::AddRecievedArchipelagoItem(const std::string& ap_item_id) { + mAPrecieveQueue.emplace(ap_item_id); + SPDLOG_TRACE("Item Pushed {}", ap_item_id); +} + +GetItemEntry Context::GetArchipelagoGIEntry() { + SPDLOG_TRACE("Trying to get Item Entry"); + if(mAPrecieveQueue.empty()) { + // something must have gone wrong here, just give a rupee + return ItemTableManager::Instance->RetrieveItemEntry(MOD_NONE, GI_HEART); + } + + // get the first item from the archipelago queue + std::string_view recieved_ap_item = mAPrecieveQueue.front(); + RandomizerGet item_id = StaticData::APitemToSoh[recieved_ap_item]; + assert(item_id != RG_NONE); + + Item& item = StaticData::RetrieveItem(item_id); + SPDLOG_TRACE("Found item! {}, {}", recieved_ap_item, (int)item_id); + GetItemEntry item_entry = item.GetGIEntry_Copy(); + mAPrecieveQueue.pop(); + return item_entry; // todo: add custom text maybe? +} + GetItemEntry Context::GetFinalGIEntry(const RandomizerCheck rc, const bool checkObtainability, const GetItemID ogItemId) { const auto itemLoc = GetItemLocation(rc); if (itemLoc->GetPlacedRandomizerGet() == RG_NONE) { @@ -392,8 +416,9 @@ void Context::ParseArchipelagoItemsLocations(std::vector scouted itemLocationTable[rc].SetPlacedItem(item); } else { // other player item - itemLocationTable[rc].SetPlacedItem(RG_RECOVERY_HEART); // Ap item doesn't work yet - //overrides[rc] = ItemOverride(rc, RG_ZELDAS_LETTER); + itemLocationTable[rc].SetPlacedItem(RG_ARCHIPELAGO_ITEM); + // i'll have to figure out custom names at some point, this currently does nothing + //overrides[rc] = ItemOverride(rc, RG_DEKU_NUTS_5); //std::string getText = ap_item.playerName + "'s " + ap_item.itemName; //overrides[rc].SetTrickName(Text(getText, getText, getText)); } diff --git a/soh/soh/Enhancements/randomizer/context.h b/soh/soh/Enhancements/randomizer/context.h index f50ddf532..c308de715 100644 --- a/soh/soh/Enhancements/randomizer/context.h +++ b/soh/soh/Enhancements/randomizer/context.h @@ -107,6 +107,8 @@ class Context { */ RandoOptionLACSCondition LACSCondition() const; GetItemEntry GetFinalGIEntry(RandomizerCheck rc, bool checkObtainability = true, GetItemID ogItemId = GI_NONE); + void AddRecievedArchipelagoItem(const std::string& ap_item_id); + GetItemEntry GetArchipelagoGIEntry(); void ParseSpoiler(const char* spoilerFileName); void ParseHashIconIndexesJson(nlohmann::json spoilerFileJson); void ParseItemLocationsJson(nlohmann::json spoilerFileJson); @@ -186,5 +188,6 @@ class Context { std::string mHash; std::string mSeedString; uint32_t mFinalSeed = 0; + std::queue mAPrecieveQueue = {}; }; } // namespace Rando \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index cc9708789..b33ed267a 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -16,6 +16,7 @@ #include "soh/Notification/Notification.h" #include "soh/SaveManager.h" #include "soh/Enhancements/randomizer/ShuffleFairies.h" +#include "archipelago.h" extern "C" { #include "macros.h" @@ -215,6 +216,12 @@ static std::queue randomizerQueuedChecks; static RandomizerCheck randomizerQueuedCheck = RC_UNKNOWN_CHECK; static GetItemEntry randomizerQueuedItemEntry = GET_ITEM_NONE; +void ArchipelagoOnRecieveItem(const std::string& ap_item_id) { + SPDLOG_TRACE("Recieve item handler called! {}", ap_item_id); + randomizerQueuedChecks.push(RC_ARCHIPELAGO_RECIEVED_ITEM); + Rando::Context::GetInstance()->AddRecievedArchipelagoItem(ap_item_id); +} + void RandomizerOnFlagSetHandler(int16_t flagType, int16_t flag) { // Consume adult trade items if (RAND_GET_OPTION(RSK_SHUFFLE_ADULT_TRADE) && flagType == FLAG_RANDOMIZER_INF) { @@ -271,11 +278,17 @@ void RandomizerOnPlayerUpdateForRCQueueHandler() { return; } + GetItemEntry getItemEntry; RandomizerCheck rc = randomizerQueuedChecks.front(); auto loc = Rando::Context::GetInstance()->GetItemLocation(rc); - RandomizerGet vanillaRandomizerGet = Rando::StaticData::GetLocation(rc)->GetVanillaItem(); - GetItemID vanillaItem = (GetItemID)Rando::StaticData::RetrieveItem(vanillaRandomizerGet).GetItemID(); - GetItemEntry getItemEntry = Rando::Context::GetInstance()->GetFinalGIEntry(rc, true, (GetItemID)vanillaRandomizerGet); + if(rc == RC_ARCHIPELAGO_RECIEVED_ITEM) { + getItemEntry = Rando::Context::GetInstance()->GetArchipelagoGIEntry(); + } else { + RandomizerGet vanillaRandomizerGet = Rando::StaticData::GetLocation(rc)->GetVanillaItem(); + GetItemID vanillaItem = (GetItemID)Rando::StaticData::RetrieveItem(vanillaRandomizerGet).GetItemID(); + getItemEntry = Rando::Context::GetInstance()->GetFinalGIEntry(rc, true, (GetItemID)vanillaRandomizerGet); + } + SPDLOG_TRACE("RC found!"); if (loc->HasObtained()) { SPDLOG_INFO("RC {} already obtained, skipping", static_cast(rc)); @@ -331,9 +344,19 @@ void RandomizerOnPlayerUpdateForItemQueueHandler() { void RandomizerOnItemReceiveHandler(GetItemEntry receivedItemEntry) { if (randomizerQueuedCheck == RC_UNKNOWN_CHECK) return; + SPDLOG_TRACE("Dropped into recieve handler!"); + auto loc = Rando::Context::GetInstance()->GetItemLocation(randomizerQueuedCheck); if (randomizerQueuedItemEntry.modIndex == receivedItemEntry.modIndex && randomizerQueuedItemEntry.itemId == receivedItemEntry.itemId) { SPDLOG_INFO("Item received mod {} item {} from RC {}", receivedItemEntry.modIndex, receivedItemEntry.itemId, static_cast(randomizerQueuedCheck)); + + // todo maybe move to seperate function + // let arhipelago know we got this check + if(randomizerQueuedCheck != RC_ARCHIPELAGO_RECIEVED_ITEM) { + ArchipelagoClient& ap_client = ArchipelagoClient::getInstance(); + ap_client.check_location(randomizerQueuedCheck); + } + loc->SetCheckStatus(RCSHOW_COLLECTED); CheckTracker::SpoilAreaFromCheck(randomizerQueuedCheck); CheckTracker::RecalculateAllAreaTotals(); @@ -2406,6 +2429,8 @@ void RandomizerRegisterHooks() { GameInteractor::Instance->UnregisterGameHook(shuffleFreestandingOnVanillaBehaviorHook); + ArchipelagoClient::getInstance().removeItemRecievedCallback(ArchipelagoOnRecieveItem); + onFlagSetHook = 0; onSceneFlagSetHook = 0; onPlayerUpdateForRCQueueHook = 0; @@ -2486,5 +2511,7 @@ void RandomizerRegisterHooks() { if (RAND_GET_OPTION(RSK_SHUFFLE_FAIRIES)) { ShuffleFairies_RegisterHooks(); } + + ArchipelagoClient::getInstance().addItemRecievedCallback(ArchipelagoOnRecieveItem); }); } diff --git a/soh/soh/Enhancements/randomizer/item_list.cpp b/soh/soh/Enhancements/randomizer/item_list.cpp index ea01565b7..16e413050 100644 --- a/soh/soh/Enhancements/randomizer/item_list.cpp +++ b/soh/soh/Enhancements/randomizer/item_list.cpp @@ -393,7 +393,7 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_TRIFORCE_PIECE] = Item(RG_TRIFORCE_PIECE, Text{ "Triforce Piece", "Triforce Piece", "Triforce-Fragment" }, ITEMTYPE_ITEM, 0xDF, true, LOGIC_TRIFORCE_PIECES, RHT_TRIFORCE_PIECE, RG_TRIFORCE_PIECE, OBJECT_GI_BOMB_2, GID_TRIFORCE_PIECE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); // Archipelago - itemTable[RG_ARCHIPELAGO_ITEM] = /* doesn't work :P*/ Item(RG_ARCHIPELAGO_ITEM, Text{"AP Item", "AP Item", "AP_Item"}, ITEMTYPE_ITEM, GI_RUPEE_GREEN, false, LOGIC_NONE, RHT_NONE, RG_ARCHIPELAGO_ITEM, OBJECT_GI_LETTER, GID_LETTER_ZELDA, TEXT_RANDOMIZER_CUSTOM_ITEM, 0, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE); + itemTable[RG_ARCHIPELAGO_ITEM] = Item(RG_ARCHIPELAGO_ITEM, Text{"AP Item", "AP Item", "AP Item"}, ITEMTYPE_EVENT, GI_RUPEE_GREEN, false, LOGIC_NONE, RHT_NONE, RG_ARCHIPELAGO_ITEM, OBJECT_GI_LETTER, GID_LETTER_ZELDA, TEXT_RANDOMIZER_CUSTOM_ITEM, 0, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_RANDOMIZER); // Init itemNameToEnum for (auto& item : itemTable) { diff --git a/soh/soh/Enhancements/randomizer/item_location.cpp b/soh/soh/Enhancements/randomizer/item_location.cpp index 00e0366ab..94ca6be6c 100644 --- a/soh/soh/Enhancements/randomizer/item_location.cpp +++ b/soh/soh/Enhancements/randomizer/item_location.cpp @@ -130,6 +130,8 @@ bool ItemLocation::HasObtained() const { } void ItemLocation::SetCheckStatus(RandomizerCheckStatus status_) { + if(rc == RC_ARCHIPELAGO_RECIEVED_ITEM) // never count the AP recieve trigger as 'collected' + return; status = status_; } diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 477c2f0da..5632c3fab 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -3332,7 +3332,7 @@ CustomMessage Randomizer::GetGoronMessage(u16 index) { void Randomizer::CreateCustomMessages() { // RANDTODO: Translate into french and german and replace GIMESSAGE_UNTRANSLATED // with GIMESSAGE(getItemID, itemID, english, german, french). - const std::array getItemMessages = {{ + const std::array getItemMessages = {{ GIMESSAGE(RG_GREG_RUPEE, ITEM_MASK_GORON, "You found %gGreg%w!", "%gGreg%w! Du hast ihn wirklich gefunden!", @@ -3767,6 +3767,7 @@ void Randomizer::CreateCustomMessages() { GIMESSAGE_NO_GERMAN(RG_DEKU_NUT_BAG, ITEM_NUT, "You found the %rDeku Nut Bag%w!&You can now hold deku nuts!", "Vous avez trouvé le %rSac de Noix Mojo%w!&Vous pouvez maintenant porter des Noix Mojo!"), + GIMESSAGE_NO_GERMAN(RG_ARCHIPELAGO_ITEM, ITEM_BEAN, "You found an %rAP_ITEM%w", "Je m'apelle not %rNot Translated%w"), }}; CreateGetItemMessages(&getItemMessages); CreateRupeeMessages(); @@ -3881,6 +3882,11 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { return Return_Item_Entry(giEntry, RG_NONE); } + //if it's an archipelago item, don't give anything + if(item == RG_ARCHIPELAGO_ITEM) { + return Return_Item_Entry(giEntry, RG_NONE); + } + //bottle items if (item >= RG_BOTTLE_WITH_RED_POTION && item <= RG_BOTTLE_WITH_BIG_POE) { for (u16 i = 0; i < 4; i++) { diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 9dd085f6c..d3685b664 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -13,7 +13,8 @@ // which doesn't exist yet. typedef enum { MOD_NONE, - MOD_RANDOMIZER + MOD_RANDOMIZER, + MOD_ARCHIPELAGO // no actually used yet i think } ModIndex; typedef enum { TABLE_VANILLA = MOD_NONE, @@ -2848,6 +2849,7 @@ typedef enum { RC_SHADOW_TEMPLE_MQ_WIND_HINT_SUN_FAIRY, RC_BOTTOM_OF_THE_WELL_MQ_CELL_SUN_FAIRY, RC_BOTTOM_OF_THE_WELL_MQ_BASEMENT_SUN_FAIRY, + RC_ARCHIPELAGO_RECIEVED_ITEM, RC_MAX } RandomizerCheck;