pull in 03c57192491178b329bda001c9fb8d8ba8a28c59

This commit is contained in:
R2DLiu 2023-08-01 15:45:33 -04:00
commit f61f2ab219
3 changed files with 190 additions and 148 deletions

View file

@ -116,7 +116,8 @@ std::string ConvertConnectCodeForGame(const std::string& input)
return connectCode; return connectCode;
} }
CEXISlippi::CEXISlippi(Core::System& system, const std::string current_file_name) : IEXIDevice(system) CEXISlippi::CEXISlippi(Core::System& system, const std::string current_file_name)
: IEXIDevice(system)
{ {
INFO_LOG_FMT(SLIPPI, "EXI SLIPPI Constructor called."); INFO_LOG_FMT(SLIPPI, "EXI SLIPPI Constructor called.");
@ -2453,6 +2454,11 @@ void CEXISlippi::prepareOnlineMatchState()
// Add the match struct block to output // Add the match struct block to output
m_read_queue.insert(m_read_queue.end(), onlineMatchBlock.begin(), onlineMatchBlock.end()); m_read_queue.insert(m_read_queue.end(), onlineMatchBlock.begin(), onlineMatchBlock.end());
// Add match id to output
std::string matchId = recentMmResult.id;
matchId.resize(51);
m_read_queue.insert(m_read_queue.end(), matchId.begin(), matchId.end());
} }
u16 CEXISlippi::getRandomStage() u16 CEXISlippi::getRandomStage()

View file

@ -14,28 +14,29 @@
#include <codecvt> #include <codecvt>
#include <locale> #include <locale>
#include <zlib.h>
#include <mbedtls/md5.h>
#include <mbedtls/md.h>
#include <json.hpp> #include <json.hpp>
#include <mbedtls/md.h>
#include <mbedtls/md5.h>
#include <zlib.h>
using json = nlohmann::json; using json = nlohmann::json;
static size_t curl_receive(char *ptr, size_t size, size_t nmemb, void *rcvBuf) static size_t curl_receive(char* ptr, size_t size, size_t nmemb, void* rcvBuf)
{ {
size_t len = size * nmemb; size_t len = size * nmemb;
INFO_LOG_FMT(SLIPPI_ONLINE, "[GameReport] Received data: {}", len); INFO_LOG_FMT(SLIPPI_ONLINE, "[GameReport] Received data: {}", len);
std::string *buf = (std::string *)rcvBuf; std::string* buf = (std::string*)rcvBuf;
buf->insert(buf->end(), ptr, ptr + len); buf->insert(buf->end(), ptr, ptr + len);
return len; return len;
} }
static size_t curl_send(char *ptr, size_t size, size_t nmemb, void *userdata) static size_t curl_send(char* ptr, size_t size, size_t nmemb, void* userdata)
{ {
std::vector<u8> *buf = (std::vector<u8> *)userdata; std::vector<u8>* buf = (std::vector<u8>*)userdata;
INFO_LOG_FMT(SLIPPI_ONLINE, "[GameReport] Sending data. Size: {}, Nmemb: {}. Buffer length: {}", size, nmemb, buf->size()); INFO_LOG_FMT(SLIPPI_ONLINE, "[GameReport] Sending data. Size: {}, Nmemb: {}. Buffer length: {}",
size, nmemb, buf->size());
size_t copy_size = size * nmemb; size_t copy_size = size * nmemb;
if (copy_size > buf->size()) if (copy_size > buf->size())
@ -51,9 +52,9 @@ static size_t curl_send(char *ptr, size_t size, size_t nmemb, void *userdata)
return copy_size; return copy_size;
} }
SlippiGameReporter::SlippiGameReporter(SlippiUser *user, const std::string current_file_name) SlippiGameReporter::SlippiGameReporter(SlippiUser* user, const std::string current_file_name)
{ {
CURL *curl_upload = curl_easy_init(); CURL* curl_upload = curl_easy_init();
if (curl_upload) if (curl_upload)
{ {
curl_easy_setopt(curl_upload, CURLOPT_READFUNCTION, &curl_send); curl_easy_setopt(curl_upload, CURLOPT_READFUNCTION, &curl_send);
@ -62,7 +63,8 @@ SlippiGameReporter::SlippiGameReporter(SlippiUser *user, const std::string curre
curl_easy_setopt(curl_upload, CURLOPT_TIMEOUT_MS, 10000); curl_easy_setopt(curl_upload, CURLOPT_TIMEOUT_MS, 10000);
// Set up HTTP Headers // Set up HTTP Headers
m_curl_upload_headers = curl_slist_append(m_curl_upload_headers, "Content-Type: application/octet-stream"); m_curl_upload_headers =
curl_slist_append(m_curl_upload_headers, "Content-Type: application/octet-stream");
curl_slist_append(m_curl_upload_headers, "Content-Encoding: gzip"); curl_slist_append(m_curl_upload_headers, "Content-Encoding: gzip");
curl_slist_append(m_curl_upload_headers, "X-Goog-Content-Length-Range: 0,10000000"); curl_slist_append(m_curl_upload_headers, "X-Goog-Content-Length-Range: 0,10000000");
curl_easy_setopt(curl_upload, CURLOPT_HTTPHEADER, m_curl_upload_headers); curl_easy_setopt(curl_upload, CURLOPT_HTTPHEADER, m_curl_upload_headers);
@ -112,14 +114,15 @@ SlippiGameReporter::~SlippiGameReporter()
} }
} }
void SlippiGameReporter::PushReplayData(u8 *data, u32 length, std::string action) { void SlippiGameReporter::PushReplayData(u8* data, u32 length, std::string action)
{
if (action == "create") if (action == "create")
{ {
m_replay_write_idx += 1; m_replay_write_idx += 1;
} }
// This makes a vector at this index if it doesn't exist // This makes a vector at this index if it doesn't exist
auto &v = m_replay_data[m_replay_write_idx]; auto& v = m_replay_data[m_replay_write_idx];
// Insert new data into vector // Insert new data into vector
v.insert(v.end(), data, data + length); v.insert(v.end(), data, data + length);
@ -138,7 +141,7 @@ void SlippiGameReporter::StartReport(GameReport report)
void SlippiGameReporter::StartNewSession() void SlippiGameReporter::StartNewSession()
{ {
game_index = 1; // Maybe we could do stuff here? We used to initialize gameIndex but that isn't required anymore
} }
void SlippiGameReporter::ReportThreadHandler() void SlippiGameReporter::ReportThreadHandler()
@ -155,14 +158,25 @@ void SlippiGameReporter::ReportThreadHandler()
// Process all messages // Process all messages
while (!game_report_queue.empty()) while (!game_report_queue.empty())
{ {
auto report = game_report_queue.front(); auto& report = game_report_queue.front();
report.report_attempts += 1;
auto isFirstAttempt = report.report_attempts == 1;
auto isLastAttempt = report.report_attempts >= 5; // Only do five attempts
auto errorSleepMs = isLastAttempt ? 0 : report.report_attempts * 100;
// If the thread is shutting down, give up after one attempt
if (!run_thread && !isFirstAttempt)
{
game_report_queue.pop(); game_report_queue.pop();
continue;
}
auto ranked = SlippiMatchmaking::OnlinePlayMode::RANKED; auto ranked = SlippiMatchmaking::OnlinePlayMode::RANKED;
auto user_info = m_user->GetUserInfo(); auto user_info = m_user->GetUserInfo();
WARN_LOG_FMT(SLIPPI_ONLINE, "Checking game report for game {}. Length: {}...", game_index, WARN_LOG_FMT(SLIPPI_ONLINE, "Checking game report for game {}. Length: {}...",
report.duration_frames); report.game_index, report.duration_frames);
// Prepare report // Prepare report
json request; json request;
@ -170,9 +184,9 @@ void SlippiGameReporter::ReportThreadHandler()
request["uid"] = user_info.uid; request["uid"] = user_info.uid;
request["playKey"] = user_info.play_key; request["playKey"] = user_info.play_key;
request["mode"] = report.mode; request["mode"] = report.mode;
request["gameIndex"] = report.mode == ranked ? report.game_index : game_index; request["gameIndex"] = report.game_index;
request["tiebreakIndex"] = report.mode == ranked ? report.tiebreak_index : 0; request["tiebreakIndex"] = report.tiebreak_index;
request["gameIndex"] = game_index; request["gameIndex"] = report.game_index;
request["gameDurationFrames"] = report.duration_frames; request["gameDurationFrames"] = report.duration_frames;
request["winnerIdx"] = report.winner_idx; request["winnerIdx"] = report.winner_idx;
request["gameEndMethod"] = report.game_end_method; request["gameEndMethod"] = report.game_end_method;
@ -197,6 +211,11 @@ void SlippiGameReporter::ReportThreadHandler()
} }
request["players"] = players; request["players"] = players;
// Just pop before request if this is the last attempt
if (isLastAttempt)
{
game_report_queue.pop();
}
auto requestString = request.dump(); auto requestString = request.dump();
@ -209,14 +228,11 @@ void SlippiGameReporter::ReportThreadHandler()
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &resp); curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &resp);
CURLcode res = curl_easy_perform(m_curl); CURLcode res = curl_easy_perform(m_curl);
// Increment game index even if this fails, because we don't currently retry
game_index++;
if (res != 0) if (res != 0)
{ {
ERROR_LOG_FMT(SLIPPI_ONLINE, "[GameReport] Got error executing request. Err code : {}", ERROR_LOG_FMT(SLIPPI_ONLINE, "[GameReport] Got error executing request. Err code : {}",
static_cast<u8>(res)); static_cast<u8>(res));
Common::SleepCurrentThread(0); Common::SleepCurrentThread(errorSleepMs);
continue; continue;
} }
@ -224,21 +240,38 @@ void SlippiGameReporter::ReportThreadHandler()
curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &responseCode); curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &responseCode);
if (responseCode != 200) if (responseCode != 200)
{ {
ERROR_LOG_FMT(SLIPPI_ONLINE, "[GameReport] Server responded with non-success status: {}", responseCode); ERROR_LOG_FMT(SLIPPI_ONLINE, "[GameReport] Server responded with non-success status: {}",
Common::SleepCurrentThread(0); responseCode);
Common::SleepCurrentThread(errorSleepMs);
continue; continue;
} }
// Grab resp // Check if response is valid json
if (!json::accept(resp))
{
ERROR_LOG_FMT(SLIPPI, "[GameReport] Server responded with invalid json: {}", resp);
Common::SleepCurrentThread(errorSleepMs);
continue;
}
// Parse the response
auto r = json::parse(resp); auto r = json::parse(resp);
bool success = r.value("success", false); bool success = r.value("success", false);
if (!success) if (!success)
{ {
ERROR_LOG_FMT(SLIPPI_ONLINE, "[GameReport] Report reached server but failed. {}", resp.c_str()); ERROR_LOG_FMT(SLIPPI_ONLINE, "[GameReport] Report reached server but failed. {}",
Common::SleepCurrentThread(0); resp.c_str());
Common::SleepCurrentThread(errorSleepMs);
continue; continue;
} }
// If this was not the last attempt, pop if we are successful. On the last attempt pop will
// already have happened
if (!isLastAttempt)
{
game_report_queue.pop();
}
std::string uploadUrl = r.value("uploadUrl", ""); std::string uploadUrl = r.value("uploadUrl", "");
UploadReplay(m_replay_last_completed_idx, uploadUrl); UploadReplay(m_replay_last_completed_idx, uploadUrl);
@ -287,20 +320,20 @@ void SlippiGameReporter::ReportAbandonment(std::string match_id)
} }
// https://stackoverflow.com/a/57699371/1249024 // https://stackoverflow.com/a/57699371/1249024
int compressToGzip(const char *input, size_t inputSize, char *output, size_t outputSize) int compressToGzip(const char* input, size_t inputSize, char* output, size_t outputSize)
{ {
z_stream zs; z_stream zs;
zs.zalloc = Z_NULL; zs.zalloc = Z_NULL;
zs.zfree = Z_NULL; zs.zfree = Z_NULL;
zs.opaque = Z_NULL; zs.opaque = Z_NULL;
zs.avail_in = (uInt)inputSize; zs.avail_in = (uInt)inputSize;
zs.next_in = (Bytef *)input; zs.next_in = (Bytef*)input;
zs.avail_out = (uInt)outputSize; zs.avail_out = (uInt)outputSize;
zs.next_out = (Bytef *)output; zs.next_out = (Bytef*)output;
// hard to believe they don't have a macro for gzip encoding, "Add 16" is the best thing zlib can do: // hard to believe they don't have a macro for gzip encoding, "Add 16" is the best thing zlib can
// "Add 16 to windowBits to write a simple gzip header and trailer around the compressed data instead of a zlib // do: "Add 16 to windowBits to write a simple gzip header and trailer around the compressed data
// wrapper" // instead of a zlib wrapper"
deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY); deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY);
deflate(&zs, Z_FINISH); deflate(&zs, Z_FINISH);
deflateEnd(&zs); deflateEnd(&zs);
@ -312,25 +345,27 @@ void SlippiGameReporter::UploadReplay(int idx, std::string url)
if (url.length() <= 0) if (url.length() <= 0)
return; return;
//INFO_LOG(SLIPPI_ONLINE, "Uploading replay: {}, {}", idx, url.c_str()); // INFO_LOG(SLIPPI_ONLINE, "Uploading replay: {}, {}", idx, url.c_str());
auto replay_data = m_replay_data[idx]; auto replay_data = m_replay_data[idx];
u32 raw_data_size = static_cast<u32>(replay_data.size()); u32 raw_data_size = static_cast<u32>(replay_data.size());
u8 *rdbs = reinterpret_cast<u8 *>(&raw_data_size); u8* rdbs = reinterpret_cast<u8*>(&raw_data_size);
// Add header and footer to replay file // Add header and footer to replay file
std::vector<u8> header({'{', 'U', 3, 'r', 'a', 'w', '[', '$', 'U', '#', 'l', rdbs[3], rdbs[2], rdbs[1], rdbs[0]}); std::vector<u8> header(
{'{', 'U', 3, 'r', 'a', 'w', '[', '$', 'U', '#', 'l', rdbs[3], rdbs[2], rdbs[1], rdbs[0]});
replay_data.insert(replay_data.begin(), header.begin(), header.end()); replay_data.insert(replay_data.begin(), header.begin(), header.end());
std::vector<u8> footer({'U', 8, 'm', 'e', 't', 'a', 'd', 'a', 't', 'a', '{', '}', '}'}); std::vector<u8> footer({'U', 8, 'm', 'e', 't', 'a', 'd', 'a', 't', 'a', '{', '}', '}'});
replay_data.insert(replay_data.end(), footer.begin(), footer.end()); replay_data.insert(replay_data.end(), footer.begin(), footer.end());
std::vector<u8> gzipped_data; std::vector<u8> gzipped_data;
gzipped_data.resize(replay_data.size()); gzipped_data.resize(replay_data.size());
auto res_size = compressToGzip(reinterpret_cast<char *>(&replay_data[0]), replay_data.size(), auto res_size = compressToGzip(reinterpret_cast<char*>(&replay_data[0]), replay_data.size(),
reinterpret_cast<char *>(&gzipped_data[0]), gzipped_data.size()); reinterpret_cast<char*>(&gzipped_data[0]), gzipped_data.size());
gzipped_data.resize(res_size); gzipped_data.resize(res_size);
INFO_LOG_FMT(SLIPPI_ONLINE, "Pre-compression size: {}. Post compression size: {}", replay_data.size(), res_size); INFO_LOG_FMT(SLIPPI_ONLINE, "Pre-compression size: {}. Post compression size: {}",
replay_data.size(), res_size);
curl_easy_setopt(m_curl_upload, CURLOPT_URL, url.c_str()); curl_easy_setopt(m_curl_upload, CURLOPT_URL, url.c_str());
curl_easy_setopt(m_curl_upload, CURLOPT_READDATA, &gzipped_data); curl_easy_setopt(m_curl_upload, CURLOPT_READDATA, &gzipped_data);
@ -339,6 +374,7 @@ void SlippiGameReporter::UploadReplay(int idx, std::string url)
if (res != 0) if (res != 0)
{ {
ERROR_LOG_FMT(SLIPPI_ONLINE, "[GameReport] Got error uploading replay file. Err code: {}", static_cast<int>(res)); ERROR_LOG_FMT(SLIPPI_ONLINE, "[GameReport] Got error uploading replay file. Err code: {}",
static_cast<int>(res));
} }
} }

View file

@ -3,12 +3,12 @@
#include <atomic> #include <atomic>
#include <condition_variable> // std::condition_variable #include <condition_variable> // std::condition_variable
#include <curl/curl.h> #include <curl/curl.h>
#include <map>
#include <mutex> // std::mutex, std::unique_lock #include <mutex> // std::mutex, std::unique_lock
#include <queue> #include <queue>
#include <string> #include <string>
#include <thread> #include <thread>
#include <vector> #include <vector>
#include <map>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Core/Slippi/SlippiMatchmaking.h" #include "Core/Slippi/SlippiMatchmaking.h"
#include "Core/Slippi/SlippiUser.h" #include "Core/Slippi/SlippiUser.h"
@ -32,6 +32,7 @@ public:
{ {
SlippiMatchmaking::OnlinePlayMode mode = SlippiMatchmaking::OnlinePlayMode::UNRANKED; SlippiMatchmaking::OnlinePlayMode mode = SlippiMatchmaking::OnlinePlayMode::UNRANKED;
std::string match_id; std::string match_id;
int report_attempts = 0;
u32 duration_frames = 0; u32 duration_frames = 0;
u32 game_index = 0; u32 game_index = 0;
u32 tiebreak_index = 0; u32 tiebreak_index = 0;
@ -49,7 +50,7 @@ public:
void ReportAbandonment(std::string match_id); void ReportAbandonment(std::string match_id);
void StartNewSession(); void StartNewSession();
void ReportThreadHandler(); void ReportThreadHandler();
void PushReplayData(u8 *data, u32 length, std::string action); void PushReplayData(u8* data, u32 length, std::string action);
void UploadReplay(int idx, std::string url); void UploadReplay(int idx, std::string url);
protected: protected:
@ -58,10 +59,9 @@ protected:
CURL* m_curl = nullptr; CURL* m_curl = nullptr;
struct curl_slist* m_curl_header_list = nullptr; struct curl_slist* m_curl_header_list = nullptr;
CURL *m_curl_upload = nullptr; CURL* m_curl_upload = nullptr;
struct curl_slist *m_curl_upload_headers = nullptr; struct curl_slist* m_curl_upload_headers = nullptr;
u32 game_index = 1;
std::vector<std::string> m_player_uids; std::vector<std::string> m_player_uids;
SlippiUser* m_user; SlippiUser* m_user;