diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt
index c4f153569d..9a56d19bdc 100644
--- a/Source/Core/Common/CMakeLists.txt
+++ b/Source/Core/Common/CMakeLists.txt
@@ -29,6 +29,7 @@ set(SRCS
PcapFile.cpp
PerformanceCounter.cpp
Profiler.cpp
+ QoSSession.cpp
SDCardUtil.cpp
SettingsHandler.cpp
StringUtil.cpp
diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj
index 0198b8f1cc..a518b531f1 100644
--- a/Source/Core/Common/Common.vcxproj
+++ b/Source/Core/Common/Common.vcxproj
@@ -137,6 +137,7 @@
+
@@ -196,6 +197,7 @@
+
diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters
index b5799a167c..50ea0f619d 100644
--- a/Source/Core/Common/Common.vcxproj.filters
+++ b/Source/Core/Common/Common.vcxproj.filters
@@ -58,6 +58,7 @@
+
@@ -282,6 +283,7 @@
+
diff --git a/Source/Core/Common/QoSSession.cpp b/Source/Core/Common/QoSSession.cpp
new file mode 100644
index 0000000000..992d15d273
--- /dev/null
+++ b/Source/Core/Common/QoSSession.cpp
@@ -0,0 +1,87 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "Common/QoSSession.h"
+#include "Core/ConfigManager.h"
+
+#if defined(_WIN32)
+#include
+#pragma comment(lib, "qwave")
+#endif
+
+namespace Common
+{
+#if defined(_WIN32)
+QoSSession::QoSSession(ENetPeer* peer, int tos_val) : m_peer(peer)
+{
+ QOS_VERSION ver = {1, 0};
+
+ if (!QOSCreateHandle(&ver, &m_qos_handle))
+ return;
+
+ sockaddr_in sin = {};
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = ENET_HOST_TO_NET_16(peer->host->address.port);
+ sin.sin_addr.s_addr = peer->host->address.host;
+
+ if (QOSAddSocketToFlow(m_qos_handle, peer->host->socket, reinterpret_cast(&sin),
+ QOSTrafficTypeControl, QOS_NON_ADAPTIVE_FLOW, &m_qos_flow_id))
+ {
+ // We shift the complete ToS value by 3 to get rid of the 3 bit ECN field
+ DWORD dscp = static_cast(tos_val >> 3);
+
+ // Sets DSCP to the same as Linux
+ // This will fail if we're not admin, but we ignore it
+ QOSSetFlow(m_qos_handle, m_qos_flow_id, QOSSetOutgoingDSCPValue, sizeof(DWORD), &dscp, 0,
+ nullptr);
+
+ m_success = true;
+ }
+}
+
+QoSSession::~QoSSession()
+{
+ if (m_qos_handle == nullptr)
+ return;
+
+ if (m_qos_flow_id != 0)
+ QOSRemoveSocketFromFlow(m_qos_handle, m_peer->host->socket, m_qos_flow_id, 0);
+
+ QOSCloseHandle(m_qos_handle);
+}
+#else
+QoSSession::QoSSession(ENetPeer* peer, int tos_val)
+{
+#if defined(__linux__)
+ constexpr int priority = 7;
+ setsockopt(peer->host->socket, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority));
+#endif
+
+ m_success = setsockopt(peer->host->socket, IPPROTO_IP, IP_TOS, &tos_val, sizeof(tos_val)) == 0;
+}
+
+QoSSession::~QoSSession() = default;
+#endif
+
+QoSSession& QoSSession::operator=(QoSSession&& session)
+{
+ if (this != &session)
+ {
+#if defined(_WIN32)
+ m_qos_handle = session.m_qos_handle;
+ m_qos_flow_id = session.m_qos_flow_id;
+ m_peer = session.m_peer;
+
+ session.m_qos_handle = nullptr;
+ session.m_qos_flow_id = 0;
+ session.m_peer = nullptr;
+#endif
+ m_success = session.m_success;
+ session.m_success = false;
+ }
+
+ return *this;
+}
+} // namespace Common
diff --git a/Source/Core/Common/QoSSession.h b/Source/Core/Common/QoSSession.h
new file mode 100644
index 0000000000..0f54e1d3d7
--- /dev/null
+++ b/Source/Core/Common/QoSSession.h
@@ -0,0 +1,40 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+#include
+
+namespace Common
+{
+class QoSSession
+{
+public:
+ // 1 0 1 1 1 0 0 0
+ // DSCP ECN
+ static constexpr int ef_tos = 0b10111000;
+
+ QoSSession() = default;
+ QoSSession(ENetPeer* peer, int tos_val = ef_tos);
+
+ ~QoSSession();
+
+ QoSSession& operator=(const QoSSession&) = delete;
+ QoSSession(const QoSSession&) = delete;
+
+ QoSSession& operator=(QoSSession&& session);
+ QoSSession(QoSSession&& session) { *this = std::move(session); }
+ bool Successful() const { return m_success; }
+private:
+#if defined(_WIN32)
+ void* m_qos_handle = nullptr;
+ unsigned long m_qos_flow_id = 0;
+
+ ENetPeer* m_peer = nullptr;
+#endif
+
+ bool m_success = false;
+};
+} // namespace Common
diff --git a/Source/Core/Core/Config/NetplaySettings.cpp b/Source/Core/Core/Config/NetplaySettings.cpp
index 3b0082329d..4336c95cef 100644
--- a/Source/Core/Core/Config/NetplaySettings.cpp
+++ b/Source/Core/Core/Config/NetplaySettings.cpp
@@ -33,4 +33,6 @@ const ConfigInfo NETPLAY_SELECTED_HOST_GAME{
{System::Main, "NetPlay", "SelectedHostGame"}, ""};
const ConfigInfo NETPLAY_USE_UPNP{{System::Main, "NetPlay", "UseUPNP"}, false};
+const ConfigInfo NETPLAY_ENABLE_QOS{{System::Main, "NetPlay", "EnableQoS"}, true};
+
} // namespace Config
diff --git a/Source/Core/Core/Config/NetplaySettings.h b/Source/Core/Core/Config/NetplaySettings.h
index 1978fdeb84..71af0ff6a8 100644
--- a/Source/Core/Core/Config/NetplaySettings.h
+++ b/Source/Core/Core/Config/NetplaySettings.h
@@ -29,4 +29,6 @@ extern const ConfigInfo NETPLAY_NICKNAME;
extern const ConfigInfo NETPLAY_SELECTED_HOST_GAME;
extern const ConfigInfo NETPLAY_USE_UPNP;
+extern const ConfigInfo NETPLAY_ENABLE_QOS;
+
} // namespace Config
diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp
index 465da26370..6b62fec45a 100644
--- a/Source/Core/Core/NetPlayClient.cpp
+++ b/Source/Core/Core/NetPlayClient.cpp
@@ -20,9 +20,11 @@
#include "Common/ENetUtil.h"
#include "Common/MD5.h"
#include "Common/MsgHandler.h"
+#include "Common/QoSSession.h"
#include "Common/StringUtil.h"
#include "Common/Timer.h"
#include "Common/Version.h"
+#include "Core/Config/NetplaySettings.h"
#include "Core/ConfigManager.h"
#include "Core/HW/EXI/EXI_DeviceIPL.h"
#include "Core/HW/SI/SI.h"
@@ -632,6 +634,17 @@ void NetPlayClient::SendAsync(sf::Packet&& packet)
// called from ---NETPLAY--- thread
void NetPlayClient::ThreadFunc()
{
+ Common::QoSSession qos_session;
+ if (Config::Get(Config::NETPLAY_ENABLE_QOS))
+ {
+ qos_session = Common::QoSSession(m_server);
+
+ if (qos_session.Successful())
+ m_dialog->AppendChat("Quality of Service (QoS) was successfully enabled.");
+ else
+ m_dialog->AppendChat("Quality of Service (QoS) couldn't be enabled.");
+ }
+
while (m_do_loop.IsSet())
{
ENetEvent netEvent;
diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp
index 595c246ec1..e41146a543 100644
--- a/Source/Core/Core/NetPlayServer.cpp
+++ b/Source/Core/Core/NetPlayServer.cpp
@@ -21,10 +21,12 @@
#include "Common/StringUtil.h"
#include "Common/UPnP.h"
#include "Common/Version.h"
+#include "Core/Config/NetplaySettings.h"
#include "Core/ConfigManager.h"
#include "Core/HW/Sram.h"
#include "Core/NetPlayClient.h" //for NetPlayUI
#include "InputCommon/GCPadStatus.h"
+
#if !defined(_WIN32)
#include
#include
@@ -273,6 +275,7 @@ unsigned int NetPlayServer::OnConnect(ENetPeer* socket)
Client player;
player.pid = pid;
player.socket = socket;
+
rpac >> player.revision;
rpac >> player.name;
@@ -343,10 +346,13 @@ unsigned int NetPlayServer::OnConnect(ENetPeer* socket)
Send(player.socket, spac);
}
+ if (Config::Get(Config::NETPLAY_ENABLE_QOS))
+ player.qos_session = Common::QoSSession(player.socket);
+
// add client to the player list
{
std::lock_guard lkp(m_crit.players);
- m_players.emplace(*(PlayerId*)player.socket->data, player);
+ m_players.emplace(*(PlayerId*)player.socket->data, std::move(player));
UpdatePadMapping(); // sync pad mappings with everyone
UpdateWiimoteMapping();
}
diff --git a/Source/Core/Core/NetPlayServer.h b/Source/Core/Core/NetPlayServer.h
index e67aaf1266..b0f52f8000 100644
--- a/Source/Core/Core/NetPlayServer.h
+++ b/Source/Core/Core/NetPlayServer.h
@@ -6,12 +6,14 @@
#include
#include