From c83f77519bc520ef6cb2c9a6bf0acc7f493a3a1e Mon Sep 17 00:00:00 2001 From: Ryan McGrath Date: Mon, 18 Sep 2023 20:47:55 -0700 Subject: [PATCH] refactor: migrate SlippiUser to be backed by a Rust layer (#403) * Modify SlippiUser to be a shim to the Rust user. - Forwards calls in and out of the Rust User Manager held by the EXI device. - Handles converting UserInfo into C++ types so that any existing logic relying on assumptions about the type doesn't need to be flagged just yet. - Chat message importing for the current user, as well as the default chat list, now live over on the Rust side. - Slight edits to Matchmaking to handle how chat messages are loaded, solely to avoid extra string allocations. Tested and no issues found, but someone with more context should test again. * Remove the old webview login that macOS still had, and remove wxWidgets WebView functionality and linking on macOS * Update to reflect condensed Rust log infrastructure * linux now requires kebab case in target_link_libraries --------- Co-authored-by: Nikhil Narayana --- CMakeLists.txt | 8 +- Externals/SlippiRustExtensions | 2 +- Externals/wxWidgets3/wx/wxcocoa.h | 4 +- Source/Core/Common/Logging/Log.h | 3 +- Source/Core/Common/Logging/LogManager.cpp | 8 +- Source/Core/Core/HW/EXI_DeviceSlippi.cpp | 38 +- Source/Core/Core/Slippi/SlippiMatchmaking.cpp | 11 +- Source/Core/Core/Slippi/SlippiUser.cpp | 355 ++---------------- Source/Core/Core/Slippi/SlippiUser.h | 34 +- Source/Core/DolphinWX/CMakeLists.txt | 6 +- Source/Core/DolphinWX/Frame.h | 16 - Source/Core/DolphinWX/FrameTools.cpp | 19 - .../SlippiAuthWebView/SlippiAuthWebView.cpp | 134 ------- .../SlippiAuthWebView/SlippiAuthWebView.h | 28 -- 14 files changed, 95 insertions(+), 571 deletions(-) delete mode 100644 Source/Core/DolphinWX/SlippiAuthWebView/SlippiAuthWebView.cpp delete mode 100644 Source/Core/DolphinWX/SlippiAuthWebView/SlippiAuthWebView.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 933e18088f..3f4634af9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -934,13 +934,7 @@ endif() if(NOT DISABLE_WX) include(FindwxWidgets OPTIONAL) - - # This is increasingly not touched, but the clarity is probably beneficial to keep around. - if(APPLE) - FIND_PACKAGE(wxWidgets COMPONENTS core aui adv webview) - else() - FIND_PACKAGE(wxWidgets COMPONENTS core aui adv) - endif() + FIND_PACKAGE(wxWidgets COMPONENTS core aui adv) if(_ARCH_32) add_definitions(-DwxSIZE_T_IS_UINT) diff --git a/Externals/SlippiRustExtensions b/Externals/SlippiRustExtensions index af04c6c187..c5643d5d2c 160000 --- a/Externals/SlippiRustExtensions +++ b/Externals/SlippiRustExtensions @@ -1 +1 @@ -Subproject commit af04c6c1878731da5ccc7e1742f355af54755bb5 +Subproject commit c5643d5d2cb7073b52dcfbdb7836771b264bc33d diff --git a/Externals/wxWidgets3/wx/wxcocoa.h b/Externals/wxWidgets3/wx/wxcocoa.h index e6c4b60162..48446f2d3b 100644 --- a/Externals/wxWidgets3/wx/wxcocoa.h +++ b/Externals/wxWidgets3/wx/wxcocoa.h @@ -340,7 +340,7 @@ #define wxUSE_STC 0 -#define wxUSE_WEBVIEW 1 +#define wxUSE_WEBVIEW 0 #ifdef __WXMSW__ #define wxUSE_WEBVIEW_IE 0 @@ -349,7 +349,7 @@ #endif #if defined(__WXGTK__) || defined(__WXOSX__) -#define wxUSE_WEBVIEW_WEBKIT 1 +#define wxUSE_WEBVIEW_WEBKIT 0 #else #define wxUSE_WEBVIEW_WEBKIT 0 #endif diff --git a/Source/Core/Common/Logging/Log.h b/Source/Core/Common/Logging/Log.h index b3053ceef6..0864d9b6e3 100644 --- a/Source/Core/Common/Logging/Log.h +++ b/Source/Core/Common/Logging/Log.h @@ -41,8 +41,7 @@ enum LOG_TYPE SLIPPI, SLIPPI_ONLINE, SLIPPI_RUST_DEPENDENCIES, - SLIPPI_RUST_EXI, - SLIPPI_RUST_GAME_REPORTER, + SLIPPI_RUST_ONLINE, SLIPPI_RUST_JUKEBOX, SP1, VIDEO, diff --git a/Source/Core/Common/Logging/LogManager.cpp b/Source/Core/Common/Logging/LogManager.cpp index d9e7504ceb..10b1b9c406 100644 --- a/Source/Core/Common/Logging/LogManager.cpp +++ b/Source/Core/Common/Logging/LogManager.cpp @@ -94,12 +94,8 @@ LogManager::LogManager() "SLIPPI_RUST_DEPENDENCIES", "[Rust] Slippi Dependencies", LogTypes::SLIPPI_RUST_DEPENDENCIES, true); // This LogContainer will register with the Rust side under the "SLIPPI_RUST_EXI" target. - m_Log[LogTypes::SLIPPI_RUST_EXI] = - new LogContainer("SLIPPI_RUST_EXI", "[Rust] Slippi EXI", LogTypes::SLIPPI_RUST_EXI, true); - - // This LogContainer will register with the Rust side under the "SLIPPI_RUST_GAME_REPORTER" target. - m_Log[LogTypes::SLIPPI_RUST_GAME_REPORTER] = new LogContainer( - "SLIPPI_RUST_GAME_REPORTER", "[Rust] Slippi Game Reporter", LogTypes::SLIPPI_RUST_GAME_REPORTER, true); + m_Log[LogTypes::SLIPPI_RUST_ONLINE] = + new LogContainer("SLIPPI_RUST_ONLINE", "[Rust] Slippi Online", LogTypes::SLIPPI_RUST_ONLINE, true); // This LogContainer will register with the Rust side under the "SLIPPI_RUST_JUKEBOX" target. m_Log[LogTypes::SLIPPI_RUST_JUKEBOX] = diff --git a/Source/Core/Core/HW/EXI_DeviceSlippi.cpp b/Source/Core/Core/HW/EXI_DeviceSlippi.cpp index 2275d8ef4e..99585a0b3e 100644 --- a/Source/Core/Core/HW/EXI_DeviceSlippi.cpp +++ b/Source/Core/Core/HW/EXI_DeviceSlippi.cpp @@ -149,13 +149,23 @@ CEXISlippi::CEXISlippi() { INFO_LOG(SLIPPI, "EXI SLIPPI Constructor called."); - // TODO: For mainline port, ISO file path can't be fetched this way. Look at the following: + // @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 std::string isoPath = SConfig::GetInstance().m_strFilename; - slprs_exi_device_ptr = slprs_exi_device_create(isoPath.c_str(), OSDMessageHandler); + + // @TODO: Eventually we should move `GetSlippiUserJSONPath` out of the File module. + std::string userJSONPath = File::GetSlippiUserJSONPath(); + + SlippiRustEXIConfig slprs_exi_config; + slprs_exi_config.iso_path = isoPath.c_str(); + slprs_exi_config.user_json_path = userJSONPath.c_str(); + slprs_exi_config.scm_slippi_semver_str = scm_slippi_semver_str.c_str(); + slprs_exi_config.osd_add_msg_fn = OSDMessageHandler; + + slprs_exi_device_ptr = slprs_exi_device_create(slprs_exi_config); m_slippiserver = SlippiSpectateServer::getInstance(); - user = std::make_unique(); + user = std::make_unique(slprs_exi_device_ptr); g_playbackStatus = std::make_unique(); matchmaking = std::make_unique(user.get()); gameFileLoader = std::make_unique(); @@ -306,10 +316,7 @@ CEXISlippi::~CEXISlippi() { ERROR_LOG(SLIPPI_ONLINE, "Exit during in-progress ranked game: %s", activeMatchId.c_str()); - auto userInfo = user->GetUserInfo(); - - slprs_exi_device_report_match_abandonment(slprs_exi_device_ptr, userInfo.uid.c_str(), userInfo.playKey.c_str(), - activeMatchId.c_str()); + slprs_exi_device_report_match_abandonment(slprs_exi_device_ptr, activeMatchId.c_str()); } handleConnectionCleanup(); @@ -2758,7 +2765,6 @@ void CEXISlippi::handleChatMessage(u8 *payload) if (slippi_netplay) { - auto userInfo = user->GetUserInfo(); auto packet = std::make_unique(); // OSD::AddMessage("[Me]: "+ msg, OSD::Duration::VERY_LONG, OSD::Color::YELLOW); slippi_netplay->remoteSentChatMessageId = messageId; @@ -3065,8 +3071,7 @@ void CEXISlippi::handleCompleteSet(const SlippiExiTypes::ReportSetCompletionQuer auto userInfo = user->GetUserInfo(); - slprs_exi_device_report_match_completion(slprs_exi_device_ptr, userInfo.uid.c_str(), userInfo.playKey.c_str(), - lastMatchId.c_str(), query.endMode); + slprs_exi_device_report_match_completion(slprs_exi_device_ptr, lastMatchId.c_str(), query.endMode); } } @@ -3076,12 +3081,10 @@ void CEXISlippi::handleGetPlayerSettings() SlippiExiTypes::GetPlayerSettingsResponse resp = {}; - std::vector> messagesByPlayer = { - SlippiUser::defaultChatMessages, SlippiUser::defaultChatMessages, SlippiUser::defaultChatMessages, - SlippiUser::defaultChatMessages}; + std::vector> messagesByPlayer = {{}, {}, {}, {}}; // These chat messages will be used when previewing messages - auto userChatMessages = user->GetUserInfo().chatMessages; + auto userChatMessages = user->GetUserChatMessages(); if (userChatMessages.size() == 16) { messagesByPlayer[0] = userChatMessages; @@ -3096,6 +3099,13 @@ void CEXISlippi::handleGetPlayerSettings() for (int i = 0; i < 4; i++) { + // If any of the users in the chat messages vector have a payload that is incorrect, + // force that player to the default chat messages. A valid payload is 16 entries. + if (messagesByPlayer[i].size() != 16) + { + messagesByPlayer[i] = user->GetDefaultChatMessages(); + } + for (int j = 0; j < 16; j++) { auto str = ConvertStringForGame(messagesByPlayer[i][j], MAX_MESSAGE_LENGTH); diff --git a/Source/Core/Core/Slippi/SlippiMatchmaking.cpp b/Source/Core/Core/Slippi/SlippiMatchmaking.cpp index 49f852f70a..11baa0ab14 100644 --- a/Source/Core/Core/Slippi/SlippiMatchmaking.cpp +++ b/Source/Core/Core/Slippi/SlippiMatchmaking.cpp @@ -511,15 +511,20 @@ void SlippiMatchmaking::handleMatchmaking() playerInfo.displayName = el.value("displayName", ""); playerInfo.connectCode = el.value("connectCode", ""); playerInfo.port = el.value("port", 0); - playerInfo.chatMessages = SlippiUser::defaultChatMessages; + if (el["chatMessages"].is_array()) { - playerInfo.chatMessages = el.value("chatMessages", SlippiUser::defaultChatMessages); + playerInfo.chatMessages = el.value("chatMessages", m_user->GetDefaultChatMessages()); + if (playerInfo.chatMessages.size() != 16) { - playerInfo.chatMessages = SlippiUser::defaultChatMessages; + playerInfo.chatMessages = m_user->GetDefaultChatMessages(); } } + else + { + playerInfo.chatMessages = m_user->GetDefaultChatMessages(); + } m_playerInfo.push_back(playerInfo); diff --git a/Source/Core/Core/Slippi/SlippiUser.cpp b/Source/Core/Core/Slippi/SlippiUser.cpp index 6ad40994ca..10b81dc2dc 100644 --- a/Source/Core/Core/Slippi/SlippiUser.cpp +++ b/Source/Core/Core/Slippi/SlippiUser.cpp @@ -1,367 +1,92 @@ -#include "SlippiUser.h" - -#ifdef _WIN32 -#include "AtlBase.h" -#include "AtlConv.h" -#endif - -#include "Common/CommonPaths.h" -#include "Common/FileUtil.h" -#include "Common/Logging/Log.h" -#include "Common/MsgHandler.h" -#include "Common/StringUtil.h" -#include "Common/Thread.h" - #include "Common/Common.h" -#include "Core/ConfigManager.h" - -#include "DolphinWX/Frame.h" -#include "DolphinWX/Main.h" - -#ifdef __APPLE__ -#include "DolphinWX/SlippiAuthWebView/SlippiAuthWebView.h" -#endif - -#include "VideoCommon/OnScreenDisplay.h" - -#include -#include - -#include -using json = nlohmann::json; - -const std::vector SlippiUser::defaultChatMessages = { - "ggs", - "one more", - "brb", - "good luck", - - "well played", - "that was fun", - "thanks", - "too good", +#include "Common/Logging/Log.h" - "sorry", - "my b", - "lol", - "wow", +#include "SlippiUser.h" - "gotta go", - "one sec", - "let's play again later", - "bad connection", -}; +#include "SlippiRustExtensions.h" -#ifdef _WIN32 -#define MAX_SYSTEM_PROGRAM (4096) -static void system_hidden(const char *cmd) +// Takes a RustChatMessages pointer and extracts messages from them, then +// frees the underlying memory safely. +std::vector ConvertChatMessagesFromRust(RustChatMessages *rsMessages) { - PROCESS_INFORMATION p_info; - STARTUPINFO s_info; - - memset(&s_info, 0, sizeof(s_info)); - memset(&p_info, 0, sizeof(p_info)); - s_info.cb = sizeof(s_info); + std::vector chatMessages; - wchar_t utf16cmd[MAX_SYSTEM_PROGRAM] = {0}; - MultiByteToWideChar(CP_UTF8, 0, cmd, -1, utf16cmd, MAX_SYSTEM_PROGRAM); - if (CreateProcessW(NULL, utf16cmd, NULL, NULL, 0, CREATE_NO_WINDOW, NULL, NULL, &s_info, &p_info)) + for (int i = 0; i < rsMessages->len; i++) { - DWORD ExitCode; - WaitForSingleObject(p_info.hProcess, INFINITE); - GetExitCodeProcess(p_info.hProcess, &ExitCode); - CloseHandle(p_info.hProcess); - CloseHandle(p_info.hThread); + std::string message = std::string(rsMessages->data[i]); + chatMessages.push_back(message); } -} -#endif - -static void RunSystemCommand(const std::string &command) -{ -#ifdef _WIN32 - _wsystem(UTF8ToUTF16(command).c_str()); -#else - system(command.c_str()); -#endif -} - -static size_t receive(char *ptr, size_t size, size_t nmemb, void *rcvBuf) -{ - size_t len = size * nmemb; - INFO_LOG(SLIPPI_ONLINE, "[User] Received data: %d", len); - std::string *buf = (std::string *)rcvBuf; + slprs_user_free_messages(rsMessages); - buf->insert(buf->end(), ptr, ptr + len); - - return len; + return chatMessages; } -SlippiUser::SlippiUser() +SlippiUser::SlippiUser(uintptr_t rs_exi_device_ptr) { - CURL *curl = curl_easy_init(); - if (curl) - { - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &receive); - curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 5000); - - // Set up HTTP Headers - m_curlHeaderList = curl_slist_append(m_curlHeaderList, "Content-Type: application/json"); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, m_curlHeaderList); - -#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; - } + slprs_exi_device_ptr = rs_exi_device_ptr; } -SlippiUser::~SlippiUser() -{ - // Wait for thread to terminate - runThread = false; - if (fileListenThread.joinable()) - fileListenThread.join(); - - if (m_curl) - { - curl_slist_free_all(m_curlHeaderList); - curl_easy_cleanup(m_curl); - } -} +SlippiUser::~SlippiUser() {} bool SlippiUser::AttemptLogin() { - std::string userFilePath = File::GetSlippiUserJSONPath(); - -// TODO: Remove a couple updates after ranked -#ifndef __APPLE__ - { -#ifdef _WIN32 - std::string oldUserFilePath = File::GetExeDirectory() + DIR_SEP + "user.json"; -#else - std::string oldUserFilePath = File::GetUserPath(D_USER_IDX) + DIR_SEP + "user.json"; -#endif - if (File::Exists(oldUserFilePath) && !File::Rename(oldUserFilePath, userFilePath)) - { - WARN_LOG(SLIPPI_ONLINE, "Could not move file %s to %s", oldUserFilePath.c_str(), userFilePath.c_str()); - } - } -#endif - - // Get user file - std::string userFileContents; - File::ReadFileToString(userFilePath, userFileContents); - - userInfo = parseFile(userFileContents); - - isLoggedIn = !userInfo.uid.empty(); - if (isLoggedIn) - { - overwriteFromServer(); - WARN_LOG(SLIPPI_ONLINE, "Found user %s (%s)", userInfo.displayName.c_str(), userInfo.uid.c_str()); - } - - return isLoggedIn; + return slprs_user_attempt_login(slprs_exi_device_ptr); } -// On macOS, this will pop open a built-in webview to handle authentication. This is likely to see less and less -// use over time but should hang around for a bit longer; macOS in particular benefits from having this for some -// testing scenarios due to the cumbersome user.json location placement on that system. -// -// Windows and Linux don't have reliable WebView components, so this just pops the user over to slippi.gg for those -// platforms. void SlippiUser::OpenLogInPage() { -#ifdef __APPLE__ - CFrame *cframe = wxGetApp().GetCFrame(); - cframe->OpenSlippiAuthenticationDialog(); -#else - std::string url = "https://slippi.gg/online/enable"; - std::string path = File::GetSlippiUserJSONPath(); - -#ifdef _WIN32 - // On windows, sometimes the path can have backslashes and slashes mixed, convert all to backslashes - path = ReplaceAll(path, "\\", "\\"); - path = ReplaceAll(path, "/", "\\"); -#endif - - std::string fullUrl = url + "?path=" + path; - INFO_LOG(SLIPPI_ONLINE, "[User] Login at path: %s", fullUrl.c_str()); - -#ifdef _WIN32 - std::string command = "explorer \"" + fullUrl + "\""; -#else - std::string command = "xdg-open \"" + fullUrl + "\""; // Linux -#endif - - RunSystemCommand(command); -#endif + slprs_user_open_login_page(slprs_exi_device_ptr); } -bool SlippiUser::UpdateApp() +void SlippiUser::ListenForLogIn() { - std::string url = "https://slippi.gg/downloads?update=true"; - -#ifdef _WIN32 - std::string command = "explorer \"" + url + "\""; -#elif defined(__APPLE__) - std::string command = "open \"" + url + "\""; -#else - std::string command = "xdg-open \"" + url + "\""; // Linux -#endif - - RunSystemCommand(command); - return true; + slprs_user_listen_for_login(slprs_exi_device_ptr); } -void SlippiUser::ListenForLogIn() +bool SlippiUser::UpdateApp() { - if (runThread) - return; - - if (fileListenThread.joinable()) - fileListenThread.join(); - - runThread = true; - fileListenThread = std::thread(&SlippiUser::FileListenThread, this); + return slprs_user_update_app(slprs_exi_device_ptr); } void SlippiUser::LogOut() { - runThread = false; - deleteFile(); - - UserInfo emptyUser; - isLoggedIn = false; - userInfo = emptyUser; + slprs_user_logout(slprs_exi_device_ptr); } void SlippiUser::OverwriteLatestVersion(std::string version) { - userInfo.latestVersion = version; + slprs_user_overwrite_latest_version(slprs_exi_device_ptr, version.c_str()); } SlippiUser::UserInfo SlippiUser::GetUserInfo() { - return userInfo; -} - -bool SlippiUser::IsLoggedIn() -{ - return isLoggedIn; -} + SlippiUser::UserInfo userInfo; -void SlippiUser::FileListenThread() -{ - while (runThread) - { - if (AttemptLogin()) - { - runThread = false; - main_frame->RaiseRenderWindow(); - break; - } + RustUserInfo *info = slprs_user_get_info(slprs_exi_device_ptr); + userInfo.uid = std::string(info->uid); + userInfo.playKey = std::string(info->play_key); + userInfo.displayName = std::string(info->display_name); + userInfo.connectCode = std::string(info->connect_code); + userInfo.latestVersion = std::string(info->latest_version); + slprs_user_free_info(info); - Common::SleepCurrentThread(500); - } + return userInfo; } -inline std::string readString(json obj, std::string key) +std::vector SlippiUser::GetDefaultChatMessages() { - auto item = obj.find(key); - if (item == obj.end() || item.value().is_null()) - { - return ""; - } - - return obj[key]; + RustChatMessages *chatMessages = slprs_user_get_default_messages(slprs_exi_device_ptr); + return ConvertChatMessagesFromRust(chatMessages); } -SlippiUser::UserInfo SlippiUser::parseFile(std::string fileContents) +std::vector SlippiUser::GetUserChatMessages() { - UserInfo info; - info.fileContents = fileContents; - - auto res = json::parse(fileContents, nullptr, false); - if (res.is_discarded() || !res.is_object()) - { - return info; - } - - info.uid = readString(res, "uid"); - info.displayName = readString(res, "displayName"); - info.playKey = readString(res, "playKey"); - info.connectCode = readString(res, "connectCode"); - info.latestVersion = readString(res, "latestVersion"); - - info.chatMessages = SlippiUser::defaultChatMessages; - if (res["chatMessages"].is_array()) - { - info.chatMessages = res.value("chatMessages", SlippiUser::defaultChatMessages); - if (info.chatMessages.size() != 16) - { - info.chatMessages = SlippiUser::defaultChatMessages; - } - } - - return info; -} - -void SlippiUser::deleteFile() -{ - std::string userFilePath = File::GetSlippiUserJSONPath(); - File::Delete(userFilePath); + RustChatMessages *chatMessages = slprs_user_get_messages(slprs_exi_device_ptr); + return ConvertChatMessagesFromRust(chatMessages); } -void SlippiUser::overwriteFromServer() +bool SlippiUser::IsLoggedIn() { - if (!m_curl) - return; - - // Generate URL. If this is a beta version, use the beta endpoint - std::string url = URL_START; - if (scm_slippi_semver_str.find("beta") != std::string::npos) - { - url = url + "-beta"; - } - - ERROR_LOG(SLIPPI_ONLINE, "URL: %s", url.c_str()); - - // Perform curl request - std::string resp; - curl_easy_setopt(m_curl, CURLOPT_URL, (url + "/" + userInfo.uid + "?additionalFields=chatMessages").c_str()); - curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &resp); - CURLcode res = curl_easy_perform(m_curl); - - if (res != 0) - { - ERROR_LOG(SLIPPI, "[User] Error fetching user info from server, code: %d", res); - return; - } - - long responseCode; - curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &responseCode); - if (responseCode != 200) - { - ERROR_LOG(SLIPPI, "[User] Server responded with non-success status: %d", responseCode); - return; - } - - // Overwrite userInfo with data from server - auto r = json::parse(resp); - userInfo.connectCode = r.value("connectCode", userInfo.connectCode); - userInfo.latestVersion = r.value("latestVersion", userInfo.latestVersion); - userInfo.displayName = r.value("displayName", userInfo.displayName); - - if (r["chatMessages"].is_array()) - { - userInfo.chatMessages = r.value("chatMessages", SlippiUser::defaultChatMessages); - if (userInfo.chatMessages.size() != 16) - { - userInfo.chatMessages = SlippiUser::defaultChatMessages; - } - } + return slprs_user_get_is_logged_in(slprs_exi_device_ptr); } diff --git a/Source/Core/Core/Slippi/SlippiUser.h b/Source/Core/Core/Slippi/SlippiUser.h index cecb17d1af..324b7a56db 100644 --- a/Source/Core/Core/Slippi/SlippiUser.h +++ b/Source/Core/Core/Slippi/SlippiUser.h @@ -8,9 +8,18 @@ #include #include +// This class is currently a shim for the Rust user interface. We're doing it this way +// to begin migrating things over without needing to do larger invasive changes. +// +// The remaining methods on here are simply layers that direct the call over to the Rust +// side. A quirk of this is that we're using the EXI device pointer, so this class absolutely +// cannot outlive the EXI device - but we control that and just need to do our due diligence +// when making changes. class SlippiUser { public: + // This type is filled in with data from the Rust side. + // Eventually, this entire class will disappear. struct UserInfo { std::string uid = ""; @@ -18,14 +27,13 @@ class SlippiUser std::string displayName = ""; std::string connectCode = ""; std::string latestVersion = ""; - std::string fileContents = ""; int port; std::vector chatMessages; }; - SlippiUser(); + SlippiUser(uintptr_t rs_exi_device_ptr); ~SlippiUser(); bool AttemptLogin(); @@ -35,24 +43,12 @@ class SlippiUser void LogOut(); void OverwriteLatestVersion(std::string version); UserInfo GetUserInfo(); + std::vector GetUserChatMessages(); + std::vector GetDefaultChatMessages(); bool IsLoggedIn(); - void FileListenThread(); - - const static std::vector defaultChatMessages; protected: - UserInfo parseFile(std::string fileContents); - void deleteFile(); - void overwriteFromServer(); - - UserInfo userInfo; - bool isLoggedIn = false; - - const std::string URL_START = "https://users-rest-dot-slippi.uc.r.appspot.com/user"; - CURL *m_curl = nullptr; - struct curl_slist *m_curlHeaderList = nullptr; - std::vector receiveBuf; - - std::thread fileListenThread; - std::atomic runThread; + // A pointer to a "shadow" EXI Device that lives on the Rust side of things. + // Do *not* do any cleanup of this! The EXI device will handle it. + uintptr_t slprs_exi_device_ptr; }; diff --git a/Source/Core/DolphinWX/CMakeLists.txt b/Source/Core/DolphinWX/CMakeLists.txt index 546e72b1cb..e2afe261e6 100644 --- a/Source/Core/DolphinWX/CMakeLists.txt +++ b/Source/Core/DolphinWX/CMakeLists.txt @@ -106,11 +106,7 @@ if(APPLE) ${CORESERV_LIBRARY} ${IOK_LIBRARY} ${OPENGL_LIBRARY} - ${WEBKIT_FRAMEWORK} ) - - # Keep the WebView login portal around only on macOS, just a bit longer - set(GUI_SRCS ${GUI_SRCS} SlippiAuthWebView/SlippiAuthWebView.cpp) endif() # Add resource files to application bundle. @@ -149,7 +145,7 @@ if(wxWidgets_FOUND) endif() corrosion_import_crate(MANIFEST_PATH ${CMAKE_SOURCE_DIR}/Externals/SlippiRustExtensions/Cargo.toml ${RUST_FEATURES}) - target_link_libraries("${DOLPHIN_EXE}" PUBLIC slippi_rust_extensions) + target_link_libraries("${DOLPHIN_EXE}" PUBLIC slippi-rust-extensions) if(APPLE) include(BundleUtilities) diff --git a/Source/Core/DolphinWX/Frame.h b/Source/Core/DolphinWX/Frame.h index 3ce3cdea78..6cf1fced1f 100644 --- a/Source/Core/DolphinWX/Frame.h +++ b/Source/Core/DolphinWX/Frame.h @@ -22,10 +22,6 @@ #include "DolphinWX/Globals.h" #include "DolphinWX/PlaybackSlider.h" -#ifdef __APPLE__ -#include "DolphinWX/SlippiAuthWebView/SlippiAuthWebView.h" -#endif - #include "InputCommon/GCPadStatus.h" #if defined(HAVE_X11) && HAVE_X11 @@ -122,10 +118,6 @@ class CFrame : public CRenderFrame void ToggleDisplayMode(bool bFullscreen); void ToggleScreenSaver(bool enable); -#ifdef __APPLE__ - void OpenSlippiAuthenticationDialog(); -#endif - static void ConnectWiimote(int wm_idx, bool connect); void UpdateTitle(const std::string& str); void OpenGeneralConfiguration(wxWindowID tab_id = wxID_ANY); @@ -162,10 +154,6 @@ class CFrame : public CRenderFrame CGameListCtrl* m_GameListCtrl = nullptr; CConfigMain* m_main_config_dialog = nullptr; -#ifdef __APPLE__ - SlippiAuthWebView* m_slippi_auth_dialog = nullptr; -#endif - wxPanel* m_Panel = nullptr; CRenderFrame* m_RenderFrame = nullptr; wxWindow* m_RenderParent = nullptr; @@ -206,10 +194,6 @@ class CFrame : public CRenderFrame void BindDebuggerMenuBarEvents(); void BindDebuggerMenuBarUpdateEvents(); -#ifdef __APPLE__ - void ShowSlippiAuthenticationDialog(); -#endif - wxToolBar* OnCreateToolBar(long style, wxWindowID id, const wxString& name) override; wxMenuBar* CreateMenuBar() const; diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index 7b0f26ba72..4e63b2fc59 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -270,25 +270,6 @@ void CFrame::OpenGeneralConfiguration(wxWindowID tab_id) m_main_config_dialog->SetFocus(); } -// Only macOS has WebView support. -#ifdef __APPLE__ -// Actually create and show the wxWebView control. -void CFrame::ShowSlippiAuthenticationDialog() -{ - m_slippi_auth_dialog = new SlippiAuthWebView(this); - m_slippi_auth_dialog->Show(); - m_slippi_auth_dialog->SetFocus(); -} - -// This is the entry point for our webview dialog. `CallAfter` is used -// as the call could come from a background thread, and `wxWebView` needs to -// be instantiated on the main thread, which this does. -void CFrame::OpenSlippiAuthenticationDialog() -{ - CallAfter(&CFrame::ShowSlippiAuthenticationDialog); -} -#endif - // Menu items // Start the game or change the disc. diff --git a/Source/Core/DolphinWX/SlippiAuthWebView/SlippiAuthWebView.cpp b/Source/Core/DolphinWX/SlippiAuthWebView/SlippiAuthWebView.cpp deleted file mode 100644 index 4be3ba20f5..0000000000 --- a/Source/Core/DolphinWX/SlippiAuthWebView/SlippiAuthWebView.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include "DolphinWX/SlippiAuthWebView/SlippiAuthWebView.h" - -#include -#include -#include -#include - -#include - -#include "Common/CommonTypes.h" -#include "Common/FileUtil.h" -#include "Common/Logging/Log.h" - -#include "Core/Slippi/SlippiUser.h" - -#include "DolphinWX/WxUtils.h" - -#include -using json = nlohmann::json; - -SlippiAuthWebView::SlippiAuthWebView(wxWindow* parent, wxWindowID id, const wxString& title, - const wxPoint& position, const wxSize& size, long style) - : wxDialog(parent, id, title, position, size, style) -{ - Bind(wxEVT_CLOSE_WINDOW, &SlippiAuthWebView::OnClose, this); - Bind(wxEVT_BUTTON, &SlippiAuthWebView::OnCloseButton, this, wxID_CLOSE); - Bind(wxEVT_SHOW, &SlippiAuthWebView::OnShow, this); - - wxDialog::SetExtraStyle(GetExtraStyle() & ~wxWS_EX_BLOCK_EVENTS); - - CreateGUIControls(); -} - -SlippiAuthWebView::~SlippiAuthWebView() -{ -} - -// On Windows, Edge *may* be available as of Oct 19th 2020. But it also might not. -// So, this is a check to silo some platform-specific logic, and should be called -// before initiating this flow (and opt to use another method of authentication, perhaps). -bool SlippiAuthWebView::IsAvailable() -{ -#ifdef _WIN32 - if (!wxWebView::IsBackendAvailable(wxWebViewBackendEdge)) - { - return false; - } -#endif - - // macOS will almost certainly have a WebView, as it's built in to the platform. - // Linux is built explicitly with webkitgtk2 and should have it, but this is admittedly - // a bit of an assumption... - return true; -} - -void SlippiAuthWebView::CreateGUIControls() -{ - std::string url = "https://slippi.gg/online/enable?isWebview=true"; - - // On Windows, we need to explicitly force it to elect to use Edge. - // The other platforms use WebKit, thankfully... which is a one-liner. -#ifdef _WIN32 - m_browser = wxWebView::New(this, wxID_ANY, url, wxDefaultPosition, wxDefaultSize, wxWebViewBackendEdge); -#else - m_browser = wxWebView::New(this, wxID_ANY, url); -#endif - - Bind(wxEVT_WEBVIEW_TITLE_CHANGED, &SlippiAuthWebView::OnTitleChanged, this, m_browser->GetId()); - - const int space5 = FromDIP(5); - wxBoxSizer* const main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->AddSpacer(space5); - main_sizer->Add(m_browser, 1, wxEXPAND, space5); - main_sizer->SetMinSize(800, 600); - - SetLayoutAdaptationMode(wxDIALOG_ADAPTATION_MODE_ENABLED); - SetLayoutAdaptationLevel(wxDIALOG_ADAPTATION_STANDARD_SIZER); - SetSizerAndFit(main_sizer); -} - -void SlippiAuthWebView::OnClose(wxCloseEvent& WXUNUSED(event)) -{ - delete this; -} - -void SlippiAuthWebView::OnShow(wxShowEvent& event) -{ - if (event.IsShown()) - CenterOnParent(); -} - -void SlippiAuthWebView::OnCloseButton(wxCommandEvent& WXUNUSED(event)) -{ - delete this; -} - -// A cross-platform method of passing data from a webview: simply shove it through the -// document title. Our JSON payload is small enough (and in practice, browsers don't seem to -// limit this) that it works out fine. -void SlippiAuthWebView::OnTitleChanged(wxWebViewEvent& evt) -{ - wxString title = evt.GetString(); - wxString prefix("SlippiUser:"); - - // If it's not the first thing, don't grab it. - if (title.Find(prefix) != 0) - { - return; - } - - int prefix_length = prefix.length(); - wxString userJSON = title.substr(prefix_length, title.length() - prefix_length); - std::string user = std::string(userJSON.mb_str()); - - // INFO_LOG(SLIPPI, "JSON: %s", user.c_str()); - - // As a sanity check, let's try to parse it before writing it and make sure it's an actual - // JSON object - i.e, don't write an arbitrary file. ;P - auto res = json::parse(user, nullptr, false); - if (res.is_discarded() || !res.is_object()) - { - ERROR_LOG(SLIPPI, "File is invalid JSON, or not an object."); - return; - } - - // Now we can write it and do some cleanup - std::string userFilePath = File::GetSlippiUserJSONPath(); - File::WriteStringToFile(user, userFilePath); - - // At this point, the background thread in SlippiUser will pick it up and the game should be - // logged in. From this point on, we want to go ahead and clean this up - having a browser instance - // sitting around in memory is no fun when running a game. ;P - delete this; -} diff --git a/Source/Core/DolphinWX/SlippiAuthWebView/SlippiAuthWebView.h b/Source/Core/DolphinWX/SlippiAuthWebView/SlippiAuthWebView.h deleted file mode 100644 index 6238951935..0000000000 --- a/Source/Core/DolphinWX/SlippiAuthWebView/SlippiAuthWebView.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#ifdef __APPLE__ -#include -#include -#include "Common/CommonTypes.h" - -class SlippiAuthWebView : public wxDialog -{ -public: - SlippiAuthWebView(wxWindow* parent, wxWindowID id = wxID_ANY, - const wxString& title = _("Sign In to Slippi"), - const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, - long style = wxDEFAULT_DIALOG_STYLE); - virtual ~SlippiAuthWebView(); - - void OnTitleChanged(wxWebViewEvent& evt); - static bool IsAvailable(); - -private: - void CreateGUIControls(); - void OnClose(wxCloseEvent& event); - void OnCloseButton(wxCommandEvent& event); - void OnShow(wxShowEvent& event); - - wxWebView* m_browser; -}; -#endif