diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 7343c193a7..226a45bf85 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -482,6 +482,9 @@ {3de9ee35-3e91-4f27-a014-2866ad8c3fe3} + + {cbc76802-c128-4b17-bf6c-23b08c313e5e} + diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index 677db50aae..8bb26feeb8 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -4,8 +4,6 @@ #include "Core/ConfigManager.h" #include "Core/Core.h" -#include "Core/Movie.h" -#include "Core/NetPlayClient.h" #include "Core/HW/EXI_DeviceIPL.h" #include "Core/HW/SI.h" #include "Core/HW/SI_DeviceDanceMat.h" @@ -15,7 +13,8 @@ #include "Core/HW/WiimoteReal/WiimoteReal.h" #include "Core/IPC_HLE/WII_IPC_HLE_Device_usb.h" #include "Core/IPC_HLE/WII_IPC_HLE_WiiMote.h" - +#include "Core/Movie.h" +#include "Core/NetPlayClient.h" static std::mutex crit_netplay_client; static NetPlayClient * netplay_client = nullptr; @@ -33,72 +32,60 @@ NetPlayClient::~NetPlayClient() m_do_loop = false; m_thread.join(); } + + if (m_server) + { + Disconnect(); + } + } // called from ---GUI--- thread -NetPlayClient::NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog, const std::string& name) : m_dialog(dialog), m_is_running(false), m_do_loop(true) +NetPlayClient::NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog, const std::string& name) + : m_dialog(dialog) + , m_client(nullptr) + , m_server(nullptr) + , m_is_running(false) + , m_do_loop(true) + , m_target_buffer_size() + , m_local_player(nullptr) + , m_current_game(0) + , m_is_recording(false) + , m_pid(0) + , m_connecting(false) { m_target_buffer_size = 20; ClearBuffers(); is_connected = false; - if (m_socket.connect(address, port, sf::seconds(5)) == sf::Socket::Done) + m_player_name = name; + + //Direct Connection + m_client = enet_host_create(nullptr, 1, 3, 0, 0); + + if (m_client == nullptr) { - // send connect message - sf::Packet spac; - spac << NETPLAY_VERSION; - spac << netplay_dolphin_ver; - spac << name; - m_socket.send(spac); + PanicAlertT("Couldn't Create Client"); + } - sf::Packet rpac; - // TODO: make this not hang - m_socket.receive(rpac); - MessageId error; - rpac >> error; + ENetAddress addr; + enet_address_set_host(&addr, address.c_str()); + addr.port = port; - // got error message - if (error) - { - switch (error) - { - case CON_ERR_SERVER_FULL : - PanicAlertT("The server is full!"); - break; - case CON_ERR_VERSION_MISMATCH : - PanicAlertT("The server and client's NetPlay versions are incompatible!"); - break; - case CON_ERR_GAME_RUNNING : - PanicAlertT("The server responded: the game is currently running!"); - break; - default : - PanicAlertT("The server sent an unknown error message!"); - break; - } - m_socket.disconnect(); - } - else - { - rpac >> m_pid; + m_server = enet_host_connect(m_client, &addr, 3, 0); - Player player; - player.name = name; - player.pid = m_pid; - player.revision = netplay_dolphin_ver; + if (m_server == nullptr) + { + PanicAlertT("Couldn't create peer."); + } - // add self to player list - m_players[m_pid] = player; - m_local_player = &m_players[m_pid]; - - m_dialog->Update(); - - //PanicAlertT("Connection successful: assigned player id: %d", m_pid); - is_connected = true; - - m_selector.add(m_socket); + ENetEvent netEvent; + int net = enet_host_service(m_client, &netEvent, 5000); + if (net > 0 && netEvent.type == ENET_EVENT_TYPE_CONNECT) + { + if (Connect()) m_thread = std::thread(&NetPlayClient::ThreadFunc, this); - } } else { @@ -106,6 +93,73 @@ NetPlayClient::NetPlayClient(const std::string& address, const u16 port, NetPlay } } +bool NetPlayClient::Connect() +{ + // send connect message + sf::Packet spac; + spac << NETPLAY_VERSION; + spac << netplay_dolphin_ver; + spac << m_player_name; + Send(spac); + enet_host_flush(m_client); + sf::Packet rpac; + // TODO: make this not hang + ENetEvent netEvent; + if (enet_host_service(m_client, &netEvent, 5000) > 0 && netEvent.type == ENET_EVENT_TYPE_RECEIVE) + { + rpac.append(netEvent.packet->data, netEvent.packet->dataLength); + } + else + { + return false; + } + + MessageId error; + rpac >> error; + + // got error message + if (error) + { + switch (error) + { + case CON_ERR_SERVER_FULL: + PanicAlertT("The server is full!"); + break; + case CON_ERR_VERSION_MISMATCH: + PanicAlertT("The server and client's NetPlay versions are incompatible!"); + break; + case CON_ERR_GAME_RUNNING: + PanicAlertT("The server responded: the game is currently running!"); + break; + default: + PanicAlertT("The server sent an unknown error message!"); + break; + } + + Disconnect(); + return false; + } + else + { + rpac >> m_pid; + + Player player; + player.name = m_player_name; + player.pid = m_pid; + player.revision = netplay_dolphin_ver; + + // add self to player list + m_players[m_pid] = player; + m_local_player = &m_players[m_pid]; + + m_dialog->Update(); + + is_connected = true; + + return true; + } +} + // called from ---NETPLAY--- thread unsigned int NetPlayClient::OnData(sf::Packet& packet) { @@ -114,135 +168,135 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) switch (mid) { - case NP_MSG_PLAYER_JOIN : - { - Player player; - packet >> player.pid; - packet >> player.name; - packet >> player.revision; + case NP_MSG_PLAYER_JOIN: + { + Player player; + packet >> player.pid; + packet >> player.name; + packet >> player.revision; - { + { std::lock_guard lkp(m_crit.players); m_players[player.pid] = player; - } - - m_dialog->Update(); } - break; - case NP_MSG_PLAYER_LEAVE : + m_dialog->Update(); + } + break; + + case NP_MSG_PLAYER_LEAVE: + { + PlayerId pid; + packet >> pid; + { - PlayerId pid; - packet >> pid; - - { std::lock_guard lkp(m_crit.players); m_players.erase(m_players.find(pid)); - } - - m_dialog->Update(); } - break; - case NP_MSG_CHAT_MESSAGE : + m_dialog->Update(); + } + break; + + case NP_MSG_CHAT_MESSAGE: + { + PlayerId pid; + packet >> pid; + std::string msg; + packet >> msg; + + // don't need lock to read in this thread + const Player& player = m_players[pid]; + + // add to gui + std::ostringstream ss; + ss << player.name << '[' << (char)(pid + '0') << "]: " << msg; + + m_dialog->AppendChat(ss.str()); + } + break; + + case NP_MSG_PAD_MAPPING: + { + for (PadMapping& mapping : m_pad_map) { - PlayerId pid; - packet >> pid; - std::string msg; - packet >> msg; - - // don't need lock to read in this thread - const Player& player = m_players[pid]; - - // add to gui - std::ostringstream ss; - ss << player.name << '[' << (char)(pid+'0') << "]: " << msg; - - m_dialog->AppendChat(ss.str()); + packet >> mapping; } - break; - case NP_MSG_PAD_MAPPING : + UpdateDevices(); + + m_dialog->Update(); + } + break; + + case NP_MSG_WIIMOTE_MAPPING: + { + for (PadMapping& mapping : m_wiimote_map) { - for (PadMapping& mapping : m_pad_map) - { - packet >> mapping; - } - - UpdateDevices(); - - m_dialog->Update(); + packet >> mapping; } - break; - case NP_MSG_WIIMOTE_MAPPING : + m_dialog->Update(); + } + break; + + case NP_MSG_PAD_DATA: + { + PadMapping map = 0; + GCPadStatus pad; + packet >> map >> pad.button >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> pad.substickX >> pad.substickY >> pad.triggerLeft >> pad.triggerRight; + + // trusting server for good map value (>=0 && <4) + // add to pad buffer + m_pad_buffer[map].Push(pad); + } + break; + + case NP_MSG_WIIMOTE_DATA: + { + PadMapping map = 0; + NetWiimote nw; + u8 size; + packet >> map >> size; + + nw.resize(size); + u8* data = new u8[size]; + for (unsigned int i = 0; i < size; ++i) + packet >> data[i]; + nw.assign(data, data + size); + delete[] data; + + // trusting server for good map value (>=0 && <4) + // add to Wiimote buffer + m_wiimote_buffer[(unsigned)map].Push(nw); + } + break; + + + case NP_MSG_PAD_BUFFER: + { + u32 size = 0; + packet >> size; + + m_target_buffer_size = size; + } + break; + + case NP_MSG_CHANGE_GAME: + { { - for (PadMapping& mapping : m_wiimote_map) - { - packet >> mapping; - } - - m_dialog->Update(); - } - break; - - case NP_MSG_PAD_DATA : - { - PadMapping map = 0; - GCPadStatus pad; - packet >> map >> pad.button >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> pad.substickX >> pad.substickY >> pad.triggerLeft >> pad.triggerRight; - - // trusting server for good map value (>=0 && <4) - // add to pad buffer - m_pad_buffer[map].Push(pad); - } - break; - - case NP_MSG_WIIMOTE_DATA : - { - PadMapping map = 0; - NetWiimote nw; - u8 size; - packet >> map >> size; - - nw.resize(size); - u8* data = new u8[size]; - for (unsigned int i = 0; i < size; ++i) - packet >> data[i]; - nw.assign(data,data+size); - delete[] data; - - // trusting server for good map value (>=0 && <4) - // add to Wiimote buffer - m_wiimote_buffer[(unsigned)map].Push(nw); - } - break; - - - case NP_MSG_PAD_BUFFER : - { - u32 size = 0; - packet >> size; - - m_target_buffer_size = size; - } - break; - - case NP_MSG_CHANGE_GAME : - { - { std::lock_guard lkg(m_crit.game); packet >> m_selected_game; - } - - // update gui - m_dialog->OnMsgChangeGame(m_selected_game); } - break; - case NP_MSG_START_GAME : + // update gui + m_dialog->OnMsgChangeGame(m_selected_game); + } + break; + + case NP_MSG_START_GAME: + { { - { std::lock_guard lkg(m_crit.game); packet >> m_current_game; packet >> g_NetPlaySettings.m_CPUthread; @@ -252,62 +306,63 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) packet >> g_NetPlaySettings.m_WriteToMemcard; packet >> g_NetPlaySettings.m_OCEnable; packet >> g_NetPlaySettings.m_OCFactor; + int tmp; packet >> tmp; - g_NetPlaySettings.m_EXIDevice[0] = (TEXIDevices) tmp; + g_NetPlaySettings.m_EXIDevice[0] = (TEXIDevices)tmp; packet >> tmp; - g_NetPlaySettings.m_EXIDevice[1] = (TEXIDevices) tmp; - } - - m_dialog->OnMsgStartGame(); + g_NetPlaySettings.m_EXIDevice[1] = (TEXIDevices)tmp; } - break; - case NP_MSG_STOP_GAME : - { - m_dialog->OnMsgStopGame(); - } - break; + m_dialog->OnMsgStartGame(); + } + break; - case NP_MSG_DISABLE_GAME : - { - PanicAlertT("Other client disconnected while game is running!! NetPlay is disabled. You manually stop the game."); - std::lock_guard lkg(m_crit.game); - m_is_running = false; - NetPlay_Disable(); - } - break; + case NP_MSG_STOP_GAME: + { + m_dialog->OnMsgStopGame(); + } + break; - case NP_MSG_PING : - { - u32 ping_key = 0; - packet >> ping_key; + case NP_MSG_DISABLE_GAME: + { + PanicAlertT("Other client disconnected while game is running!! NetPlay is disabled. You manually stop the game."); + std::lock_guard lkg(m_crit.game); + m_is_running = false; + NetPlay_Disable(); + } + break; - sf::Packet spac; - spac << (MessageId)NP_MSG_PONG; - spac << ping_key; + case NP_MSG_PING: + { + u32 ping_key = 0; + packet >> ping_key; - std::lock_guard lks(m_crit.send); - m_socket.send(spac); - } - break; + sf::Packet spac; + spac << (MessageId)NP_MSG_PONG; + spac << ping_key; + + std::lock_guard lks(m_crit.send); + Send(spac); + } + break; case NP_MSG_PLAYER_PING_DATA: - { - PlayerId pid; - packet >> pid; + { + PlayerId pid; + packet >> pid; - { + { std::lock_guard lkp(m_crit.players); Player& player = m_players[pid]; packet >> player.ping; - } - - m_dialog->Update(); } - break; - default : + m_dialog->Update(); + } + break; + + default: PanicAlertT("Unknown message received with id : %d", mid); break; @@ -316,34 +371,70 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) return 0; } +void NetPlayClient::Send(sf::Packet& packet) +{ + ENetPacket* epac = enet_packet_create(packet.getData(), packet.getDataSize(), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(m_server, 0, epac); +} + +void NetPlayClient::Disconnect() +{ + ENetEvent netEvent; + enet_peer_disconnect(m_server, 0); + while (enet_host_service(m_client, &netEvent, 3000) > 0) + { + switch (netEvent.type) + { + case ENET_EVENT_TYPE_RECEIVE: + enet_packet_destroy(netEvent.packet); + break; + case ENET_EVENT_TYPE_DISCONNECT: + m_server = nullptr; + return; + } + } + //didn't disconnect gracefully force disconnect + enet_peer_reset(m_server); + m_server = nullptr; +} + // called from ---NETPLAY--- thread void NetPlayClient::ThreadFunc() { while (m_do_loop) { - if (m_selector.wait(sf::milliseconds(10))) + ENetEvent netEvent; + int net; + { + std::lock_guard lks(m_crit.send); + net = enet_host_service(m_client, &netEvent, 4); + } + if (net > 0) { sf::Packet rpac; - switch (m_socket.receive(rpac)) + switch (netEvent.type) { - case sf::Socket::Done : + case ENET_EVENT_TYPE_RECEIVE: + rpac.append(netEvent.packet->data, netEvent.packet->dataLength); OnData(rpac); - break; - //case sf::Socket::Disconnected : - default : + enet_packet_destroy(netEvent.packet); + break; + case ENET_EVENT_TYPE_DISCONNECT: m_is_running = false; NetPlay_Disable(); m_dialog->AppendChat("< LOST CONNECTION TO SERVER >"); PanicAlertT("Lost connection to server!"); m_do_loop = false; + + netEvent.peer->data = nullptr; break; } } + } - m_socket.disconnect(); - + Disconnect(); return; } @@ -357,7 +448,7 @@ void NetPlayClient::GetPlayerList(std::string& list, std::vector& pid_list) std::map::const_iterator i = m_players.begin(), e = m_players.end(); - for ( ; i!=e; ++i) + for (; i != e; ++i) { const Player *player = &(i->second); ss << player->name << "[" << (int)player->pid << "] : " << player->revision << " | "; @@ -389,7 +480,7 @@ void NetPlayClient::GetPlayers(std::vector &player_list) std::map::const_iterator i = m_players.begin(), e = m_players.end(); - for ( ; i!=e; ++i) + for (; i != e; ++i) { const Player *player = &(i->second); player_list.push_back(player); @@ -405,7 +496,7 @@ void NetPlayClient::SendChatMessage(const std::string& msg) spac << msg; std::lock_guard lks(m_crit.send); - m_socket.send(spac); + Send(spac); } // called from ---CPU--- thread @@ -418,7 +509,7 @@ void NetPlayClient::SendPadState(const PadMapping in_game_pad, const GCPadStatus 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); - m_socket.send(spac); + Send(spac); } // called from ---CPU--- thread @@ -435,7 +526,7 @@ void NetPlayClient::SendWiimoteState(const PadMapping in_game_pad, const NetWiim } std::lock_guard lks(m_crit.send); - m_socket.send(spac); + Send(spac); } // called from ---GUI--- thread @@ -450,7 +541,7 @@ bool NetPlayClient::StartGame(const std::string &path) spac << (char *)&g_NetPlaySettings; std::lock_guard lks(m_crit.send); - m_socket.send(spac); + Send(spac); if (m_is_running) { @@ -529,7 +620,7 @@ void NetPlayClient::UpdateDevices() void NetPlayClient::ClearBuffers() { // clear pad buffers, Clear method isn't thread safe - for (unsigned int i=0; i<4; ++i) + for (unsigned int i = 0; i<4; ++i) { while (m_pad_buffer[i].Size()) m_pad_buffer[i].Pop(); @@ -616,44 +707,44 @@ bool NetPlayClient::GetNetPads(const u8 pad_nb, GCPadStatus* pad_status) bool NetPlayClient::WiimoteUpdate(int _number, u8* data, const u8 size) { NetWiimote nw; - static u8 previousSize[4] = {4,4,4,4}; + static u8 previousSize[4] = { 4, 4, 4, 4 }; { - std::lock_guard lkp(m_crit.players); + std::lock_guard lkp(m_crit.players); - // in game mapping for this local Wiimote - unsigned int in_game_num = LocalWiimoteToInGameWiimote(_number); - // does this local Wiimote map in game? - if (in_game_num < 4) - { - if (previousSize[in_game_num] == size) + // in game mapping for this local Wiimote + unsigned int in_game_num = LocalWiimoteToInGameWiimote(_number); + // does this local Wiimote map in game? + if (in_game_num < 4) { - nw.assign(data, data + size); - do + if (previousSize[in_game_num] == size) { - // add to buffer - m_wiimote_buffer[in_game_num].Push(nw); + nw.assign(data, data + size); + do + { + // add to buffer + m_wiimote_buffer[in_game_num].Push(nw); - SendWiimoteState(in_game_num, nw); - } while (m_wiimote_buffer[in_game_num].Size() <= m_target_buffer_size * 200 / 120); // TODO: add a seperate setting for wiimote buffer? - } - else - { - while (m_wiimote_buffer[in_game_num].Size() > 0) - { - // Reporting mode changed, so previous buffer is no good. - m_wiimote_buffer[in_game_num].Pop(); + SendWiimoteState(in_game_num, nw); + } while (m_wiimote_buffer[in_game_num].Size() <= m_target_buffer_size * 200 / 120); // TODO: add a seperate setting for wiimote buffer? } - nw.resize(size, 0); + else + { + while (m_wiimote_buffer[in_game_num].Size() > 0) + { + // Reporting mode changed, so previous buffer is no good. + m_wiimote_buffer[in_game_num].Pop(); + } + nw.resize(size, 0); - m_wiimote_buffer[in_game_num].Push(nw); - m_wiimote_buffer[in_game_num].Push(nw); - m_wiimote_buffer[in_game_num].Push(nw); - m_wiimote_buffer[in_game_num].Push(nw); - m_wiimote_buffer[in_game_num].Push(nw); - m_wiimote_buffer[in_game_num].Push(nw); - previousSize[in_game_num] = size; + m_wiimote_buffer[in_game_num].Push(nw); + m_wiimote_buffer[in_game_num].Push(nw); + m_wiimote_buffer[in_game_num].Push(nw); + m_wiimote_buffer[in_game_num].Push(nw); + m_wiimote_buffer[in_game_num].Push(nw); + m_wiimote_buffer[in_game_num].Push(nw); + previousSize[in_game_num] = size; + } } - } } // unlock players @@ -753,7 +844,7 @@ void NetPlayClient::Stop() { sf::Packet spac; spac << (MessageId)NP_MSG_STOP_GAME; - m_socket.send(spac); + Send(spac); } } @@ -844,7 +935,7 @@ u32 CEXIIPL::NetPlay_GetGCTime() std::lock_guard lk(crit_netplay_client); if (netplay_client) - return NETPLAY_INITIAL_GCTIME; // watev + return NETPLAY_INITIAL_GCTIME; else return 0; } diff --git a/Source/Core/Core/NetPlayClient.h b/Source/Core/Core/NetPlayClient.h index 521b0bb63c..6d923f0697 100644 --- a/Source/Core/Core/NetPlayClient.h +++ b/Source/Core/Core/NetPlayClient.h @@ -8,16 +8,14 @@ #include #include -#include - +#include "enet/enet.h" #include "Common/CommonTypes.h" #include "Common/FifoQueue.h" #include "Common/Thread.h" #include "Common/Timer.h" - #include "Core/NetPlayProto.h" - #include "InputCommon/GCPadStatus.h" +#include class NetPlayUI { @@ -38,7 +36,7 @@ public: class Player { - public: +public: PlayerId pid; std::string name; std::string revision; @@ -87,9 +85,10 @@ protected: Common::FifoQueue m_wiimote_buffer[4]; NetPlayUI* m_dialog; - sf::TcpSocket m_socket; - std::thread m_thread; - sf::SocketSelector m_selector; + + ENetHost* m_client; + ENetPeer* m_server; + std::thread m_thread; std::string m_selected_game; volatile bool m_is_running; @@ -111,9 +110,17 @@ private: void SendPadState(const PadMapping in_game_pad, const GCPadStatus& np); void SendWiimoteState(const PadMapping in_game_pad, const NetWiimote& nw); unsigned int OnData(sf::Packet& packet); + void Send(sf::Packet& packet); + void Disconnect(); + bool Connect(); + + void OnTraversalDisconnect(int fail); PlayerId m_pid; std::map m_players; + std::string m_host_spec; + std::string m_player_name; + bool m_connecting; }; void NetPlay_Enable(NetPlayClient* const np); diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp index 9b7dced971..f8861c32e4 100644 --- a/Source/Core/Core/NetPlayServer.cpp +++ b/Source/Core/Core/NetPlayServer.cpp @@ -7,6 +7,8 @@ #include "Common/StdMakeUnique.h" #include "Common/StringUtil.h" +#include "Core/HW/EXI_DeviceIPL.h" +#include "Core/NetPlayClient.h" //for NetPlayUI #include "Core/NetPlayServer.h" #include "InputCommon/GCPadStatus.h" @@ -16,7 +18,7 @@ NetPlayServer::~NetPlayServer() { m_do_loop = false; m_thread.join(); - m_socket.close(); + enet_host_destroy(m_server); } #ifdef USE_UPNP @@ -28,15 +30,35 @@ NetPlayServer::~NetPlayServer() } // called from ---GUI--- thread -NetPlayServer::NetPlayServer(const u16 port) : is_connected(false), m_is_running(false) +NetPlayServer::NetPlayServer(const u16 port) + : is_connected(false) + , m_is_running(false) + , m_do_loop(false) + , m_ping_key(0) + , m_update_pings(false) + , m_current_game(0) + , m_target_buffer_size(0) + , m_selected_game("") + , m_server(nullptr) { + //--use server time + if (enet_initialize() != 0) + { + PanicAlertT("Enet Didn't Initialize"); + } + memset(m_pad_map, -1, sizeof(m_pad_map)); memset(m_wiimote_map, -1, sizeof(m_wiimote_map)); - if (m_socket.listen(port) == sf::Socket::Done) + + ENetAddress serverAddr; + serverAddr.host = ENET_HOST_ANY; + serverAddr.port = port; + m_server = enet_host_create(&serverAddr, 10, 3, 0, 0); + + if (m_server != nullptr) { is_connected = true; m_do_loop = true; - m_selector.add(m_socket); m_thread = std::thread(&NetPlayServer::ThreadFunc, this); m_target_buffer_size = 5; } @@ -48,10 +70,9 @@ void NetPlayServer::ThreadFunc() while (m_do_loop) { // update pings every so many seconds + if ((m_ping_timer.GetTimeElapsed() > 1000) || m_update_pings) { - //PanicAlertT("Sending pings"); - m_ping_key = Common::Timer::GetTimeMs(); sf::Packet spac; @@ -61,23 +82,26 @@ void NetPlayServer::ThreadFunc() std::lock_guard lks(m_crit.send); m_ping_timer.Start(); SendToClients(spac); - m_update_pings = false; } - // check if any sockets need attention - if (m_selector.wait(sf::milliseconds(10))) + ENetEvent netEvent; + int net; { - // listening socket - if (m_selector.isReady(m_socket)) + std::lock_guard lks(m_crit.send); + net = enet_host_service(m_server, &netEvent, 4); + } + if (net > 0) + { + switch (netEvent.type) { - auto accept_socket = std::make_unique(); - m_socket.accept(*accept_socket); - + case ENET_EVENT_TYPE_CONNECT: + { + ENetPeer* accept_peer = netEvent.peer; unsigned int error; { - std::lock_guard lkg(m_crit.game); - error = OnConnect(accept_socket); + std::lock_guard lkg(m_crit.game); + error = OnConnect(accept_peer); } if (error) @@ -85,51 +109,61 @@ void NetPlayServer::ThreadFunc() sf::Packet spac; spac << (MessageId)error; // don't need to lock, this client isn't in the client map - accept_socket->send(spac); - accept_socket->disconnect(); + std::lock_guard lks(m_crit.send); + Send(accept_peer, spac); + + enet_peer_disconnect(accept_peer, 0); } } - // client sockets - for (auto it = m_players.begin(); it != m_players.end();) + break; + case ENET_EVENT_TYPE_RECEIVE: { - // move iterator on immediately so client can be removed - Client& client = *it; - it++; + sf::Packet rpac; + rpac.append(netEvent.packet->data, netEvent.packet->dataLength); - if (m_selector.isReady(*client.socket)) + auto it = m_players.find(netEvent.peer->connectID); + Client& client = it->second; + if (OnData(rpac, client) != 0) { - sf::Packet rpac; - switch (client.socket->receive(rpac)) - { - case sf::Socket::Done : - // if a bad packet is received, disconnect the client - if (0 == OnData(rpac, client)) - break; - - //case sf::Socket::Disconnected : - default : - { - std::lock_guard lkg(m_crit.game); - OnDisconnect(client); - break; - } - } + // if a bad packet is received, disconnect the client + std::lock_guard lkg(m_crit.game); + OnDisconnect(client); } + enet_packet_destroy(netEvent.packet); + } + break; + case ENET_EVENT_TYPE_DISCONNECT: + { + std::lock_guard lkg(m_crit.game); + auto it = m_players.find(netEvent.peer->connectID); + if (it != m_players.end()) + { + Client& client = it->second; + OnDisconnect(client); + + netEvent.peer->data = nullptr; + } + } + break; } } } // close listening socket and client sockets for (auto& player_entry : m_players) - player_entry.socket->disconnect(); + enet_peer_disconnect(player_entry.second.socket, 0); } // called from ---NETPLAY--- thread -unsigned int NetPlayServer::OnConnect(std::unique_ptr& socket) +unsigned int NetPlayServer::OnConnect(ENetPeer* socket) { sf::Packet rpac; - // TODO: make this not hang / check if good packet - socket->receive(rpac); + ENetPacket* epack; + do + { + epack = enet_peer_receive(socket, 0); + } while (epack == nullptr); + rpac.append(epack->data, epack->dataLength); std::string npver; rpac >> npver; @@ -149,15 +183,17 @@ unsigned int NetPlayServer::OnConnect(std::unique_ptr& socket) m_update_pings = true; Client player; - player.socket = std::move(socket); + player.socket = socket; rpac >> player.revision; rpac >> player.name; + enet_packet_destroy(epack); + // give new client first available id PlayerId pid = 1; for (auto i = m_players.begin(); i != m_players.end(); ++i) { - if (i->pid == pid) + if (i->second.pid == pid) { pid++; i = m_players.begin(); @@ -176,56 +212,53 @@ unsigned int NetPlayServer::OnConnect(std::unique_ptr& socket) } { - std::lock_guard lks(m_crit.send); + std::lock_guard lks(m_crit.send); - // 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; - player.socket->send(spac); - - // send new client the selected game - if (m_selected_game != "") - { - spac.clear(); - spac << (MessageId)NP_MSG_CHANGE_GAME; - spac << m_selected_game; - player.socket->send(spac); - } - - // send the pad buffer value - spac.clear(); - spac << (MessageId)NP_MSG_PAD_BUFFER; - spac << (u32)m_target_buffer_size; - player.socket->send(spac); - - // sync values with new client - for (const auto& p : m_players) - { - spac.clear(); + // send join message to already connected clients + sf::Packet spac; spac << (MessageId)NP_MSG_PLAYER_JOIN; - spac << p.pid << p.name << p.revision; - player.socket->send(spac); - } + 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 != "") + { + 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 selector/ used for receiving - m_selector.add(*player.socket); - // add client to the player list { - std::lock_guard lkp(m_crit.players); - m_players.push_back(std::move(player)); - std::lock_guard lks(m_crit.send); - UpdatePadMapping(); // sync pad mappings with everyone - UpdateWiimoteMapping(); + std::lock_guard lkp(m_crit.players); + m_players.emplace(player.socket->connectID, player); + std::lock_guard lks(m_crit.send); + UpdatePadMapping(); // sync pad mappings with everyone + UpdateWiimoteMapping(); } return 0; @@ -260,10 +293,12 @@ unsigned int NetPlayServer::OnDisconnect(Client& player) spac << (MessageId)NP_MSG_PLAYER_LEAVE; spac << pid; - m_selector.remove(*player.socket); + enet_peer_disconnect(player.socket, 0); std::lock_guard lkp(m_crit.players); - m_players.remove(player); + auto it = m_players.find(player.pid); + if (it != m_players.end()) + m_players.erase(it); // alert other players of disconnect std::lock_guard lks(m_crit.send); @@ -371,124 +406,124 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player) switch (mid) { - case NP_MSG_CHAT_MESSAGE : + case NP_MSG_CHAT_MESSAGE: + { + std::string msg; + packet >> msg; + + // send msg to other clients + sf::Packet spac; + spac << (MessageId)NP_MSG_CHAT_MESSAGE; + spac << player.pid; + spac << msg; + { - std::string msg; - packet >> msg; - - // send msg to other clients - sf::Packet spac; - spac << (MessageId)NP_MSG_CHAT_MESSAGE; - spac << player.pid; - spac << msg; - - { - std::lock_guard lks(m_crit.send); - SendToClients(spac, player.pid); - } - } - break; - - case NP_MSG_PAD_DATA : - { - // if this is pad data from the last game still being received, ignore it - if (player.current_game != m_current_game) - break; - - PadMapping map = 0; - GCPadStatus pad; - packet >> map >> pad.button >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> pad.substickX >> pad.substickY >> pad.triggerLeft >> pad.triggerRight; - - // If the data is not from the correct player, - // then disconnect them. - if (m_pad_map[map] != player.pid) - return 1; - - // Relay to clients - sf::Packet spac; - 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; + } + break; - case NP_MSG_WIIMOTE_DATA : + case NP_MSG_PAD_DATA: + { + // if this is pad data from the last game still being received, ignore it + if (player.current_game != m_current_game) + break; + + PadMapping map = 0; + GCPadStatus pad; + packet >> map >> pad.button >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> pad.substickX >> pad.substickY >> pad.triggerLeft >> pad.triggerRight; + + // If the data is not from the correct player, + // then disconnect them. + if (m_pad_map[map] != player.pid) + return 1; + + // Relay to clients + sf::Packet spac; + 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; + + case NP_MSG_WIIMOTE_DATA: + { + // if this is Wiimote data from the last game still being received, ignore it + if (player.current_game != m_current_game) + break; + + PadMapping map = 0; + u8 size; + packet >> map >> size; + std::vector data(size); + for (size_t i = 0; i < data.size(); ++i) + packet >> data[i]; + + // If the data is not from the correct player, + // then disconnect them. + if (m_wiimote_map[map] != player.pid) { - // if this is Wiimote data from the last game still being received, ignore it - if (player.current_game != m_current_game) - break; - - PadMapping map = 0; - u8 size; - packet >> map >> size; - std::vector data(size); - for (size_t i = 0; i < data.size(); ++i) - packet >> data[i]; - - // If the data is not from the correct player, - // then disconnect them. - if (m_wiimote_map[map] != player.pid) - { - return 1; - } - - // relay to clients - sf::Packet spac; - spac << (MessageId)NP_MSG_WIIMOTE_DATA; - spac << map; - spac << size; - for (const u8& byte : data) - spac << byte; - - std::lock_guard lks(m_crit.send); - SendToClients(spac, player.pid); + return 1; } - break; - case NP_MSG_PONG : + // relay to clients + sf::Packet spac; + spac << (MessageId)NP_MSG_WIIMOTE_DATA; + spac << map; + spac << size; + for (const u8& byte : data) + spac << byte; + + std::lock_guard lks(m_crit.send); + SendToClients(spac, player.pid); + } + break; + + case NP_MSG_PONG: + { + const u32 ping = (u32)m_ping_timer.GetTimeElapsed(); + u32 ping_key = 0; + packet >> ping_key; + + if (m_ping_key == ping_key) { - const u32 ping = (u32)m_ping_timer.GetTimeElapsed(); - u32 ping_key = 0; - packet >> ping_key; - - if (m_ping_key == ping_key) - { - player.ping = ping; - } - - sf::Packet spac; - spac << (MessageId)NP_MSG_PLAYER_PING_DATA; - spac << player.pid; - spac << player.ping; - - std::lock_guard lks(m_crit.send); - SendToClients(spac); + player.ping = ping; } - break; - case NP_MSG_START_GAME : - { - packet >> player.current_game; - } - break; + sf::Packet spac; + spac << (MessageId)NP_MSG_PLAYER_PING_DATA; + spac << player.pid; + spac << player.ping; + + std::lock_guard lks(m_crit.send); + SendToClients(spac); + } + break; + + case NP_MSG_START_GAME: + { + packet >> player.current_game; + } + break; case NP_MSG_STOP_GAME: - { - // tell clients to stop game - sf::Packet spac; - spac << (MessageId)NP_MSG_STOP_GAME; + { + // tell clients to stop game + sf::Packet spac; + spac << (MessageId)NP_MSG_STOP_GAME; - std::lock_guard lkp(m_crit.players); - std::lock_guard lks(m_crit.send); - SendToClients(spac); + std::lock_guard lkp(m_crit.players); + std::lock_guard lks(m_crit.send); + SendToClients(spac); - m_is_running = false; - } - break; + m_is_running = false; + } + break; - default : + default: PanicAlertT("Unknown message with id:%d received from player:%d Kicking player!", mid, player.pid); // unknown message, kick the client return 1; @@ -573,25 +608,36 @@ void NetPlayServer::SendToClients(sf::Packet& packet, const PlayerId skip_pid) { for (auto& p : m_players) { - if (p.pid && p.pid != skip_pid) + if (p.second.pid && p.second.pid != skip_pid) { - p.socket->send(packet); + Send(p.second.socket, packet); } } } -void NetPlayServer::KickPlayer(u8 player) +void NetPlayServer::Send(ENetPeer* socket, sf::Packet& packet) +{ + ENetPacket* epac = enet_packet_create(packet.getData(), packet.getDataSize(), ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(socket, 0, epac); +} + +void NetPlayServer::KickPlayer(PlayerId player) { for (auto& current_player : m_players) { - if (current_player.pid == player) + if (current_player.second.pid == player) { - current_player.socket->disconnect(); + enet_peer_disconnect(current_player.second.socket, 0); return; } } } +u16 NetPlayServer::GetPort() +{ + return m_server->address.port; +} + #ifdef USE_UPNP #include #include @@ -615,7 +661,11 @@ void NetPlayServer::TryPortmapping(u16 port) // UPnP thread: try to map a port void NetPlayServer::mapPortThread(const u16 port) { - std::string ourIP = sf::IpAddress::getLocalAddress().toString(); + ENetAddress adr; + char cIP[20]; + + enet_address_get_host_ip(&adr, cIP, 20); + std::string ourIP(cIP); if (!m_upnp_inited) if (!initUPnP()) @@ -677,7 +727,7 @@ bool NetPlayServer::initUPnP() for (const UPNPDev* dev : igds) { - char* descXML = (char*) miniwget(dev->descURL, &descXMLsize, 0); + char* descXML = (char*)miniwget(dev->descURL, &descXMLsize, 0); if (descXML) { parserootdesc(descXML, descXMLsize, &m_upnp_data); @@ -708,9 +758,9 @@ bool NetPlayServer::UPnPMapPort(const std::string& addr, const u16 port) std::string port_str = StringFromFormat("%d", port); int result = UPNP_AddPortMapping(m_upnp_urls.controlURL, m_upnp_data.first.servicetype, - port_str.c_str(), port_str.c_str(), addr.c_str(), - (std::string("dolphin-emu TCP on ") + addr).c_str(), - "TCP", nullptr, nullptr); + port_str.c_str(), port_str.c_str(), addr.c_str(), + (std::string("dolphin-emu TCP on ") + addr).c_str(), + "TCP", nullptr, nullptr); if (result != 0) return false; @@ -732,7 +782,7 @@ bool NetPlayServer::UPnPUnmapPort(const u16 port) { std::string port_str = StringFromFormat("%d", port); UPNP_DeletePortMapping(m_upnp_urls.controlURL, m_upnp_data.first.servicetype, - port_str.c_str(), "TCP", nullptr); + port_str.c_str(), "TCP", nullptr); return true; } diff --git a/Source/Core/Core/NetPlayServer.h b/Source/Core/Core/NetPlayServer.h index 9f51f760eb..d82195f5f7 100644 --- a/Source/Core/Core/NetPlayServer.h +++ b/Source/Core/Core/NetPlayServer.h @@ -7,15 +7,13 @@ #include #include #include +#include -#include - -#include "Common/CommonTypes.h" -#include "Common/CommonTypes.h" +#include "enet/enet.h" #include "Common/Thread.h" #include "Common/Timer.h" - #include "Core/NetPlayProto.h" +#include class NetPlayServer { @@ -40,7 +38,9 @@ public: void AdjustPadBufferSize(unsigned int size); - void KickPlayer(u8 player); + void KickPlayer(PlayerId player); + + u16 GetPort(); bool is_connected; @@ -56,20 +56,10 @@ private: std::string name; std::string revision; - std::unique_ptr socket; + ENetPeer* socket; u32 ping; u32 current_game; - // VS2013 does not generate the right constructors here automatically - // like GCC does, so we implement them manually - Client() = default; - Client(const Client& other) = delete; - Client(Client&& other) - : pid(other.pid), name(std::move(other.name)), revision(std::move(other.revision)), - socket(std::move(other.socket)), ping(other.ping), current_game(other.current_game) - { - } - bool operator==(const Client& other) const { return this == &other; @@ -77,7 +67,8 @@ private: }; void SendToClients(sf::Packet& packet, const PlayerId skip_pid = 0); - unsigned int OnConnect(std::unique_ptr& socket); + void Send(ENetPeer* socket, sf::Packet& packet); + unsigned int OnConnect(ENetPeer* socket); unsigned int OnDisconnect(Client& player); unsigned int OnData(sf::Packet& packet, Client& player); void UpdatePadMapping(); @@ -95,7 +86,7 @@ private: PadMapping m_pad_map[4]; PadMapping m_wiimote_map[4]; - std::list m_players; + std::map m_players; struct { @@ -105,10 +96,9 @@ private: } m_crit; std::string m_selected_game; - - sf::TcpListener m_socket; std::thread m_thread; - sf::SocketSelector m_selector; + + ENetHost* m_server; #ifdef USE_UPNP static void mapPortThread(const u16 port);