diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp index 046da4eb8e..b5d8db07b0 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp @@ -137,6 +137,16 @@ CEXISlippi::CEXISlippi() user->ListenForLogIn(); #endif + // Use sane stage defaults (should get overwritten) + allowedStages = { + 0x2, // FoD + 0x3, // Pokemon + 0x8, // Yoshi's Story + 0x1C, // Dream Land + 0x1F, // Battlefield + 0x20, // Final Destination + }; + #ifdef CREATE_DIFF_FILES // MnMaAll.usd std::string origStr; @@ -1838,8 +1848,9 @@ void CEXISlippi::startFindMatch(u8* payload) } // Stage check - if (localSelections.isStageSelected && std::find(legalStages.begin(), legalStages.end(), - localSelections.stageId) == legalStages.end()) + if (localSelections.isStageSelected && + std::find(allowedStages.begin(), allowedStages.end(), localSelections.stageId) == + allowedStages.end()) { forcedError = "The stage being requested is not allowed in this mode"; return; @@ -1923,6 +1934,13 @@ void CEXISlippi::prepareOnlineMatchState() slippi_netplay = matchmaking->GetNetplayClient(); #endif + // This happens on the initial connection to a player. Let's now grab the allowed stages + // returned to us from the matchmaking service and pick a new random stage before sending + // the selections to the opponent + allowedStages = matchmaking->GetStages(); + stagePool + .clear(); // Clear stage pool so that when we call getRandomStage it will use full list + localSelections.stageId = getRandomStage(); slippi_netplay->SetMatchSelections(localSelections); } @@ -2120,7 +2138,7 @@ void CEXISlippi::prepareOnlineMatchState() return; } - if (std::find(legalStages.begin(), legalStages.end(), stageId) == legalStages.end()) + if (std::find(allowedStages.begin(), allowedStages.end(), stageId) == allowedStages.end()) { handleConnectionCleanup(); prepareOnlineMatchState(); @@ -2279,22 +2297,13 @@ void CEXISlippi::prepareOnlineMatchState() m_read_queue.insert(m_read_queue.end(), onlineMatchBlock.begin(), onlineMatchBlock.end()); } -std::vector CEXISlippi::legalStages = { - 0x2, // FoD - 0x3, // Pokemon - 0x8, // Yoshi's Story - 0x1C, // Dream Land - 0x1F, // Battlefield - 0x20, // Final Destination -}; - -u16 CEXISlippi::getRandomStage(u8 onlineMode) +u16 CEXISlippi::getRandomStage() { static u16 selectedStage; // Reset stage pool if it's empty if (stagePool.empty()) - stagePool.insert(stagePool.end(), legalStages.begin(), legalStages.end()); + stagePool.insert(stagePool.end(), allowedStages.begin(), allowedStages.end()); // Get random stage int randIndex = generator() % stagePool.size(); @@ -2303,14 +2312,6 @@ u16 CEXISlippi::getRandomStage(u8 onlineMode) // Remove last selection from stage pool stagePool.erase(stagePool.begin() + randIndex); - // If the mode is teams, don't allow FoD to be selected, re-roll instead. Note that this will - // cause a stack overflow exception/infinite recursion in the case where a dev removes all - // stages but FoD from the legalStages vector - if (onlineMode == (u8)SlippiMatchmaking::OnlinePlayMode::TEAMS && selectedStage == 0x2) - { - return getRandomStage(onlineMode); - } - return selectedStage; } @@ -2325,13 +2326,13 @@ void CEXISlippi::setMatchSelections(u8* payload) s.stageId = Common::swap16(&payload[4]); u8 stageSelectOption = payload[6]; - u8 onlineMode = payload[7]; + // u8 onlineMode = payload[7]; s.isStageSelected = stageSelectOption == 1 || stageSelectOption == 3; if (stageSelectOption == 3) { // If stage requested is random, select a random stage - s.stageId = getRandomStage(onlineMode); + s.stageId = getRandomStage(); } INFO_LOG(SLIPPI, "LPS set char: %d, iSS: %d, %d, stage: %d, team: %d", s.isCharacterSelected, diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h index e8f731a0de..c8860b9a88 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h @@ -163,7 +163,7 @@ private: std::vector m_payload; // online play stuff - u16 getRandomStage(u8 onlineMode); + u16 getRandomStage(); bool isDisconnected(); void handleOnlineInputs(u8* payload); void prepareOpponentInputs(u8* payload); @@ -251,6 +251,6 @@ private: std::map> activeSavestates; std::deque> availableSavestates; - static std::vector legalStages; + std::vector allowedStages; }; } // namespace ExpansionInterface diff --git a/Source/Core/Core/Slippi/SlippiMatchmaking.cpp b/Source/Core/Core/Slippi/SlippiMatchmaking.cpp index 62db67f54b..78c44b4394 100644 --- a/Source/Core/Core/Slippi/SlippiMatchmaking.cpp +++ b/Source/Core/Core/Slippi/SlippiMatchmaking.cpp @@ -483,6 +483,36 @@ void SlippiMatchmaking::handleMatchmaking() } m_isHost = getResp.value("isHost", false); + // Get allowed stages. For stage select modes like direct and teams, this will only impact the + // first map selected + m_allowedStages.clear(); + auto stages = getResp["stages"]; + if (stages.is_array()) + { + for (json::iterator it = stages.begin(); it != stages.end(); ++it) + { + json el = *it; + auto stageId = el.get(); + m_allowedStages.push_back(stageId); + } + } + + if (m_allowedStages.empty()) + { + // Default case, shouldn't ever really be hit but it's here just in case + m_allowedStages.push_back(0x3); // Pokemon + m_allowedStages.push_back(0x8); // Yoshi's Story + m_allowedStages.push_back(0x1C); // Dream Land + m_allowedStages.push_back(0x1F); // Battlefield + m_allowedStages.push_back(0x20); // Final Destination + + // Add FoD if singles + if (m_playerInfo.size() == 2) + { + m_allowedStages.push_back(0x2); // FoD + } + } + // Disconnect and destroy enet client to mm server terminateMmConnection(); @@ -501,6 +531,11 @@ std::vector SlippiMatchmaking::GetPlayerInfo() return m_playerInfo; } +std::vector SlippiMatchmaking::GetStages() +{ + return m_allowedStages; +} + std::string SlippiMatchmaking::GetPlayerName(u8 port) { if (port >= m_playerInfo.size()) diff --git a/Source/Core/Core/Slippi/SlippiMatchmaking.h b/Source/Core/Core/Slippi/SlippiMatchmaking.h index d8ab24bf27..52b395478f 100644 --- a/Source/Core/Core/Slippi/SlippiMatchmaking.h +++ b/Source/Core/Core/Slippi/SlippiMatchmaking.h @@ -56,6 +56,7 @@ public: int LocalPlayerIndex(); std::vector GetPlayerInfo(); std::string GetPlayerName(u8 port); + std::vector GetStages(); u8 RemotePlayerCount(); static bool IsFixedRulesMode(OnlinePlayMode mode); @@ -88,6 +89,7 @@ protected: int m_localPlayerIndex; std::vector m_remoteIps; std::vector m_playerInfo; + std::vector m_allowedStages; bool m_joinedLobby; bool m_isHost;