diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index e80b2cb786..6c3b6ae79b 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -1,6 +1,7 @@ set(SRCS BreakPoints.cpp CDUtils.cpp ColorUtil.cpp + ENetUtil.cpp FileSearch.cpp FileUtil.cpp GekkoDisassembler.cpp diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj index ced0c431df..7b2c278cd1 100644 --- a/Source/Core/Common/Common.vcxproj +++ b/Source/Core/Common/Common.vcxproj @@ -51,6 +51,7 @@ + @@ -94,6 +95,7 @@ + @@ -146,4 +148,4 @@ - \ No newline at end of file + diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters index 8545274fc4..ffaf7d6be8 100644 --- a/Source/Core/Common/Common.vcxproj.filters +++ b/Source/Core/Common/Common.vcxproj.filters @@ -25,6 +25,7 @@ + @@ -78,6 +79,7 @@ + diff --git a/Source/Core/Common/ENetUtil.cpp b/Source/Core/Common/ENetUtil.cpp new file mode 100644 index 0000000000..ae5b19b2c3 --- /dev/null +++ b/Source/Core/Common/ENetUtil.cpp @@ -0,0 +1,40 @@ +// Copyright 2015 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "ENetUtil.h" + +namespace ENetUtil +{ + +void WakeupThread(ENetHost* host) +{ + // Send ourselves a spurious message. This is hackier than it should be. + // comex reported this as https://github.com/lsalzman/enet/issues/23, so + // hopefully there will be a better way to do it in the future. + ENetAddress address; + if (host->address.port != 0) + address.port = host->address.port; + else + enet_socket_get_address(host->socket, &address); + address.host = 0x0100007f; // localhost + u8 byte = 0; + ENetBuffer buf; + buf.data = &byte; + buf.dataLength = 1; + enet_socket_send(host->socket, &address, &buf, 1); +} + +int ENET_CALLBACK InterceptCallback(ENetHost* host, ENetEvent* event) +{ + // wakeup packet received + if (host->receivedDataLength == 1 && host->receivedData[0] == 0) + { + event->type = (ENetEventType) 42; + return 1; + } + return 0; +} + + +} diff --git a/Source/Core/Common/ENetUtil.h b/Source/Core/Common/ENetUtil.h new file mode 100644 index 0000000000..219f51d425 --- /dev/null +++ b/Source/Core/Common/ENetUtil.h @@ -0,0 +1,16 @@ +// Copyright 2015 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. +// +#pragma once + +#include +#include "Common.h" + +namespace ENetUtil +{ + +void WakeupThread(ENetHost* host); +int ENET_CALLBACK InterceptCallback(ENetHost* host, ENetEvent* event); + +} diff --git a/Source/Core/Common/TraversalClient.cpp b/Source/Core/Common/TraversalClient.cpp index 72d18244f3..f17ce0d921 100644 --- a/Source/Core/Common/TraversalClient.cpp +++ b/Source/Core/Common/TraversalClient.cpp @@ -7,9 +7,10 @@ static void GetRandomishBytes(u8* buf, size_t size) { // We don't need high quality random numbers (which might not be available), // just non-repeating numbers! - srand(enet_time_get()); + static std::mt19937 prng(enet_time_get()); + static std::uniform_int_distribution u8_distribution(0, 255); for (size_t i = 0; i < size; i++) - buf[i] = rand() & 0xff; + buf[i] = u8_distribution(prng); } TraversalClient::TraversalClient(ENetHost* netHost, const std::string& server, const u16 port) @@ -301,7 +302,8 @@ void TraversalClient::Reset() int ENET_CALLBACK TraversalClient::InterceptCallback(ENetHost* host, ENetEvent* event) { auto traversalClient = g_TraversalClient.get(); - if (traversalClient->TestPacket(host->receivedData, host->receivedDataLength, &host->receivedAddress)) + if (traversalClient->TestPacket(host->receivedData, host->receivedDataLength, &host->receivedAddress) + || (host->receivedDataLength == 1 && host->receivedData[0] == 0)) { event->type = (ENetEventType)42; return 1; diff --git a/Source/Core/Common/TraversalClient.h b/Source/Core/Common/TraversalClient.h index 0263d863fb..ddfb38fce1 100644 --- a/Source/Core/Common/TraversalClient.h +++ b/Source/Core/Common/TraversalClient.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "Common/Common.h" #include "Common/Thread.h" diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index b2012c10ce..b424932e77 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -103,7 +103,10 @@ NetPlayClient::NetPlayClient(const std::string& address, const u16 port, NetPlay if (net > 0 && netEvent.type == ENET_EVENT_TYPE_CONNECT) { if (Connect()) + { + m_client->intercept = ENetUtil::InterceptCallback; m_thread = std::thread(&NetPlayClient::ThreadFunc, this); + } } else { @@ -416,7 +419,6 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) spac << (MessageId)NP_MSG_PONG; spac << ping_key; - std::lock_guard lks(m_crit.send); Send(spac); } break; @@ -480,6 +482,15 @@ void NetPlayClient::Disconnect() m_server = nullptr; } +void NetPlayClient::SendAsync(sf::Packet* packet) +{ + { + std::lock_guard lkq(m_crit.async_queue_write); + m_async_queue.Push(std::unique_ptr(packet)); + } + ENetUtil::WakeupThread(m_client); +} + // called from ---NETPLAY--- thread void NetPlayClient::ThreadFunc() { @@ -487,11 +498,13 @@ void NetPlayClient::ThreadFunc() { ENetEvent netEvent; int net; + if (m_traversal_client) + m_traversal_client->HandleResends(); + net = enet_host_service(m_client, &netEvent, 250); + while (!m_async_queue.Empty()) { - std::lock_guard lks(m_crit.send); - if (m_traversal_client) - m_traversal_client->HandleResends(); - net = enet_host_service(m_client, &netEvent, 4); + Send(*(m_async_queue.Front().get())); + m_async_queue.Pop(); } if (net > 0) { @@ -517,7 +530,6 @@ void NetPlayClient::ThreadFunc() break; } } - } Disconnect(); @@ -577,57 +589,47 @@ void NetPlayClient::GetPlayers(std::vector &player_list) // called from ---GUI--- thread void NetPlayClient::SendChatMessage(const std::string& msg) { - sf::Packet spac; - spac << (MessageId)NP_MSG_CHAT_MESSAGE; - spac << msg; - - std::lock_guard lks(m_crit.send); - Send(spac); + sf::Packet* spac = new sf::Packet; + *spac << (MessageId)NP_MSG_CHAT_MESSAGE; + *spac << msg; + SendAsync(spac); } // called from ---CPU--- thread void NetPlayClient::SendPadState(const PadMapping in_game_pad, const GCPadStatus& pad) { - // send to server - sf::Packet spac; - spac << (MessageId)NP_MSG_PAD_DATA; - spac << in_game_pad; - spac << pad.button << pad.analogA << pad.analogB << pad.stickX << pad.stickY << pad.substickX << pad.substickY << pad.triggerLeft << pad.triggerRight; + sf::Packet* spac = new sf::Packet; + *spac << (MessageId)NP_MSG_PAD_DATA; + *spac << in_game_pad; + *spac << pad.button << pad.analogA << pad.analogB << pad.stickX << pad.stickY << pad.substickX << pad.substickY << pad.triggerLeft << pad.triggerRight; - std::lock_guard lks(m_crit.send); - Send(spac); + SendAsync(spac); } // called from ---CPU--- thread void NetPlayClient::SendWiimoteState(const PadMapping in_game_pad, const NetWiimote& nw) { - // send to server - sf::Packet spac; - spac << (MessageId)NP_MSG_WIIMOTE_DATA; - spac << in_game_pad; - spac << (u8)nw.size(); + sf::Packet* spac = new sf::Packet; + *spac << (MessageId)NP_MSG_WIIMOTE_DATA; + *spac << in_game_pad; + *spac << (u8)nw.size(); for (auto it : nw) { - spac << it; + *spac << it; } - - std::lock_guard lks(m_crit.send); - Send(spac); + SendAsync(spac); } // called from ---GUI--- thread bool NetPlayClient::StartGame(const std::string &path) { std::lock_guard lkg(m_crit.game); - // tell server i started the game - sf::Packet spac; - spac << (MessageId)NP_MSG_START_GAME; - spac << m_current_game; - spac << (char *)&g_NetPlaySettings; - - std::lock_guard lks(m_crit.send); - Send(spac); + sf::Packet* spac = new sf::Packet; + *spac << (MessageId)NP_MSG_START_GAME; + *spac << m_current_game; + *spac << (char *)&g_NetPlaySettings; + SendAsync(spac); if (m_is_running) { @@ -954,6 +956,7 @@ bool NetPlayClient::StopGame() return true; } +// called from ---GUI--- thread void NetPlayClient::Stop() { if (m_is_running == false) @@ -976,9 +979,9 @@ void NetPlayClient::Stop() // tell the server to stop if we have a pad mapped in game. if (isPadMapped) { - sf::Packet spac; - spac << (MessageId)NP_MSG_STOP_GAME; - Send(spac); + sf::Packet* spac = new sf::Packet; + *spac << (MessageId)NP_MSG_STOP_GAME; + SendAsync(spac); } } diff --git a/Source/Core/Core/NetPlayClient.h b/Source/Core/Core/NetPlayClient.h index ab5763d313..e47dcb7e94 100644 --- a/Source/Core/Core/NetPlayClient.h +++ b/Source/Core/Core/NetPlayClient.h @@ -9,6 +9,7 @@ #include #include #include "Common/CommonTypes.h" +#include "Common/ENetUtil.h" #include "Common/FifoQueue.h" #include "Common/Thread.h" #include "Common/Timer.h" @@ -47,6 +48,7 @@ class NetPlayClient : public TraversalClientClient { public: void ThreadFunc(); + void SendAsync(sf::Packet* packet); NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog, const std::string& name, bool traversal, std::string centralServer, u16 centralPort); ~NetPlayClient(); @@ -92,9 +94,12 @@ protected: { std::recursive_mutex game; // lock order - std::recursive_mutex players, send; + std::recursive_mutex players; + std::recursive_mutex async_queue_write; } m_crit; + Common::FifoQueue, false> m_async_queue; + Common::FifoQueue m_pad_buffer[4]; Common::FifoQueue m_wiimote_buffer[4]; diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp index 1dcd5c1c8f..977d87e1fc 100644 --- a/Source/Core/Core/NetPlayServer.cpp +++ b/Source/Core/Core/NetPlayServer.cpp @@ -91,8 +91,9 @@ NetPlayServer::NetPlayServer(const u16 port, bool traversal, std::string central serverAddr.host = ENET_HOST_ANY; serverAddr.port = port; m_server = enet_host_create(&serverAddr, 10, 3, 0, 0); + if (m_server != nullptr) + m_server->intercept = ENetUtil::InterceptCallback; } - if (m_server != nullptr) { is_connected = true; @@ -117,7 +118,6 @@ void NetPlayServer::ThreadFunc() spac << (MessageId)NP_MSG_PING; spac << m_ping_key; - std::lock_guard lks(m_crit.send); m_ping_timer.Start(); SendToClients(spac); m_update_pings = false; @@ -125,11 +125,16 @@ void NetPlayServer::ThreadFunc() ENetEvent netEvent; int net; + if (m_traversal_client) + m_traversal_client->HandleResends(); + net = enet_host_service(m_server, &netEvent, 1000); + while (!m_async_queue.Empty()) { - std::lock_guard lks(m_crit.send); - if (m_traversal_client) - m_traversal_client->HandleResends(); - net = enet_host_service(m_server, &netEvent, 4); + { + std::lock_guard lkp(m_crit.players); + SendToClients(*(m_async_queue.Front().get())); + } + m_async_queue.Pop(); } if (net > 0) { @@ -149,7 +154,6 @@ void NetPlayServer::ThreadFunc() sf::Packet spac; spac << (MessageId)error; // don't need to lock, this client isn't in the client map - std::lock_guard lks(m_crit.send); Send(accept_peer, spac); if (netEvent.peer->data) { @@ -272,52 +276,46 @@ unsigned int NetPlayServer::OnConnect(ENetPeer* socket) } } + // send join message to already connected clients + sf::Packet spac; + spac << (MessageId)NP_MSG_PLAYER_JOIN; + spac << player.pid << player.name << player.revision; + SendToClients(spac); + + // send new client success message with their id + spac.clear(); + spac << (MessageId)0; + spac << player.pid; + Send(player.socket, spac); + + // send new client the selected game + if (m_selected_game != "") { - std::lock_guard lks(m_crit.send); + spac.clear(); + spac << (MessageId)NP_MSG_CHANGE_GAME; + spac << m_selected_game; + Send(player.socket, spac); + } - // send join message to already connected clients - sf::Packet spac; + // send the pad buffer value + spac.clear(); + spac << (MessageId)NP_MSG_PAD_BUFFER; + spac << (u32)m_target_buffer_size; + Send(player.socket, spac); + + // sync values with new client + for (const auto& p : m_players) + { + spac.clear(); spac << (MessageId)NP_MSG_PLAYER_JOIN; - spac << player.pid << player.name << player.revision; - SendToClients(spac); - - // send new client success message with their id - spac.clear(); - spac << (MessageId)0; - spac << player.pid; + spac << p.second.pid << p.second.name << p.second.revision; Send(player.socket, spac); - - // send new client the selected game - if (m_selected_game != "") - { - spac.clear(); - spac << (MessageId)NP_MSG_CHANGE_GAME; - spac << m_selected_game; - Send(player.socket, spac); - } - - // send the pad buffer value - spac.clear(); - spac << (MessageId)NP_MSG_PAD_BUFFER; - spac << (u32)m_target_buffer_size; - Send(player.socket, spac); - - // sync values with new client - for (const auto& p : m_players) - { - spac.clear(); - spac << (MessageId)NP_MSG_PLAYER_JOIN; - spac << p.second.pid << p.second.name << p.second.revision; - Send(player.socket, spac); - } - - } // unlock send + } // add client to the player list { std::lock_guard lkp(m_crit.players); m_players.insert(std::pair(*(PlayerId *)player.socket->data, player)); - std::lock_guard lks(m_crit.send); UpdatePadMapping(); // sync pad mappings with everyone UpdateWiimoteMapping(); } @@ -343,7 +341,6 @@ unsigned int NetPlayServer::OnDisconnect(Client& player) sf::Packet spac; spac << (MessageId)NP_MSG_DISABLE_GAME; // this thread doesn't need players lock - std::lock_guard lks(m_crit.send); SendToClients(spac, 1); break; } @@ -362,7 +359,6 @@ unsigned int NetPlayServer::OnDisconnect(Client& player) m_players.erase(it); // alert other players of disconnect - std::lock_guard lks(m_crit.send); SendToClients(spac); for (PadMapping& mapping : m_pad_map) @@ -447,13 +443,20 @@ void NetPlayServer::AdjustPadBufferSize(unsigned int size) m_target_buffer_size = size; // tell clients to change buffer size - sf::Packet spac; - spac << (MessageId)NP_MSG_PAD_BUFFER; - spac << (u32)m_target_buffer_size; + sf::Packet* spac = new sf::Packet; + *spac << (MessageId)NP_MSG_PAD_BUFFER; + *spac << (u32)m_target_buffer_size; - std::lock_guard lkp(m_crit.players); - std::lock_guard lks(m_crit.send); - SendToClients(spac); + SendAsyncToClients(spac); +} + +void NetPlayServer::SendAsyncToClients(sf::Packet* packet) +{ + { + std::lock_guard lkq(m_crit.async_queue_write); + m_async_queue.Push(std::unique_ptr(packet)); + } + ENetUtil::WakeupThread(m_server); } // called from ---NETPLAY--- thread @@ -478,10 +481,7 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player) spac << player.pid; spac << msg; - { - std::lock_guard lks(m_crit.send); - SendToClients(spac, player.pid); - } + SendToClients(spac, player.pid); } break; @@ -505,7 +505,6 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player) spac << (MessageId)NP_MSG_PAD_DATA; spac << map << pad.button << pad.analogA << pad.analogB << pad.stickX << pad.stickY << pad.substickX << pad.substickY << pad.triggerLeft << pad.triggerRight; - std::lock_guard lks(m_crit.send); SendToClients(spac, player.pid); } break; @@ -538,7 +537,6 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player) for (const u8& byte : data) spac << byte; - std::lock_guard lks(m_crit.send); SendToClients(spac, player.pid); } break; @@ -559,7 +557,6 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player) spac << player.pid; spac << player.ping; - std::lock_guard lks(m_crit.send); SendToClients(spac); } break; @@ -577,7 +574,6 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player) spac << (MessageId)NP_MSG_STOP_GAME; std::lock_guard lkp(m_crit.players); - std::lock_guard lks(m_crit.send); SendToClients(spac); m_is_running = false; @@ -601,17 +597,15 @@ void NetPlayServer::OnTraversalStateChanged() m_dialog->Update(); } -// called from ---GUI--- thread / and ---NETPLAY--- thread +// called from ---GUI--- thread void NetPlayServer::SendChatMessage(const std::string& msg) { - sf::Packet spac; - spac << (MessageId)NP_MSG_CHAT_MESSAGE; - spac << (PlayerId)0; // server id always 0 - spac << msg; + sf::Packet* spac = new sf::Packet; + *spac << (MessageId)NP_MSG_CHAT_MESSAGE; + *spac << (PlayerId)0; // server id always 0 + *spac << msg; - std::lock_guard lkp(m_crit.players); - std::lock_guard lks(m_crit.send); - SendToClients(spac); + SendAsyncToClients(spac); } // called from ---GUI--- thread @@ -622,13 +616,11 @@ bool NetPlayServer::ChangeGame(const std::string &game) m_selected_game = game; // send changed game to clients - sf::Packet spac; - spac << (MessageId)NP_MSG_CHANGE_GAME; - spac << game; + sf::Packet* spac = new sf::Packet; + *spac << (MessageId)NP_MSG_CHANGE_GAME; + *spac << game; - std::lock_guard lkp(m_crit.players); - std::lock_guard lks(m_crit.send); - SendToClients(spac); + SendAsyncToClients(spac); return true; } @@ -651,24 +643,22 @@ bool NetPlayServer::StartGame() g_netplay_initial_gctime = Common::Timer::GetLocalTimeSinceJan1970(); // tell clients to start game - sf::Packet spac; - spac << (MessageId)NP_MSG_START_GAME; - spac << m_current_game; - spac << m_settings.m_CPUthread; - spac << m_settings.m_CPUcore; - spac << m_settings.m_DSPEnableJIT; - spac << m_settings.m_DSPHLE; - spac << m_settings.m_WriteToMemcard; - spac << m_settings.m_OCEnable; - spac << m_settings.m_OCFactor; - spac << m_settings.m_EXIDevice[0]; - spac << m_settings.m_EXIDevice[1]; - spac << (u32)g_netplay_initial_gctime; - spac << (u32)g_netplay_initial_gctime << 32; + sf::Packet* spac = new sf::Packet; + *spac << (MessageId)NP_MSG_START_GAME; + *spac << m_current_game; + *spac << m_settings.m_CPUthread; + *spac << m_settings.m_CPUcore; + *spac << m_settings.m_DSPEnableJIT; + *spac << m_settings.m_DSPHLE; + *spac << m_settings.m_WriteToMemcard; + *spac << m_settings.m_OCEnable; + *spac << m_settings.m_OCFactor; + *spac << m_settings.m_EXIDevice[0]; + *spac << m_settings.m_EXIDevice[1]; + *spac << (u32)g_netplay_initial_gctime; + *spac << (u32)g_netplay_initial_gctime << 32; - std::lock_guard lkp(m_crit.players); - std::lock_guard lks(m_crit.send); - SendToClients(spac); + SendAsyncToClients(spac); m_is_running = true; diff --git a/Source/Core/Core/NetPlayServer.h b/Source/Core/Core/NetPlayServer.h index 5e8ac76411..e1a22c8de9 100644 --- a/Source/Core/Core/NetPlayServer.h +++ b/Source/Core/Core/NetPlayServer.h @@ -9,6 +9,7 @@ #include #include #include +#include "Common/ENetUtil.h" #include "Common/Thread.h" #include "Common/Timer.h" #include "Common/TraversalClient.h" @@ -20,6 +21,7 @@ class NetPlayServer : public TraversalClientClient { public: void ThreadFunc(); + void SendAsyncToClients(sf::Packet* packet); NetPlayServer(const u16 port, bool traversal, std::string centralServer, u16 centralPort); ~NetPlayServer(); @@ -101,11 +103,13 @@ private: { std::recursive_mutex game; // lock order - std::recursive_mutex players, send; + std::recursive_mutex players; + std::recursive_mutex async_queue_write; } m_crit; std::string m_selected_game; std::thread m_thread; + Common::FifoQueue, false> m_async_queue; ENetHost* m_server; TraversalClient* m_traversal_client; diff --git a/Source/Core/DolphinWX/NetWindow.cpp b/Source/Core/DolphinWX/NetWindow.cpp index 02e16cda3b..bae82f5bec 100644 --- a/Source/Core/DolphinWX/NetWindow.cpp +++ b/Source/Core/DolphinWX/NetWindow.cpp @@ -360,10 +360,10 @@ void NetPlaySetupDiag::OnHost(wxCommandEvent&) unsigned long centralPort = 0; m_traversal_port->GetValue().ToULong(¢ralPort); netplay_server = new NetPlayServer(u16(port), trav, WxStrToStr(m_traversal_server->GetValue()), u16(centralPort)); - netplay_server->ChangeGame(game); - netplay_server->AdjustPadBufferSize(INITIAL_PAD_BUFFER_SIZE); if (netplay_server->is_connected) { + netplay_server->ChangeGame(game); + netplay_server->AdjustPadBufferSize(INITIAL_PAD_BUFFER_SIZE); #ifdef USE_UPNP if (m_upnp_chk->GetValue()) netplay_server->TryPortmapping(port);