Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 40 additions & 3 deletions Source/Core/Common/HttpRequest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@ class HttpRequest::Impl final
{
GET,
POST,
Delete,
};

explicit Impl(std::chrono::milliseconds timeout_ms, ProgressCallback callback);

bool IsValid() const;
std::string GetHeaderValue(std::string_view name) const;
void SetCookies(const std::string& cookies);
void SetTimeout(std::chrono::milliseconds timeout);
void SetSensitive(bool sensitive) { m_sensitive = sensitive; }
void UseIPv4();
void FollowRedirects(long max);
s32 GetLastResponseCode();
Expand All @@ -49,6 +52,7 @@ class HttpRequest::Impl final
std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> m_curl{nullptr, curl_easy_cleanup};
std::string m_error_string;
std::string m_final_url;
bool m_sensitive = false;
};

HttpRequest::HttpRequest(std::chrono::milliseconds timeout_ms, ProgressCallback callback)
Expand Down Expand Up @@ -77,6 +81,27 @@ void HttpRequest::SetCookies(const std::string& cookies)
m_impl->SetCookies(cookies);
}

void HttpRequest::SetTimeout(std::chrono::milliseconds timeout)
{
m_impl->SetTimeout(timeout);
}

void HttpRequest::SetSensitive(bool sensitive)
{
m_impl->SetSensitive(sensitive);
}

void HttpRequest::Impl::SetTimeout(std::chrono::milliseconds timeout)
{
curl_easy_setopt(m_curl.get(), CURLOPT_TIMEOUT_MS, static_cast<long>(timeout.count()));
}

HttpRequest::Response HttpRequest::Delete(const std::string& url, const Headers& headers,
AllowedReturnCodes codes)
{
return m_impl->Fetch(url, Impl::Method::Delete, headers, nullptr, 0, codes);
}

void HttpRequest::UseIPv4()
{
m_impl->UseIPv4();
Expand Down Expand Up @@ -270,6 +295,9 @@ HttpRequest::Response HttpRequest::Impl::Fetch(const std::string& url, Method me
const std::vector<Multiform>& multiform)
{
m_response_headers.clear();
// CUSTOMREQUEST persists across transfers, so reset it for GET/POST reuse.
curl_easy_setopt(m_curl.get(), CURLOPT_CUSTOMREQUEST,
method == Impl::Method::Delete ? "DELETE" : nullptr);
curl_easy_setopt(m_curl.get(), CURLOPT_POST, method == Impl::Method::POST);
curl_easy_setopt(m_curl.get(), CURLOPT_URL, url.c_str());
if (method == Impl::Method::POST && multiform.empty())
Expand Down Expand Up @@ -316,11 +344,16 @@ HttpRequest::Response HttpRequest::Impl::Fetch(const std::string& url, Method me
curl_easy_setopt(m_curl.get(), CURLOPT_WRITEFUNCTION, CurlWriteCallback);
curl_easy_setopt(m_curl.get(), CURLOPT_WRITEDATA, &buffer);

const char* type = method == Impl::Method::POST ? "POST" : "GET";
const char* type = method == Impl::Method::POST ? "POST" :
method == Impl::Method::Delete ? "DELETE" :
"GET";
const CURLcode res = curl_easy_perform(m_curl.get());
if (res != CURLE_OK)
{
ERROR_LOG_FMT(COMMON, "Failed to {} {}: {}", type, url, m_error_string);
if (m_sensitive)
ERROR_LOG_FMT(COMMON, "Sensitive HTTP request failed");
else
ERROR_LOG_FMT(COMMON, "Failed to {} {}: {}", type, url, m_error_string);
return {};
}

Expand All @@ -337,7 +370,11 @@ HttpRequest::Response HttpRequest::Impl::Fetch(const std::string& url, Method me
curl_easy_getinfo(m_curl.get(), CURLINFO_RESPONSE_CODE, &response_code);
if (response_code != 200)
{
if (buffer.empty())
if (m_sensitive)
{
ERROR_LOG_FMT(COMMON, "Sensitive HTTP request failed with status {}", response_code);
}
else if (buffer.empty())
{
ERROR_LOG_FMT(COMMON, "Failed to {} {}: server replied with code {}", type, url,
response_code);
Expand Down
7 changes: 7 additions & 0 deletions Source/Core/Common/HttpRequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ class HttpRequest final
};

void SetCookies(const std::string& cookies);
// Optional total transfer deadline; existing callers retain their timeout behavior.
void SetTimeout(std::chrono::milliseconds timeout);
// Suppress URLs, response bodies and curl diagnostics containing bearer capabilities.
void SetSensitive(bool sensitive);
void UseIPv4();
void FollowRedirects(long max = 1);
s32 GetLastResponseCode() const;
Expand All @@ -58,6 +62,9 @@ class HttpRequest final
Response Post(const std::string& url, std::string_view payload, const Headers& headers = {},
AllowedReturnCodes codes = AllowedReturnCodes::Ok_Only);

Response Delete(const std::string& url, const Headers& headers = {},
AllowedReturnCodes codes = AllowedReturnCodes::Ok_Only);

Response PostMultiform(const std::string& url, const std::vector<Multiform>& multiform,
const Headers& headers = {},
AllowedReturnCodes codes = AllowedReturnCodes::Ok_Only);
Expand Down
11 changes: 11 additions & 0 deletions Source/Core/Core/Config/NetplaySettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ static constexpr u16 DEFAULT_LISTEN_PORT = 2626;

// Main.NetPlay

const Info<std::string> NETPLAY_QUICKPLAY_MATCHMAKING{
{System::Main, "NetPlay", "QuickPlayMatchmaking"}, "lobby"};
const Info<std::string> NETPLAY_QUICKPLAY_COORDINATOR{
{System::Main, "NetPlay", "QuickPlayCoordinator"}, ""};
const Info<std::string> NETPLAY_QUICKPLAY_REGIONS{{System::Main, "NetPlay", "QuickPlayRegions"},
""};
const Info<std::string> NETPLAY_QUICKPLAY_REGION{{System::Main, "NetPlay", "QuickPlayRegion"},
"us-east"};
const Info<std::string> NETPLAY_QUICKPLAY_HOST_PREFERENCE{
{System::Main, "NetPlay", "QuickPlayHostPreference"}, "none"};

const Info<std::string> NETPLAY_TRAVERSAL_SERVER{{System::Main, "NetPlay", "TraversalServer"},
"stun.dolphin-emu.org"};
const Info<u16> NETPLAY_TRAVERSAL_PORT{{System::Main, "NetPlay", "TraversalPort"}, 6262};
Expand Down
10 changes: 10 additions & 0 deletions Source/Core/Core/Config/NetplaySettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ namespace Config

// Main.NetPlay

// "lobby" (Dolphin's NetPlay index, NETPLAY_INDEX_URL) or "coordinator".
extern const Info<std::string> NETPLAY_QUICKPLAY_MATCHMAKING;
extern const Info<std::string> NETPLAY_QUICKPLAY_COORDINATOR;
// Comma-separated region tokens. Empty means not saved yet: read the legacy single
// QuickPlayRegion instead (see QuickPlayMatchmaker::LoadRegions).
extern const Info<std::string> NETPLAY_QUICKPLAY_REGIONS;
extern const Info<std::string> NETPLAY_QUICKPLAY_REGION;
// "host", "none" or "join".
extern const Info<std::string> NETPLAY_QUICKPLAY_HOST_PREFERENCE;

extern const Info<std::string> NETPLAY_TRAVERSAL_SERVER;
extern const Info<u16> NETPLAY_TRAVERSAL_PORT;
extern const Info<u16> NETPLAY_TRAVERSAL_PORT_ALT;
Expand Down
4 changes: 4 additions & 0 deletions Source/Core/Core/NetPlayClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ class NetPlayUI
virtual void OnConnectionError(const std::string& message) = 0;
virtual void OnTraversalError(Common::TraversalClient::FailureReason error) = 0;
virtual void OnTraversalStateChanged(Common::TraversalClient::State state) = 0;
// Host-owned value snapshot; default no-op for other frontends.
virtual void OnHostTraversalStateChanged(u64, Common::TraversalClient::State, const std::string&)
{
}
virtual void OnGameStartAborted() = 0;
virtual void OnGolferChanged(bool is_golfer, const std::string& golfer_name) = 0;
virtual void OnTtlDetermined(u8 ttl) = 0;
Expand Down
35 changes: 31 additions & 4 deletions Source/Core/Core/NetPlayServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ NetPlayServer::~NetPlayServer()

// called from ---GUI--- thread
NetPlayServer::NetPlayServer(const u16 port, const bool forward_port, NetPlayUI* dialog,
const NetTraversalConfig& traversal_config)
: m_dialog(dialog)
const NetTraversalConfig& traversal_config, u64 quickplay_attempt)
: m_quickplay_attempt(quickplay_attempt), m_dialog(dialog)
{
//--use server time
if (enet_initialize() != 0)
Expand Down Expand Up @@ -166,6 +166,8 @@ NetPlayServer::NetPlayServer(const u16 port, const bool forward_port, NetPlayUI*
{
is_connected = true;
m_do_loop = true;
// Snapshot before starting the service thread: handles an already-connected shared client.
NotifyHostTraversalState();
m_thread = std::thread(&NetPlayServer::ThreadFunc, this);
m_minimum_buffer_size = 3;
m_chunked_data_thread = std::thread(&NetPlayServer::ChunkedDataThreadFunc, this);
Expand Down Expand Up @@ -199,7 +201,9 @@ static bool IsValidPadIndex(const T& map_array, PadIndex index)

void NetPlayServer::SetupIndex()
{
if (!Config::Get(Config::NETPLAY_USE_INDEX) || Config::Get(Config::NETPLAY_INDEX_NAME).empty() ||
// Immutable per-server override, including every traversal reconnect. Manual config is untouched.
if (m_quickplay_attempt != 0 || !Config::Get(Config::NETPLAY_USE_INDEX) ||
Config::Get(Config::NETPLAY_INDEX_NAME).empty() ||
Config::Get(Config::NETPLAY_INDEX_REGION).empty())
{
return;
Expand Down Expand Up @@ -434,6 +438,10 @@ static void SendSyncIdentifier(sf::Packet& spac, const SyncIdentifier& sync_iden
// called from ---NETPLAY--- thread
ConnectionError NetPlayServer::OnConnect(ENetPeer* incoming_connection, sf::Packet& received_packet)
{
// A Quick Play room is for one opponent, even after that opponent leaves.
if (m_quickplay_attempt != 0 && m_quickplay_had_peer)
return ConnectionError::ServerFull;

std::string netplay_version;
received_packet >> netplay_version;
if (netplay_version != Common::GetScmRevGitStr())
Expand Down Expand Up @@ -463,6 +471,9 @@ ConnectionError NetPlayServer::OnConnect(ENetPeer* incoming_connection, sf::Pack
// force a ping on first netplay loop
m_update_pings = true;

if (m_quickplay_attempt != 0 && new_player.pid != 1)
m_quickplay_had_peer = true;

AssignNewUserAPad(new_player);

// tell other players a new player joined
Expand Down Expand Up @@ -1337,8 +1348,24 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player)
return 0;
}

void NetPlayServer::NotifyHostTraversalState()
{
if (!m_quickplay_attempt || !m_dialog || !m_traversal_client)
return;
const auto state = m_traversal_client->GetState();
std::string code;
if (state == Common::TraversalClient::State::Connected)
{
const auto id = m_traversal_client->GetHostID();
if (id[0] != '\0')
code.assign(id.begin(), id.end());
}
m_dialog->OnHostTraversalStateChanged(m_quickplay_attempt, state, code);
}

void NetPlayServer::OnTraversalStateChanged()
{
NotifyHostTraversalState();
const Common::TraversalClient::State state = m_traversal_client->GetState();

if (Common::g_TraversalClient->GetHostID()[0] != '\0')
Expand All @@ -1347,7 +1374,7 @@ void NetPlayServer::OnTraversalStateChanged()
if (!m_dialog)
return;

if (state == Common::TraversalClient::State::Failure)
if (state == Common::TraversalClient::State::Failure && !m_quickplay_attempt)
m_dialog->OnTraversalError(m_traversal_client->GetFailureReason());

m_dialog->OnTraversalStateChanged(state);
Expand Down
7 changes: 6 additions & 1 deletion Source/Core/Core/NetPlayServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ class NetPlayServer : public Common::TraversalClientClient
const std::string& title = "");

NetPlayServer(u16 port, bool forward_port, NetPlayUI* dialog,
const NetTraversalConfig& traversal_config);
const NetTraversalConfig& traversal_config, u64 quickplay_attempt = 0);
u64 GetQuickPlayAttempt() const { return m_quickplay_attempt; }
~NetPlayServer() override;

bool ChangeGame(const SyncIdentifier& sync_identifier, const std::string& netplay_name);
Expand Down Expand Up @@ -181,6 +182,10 @@ class NetPlayServer : public Common::TraversalClientClient
PlayerId m_current_golfer = 1;
PlayerId m_pending_golfer = 0;

const u64 m_quickplay_attempt;
bool m_quickplay_had_peer = false; // Latched under m_crit.game.
void NotifyHostTraversalState();

std::map<PlayerId, Client> m_players;

std::unordered_map<u32, std::vector<std::pair<PlayerId, u64>>> m_timebase_by_frame;
Expand Down
16 changes: 16 additions & 0 deletions Source/Core/DolphinQt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,22 @@ add_executable(project-plus-dolphin
ProjectPlus/InstallUpdateDialog.h
ProjectPlus/UpdateDialog.cpp
ProjectPlus/UpdateDialog.h
QuickPlay/QuickPlayClient.cpp
QuickPlay/QuickPlayClient.h
QuickPlay/QuickPlayController.cpp
QuickPlay/QuickPlayController.h
QuickPlay/QuickPlayDialog.cpp
QuickPlay/QuickPlayDialog.h
QuickPlay/QuickPlayIndexMatchmaker.cpp
QuickPlay/QuickPlayIndexMatchmaker.h
QuickPlay/QuickPlayLauncher.cpp
QuickPlay/QuickPlayLauncher.h
QuickPlay/QuickPlayMatchmaker.cpp
QuickPlay/QuickPlayMatchmaker.h
QuickPlay/QuickPlaySession.cpp
QuickPlay/QuickPlaySession.h
QuickPlay/QuickPlaySettingsDialog.cpp
QuickPlay/QuickPlaySettingsDialog.h
MenuBar.cpp
MenuBar.h
NetPlay/ChunkedProgressDialog.cpp
Expand Down
16 changes: 16 additions & 0 deletions Source/Core/DolphinQt/DolphinQt.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,14 @@
<ClCompile Include="ProjectPlus/DownloadWorker.cpp" />
<ClCompile Include="ProjectPlus/InstallUpdateDialog.cpp" />
<ClCompile Include="ProjectPlus/UpdateDialog.cpp" />
<ClCompile Include="QuickPlay\QuickPlayClient.cpp" />
<ClCompile Include="QuickPlay\QuickPlayController.cpp" />
<ClCompile Include="QuickPlay\QuickPlayDialog.cpp" />
<ClCompile Include="QuickPlay\QuickPlayIndexMatchmaker.cpp" />
<ClCompile Include="QuickPlay\QuickPlayLauncher.cpp" />
<ClCompile Include="QuickPlay\QuickPlayMatchmaker.cpp" />
<ClCompile Include="QuickPlay\QuickPlaySession.cpp" />
<ClCompile Include="QuickPlay\QuickPlaySettingsDialog.cpp" />
<ClCompile Include="MenuBar.cpp" />
<ClCompile Include="NetPlay\ChunkedProgressDialog.cpp" />
<ClCompile Include="NetPlay\GameDigestDialog.cpp" />
Expand Down Expand Up @@ -279,6 +287,10 @@
<QtMoc Include="Config\PatchesWidget.h" />
<ClInclude Include="Debugger\RegisterColumn.h" />
<ClInclude Include="GBAHost.h" />
<ClInclude Include="QuickPlay\QuickPlayClient.h" />
<ClInclude Include="QuickPlay\QuickPlayIndexMatchmaker.h" />
<ClInclude Include="QuickPlay\QuickPlayLauncher.h" />
<ClInclude Include="QuickPlay\QuickPlayMatchmaker.h" />
<ClInclude Include="QtUtils\ActionHelper.h" />
<ClInclude Include="QtUtils\ClearLayoutRecursively.h" />
<QtMoc Include="QtUtils\ClickableStatusBar.h" />
Expand Down Expand Up @@ -430,6 +442,10 @@
<QtMoc Include="ProjectPlus/DownloadWorker.h" />
<QtMoc Include="ProjectPlus/InstallUpdateDialog.h" />
<QtMoc Include="ProjectPlus/UpdateDialog.h" />
<QtMoc Include="QuickPlay\QuickPlayController.h" />
<QtMoc Include="QuickPlay\QuickPlayDialog.h" />
<QtMoc Include="QuickPlay\QuickPlaySession.h" />
<QtMoc Include="QuickPlay\QuickPlaySettingsDialog.h" />
<QtMoc Include="MenuBar.h" />
<QtMoc Include="NetPlay\ChunkedProgressDialog.h" />
<QtMoc Include="NetPlay\GameDigestDialog.h" />
Expand Down
Loading