diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp index c031f96f43..9e8aeb7667 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp @@ -2933,6 +2933,18 @@ void CEXISlippi::prepareGamePrepOppStep(const SlippiExiTypes::GpFetchStepQuery& data_ptr + sizeof(SlippiExiTypes::GpFetchStepResponse)); } +void CEXISlippi::handleCompleteSet(const SlippiExiTypes::ReportSetCompletionQuery& query) +{ + ERROR_LOG_FMT(SLIPPI_ONLINE, "Hello"); + + auto lastMatchId = recentMmResult.id; + if (lastMatchId.find("mode.ranked") != std::string::npos) + { + ERROR_LOG_FMT(SLIPPI_ONLINE, "Reporting set completion: {}", lastMatchId); + game_reporter->ReportCompletion(lastMatchId, query.endMode); + } +} + void CEXISlippi::DMAWrite(u32 _uAddr, u32 _uSize) { auto& system = Core::System::GetInstance(); @@ -3103,6 +3115,10 @@ void CEXISlippi::DMAWrite(u32 _uAddr, u32 _uSize) handleGamePrepStepComplete( SlippiExiTypes::Convert(&memPtr[bufLoc])); break; + case CMD_REPORT_SET_COMPLETE: + handleCompleteSet( + SlippiExiTypes::Convert(&memPtr[bufLoc])); + break; default: writeToFileAsync(&memPtr[bufLoc], payloadLen + 1, ""); SlippiSpectateServer::getInstance().write(&memPtr[bufLoc], payloadLen + 1); diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h index 5b2deef0e2..c4b61fe842 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h @@ -79,6 +79,7 @@ private: CMD_OVERWRITE_SELECTIONS = 0xBF, CMD_GP_COMPLETE_STEP = 0xC0, CMD_GP_FETCH_STEP = 0xC1, + CMD_REPORT_SET_COMPLETE = 0xC2, // Misc CMD_LOG_MESSAGE = 0xD0, @@ -134,6 +135,8 @@ private: static_cast(sizeof(SlippiExiTypes::OverwriteSelectionsQuery) - 1)}, {CMD_GP_COMPLETE_STEP, static_cast(sizeof(SlippiExiTypes::GpCompleteStepQuery) - 1)}, {CMD_GP_FETCH_STEP, static_cast(sizeof(SlippiExiTypes::GpFetchStepQuery) - 1)}, + {CMD_REPORT_SET_COMPLETE, + static_cast(sizeof(SlippiExiTypes::ReportSetCompletionQuery) - 1)}, // Misc {CMD_LOG_MESSAGE, 0xFFFF}, // Variable size... will only work if by itself @@ -199,6 +202,7 @@ private: void handleOverwriteSelections(const SlippiExiTypes::OverwriteSelectionsQuery& query); void handleGamePrepStepComplete(const SlippiExiTypes::GpCompleteStepQuery& query); void prepareGamePrepOppStep(const SlippiExiTypes::GpFetchStepQuery& query); + void handleCompleteSet(const SlippiExiTypes::ReportSetCompletionQuery& query); // replay playback stuff void prepareGameInfo(u8* payload); diff --git a/Source/Core/Core/Slippi/SlippiExiTypes.h b/Source/Core/Core/Slippi/SlippiExiTypes.h index 13c7dd031e..cffa17447c 100644 --- a/Source/Core/Core/Slippi/SlippiExiTypes.h +++ b/Source/Core/Core/Slippi/SlippiExiTypes.h @@ -37,6 +37,12 @@ struct ReportGameQuery u8 game_info_block[312]; }; +struct ReportSetCompletionQuery +{ + u8 command; + u8 endMode; +}; + struct GpCompleteStepQuery { u8 command; diff --git a/Source/Core/Core/Slippi/SlippiGameReporter.cpp b/Source/Core/Core/Slippi/SlippiGameReporter.cpp index 505a2d5840..cc656f78f5 100644 --- a/Source/Core/Core/Slippi/SlippiGameReporter.cpp +++ b/Source/Core/Core/Slippi/SlippiGameReporter.cpp @@ -54,6 +54,24 @@ static size_t curl_send(char* ptr, size_t size, size_t nmemb, void* userdata) SlippiGameReporter::SlippiGameReporter(SlippiUser* user, const std::string current_file_name) { + CURL* curl = curl_easy_init(); + if (curl) + { + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_receive); + curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 10000); + // Set up HTTP Headers + m_curl_header_list = curl_slist_append(m_curl_header_list, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, m_curl_header_list); + + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, m_curl_err_buf); + curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); +#ifdef _WIN32 + // ALPN support is enabled by default but requires Windows >= 8.1. + curl_easy_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, false); +#endif + m_curl = curl; + } + CURL* curl_upload = curl_easy_init(); if (curl_upload) { @@ -61,6 +79,8 @@ SlippiGameReporter::SlippiGameReporter(SlippiUser* user, const std::string curre curl_easy_setopt(curl_upload, CURLOPT_UPLOAD, 1L); curl_easy_setopt(curl_upload, CURLOPT_WRITEFUNCTION, &curl_receive); curl_easy_setopt(curl_upload, CURLOPT_TIMEOUT_MS, 10000); + curl_easy_setopt(curl_upload, CURLOPT_ERRORBUFFER, m_curl_upload_err_buf); + curl_easy_setopt(curl_upload, CURLOPT_FAILONERROR, 1L); // Set up HTTP Headers m_curl_upload_headers = @@ -79,8 +99,6 @@ SlippiGameReporter::SlippiGameReporter(SlippiUser* user, const std::string curre m_user = user; - // TODO: For mainline port, ISO file path can't be fetched this way. Look at the following: - // https://github.com/dolphin-emu/dolphin/blob/7f450f1d7e7d37bd2300f3a2134cb443d07251f9/Source/Core/Core/Movie.cpp#L246-L249; static const mbedtls_md_info_t* s_md5_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5); m_md5_thread = std::thread([this, current_file_name]() { std::array md5_array; @@ -319,6 +337,34 @@ void SlippiGameReporter::ReportAbandonment(std::string match_id) } } +void SlippiGameReporter::ReportCompletion(std::string matchId, u8 endMode) +{ + auto userInfo = m_user->GetUserInfo(); + + // Prepare report + json request; + request["matchId"] = matchId; + request["uid"] = userInfo.uid; + request["playKey"] = userInfo.play_key; + request["endMode"] = endMode; + + auto requestString = request.dump(); + + // Send report + curl_easy_setopt(m_curl, CURLOPT_POST, true); + curl_easy_setopt(m_curl, CURLOPT_URL, COMPLETE_URL.c_str()); + curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, requestString.c_str()); + curl_easy_setopt(m_curl, CURLOPT_POSTFIELDSIZE, requestString.length()); + CURLcode res = curl_easy_perform(m_curl); + + if (res != 0) + { + ERROR_LOG_FMT(SLIPPI_ONLINE, + "[GameReport] Got error executing completion request. Err code: {}", + static_cast(res)); + } +} + // https://stackoverflow.com/a/57699371/1249024 int compressToGzip(const char* input, size_t inputSize, char* output, size_t outputSize) { diff --git a/Source/Core/Core/Slippi/SlippiGameReporter.h b/Source/Core/Core/Slippi/SlippiGameReporter.h index 77be5a601b..a856cab602 100644 --- a/Source/Core/Core/Slippi/SlippiGameReporter.h +++ b/Source/Core/Core/Slippi/SlippiGameReporter.h @@ -48,6 +48,7 @@ public: void StartReport(GameReport report); void ReportAbandonment(std::string match_id); + void ReportCompletion(std::string matchId, u8 endMode); void StartNewSession(); void ReportThreadHandler(); void PushReplayData(u8* data, u32 length, std::string action); @@ -56,12 +57,16 @@ public: protected: const std::string REPORT_URL = "https://rankings-dot-slippi.uc.r.appspot.com/report"; const std::string ABANDON_URL = "https://rankings-dot-slippi.uc.r.appspot.com/abandon"; + const std::string COMPLETE_URL = "https://rankings-dot-slippi.uc.r.appspot.com/complete"; CURL* m_curl = nullptr; struct curl_slist* m_curl_header_list = nullptr; CURL* m_curl_upload = nullptr; struct curl_slist* m_curl_upload_headers = nullptr; + char m_curl_err_buf[CURL_ERROR_SIZE]; + char m_curl_upload_err_buf[CURL_ERROR_SIZE]; + std::vector m_player_uids; SlippiUser* m_user;