diff --git a/Externals/SFML/src/SFML/Network/IPAddress.cpp b/Externals/SFML/src/SFML/Network/IPAddress.cpp index 985ef41c75..602a1474e2 100644 --- a/Externals/SFML/src/SFML/Network/IPAddress.cpp +++ b/Externals/SFML/src/SFML/Network/IPAddress.cpp @@ -20,6 +20,7 @@ // // 3. This notice may not be removed or altered from any source distribution. // +// ** ALTERED SOURCE : replaced GetPublicAddress() ** //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// @@ -210,16 +211,24 @@ IPAddress IPAddress::GetPublicAddress() // (not very hard : the web page contains only our IP address) IPAddress PublicAddress; + std::string PageBody; // Connect to the web server and get its index page - Http Server("www.whatismyip.org"); + // www.whatismyip.org is so slow that it time outs after ~60s + // better use this one instead, it is must faster... here :P + Http Server("www.monip.org"); Http::Request Request(Http::Request::Get, "/"); Http::Response Page = Server.SendRequest(Request); // If the request was successful, we can extract // the address from the body of the web page if (Page.GetStatus() == Http::Response::Ok) - PublicAddress = Page.GetBody(); + PageBody = Page.GetBody(); + + size_t str_start = PageBody.find("IP : ", 0) + 5; + size_t str_end = PageBody.find('<', str_start); + + PublicAddress = PageBody.substr(str_start, str_end - str_start); return PublicAddress; } diff --git a/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp b/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp index b58e8bbda9..6e899f327a 100644 --- a/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp +++ b/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp @@ -20,7 +20,6 @@ #include "SI_Device.h" #include "SI_DeviceGCController.h" -#include "../PluginManager.h" #include "EXI_Device.h" #include "EXI_DeviceMic.h" @@ -124,14 +123,28 @@ bool CSIDevice_GCController::GetData(u32& _Hi, u32& _Low) { SPADStatus PadStatus; - memset(&PadStatus, 0 ,sizeof(PadStatus)); + u32 netValues[2] = {0}; + memset(&PadStatus, 0, sizeof(PadStatus)); Common::PluginPAD* pad = CPluginManager::GetInstance().GetPad(ISIDevice::m_iDeviceNumber); pad->PAD_GetStatus(ISIDevice::m_iDeviceNumber, &PadStatus); + int NetPlay = GetNetInput(ISIDevice::m_iDeviceNumber, PadStatus, netValues); + + if (NetPlay != 2) + { + if (NetPlay == 1) + { + _Hi = netValues[0]; // first 4 bytes + _Low = netValues[1]; // last 4 bytes + return true; + } + else + return false; + } _Hi = (u32)((u8)PadStatus.stickY); _Hi |= (u32)((u8)PadStatus.stickX << 8); _Hi |= (u32)((u16)PadStatus.button << 16); - _Hi |= 0x00800000; // F|RES: means that the pad must be "combined" with the origin to math the "final" OSPad-Struct + _Hi |= 0x00800000; // F|RES: means that the pad must be "combined" with the origin to match the "final" OSPad-Struct //_Hi |= 0x20000000; // ? _Low = (u8)PadStatus.triggerRight; diff --git a/Source/Core/Core/Src/HW/SI_DeviceGCController.h b/Source/Core/Core/Src/HW/SI_DeviceGCController.h index 7f015fb476..e68415afd8 100644 --- a/Source/Core/Core/Src/HW/SI_DeviceGCController.h +++ b/Source/Core/Core/Src/HW/SI_DeviceGCController.h @@ -18,6 +18,8 @@ #ifndef _SI_DEVICEGCCONTROLLER_H #define _SI_DEVICEGCCONTROLLER_H +#include "../PluginManager.h" + ////////////////////////////////////////////////////////////////////////// // standard gamecube controller ////////////////////////////////////////////////////////////////////////// @@ -80,6 +82,9 @@ public: // Run the SI Buffer virtual int RunBuffer(u8* _pBuffer, int _iLength); + // Send and Receive pad input from network + static int GetNetInput(u8 numPAD, SPADStatus, u32 *PADStatus); + // Return true on new data virtual bool GetData(u32& _Hi, u32& _Low); diff --git a/Source/Core/DolphinWX/DolphinWX.vcproj b/Source/Core/DolphinWX/DolphinWX.vcproj index b41bbb334c..fb3811fa83 100644 --- a/Source/Core/DolphinWX/DolphinWX.vcproj +++ b/Source/Core/DolphinWX/DolphinWX.vcproj @@ -1,1342 +1,1363 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ProjectType="Visual C++" + Version="9,00" + Name="Dolphin" + ProjectGUID="{A72606EF-C5C1-4954-90AD-F0F93A8D97D9}" + RootNamespace="DolphinWX" + TargetFrameworkVersion="131072" + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/Core/DolphinWX/Src/Frame.cpp b/Source/Core/DolphinWX/Src/Frame.cpp index e40d8e328f..5e99352055 100644 --- a/Source/Core/DolphinWX/Src/Frame.cpp +++ b/Source/Core/DolphinWX/Src/Frame.cpp @@ -256,6 +256,7 @@ EVT_MENU(IDM_CONFIG_DSP_PLUGIN, CFrame::OnPluginDSP) EVT_MENU(IDM_CONFIG_PAD_PLUGIN, CFrame::OnPluginPAD) EVT_MENU(IDM_CONFIG_WIIMOTE_PLUGIN, CFrame::OnPluginWiimote) +EVT_MENU(IDM_NETPLAY, CFrame::OnNetPlay) EVT_MENU(IDM_BROWSE, CFrame::OnBrowse) EVT_MENU(IDM_MEMCARD, CFrame::OnMemcard) EVT_MENU(IDM_CHEATS, CFrame::OnShow_CheatsWindow) diff --git a/Source/Core/DolphinWX/Src/Frame.h b/Source/Core/DolphinWX/Src/Frame.h index 7a2b0bfa55..4a7ab19f16 100644 --- a/Source/Core/DolphinWX/Src/Frame.h +++ b/Source/Core/DolphinWX/Src/Frame.h @@ -195,6 +195,8 @@ class CFrame : public wxFrame void OnMemcard(wxCommandEvent& event); // Misc + void OnNetPlay(wxCommandEvent& event); + void OnShow_CheatsWindow(wxCommandEvent& event); void OnShow_InfoWindow(wxCommandEvent& event); void OnLoadWiiMenu(wxCommandEvent& event); diff --git a/Source/Core/DolphinWX/Src/FrameTools.cpp b/Source/Core/DolphinWX/Src/FrameTools.cpp index 205c1b9bf4..8fea012f08 100644 --- a/Source/Core/DolphinWX/Src/FrameTools.cpp +++ b/Source/Core/DolphinWX/Src/FrameTools.cpp @@ -34,6 +34,8 @@ be accessed from Core::GetWindowHandle(). // Includes // ---------------------------------------------------------------------------- +#include "NetWindow.h" + #include "Globals.h" // Local #include "Frame.h" #include "ConfigMain.h" @@ -158,6 +160,9 @@ void CFrame::CreateMenu() toolsMenu->Append(IDM_MEMCARD, _T("&Memcard Manager")); toolsMenu->Append(IDM_CHEATS, _T("Action &Replay Manager")); toolsMenu->Append(IDM_INFO, _T("System Information")); + + toolsMenu->Append(IDM_NETPLAY, _T("Start &NetPlay")); + // toolsMenu->Append(IDM_SDCARD, _T("Mount &SDCard")); // Disable for now if (DiscIO::CNANDContentManager::Access().GetNANDLoader(FULL_WII_MENU_DIR).IsValid()) @@ -646,6 +651,12 @@ void CFrame::OnHelp(wxCommandEvent& event) } } +// NetPlay stuff +void CFrame::OnNetPlay(wxCommandEvent& WXUNUSED (event)) +{ + new NetPlay(this, m_GameListCtrl->GetGamePaths(), m_GameListCtrl->GetGameNames()); +} + // Miscellaneous menu void CFrame::OnMemcard(wxCommandEvent& WXUNUSED (event)) { diff --git a/Source/Core/DolphinWX/Src/Globals.h b/Source/Core/DolphinWX/Src/Globals.h index fdf0e002f7..fdededc391 100644 --- a/Source/Core/DolphinWX/Src/Globals.h +++ b/Source/Core/DolphinWX/Src/Globals.h @@ -60,6 +60,7 @@ enum IDM_MEMCARD, // Misc menu IDM_CHEATS, + IDM_NETPLAY, IDM_INFO, IDM_CHANGEDISC, IDM_PROPERTIES, diff --git a/Source/Core/DolphinWX/Src/NetEvent.cpp b/Source/Core/DolphinWX/Src/NetEvent.cpp new file mode 100644 index 0000000000..c12eb670cc --- /dev/null +++ b/Source/Core/DolphinWX/Src/NetEvent.cpp @@ -0,0 +1,281 @@ +// Copyright (C) 2003-2009 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "NetWindow.h" + +void ClientSide::OnClientData(unsigned char data) +{ + unsigned char sent = 0; + u32 buffer_size; + size_t recv_size; + char *buffer = NULL; + + switch (data) + { + case 0x10: // Player joined server + { + // Read GameFound + m_socket.Receive((char*)&sent, 1, recv_size); + + // Read nickname + m_socket.Receive((char*)&buffer_size, 4, recv_size); + buffer = new char[buffer_size+1]; + m_socket.Receive(buffer, buffer_size+1, recv_size); + Event->AppendText(wxString::Format(wxT("*Player : %s is now connected to Host...\n"), buffer)); + + if (sent != 0x1F) + for (int i = 0; i < 4; i++) + Event->AppendText(_("WARNING : Game Not Found on Client Side!\n")); + + m_numplayers++; + Event->SendEvent(HOST_NEWPLAYER); + break; + } + case 0x11: // Player left server + { + // Read Nickname + m_socket.Receive((char*)&buffer_size, 4, recv_size); + buffer = new char[buffer_size+1]; + m_socket.Receive(buffer, buffer_size+1, recv_size); + + Event->AppendText(wxString::Format(wxT("*Player : %s left the game\n\n"), buffer)); + + m_numplayers--; + Event->SendEvent(HOST_PLAYERLEFT); + break; + } + case 0x15: // Ping Player + { + m_socket.Receive((char*)&buffer_size, 4, recv_size); + m_socket.Send((const char*)&buffer_size, 4); + + break; + } + case 0x20: // IP request + { + //buffer_size = m_addr.size(); + //m_socket.Send((const char*)&buffer_size, 4); + m_socket.Send((const char*)&data, 1); + m_socket.Send(m_addr.c_str(), m_addr.size() + 1); + + break; + } + case 0x30: // Chat message received from server + { + m_socket.Receive((char*)&buffer_size, 4, recv_size); + buffer = new char[buffer_size+1]; + m_socket.Receive(buffer, buffer_size+1, recv_size); + + if (recv_size > 1024) + { + //something wrong... + delete[] buffer; + return; + } + + Event->AppendText(wxString::FromAscii(buffer)); + + break; + } + case 0x35: // ChangeGame message received + { + m_socket.Receive((char*)&buffer_size, 4, recv_size); + buffer = new char[buffer_size+1]; + m_socket.Receive(buffer, buffer_size+1, recv_size); + + m_selectedgame = std::string(buffer); + Event->AppendText(wxString::Format(wxT("*Host changed Game to : %s\n"), buffer)); + + // Tell the server if the game's been found + m_socket.Send((const char*)&data, 1); + CheckGameFound(); + + Event->SendEvent(GUI_UPDATE); + + break; + } + case 0x40: // Ready message received + { + m_socket.Receive((char*)&buffer_size, 4, recv_size); + buffer = new char[buffer_size+1]; + m_socket.Receive(buffer, buffer_size+1, recv_size); + + if (recv_size > 1024) + { + delete[] buffer; + return; + } + + Event->AppendText(wxString::FromAscii(buffer)); + + break; + } + case 0x50: // Everyone is Ready message received + { + // Load the game and start synching + Event->SendEvent(CLIENTS_READY, "NULL", 1); + + break; + } + case 0xA1: // Received pad data from host in versus mode + { + m_socket.Receive((char*)m_netvalues[0], 8, recv_size); + m_data_received = true; +#ifdef NET_DEBUG + char sent[64]; + sprintf(sent, "Received Values: 0x%08x : 0x%08x \n", m_netvalues[0][0], m_netvalues[0][1]); + Event->AppendText(wxString::FromAscii(sent)); +#endif + break; + } + } + + delete[] buffer; +} + +void ServerSide::OnServerData(char sock, unsigned char data) +{ + size_t recv_size; + char *buffer = NULL; + unsigned char sent; + unsigned int four_bytes; + + switch (data) + { + case 0x15: // Ping Request + { + m_client[sock].socket.Receive((char*)&four_bytes, 4, recv_size); + m_client[sock].socket.Send((const char*)&four_bytes, 4); + + break; + } + case 0x20: // IP request response + { + buffer = new char[24]; + // Read IP Address + m_client[sock].socket.Receive(buffer, 24, recv_size); + + Event->AppendText(wxString::Format(wxT("> Your IP is : %s\n"), buffer)); + + break; + } + case 0x30: // Chat message + { + buffer = new char[1024]; + + m_client[sock].socket.Receive((char*)&four_bytes, 4, recv_size); + m_client[sock].socket.Receive((char*)buffer, four_bytes + 1, recv_size); + + if (recv_size > 1024) + { + //something wrong... + delete[] buffer; + return; + } + + sent = 0x30; + // Send to all + for (char i=0; i < m_numplayers ; i++) + { + if (i == sock) + continue; + + m_client[i].socket.Send((const char*)&sent, 1); + + m_client[1].socket.Send((const char*)&four_bytes, 4); + m_client[i].socket.Send(buffer, recv_size); + } + + Event->AppendText(wxString::FromAscii(buffer)); + + break; + } + case 0x35: // Change game response received + { + // Receive isGameFound response (0x1F / 0x1A) + m_client[sock].socket.Receive((char*)&sent, 1, recv_size); + + // If game is not found + if (sent != 0x1F) + { + sent = 0x30; + + wxString error_str = wxString::Format( + wxT("WARNING : Player %s does Not have this Game !\n"), m_client[sock].nick.c_str()); + four_bytes = error_str.size(); + + for (char i=0; i < 2; i++) + Event->AppendText(error_str); + + // Send to all + for (char i=0; i < m_numplayers ; i++) + { + if (i == sock) + continue; + m_client[i].socket.Send((const char*)&sent, 1); + + m_client[i].socket.Send((const char*)&four_bytes, 4); + m_client[i].socket.Send(error_str.mb_str(), four_bytes + 1); + } + } + + break; + } + case 0x40: // Ready message received + { + std::string buffer_str; + + m_client[sock].ready = !m_client[sock].ready; + + if (m_client[sock].ready) + buffer_str = ">> "+m_client[sock].nick+" is now ready !\n"; + else + buffer_str = ">> "+m_client[sock].nick+" is now Unready !\n"; + + four_bytes = (int)buffer_str.size(); + + // Send to all + for (char i=0; i < m_numplayers ; i++) + { + m_client[i].socket.Send((const char*)&data, 1); + + m_client[i].socket.Send((const char*)&four_bytes, 4); + m_client[i].socket.Send(buffer_str.c_str(), four_bytes+1); + } + + Event->AppendText(wxString::FromAscii(buffer_str.c_str())); + IsEveryoneReady(); + + break; + } + case 0xA1: // Received pad data from a client + { + m_client[sock].socket.Receive((char*)m_netvalues[sock], 8, recv_size); + m_data_received = true; + +#ifdef NET_DEBUG + char sent[64]; + sprintf(sent, "Received Values: 0x%08x : 0x%08x \n", m_netvalues[sock][0], m_netvalues[sock][1]); + Event->AppendText(wxString::FromAscii(sent)); +#endif + break; + } + } + + delete[] buffer; +} + diff --git a/Source/Core/DolphinWX/Src/NetFunctions.cpp b/Source/Core/DolphinWX/Src/NetFunctions.cpp new file mode 100644 index 0000000000..fd4c65b8f1 --- /dev/null +++ b/Source/Core/DolphinWX/Src/NetFunctions.cpp @@ -0,0 +1,426 @@ +// Copyright (C) 2003-2009 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "NetWindow.h" + +NetPlay *NetClass_ptr = NULL; + +void NetPlay::IsGameFound(unsigned char * ptr, std::string m_selected) +{ + m_critical.Enter(); + + m_selectedGame = m_selected; + + if (m_games.find(m_selected) != std::string::npos) + { + *ptr = 0x1F; + } + else + *ptr = 0x1A; + + m_critical.Leave(); +} + +void NetPlay::OnNetEvent(wxCommandEvent& event) +{ + switch (event.GetId()) + { + case HOST_FULL: + { + AppendText(_(" Server is Full !\n*You have been Disconnected.\n\n")); + m_isHosting = 2; + } + break; + case HOST_ERROR: + { + if (m_isHosting == 0) + { + AppendText(_("ERROR : Network Error !\n*You have been Disconnected.\n\n")); + m_isHosting = 2; + } + else + { + m_numClients--; + AppendText( wxString::Format(wxT("ERROR : Network Error !\n" + "*Player : %s has been dropped from the game.\n\n"), + event.GetString().mb_str()) ); + } + } + break; + case HOST_DISCONNECTED: + { + // Event sent from Client's thread, it means that the thread + // has been killed and so we tell the GUI thread. + AppendText(_("*Connection to Host lost.\n*You have been Disconnected.\n\n")); + m_isHosting = 2; + } + break; + case HOST_PLAYERLEFT: + { + m_numClients--; + } + break; + case HOST_NEWPLAYER: + { + m_numClients++; + } + break; + case CLIENTS_READY: + { + m_clients_ready = true; + if (m_ready || event.GetInt()) + LoadGame(); + } + break; + case CLIENTS_NOTREADY: + { + m_clients_ready = false; + } + break; + case GUI_UPDATE: + UpdateNetWindow(false); + break; + case ADD_TEXT: + AppendText(event.GetString()); + break; + case ADD_INFO: + UpdateNetWindow(true, event.GetString()); + break; + } +} + +void ServerSide::IsEveryoneReady() +{ + int nb_ready = 0; + + for (int i=0; i < m_numplayers ; i++) + if (m_client[i].ready) + nb_ready++; + + if (nb_ready == m_numplayers) + Event->SendEvent(CLIENTS_READY); + else + Event->SendEvent(CLIENTS_NOTREADY); +} + +// Actual Core function which is called on every frame +int CSIDevice_GCController::GetNetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus) +{ + if (NetClass_ptr != NULL) + return NetClass_ptr->GetNetPads(numPAD, PadStatus, PADStatus) ? 1 : 0; + else + return 2; +} + +void NetPlay::LoadGame() +{ + // Two implementations, one "p2p" implementation which sends to peer + // and receive from peer 2 players max. and another which uses server model + // and always sends to the server which then send it back to all the clients + // -> P2P model is faster, but is limited to 2 players + // -> Server model is slower, but supports up to 4 players + + m_Logging->AppendText(_("** Everyone is ready... Loading Game ! **\n")); + + // Tell clients everyone is ready... + if (m_isHosting == 1) + { + unsigned char value = 0x50; + + for (int i=0; i < m_numClients ; i++) + m_sock_server->Write(i, (const char*)&value, 1); + } + + // TODO : Throttle should be on by default, to avoid stuttering + //soundStream->GetMixer()->SetThrottle(true); + + int line_p = 0; + int line_n = 0; + + std::string tmp = m_games.substr(0, m_games.find(m_selectedGame)); + for (int i=0; i < (int)tmp.size(); i++) + if (tmp.c_str()[i] == '\n') + line_n++; + + // Enable + NetClass_ptr = this; + m_timer.Start(); + + // Find corresponding game path + for (int i=0; i < (int)m_paths.size(); i++) + { + if (m_paths.c_str()[i] == '\n') + line_p++; + + if (line_n == line_p) { + // Game path found, get its string + int str_pos = line_p > 0 ? i+1 : i; + int str_end = (int)m_paths.find('\n', str_pos); + // Boot the selected game + BootManager::BootCore(m_paths.substr(str_pos, str_end - str_pos)); + break; + } + } +} + +bool NetPlay::GetNetPads(u8 padnb, SPADStatus PadStatus, u32 *netValues) +{ + // Store current pad status in netValues[] + netValues[0] = (u32)((u8)PadStatus.stickY); + netValues[0] |= (u32)((u8)PadStatus.stickX << 8); + netValues[0] |= (u32)((u16)PadStatus.button << 16); + netValues[0] |= 0x00800000; + netValues[1] = (u8)PadStatus.triggerRight; + netValues[1] |= (u32)((u8)PadStatus.triggerLeft << 8); + netValues[1] |= (u32)((u8)PadStatus.substickY << 16); + netValues[1] |= (u32)((u8)PadStatus.substickX << 24); + + // TODO : actually show this on the GUI :p + // Update the timer and increment total frame number + m_frame++; + m_timer.Update(); + + // We make sure everyone's pad is enabled + for (char i = 0; i < m_numClients; i++) + SerialInterface::ChangeDevice(SI_GC_CONTROLLER, (int)i); + + // Better disable unused ports + for (char i = m_numClients; i < 3; i++) + SerialInterface::ChangeDevice(SI_DUMMY, (int)i); + + if (m_NetModel == 0 && m_numClients == 1) // Use P2P Model + { + if (padnb == 0) + { +#ifdef NET_DEBUG + char sent[64]; + sprintf(sent, "Sent Values: 0x%08x : 0x%08x \n", netValues[0], netValues[1]); + m_Logging->AppendText(wxString::FromAscii(sent)); +#endif + unsigned char init_value = 0xA1; + unsigned char recv_value = 0; + unsigned char player = 0; + + if (m_isHosting == 1) { + // Send pads values + m_sock_server->Write(0, (const char*)&init_value, 1); + m_sock_server->Write(0, (const char*)netValues, 8); + player = 0; // Host is player 1 + } + else { + // Send pads values + m_sock_client->Write((const char*)&init_value, 1); + m_sock_client->Write((const char*)netValues, 8); + player = 1; // Client is player 2 + } + + if (!m_data_received) + { + // Save pad values + m_pads[player].nHi[m_loopframe] = netValues[0]; + m_pads[player].nLow[m_loopframe] = netValues[1]; + + // Try to read from peer... + if (m_isHosting == 1) + m_data_received = m_sock_server->isNewPadData(0, m_data_received); + else + m_data_received = m_sock_client->isNewPadData(0, m_data_received); + + if (m_data_received) + { + // First Data has been received ! + m_Logging->AppendText(_("** Data received from Peer. Starting Sync !")); + + // Set our practical frame delay + if (recv_value == 0xA1) // init number + { + m_frameDelay = m_loopframe; + m_loopframe = 0; + m_Logging->AppendText(wxString::Format(wxT(" Frame Delay : %d **\n"), m_frameDelay)); + } + } + else { + m_loopframe++; + return false; + } + } + + if (m_data_received) + { + // We have successfully received the data, now use it... + // If we received data, we can update our pads on each frame, here's the behaviour : + // we received our init number, so we should receive our pad values on each frames + // with a frame delay of 'm_frameDelay' frames from the peer. So here, we just wait + // for the pad status. note : if the peer can't keep up, sending the values + // (i.e : framerate is too low) we have to wait for it thus slowing down emulation + + // Save current pad values, it will be used in 'm_frameDelay' frames :D + int saveslot = (m_loopframe-1 < 0 ? m_frameDelay : m_loopframe-1); + + m_pads[player].nHi[saveslot] = netValues[0]; + m_pads[player].nLow[saveslot] = netValues[1]; + + // Read the socket for pad values + if (m_isHosting == 1) + while (!m_sock_server->isNewPadData(netValues, 1)) { /* Wait Data */ } + else + while (!m_sock_client->isNewPadData(netValues, 1)) { } + + if (player == 0) + { + // Store received peer values + m_pads[1].nHi[m_loopframe] = netValues[0]; + m_pads[1].nLow[m_loopframe] = netValues[1]; + // Apply synced pad values + netValues[0] = m_pads[0].nHi[m_loopframe]; + netValues[1] = m_pads[0].nLow[m_loopframe]; + } + } + +#ifdef NET_DEBUG + char usedval[64]; + sprintf(usedval, "Player 1 Values: 0x%08x : 0x%08x \n", netValues[0], netValues[1]); + m_Logging->AppendText(wxString::FromAscii(usedval)); +#endif + return true; + } + else if (padnb == 1) + { + if (m_data_received) + { + netValues[0] = m_pads[1].nHi[m_loopframe]; + netValues[1] = m_pads[1].nLow[m_loopframe]; + + // Reset the loop to avoid reading unused values + if (m_loopframe == m_frameDelay) + m_loopframe = 0; + else + m_loopframe++; + } + else + return false; +#ifdef NET_DEBUG + char usedval[64]; + sprintf(usedval, "Player 2 Values: 0x%08x : 0x%08x \n", netValues[0], netValues[1]); + m_Logging->AppendText(wxString::FromAscii(usedval)); +#endif + + return true; + } + } + else + { + // TODO : :D + return false; + } + + return false; +} + +void NetPlay::ChangeSelectedGame(std::string game) +{ + wxCriticalSectionLocker lock(m_critical); + if (m_isHosting == 0) + { + m_selectedGame = game; + return; + } + + if (game != m_selectedGame) + { + unsigned char value = 0x35; + int game_size = game.size(); + + // Send command then Game String + for (int i=0; i < m_numClients ; i++) + { + m_sock_server->Write(i, (const char*)&value, 1); // 0x35 -> Change game + + m_sock_server->Write(i, (const char*)&game_size, 4); + //m_sock_server->Write(i, m_selectedGame.c_str(), game_size + 1); + m_sock_server->Write(i, game.c_str(), game_size + 1); + } + + m_selectedGame = game; + UpdateNetWindow(false); + m_Logging->AppendText(wxString::Format(wxT("*Game has been changed to : %s\n"), game.c_str())); + } +} + +void NetPlay::OnQuit(wxCloseEvent& WXUNUSED(event)) +{ + NetClass_ptr = NULL; + + // We Kill the threads + if (m_isHosting == 0) + m_sock_client->Delete(); + else if (m_isHosting == 1) { + m_sock_server->Delete(); + // Stop listening, we're doing it here cause Doing it in the thread + // Cause SFML to crash when built in release build, odd ? :( + m_listensocket.Close(); + } + + // Destroy the Frame + Destroy(); +} + +void NetPlay::OnDisconnect(wxCommandEvent& WXUNUSED(event)) +{ + wxCloseEvent close; + OnQuit(close); +} + +bool ClientSide::isNewPadData(u32 *netValues, bool current, bool isVersus) +{ + // TODO : adapt it to more than 2 players + wxCriticalSectionLocker lock(m_CriticalSection); + + if (current) + { + if (m_data_received) + { + if (isVersus) { + netValues[0] = m_netvalues[0][0]; + netValues[1] = m_netvalues[0][1]; + } + } + else + return false; + } + + return m_data_received; +} + +bool ServerSide::isNewPadData(u32 *netValues, bool current, char client) +{ + wxCriticalSectionLocker lock(m_CriticalSection); + + if (current) + { + if (m_data_received) + { + netValues[0] = m_netvalues[client][0]; + netValues[1] = m_netvalues[client][1]; + } + else + return false; + } + + return m_data_received; +} + diff --git a/Source/Core/DolphinWX/Src/NetSockets.cpp b/Source/Core/DolphinWX/Src/NetSockets.cpp new file mode 100644 index 0000000000..03da1f36a4 --- /dev/null +++ b/Source/Core/DolphinWX/Src/NetSockets.cpp @@ -0,0 +1,462 @@ +// Copyright (C) 2003-2009 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "NetWindow.h" + +//-------------------------------- +// GUI EVENTS +//-------------------------------- + +void NetEvent::AppendText(const wxString text) +{ + // I have the feeling SendEvent may be a bit better... +#if 0 + SendEvent(ADD_TEXT, std::string(text.mb_str())) +#else + wxMutexGuiEnter(); + m_netptr->AppendText(text); + wxMutexGuiLeave(); +#endif +} + +void NetEvent::SendEvent(int EventType, const std::string text, int integer) +{ + wxCommandEvent event(wxEVT_HOST_COMMAND, wxID_ANY); + + event.SetId( EventType ); + event.SetInt( integer ); + event.SetString( wxString::FromAscii(text.c_str()) ); + + m_netptr->AddPendingEvent(event); +} + +//-------------------------------- +// SERVER SIDE THREAD +//-------------------------------- + +ServerSide::ServerSide(NetPlay* netptr, sf::SocketTCP socket, int netmodel, std::string nick) + : wxThread() +{ + m_numplayers = 0; + m_data_received = false; + m_netmodel = netmodel; + m_socket = socket; + m_netptr = netptr; + m_nick = nick; + Event = new NetEvent(m_netptr); +} + +char ServerSide::GetSocket(sf::SocketTCP Socket) +{ + for (char i=0; i < m_numplayers; i++) + { + if(m_client[i].socket == Socket) + return i; + } + + return 0xE; +} + +void *ServerSide::Entry() +{ + // Add listening socket + m_selector.Add(m_socket); + + while (1) + { + char nbSocketReady = m_selector.Wait(0.5); + + for (char i = 0; i < nbSocketReady; ++i) + { + m_CriticalSection.Enter(); + + sf::SocketTCP Socket = m_selector.GetSocketReady(i); + + if (Socket == m_socket) + { + // Incoming connection + Event->AppendText(_("*Connection Request... ")); + + sf::SocketTCP Incoming; + sf::IPAddress Address; + m_socket.Accept(Incoming, &Address); + + unsigned char sent = 0x12; + if ((m_netmodel == 0 && m_numplayers > 0) || m_numplayers == 3) + { + Incoming.Send((const char *)&sent, 1); // Tell it the server is full... + Incoming.Close(); // Then close the connection + Event->AppendText(_(" Server is Full !\n")); + } + else + { + Event->AppendText(_(" Connection accepted\n")); + m_client[m_numplayers].socket = Incoming; + + SyncValues(m_numplayers, Address); + } + + // Add it to the selector + m_selector.Add(Incoming); + Event->SendEvent(HOST_NEWPLAYER); + + m_numplayers++; + } + else + { + unsigned char recv; + char socket_nb; + size_t recv_size; + sf::Socket::Status recv_status; + + if ((socket_nb = GetSocket(Socket)) == 0xE) + PanicAlert("ERROR: How did you get there ?! Is that even possible ?!"); + + if ((recv_status = Socket.Receive((char *)&recv, 1, recv_size)) == sf::Socket::Done) + { +#ifdef NET_DEBUG + char recv_str[32]; + sprintf(recv_str, "received: 0x%02x | %c\n", recv, recv); + Event->AppendText(wxString::FromAscii(recv_str)); +#endif + OnServerData(socket_nb, recv); + } + else + { + if (recv_status == sf::Socket::Disconnected) + { + Event->SendEvent(HOST_PLAYERLEFT); + m_numplayers--; + + std::string player_left = m_client[socket_nb].nick; + Event->AppendText( wxString::Format(wxT("*Player : %s left the game.\n\n"), + player_left.c_str()) ); + + // We need to adjust the struct... + for (char i = socket_nb; i < m_numplayers; i++) + { + m_client[i].socket = m_client[i+1].socket; + m_client[i].nick = m_client[i+1].nick; + m_client[i].ready = m_client[i+1].ready; + } + + // Send disconnected message to all + unsigned char send = 0x11; + unsigned int str_size = player_left.size(); + + for (int i=0; i < m_numplayers ; i++) + { + m_client[i].socket.Send((const char*)&send, 1); + m_client[i].socket.Send((const char*)&str_size, 4); + m_client[i].socket.Send(player_left.c_str(), (int)str_size + 1); + } + } + else + { + // Hopefully this should never happen, the client is not + // Even warned that he is being dropped... + Event->SendEvent(HOST_ERROR, m_client[socket_nb].nick); + } + + m_selector.Remove(Socket); + Socket.Close(); + } + } + + m_CriticalSection.Leave(); + } + + if(TestDestroy()) + { + // Delete the Thread and close clients sockets + for (char i=0; i < m_numplayers ; i++) + m_client[i].socket.Close(); + + break; + } + } + + return NULL; +} + +void ServerSide::SyncValues(unsigned char socketnb, sf::IPAddress Address) +{ + sf::SocketTCP Socket = m_client[socketnb].socket; + + std::string buffer_str = m_netptr->GetSelectedGame(); + char *buffer = NULL; + unsigned char init_number; + u32 buffer_size = (u32)buffer_str.size(); + size_t received; + + // First, Send the number of connected clients & netmodel + Socket.Send((const char *)&m_numplayers, 1); + Socket.Send((const char *)&m_netmodel, 4); + + // Send the Game String + Socket.Send((const char *)&buffer_size, 4); + Socket.Send(buffer_str.c_str(), buffer_size + 1); + + // Send the host Nickname + buffer_size = (u32)m_nick.size(); + Socket.Send((const char *)&buffer_size, 4); + Socket.Send(m_nick.c_str(), buffer_size + 1); + + + // Read returned nickname + Socket.Receive((char *)&buffer_size, 4, received); + buffer = new char[buffer_size + 1]; + Socket.Receive(buffer, buffer_size + 1, received); + + m_client[socketnb].nick = std::string(buffer); + m_client[socketnb].ready = false; + + // Check if the client has the game + Socket.Receive((char *)&init_number, 1, received); + + // Send to all connected clients + if (m_numplayers > 0) + { + unsigned char send = 0x10; + buffer_size = (int)m_client[socketnb].nick.size(); + for (int i=0; i < m_numplayers ; i++) + { + // Do not send to connecting player + if (i == socketnb) + continue; + + m_client[i].socket.Send((const char *)&send, 1); // Init new connection + m_client[i].socket.Send((const char *)&init_number, 1); // Send Game found ? + m_client[i].socket.Send((const char *)&buffer_size, 4); // Send client nickname + m_client[i].socket.Send(m_client[socketnb].nick.c_str(), buffer_size + 1); + } + } + + Event->AppendText( wxString::Format(wxT("*Connection established to %s (%s)\n"), + m_client[socketnb].nick.c_str(), Address.ToString().c_str()) ); + + if (init_number != 0x1F) // Not Found + for (int i = 0; i < 4; i++) + Event->AppendText(_("WARNING : Game Not Found on Client Side !\n")); + + delete[] buffer; +} + +void ServerSide::Write(char socknb, const char *data, size_t size, long *ping) +{ + wxCriticalSectionLocker lock(m_CriticalSection); + + if (ping != NULL) + { + // Ask for ping + unsigned char value = 0x15; + size_t recv_size; + int four_bytes = 0x101A7FA6; + + Common::Timer timer; + timer.Start(); + + for (int i=0; i < m_numplayers ; i++) + { + m_client[i].socket.Send((const char*)&value, 1); + + timer.Update(); + m_client[i].socket.Send((const char*)&four_bytes, 4); + m_client[i].socket.Receive((char*)&four_bytes, 4, recv_size); + ping[i] = (long)timer.GetTimeDifference(); + } + + return; + } + + // Send the data safely, without intefering with another call + m_client[socknb].socket.Send(data, size); +} + +//-------------------------------- +// CLIENT SIDE THREAD +//-------------------------------- + +ClientSide::ClientSide(NetPlay* netptr, sf::SocketTCP socket, std::string addr, std::string nick) + : wxThread() +{ + m_numplayers = 0; + m_data_received = false; + m_netmodel = 0; + m_socket = socket; + m_netptr = netptr; + m_nick = nick; + m_addr = addr; + Event = new NetEvent(m_netptr); +} + +void *ClientSide::Entry() +{ + Event->AppendText(_("*Connection Request... ")); + + // If we get here, the connection is already accepted, however the game may be full + if (SyncValues()) + { + Event->AppendText(_("Connection successful !\n")); + Event->AppendText( wxString::Format(wxT("*Connection established to %s (%s)\n*Game is : %s\n"), + m_hostnick.c_str(), m_addr.c_str(), m_selectedgame.c_str() ) ); + + // Tell the server if the client has the game + CheckGameFound(); + } + else { + // Post ServerFull event to GUI + m_socket.Close(); + Event->SendEvent(HOST_FULL); + return NULL; + } + + m_netptr->ChangeSelectedGame(m_selectedgame); + Event->SendEvent(HOST_NEWPLAYER); + Event->SendEvent(GUI_UPDATE); + + m_selector.Add(m_socket); + + while (1) + { + unsigned char recv; + size_t recv_size; + sf::Socket::Status recv_status; + + // we use a selector because of the useful timeout + if (m_selector.Wait(0.5) > 0) + { + m_CriticalSection.Enter(); + + if ((recv_status = m_socket.Receive((char *)&recv, 1, recv_size)) == sf::Socket::Done) + { +#ifdef NET_DEBUG + char recv_str[32]; + sprintf(recv_str, "received: 0x%02x | %c\n", recv, recv); + Event->AppendText(wxString::FromAscii(recv_str)); +#endif + OnClientData(recv); + } + else + { + if (recv_status == sf::Socket::Disconnected) + { + Event->SendEvent(HOST_DISCONNECTED); + } + else + { + Event->SendEvent(HOST_ERROR); + } + + m_selector.Remove(m_socket); + m_socket.Close(); + + return NULL; + } + + m_CriticalSection.Leave(); + } + + if(TestDestroy()) + { + m_socket.Close(); + + break; + } + } + + return NULL; +} + +bool ClientSide::SyncValues() +{ + unsigned int buffer_size = m_nick.size(); + char *buffer = NULL; + size_t recv_size; + + // First, Read the init number : nbplayers (0-2) or server full (0x12) + m_socket.Receive((char *)&m_numplayers, 1, recv_size); + if (m_numplayers == 0x12) + return false; + m_socket.Receive((char *)&m_netmodel, 4, recv_size); + + // Send client's nickname + m_socket.Send((const char *)&buffer_size, 4); + m_socket.Send(m_nick.c_str(), buffer_size + 1); + + // Read the Game String + m_socket.Receive((char *)&buffer_size, 4, recv_size); + buffer = new char[buffer_size + 1]; + m_socket.Receive(buffer, buffer_size + 1, recv_size); + m_selectedgame = std::string(buffer); + + // Read the host Nickname + m_socket.Receive((char *)&buffer_size, 4, recv_size); + buffer = new char[buffer_size + 1]; + m_socket.Receive(buffer, buffer_size + 1, recv_size); + m_hostnick = std::string(buffer); + + delete[] buffer; + return true; +} + +void ClientSide::CheckGameFound() +{ + unsigned char send_value; + + // Check if the game selected by Host is in Client's Game List + m_netptr->IsGameFound(&send_value, m_selectedgame); + + if (send_value == 0x1F) // Found + { + m_socket.Send((const char *)&send_value, 1); + } + else + { + m_socket.Send((const char *)&send_value, 1); + + for (int i = 0; i < 2; i++) + Event->AppendText(_("WARNING : You do not have the Selected Game !\n")); + } +} + +void ClientSide::Write(const char *data, size_t size, long *ping) +{ + wxCriticalSectionLocker lock(m_CriticalSection); + + if (ping != NULL) + { + // Ask for ping + unsigned char value = 0x15; + size_t recv_size; + int four_bytes = 0x101A7FA6; + + Common::Timer timer; + timer.Start(); + + m_socket.Send((const char*)&value, 1); + m_socket.Send((const char*)&four_bytes, 4); + m_socket.Receive((char*)&four_bytes, 4, recv_size); + + *ping = (long)timer.GetTimeElapsed(); + + return; + } + + m_socket.Send(data, size); +} + diff --git a/Source/Core/DolphinWX/Src/NetWindow.cpp b/Source/Core/DolphinWX/Src/NetWindow.cpp new file mode 100644 index 0000000000..7d58ba79a7 --- /dev/null +++ b/Source/Core/DolphinWX/Src/NetWindow.cpp @@ -0,0 +1,507 @@ +// Copyright (C) 2003-2009 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "NetWindow.h" + +/////////////////////// +// Main Frame window + +BEGIN_EVENT_TABLE(NetPlay, wxDialog) + EVT_BUTTON(ID_BUTTON_JOIN, NetPlay::OnJoin) + EVT_BUTTON(ID_BUTTON_HOST, NetPlay::OnHost) + + EVT_HOST_COMMAND(wxID_ANY, NetPlay::OnNetEvent) + + EVT_CHECKBOX(ID_READY, NetPlay::OnGUIEvent) + EVT_CHECKBOX(ID_RECORD, NetPlay::OnGUIEvent) + EVT_BUTTON(ID_CHANGEGAME, NetPlay::OnGUIEvent) + EVT_BUTTON(ID_BUTTON_GETIP, NetPlay::OnGUIEvent) + EVT_BUTTON(ID_BUTTON_GETPING, NetPlay::OnGUIEvent) + EVT_BUTTON(ID_BUTTON_CHAT, NetPlay::OnGUIEvent) + EVT_TEXT_ENTER(ID_CHAT, NetPlay::OnGUIEvent) + EVT_BUTTON(ID_BUTTON_QUIT, NetPlay::OnDisconnect) + EVT_CLOSE(NetPlay::OnQuit) +END_EVENT_TABLE() + +NetPlay::NetPlay(wxWindow* parent, std::string GamePaths, std::string GameNames) : + wxDialog(parent, wxID_ANY, _T("Net Play"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE & ~ wxMAXIMIZE_BOX ) +{ + m_selectedGame = 'a'; m_hostaddr = 'a'; + m_games = GameNames; m_paths = GamePaths; + m_isHosting = 2; m_ready = m_clients_ready = false; + m_loopframe = m_frame = 0; + + DrawGUI(); +} + +void NetPlay::OnJoin(wxCommandEvent& WXUNUSED(event)) +{ + wxString addr = m_ConAddr->GetValue(); + wxString host = addr.substr(0, addr.find(':')); + wxString port_str = addr.substr(addr.find(':')+1); + int port; TryParseInt(port_str.mb_str(), &port); + + m_nick = std::string(m_SetNick->GetValue().mb_str()); + if (m_nick.size() > 255) + m_nick = m_nick.substr(0 , 255); + + // Create the client socket + sf::SocketTCP sock_client; + + if (sock_client.Connect(port, host.mb_str(), 1.5) == sf::Socket::Done) + { + m_sock_client = new ClientSide(this, sock_client, std::string(addr.mb_str()), m_nick); + m_sock_client->Create(); + m_sock_client->Run(); + + // Create the GUI + m_isHosting = false; + DrawNetWindow(); + } + else + { + PanicAlert("Can't connect to the specified IP Address ! \nMake sure Hosting port is forwarded !"); + } +} + +void NetPlay::OnHost(wxCommandEvent& WXUNUSED(event)) +{ + TryParseInt(m_SetPort->GetValue().mb_str(), (int*)&m_port); + + if (m_GameList->GetSelection() == wxNOT_FOUND) { + PanicAlert("No Game Selected ! Please select a Game..."); + return; + } + if (!m_SetPort->GetValue().size() || m_port < 100 || m_port > 65535) { + PanicAlert("Bad Port entered (%d) ! Please enter a working socket port...", m_port); + return; + } + + m_nick = std::string(m_SetNick->GetValue().mb_str()); + if (m_nick.size() > 255) m_nick = m_nick.substr(0 , 255); + + m_NetModel = m_NetMode->GetSelection(); + m_selectedGame = std::string(m_GameList_str[m_GameList->GetSelection()].mb_str()); + m_numClients = 0; + + // Start the listening socket + if (m_listensocket.Listen(m_port)) + { + m_sock_server = new ServerSide(this, m_listensocket, m_NetModel, m_nick); + m_sock_server->Create(); + m_sock_server->Run(); + + // Create the GUI + m_isHosting = true; + DrawNetWindow(); + m_Logging->AppendText(wxString::Format(wxT("WARNING : Hosting requires port to be forwarded in firewall!\n" + "*Creation Successful on port %d : Waiting for peers...\n"), m_port)); + } + else + { + PanicAlert("Could not listen at specified port !\nMake sure hosting port is not in use !"); + return; + } +} + +void NetPlay::DrawGUI() +{ + int str_end = -1; + int str_start = -1; + wxArrayString netmodes_str; + + for(int i = 0; i < (int)m_games.size(); i++) + { + str_start = str_end + 1; + str_end = (int)m_games.find('\n', str_start); + std::string buffer = m_games.substr(str_start, str_end - str_start); + + if (str_end == (int)std::string::npos || buffer.size() < 1) + break; // we reached the end of the string + + m_GameList_str.Add(wxString::FromAscii(buffer.c_str())); + } + + netmodes_str.Add(wxT("P2P Versus (2 players, faster)")); + netmodes_str.Add(wxT("Server Mode (4 players, slower)")); + + // Tabs + m_Notebook = new wxNotebook(this, ID_NOTEBOOK, wxDefaultPosition, wxDefaultSize); + m_Tab_Connect = new wxPanel(m_Notebook, ID_TAB_CONN, wxDefaultPosition, wxDefaultSize); + m_Notebook->AddPage(m_Tab_Connect, wxT("Connect")); + m_Tab_Host = new wxPanel(m_Notebook, ID_TAB_HOST, wxDefaultPosition, wxDefaultSize); + m_Notebook->AddPage(m_Tab_Host, wxT("Host")); + + // Nickname setting + m_SetNick_text = new wxStaticText(this, ID_SETNICK_TXT, wxT(" Nickname : "), wxDefaultPosition, wxDefaultSize); + m_SetNick = new wxTextCtrl(this, ID_SETNICK, wxT("Mingebag(r)"), wxDefaultPosition, wxDefaultSize, 0); + m_NetMode = new wxChoice(this, ID_NETMODE, wxDefaultPosition, wxDefaultSize, netmodes_str, 0, wxDefaultValidator); + m_NetMode->SetSelection(0); + + // CONNECTION TAB + m_ConAddr_text = new wxStaticText(m_Tab_Connect, ID_CONNADDR_TXT, wxT(" IP Address :"), wxDefaultPosition, wxDefaultSize); + m_ConAddr = new wxTextCtrl(m_Tab_Connect, ID_CONNADDR, wxT("127.0.0.1:12345"), wxDefaultPosition, wxSize(250,20), 0); + m_UseRandomPort = new wxCheckBox(m_Tab_Connect, ID_USE_RANDOMPORT, wxT("Use random client port for connection")); + m_UseRandomPort->SetValue(true); m_UseRandomPort->Enable(false); + m_JoinGame = new wxButton(m_Tab_Connect, ID_BUTTON_JOIN, wxT("Connect"), wxDefaultPosition, wxDefaultSize); + + // Sizers CONNECT + wxBoxSizer* sConnectTop = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer* sConnectSizer = new wxBoxSizer(wxVERTICAL); + + sConnectTop->Add(m_ConAddr_text, 0, wxALL|wxALIGN_CENTER, 5); + sConnectTop->Add(m_ConAddr, 3, wxALL|wxEXPAND, 5); + sConnectTop->Add(m_JoinGame, 0, wxALL|wxALIGN_RIGHT, 5); + sConnectSizer->Add(sConnectTop, 0, wxALL|wxEXPAND, 5); + sConnectSizer->Add(m_UseRandomPort, 0, wxALL|wxALIGN_CENTER, 5); + + m_Tab_Connect->SetSizer(sConnectSizer); + + // HOSTING TAB + m_SetPort_text = new wxStaticText(m_Tab_Host, ID_SETPORT_TXT, wxT(" Use Port : "), wxDefaultPosition, wxDefaultSize); + m_SetPort = new wxTextCtrl(m_Tab_Host, ID_SETPORT, wxT("12345"), wxDefaultPosition, wxDefaultSize, 0); + m_GameList_text = new wxStaticText(m_Tab_Host, ID_GAMELIST_TXT, wxT("Warning: Use a forwarded port ! Select Game and press Host :"), wxDefaultPosition, wxDefaultSize); + m_GameList = new wxListBox(m_Tab_Host, ID_GAMELIST, wxDefaultPosition, wxDefaultSize, + m_GameList_str, wxLB_SINGLE | wxLB_SORT | wxLB_NEEDED_SB); + m_HostGame = new wxButton(m_Tab_Host, ID_BUTTON_HOST, wxT("Host"), wxDefaultPosition, wxDefaultSize); + + // Sizers HOST + wxBoxSizer *sHostBox = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *sHostTop = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *sHostMid = new wxBoxSizer(wxHORIZONTAL); + + sHostTop->Add(m_SetPort_text, 0, wxALL|wxALIGN_CENTER, 5); + sHostTop->Add(m_SetPort, 1, wxALL|wxEXPAND, 5); + sHostMid->Add(m_GameList, 1, wxALL|wxEXPAND, 5); + + sHostBox->Add(m_GameList_text, 0, wxALL|wxALIGN_CENTER, 5); + sHostBox->Add(sHostTop, 0, wxALL|wxEXPAND, 1); + sHostBox->Add(sHostMid, 1, wxALL|wxEXPAND, 1); + sHostBox->Add(m_HostGame, 0, wxALL|wxALIGN_RIGHT, 10); + + m_Tab_Host->SetSizer(sHostBox); + sHostBox->Layout(); + + // main sizers + wxBoxSizer* sMain = new wxBoxSizer(wxVERTICAL); + wxBoxSizer* sMain_top = new wxBoxSizer(wxHORIZONTAL); + + sMain_top->Add(m_SetNick_text, 0, wxALL|wxALIGN_CENTER, 3); + sMain_top->Add(m_SetNick, 0, wxALL, 3); + sMain_top->AddStretchSpacer(1); + sMain_top->Add(m_NetMode, 0, wxALL|wxALIGN_RIGHT, 3); + + sMain->Add(sMain_top, 0, wxALL|wxEXPAND, 5); + sMain->Add(m_Notebook, 1, wxALL|wxEXPAND, 5); + + SetSizerAndFit(sMain); + Center(); Layout(); Show(); +} + +void NetPlay::DrawNetWindow() +{ + // Remove everything from the precedent GUI :D + DestroyChildren(); + + SetTitle(_("Connection Window")); + + wxBoxSizer* sMain = new wxBoxSizer(wxVERTICAL); + wxBoxSizer* sTop = new wxBoxSizer(wxVERTICAL); + wxBoxSizer* sBottom = new wxBoxSizer(wxHORIZONTAL); + + m_Game_str = new wxButton(this, wxID_ANY, wxT(" Game : "), wxDefaultPosition, wxSize(400, 25), wxBU_LEFT); + m_Game_str->Disable(); + + m_Logging = new wxTextCtrl(this, ID_LOGGING_TXT, wxEmptyString, + wxDefaultPosition, wxSize(400, 250), + wxTE_RICH2 | wxTE_MULTILINE | wxTE_READONLY); + + // Too bad this doesn't work... + //m_Logging->SetDefaultStyle(wxTextAttr(*wxRED)); + + m_Chat = new wxTextCtrl(this, ID_CHAT, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); + m_Chat_ok = new wxButton(this, ID_BUTTON_CHAT, wxT("Send"));; + + m_Ready = new wxCheckBox(this, ID_READY, wxT("Click here when ready"), wxDefaultPosition, wxDefaultSize, 0); + m_RecordGame = new wxCheckBox(this, ID_RECORD, wxT("Record Game Input"), wxDefaultPosition, wxDefaultSize, 0); + // TODO: Fix the recording ? + m_RecordGame->Disable(); + + m_ConInfo_text = new wxStaticText(this, ID_CONNINFO_TXT, wxT(" Fps : 0 | Ping : 00 ms")); + m_GetPing = new wxButton(this, ID_BUTTON_GETPING, wxT("Ping"), wxDefaultPosition, wxDefaultSize); + m_Disconnect = new wxButton(this, ID_BUTTON_QUIT, wxT("Disconnect"), wxDefaultPosition, wxDefaultSize); + + wxBoxSizer* sChat = new wxBoxSizer(wxHORIZONTAL); + + sTop->Add(m_Game_str, 0, wxALL|wxEXPAND, 1); + sTop->Add(m_Logging, 1, wxALL|wxEXPAND, 5); + sChat->Add(m_Chat, 1, wxALL|wxEXPAND, 2); + sChat->Add(m_Chat_ok, 0, wxALL, 2); + sTop->Add(sChat, 0, wxALL|wxEXPAND, 2); + + wxBoxSizer* sBottom0 = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer* sBottom1 = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer* sBottomM = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer* sBottom2 = new wxBoxSizer(wxVERTICAL); + + sBottom0->Add(m_Ready, 0, wxALL, 5); + sBottom0->Add(m_RecordGame, 0, wxALL, 5); + sBottomM->Add(m_ConInfo_text, 0, wxALL, 5); + sBottom1->Add(m_Disconnect, 0, wxALL|wxALIGN_LEFT, 5); + sBottom1->AddStretchSpacer(1); + sBottom1->Add(m_GetPing, 0, wxALL|wxALIGN_RIGHT, 5); + + sBottom2->Add(sBottom0, 0, wxALL, 0); + sBottom2->Add(sBottomM, 0, wxALL | wxALIGN_LEFT, 0); + sBottom2->Add(sBottom1, 0, wxALL | wxEXPAND, 5); + + sBottom->Add(sBottom2, 2, wxALL | wxEXPAND | wxALIGN_CENTER, 0); + + if (m_isHosting) + { + m_wtfismyip = new wxButton(this, ID_BUTTON_GETIP, wxT("What is my IP")); + m_ChangeGame = new wxButton(this, ID_CHANGEGAME, wxT("Change Game")); + + wxStaticBoxSizer* sBottom3 = new wxStaticBoxSizer(wxVERTICAL, this, _("Host")); + + sBottom3->Add(m_ChangeGame, 0, wxALL | wxEXPAND, 5); + sBottom3->Add(m_wtfismyip, 0, wxALL | wxEXPAND, 5); + sBottom->Add(sBottom3, 1, wxALL | wxEXPAND, 0); + + UpdateNetWindow(false); + } + + sMain->Add(sTop, 1, wxALL | wxEXPAND, 5); + sMain->Add(sBottom, 0, wxALL | wxEXPAND | wxALIGN_CENTER, 2); + + SetSizerAndFit(sMain); + Center(); Layout(); + Show(); +} + +void NetPlay::UpdateNetWindow(bool update_infos, wxString infos/*int fps, float ping, int frame_delay*/) +{ + std::vector str_arr; + + if (update_infos) + { + // String of the type : FPSxPINGxFRAME_DELAY + SplitString(std::string(infos.mb_str()), "x", str_arr); + + m_ConInfo_text->SetLabel( + wxString::Format( " Fps : %s | Ping : %s | Frame Delay : %s", + str_arr[0].c_str(), str_arr[1].c_str(), str_arr[2].c_str() )); + } + else + { + m_critical.Enter(); + m_Game_str->SetLabel(wxString::Format(" Game : %s", m_selectedGame.c_str())); + m_critical.Leave(); + } +} + +void NetPlay::OnGUIEvent(wxCommandEvent& event) +{ + unsigned char value;; + switch (event.GetId()) + { + case ID_READY: + { + std::string buffer; + value = 0x40; + + if (!m_ready) + buffer = ">> "+m_nick+" is now ready !\n"; + else + buffer = ">> "+m_nick+" is now Unready !\n"; + + m_ready = !m_ready; + + if (m_isHosting == 1) + { + if (m_numClients > 0) + { + int buffer_size = buffer.size(); + for (int i=0; i < m_numClients ; i++) + { + m_sock_server->Write(i, (const char*)&value, 1); + + m_sock_server->Write(i, (const char*)&buffer_size, 4); + m_sock_server->Write(i, buffer.c_str(), buffer_size + 1); + } + } + + m_Logging->AppendText(wxString::FromAscii(buffer.c_str())); + + if (m_ready && m_clients_ready) + LoadGame(); + } + else { + if (m_numClients > 0) + m_sock_client->Write((const char*)&value, 1); // 0x40 -> Ready + } + break; + } + case ID_BUTTON_GETIP: + { + if (m_numClients == 0) // Get IP Address from the Internet + { + // simple IP address caching + if (m_hostaddr.at(0) != 'a') + { + m_Logging->AppendText(wxString::FromAscii(m_hostaddr.c_str())); + return; + } + + char buffer[8]; + sprintf(buffer, "%d", m_port); + + m_hostaddr = "> Your IP is : " + sf::IPAddress::GetPublicAddress().ToString() + + ':' + std::string(buffer) + '\n'; + m_Logging->AppendText(wxString::FromAscii(m_hostaddr.c_str())); + } + else // Ask client to send server IP + { + value = 0x20; + m_sock_server->Write(0, (const char*)&value, 1); + } + break; + } + case ID_BUTTON_GETPING: + { + if (m_numClients == 0) + return; + + // TODO : This is not designed for > 2 players + long ping[3] = {0}; + float fping; + + if (m_isHosting == 1) { + m_sock_server->Write(0, 0, 0, ping); + fping = (ping[0]+ping[1]+ping[2])/(float)m_numClients; + } + else { + m_sock_client->Write(0, 0, ping); + fping = ping[0]; + } + + UpdateNetWindow( true, wxString::Format(wxT("000x%fx%d"), fping, (int)ceil(fping/(1000.0/60.0))) ); + break; + } + case ID_BUTTON_CHAT: + case ID_CHAT: + { + value = 0x30; + wxString chat_str = wxString::Format(wxT("> %s : %s\n"), m_nick.c_str(), m_Chat->GetValue().c_str()); + int chat_size = chat_str.size(); + + // If there's no distant connection, we write but we don't send + if (m_numClients == 0) { + m_Logging->AppendText(chat_str); + return; + } + // Max size that we handle is 1024, there's no need for more + if ((chat_str.size()+1) * sizeof(char) > 1024) { + m_Logging->AppendText(wxT("ERROR : Packet too large !\n")); + return; + } + + // Send to all + if (m_isHosting == 1) + { + for (int i=0; i < m_numClients ; i++) { + // Send Chat command + m_sock_server->Write(i, (const char*)&value, 1); // 0x30 -> Chat + + // Send Chat string + m_sock_server->Write(i, (const char*)&chat_size, 4); + m_sock_server->Write(i, chat_str.c_str(), chat_size + 1); + } + } + else { + m_sock_client->Write((const char*)&value, 1); + m_sock_client->Write((const char*)&chat_size, 4); + m_sock_client->Write(chat_str.c_str(), chat_size + 1); + } + + m_Chat->Clear(); + + // We should maybe wait for the server to send it... + // but we write it anyway :p + m_Logging->AppendText(chat_str); + + break; + } + case ID_RECORD: + // TODO : + // Record raw pad data + break; + case ID_CHANGEGAME: + { + GameListPopup PopUp(this, m_GameList_str); + PopUp.ShowModal(); + break; + } + } +} + +///////////////////////// +// GameList popup window + +BEGIN_EVENT_TABLE(GameListPopup, wxDialog) + EVT_BUTTON(wxID_OK, GameListPopup::OnButtons) + EVT_BUTTON(wxID_CANCEL, GameListPopup::OnButtons) +END_EVENT_TABLE() + +GameListPopup::GameListPopup(NetPlay *parent, wxArrayString GameNames) : + wxDialog(parent, wxID_ANY, _T("Choose a Game :"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE) +{ + m_netParent = parent; + m_GameList_str = GameNames; + m_GameList = new wxListBox(this, ID_GAMELIST, wxDefaultPosition, wxSize(300, 250), + GameNames, wxLB_SINGLE | wxLB_SORT | wxLB_NEEDED_SB); + m_Cancel = new wxButton(this, wxID_CANCEL, wxT("Cancel"), wxDefaultPosition, wxDefaultSize); + m_Accept = new wxButton(this, wxID_OK, wxT("Apply"), wxDefaultPosition, wxDefaultSize); + + wxBoxSizer* sButtons = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer* sMain = new wxBoxSizer(wxVERTICAL); + + sButtons->Add(m_Cancel, 0, wxALL, 0); + sButtons->AddStretchSpacer(1); + sButtons->Add(m_Accept, 0, wxALL | wxALIGN_RIGHT, 0); + + sMain->Add(m_GameList, 0, wxALL | wxEXPAND, 2); + sMain->Add(sButtons, 0, wxALL | wxEXPAND, 5); + + SetSizerAndFit(sMain); + Center(); Layout(); Show(); +} + +void GameListPopup::OnButtons(wxCommandEvent& event) +{ + switch (event.GetId()) + { + case wxID_OK: + m_netParent->ChangeSelectedGame(std::string(m_GameList_str[m_GameList->GetSelection()].mb_str())); + Destroy(); + break; + case wxID_CANCEL: + Destroy(); + break; + } +} + diff --git a/Source/Core/DolphinWX/Src/NetWindow.h b/Source/Core/DolphinWX/Src/NetWindow.h new file mode 100644 index 0000000000..baf99b06d3 --- /dev/null +++ b/Source/Core/DolphinWX/Src/NetWindow.h @@ -0,0 +1,307 @@ +// Copyright (C) 2003-2009 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _NETWINDOW_H_ +#define _NETWINDOW_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Globals.h" +#include "BootManager.h" +#include "Common.h" +#include "Core.h" +#include "pluginspecs_pad.h" +#include "HW/SI.h" +#include "HW/SI_Device.h" +#include "HW/SI_DeviceGCController.h" +#include "Timer.h" + +#ifdef _DEBUG + #define NET_DEBUG +#endif + +class NetPlay; + +struct Netpads { + int nHi[128]; + int nLow[128]; +}; + +struct Clients { + std::string nick; + sf::SocketTCP socket; + bool ready; +}; + +class NetEvent +{ + public: + NetEvent(NetPlay* netptr) { m_netptr = netptr; } + ~NetEvent() {}; + + void SendEvent(int EventType, std::string="NULL", int=NULL); + void AppendText(const wxString text); + + private: + NetPlay *m_netptr; +}; + +class ServerSide : public wxThread +{ + public: + ServerSide(NetPlay* netptr, sf::SocketTCP socket, int netmodel, std::string nick); + ~ServerSide() {}; + + virtual void *Entry(); + + void Write(char socknb, const char *data, size_t size, long *ping=NULL); + bool isNewPadData(u32 *netValues, bool current, char client=0); + + private: + void SyncValues(unsigned char, sf::IPAddress); + char GetSocket(sf::SocketTCP Socket); + void OnServerData(char sock, unsigned char data); + void IsEveryoneReady(); + + NetPlay *m_netptr; + NetEvent *Event; + + u32 m_netvalues[3][2]; + bool m_data_received; // New Pad data received ? + + unsigned char m_numplayers; + int m_netmodel; + std::string m_nick; + + Clients m_client[3]; // Connected client objects + sf::SelectorTCP m_selector; + sf::SocketTCP m_socket; // Server 'listening' socket + + wxCriticalSection m_CriticalSection; +}; + +class ClientSide : public wxThread +{ + public: + ClientSide(NetPlay* netptr, sf::SocketTCP socket, std::string addr, std::string nick); + ~ClientSide() {} + + virtual void *Entry(); + + void Write(const char *data, size_t size, long *ping=NULL); + bool isNewPadData(u32 *netValues, bool current, bool isVersus=true); + + private: + bool SyncValues(); + void CheckGameFound(); + void OnClientData(unsigned char data); + + NetPlay *m_netptr; + NetEvent *Event; + + u32 m_netvalues[3][2]; + bool m_data_received; // New Pad data received ? + + unsigned char m_numplayers; + int m_netmodel; + std::string m_nick; + std::string m_hostnick; + std::string m_selectedgame; + + sf::SelectorTCP m_selector; + sf::SocketTCP m_socket; // Client I/O socket + std::string m_addr; // Contains the server addr + + wxCriticalSection m_CriticalSection; +}; + +class NetPlay : public wxDialog +{ + public: + NetPlay(wxWindow* parent, std::string GamePath = "", std::string GameName = ""); + ~NetPlay() {} + + void UpdateNetWindow(bool update_infos, wxString="NULL"); + void AppendText(const wxString text) { m_Logging->AppendText(text); } + + // Send and receive pads values + bool GetNetPads(u8 pad_nb, SPADStatus, u32 *netvalues); + void ChangeSelectedGame(std::string game); + void IsGameFound(unsigned char*, std::string); + bool IsReady() { wxCriticalSectionLocker lock(m_critical); return m_ready; } + std::string GetSelectedGame() { wxCriticalSectionLocker lock(m_critical); return m_selectedGame; } + + protected: + // Protects our vars from being fuxored by threads + wxCriticalSection m_critical; + + // this draws the GUI, ya rly + void DrawGUI(); + void DrawNetWindow(); + + // event handlers + void OnGUIEvent(wxCommandEvent& event); + void OnDisconnect(wxCommandEvent& event); + void OnNetEvent(wxCommandEvent& event); + void OnQuit(wxCloseEvent& event); + + void OnJoin(wxCommandEvent& event); + void OnHost(wxCommandEvent& event); + + void LoadGame(); + + // Net play vars (used ingame) + int m_frame; + Common::Timer m_timer; + int m_loopframe; + int m_frameDelay; + bool m_data_received;// True if first frame data received + + // Basic vars + std::string m_paths; // Game paths list + std::string m_games; // Game names list + + std::string m_selectedGame;// Selected game's string + std::string m_hostaddr; // Used with OnGetIP to cache it + bool m_ready, m_clients_ready; + std::string m_nick; + + int m_NetModel; // Using P2P model (0) or Server model (1) + int m_isHosting; // 0 = false ; 1 = true ; 2 = Not set + unsigned char m_numClients; // starting from 0, 4 players max thus 3 clients + unsigned short m_port; + + Netpads m_pads[4]; // this struct is used to save synced pad values + + // Sockets objects + sf::SocketTCP m_listensocket; + ServerSide *m_sock_server; + ClientSide *m_sock_client; + + // ----------- + // GUI objects + // ----------- + wxNotebook *m_Notebook; + wxPanel *m_Tab_Connect; + wxPanel *m_Tab_Host; + wxStaticText *m_SetNick_text; + wxTextCtrl *m_SetNick; + wxChoice *m_NetMode; + + // Host tab : + wxArrayString m_GameList_str; + wxStaticText *m_GameList_text; + wxListBox *m_GameList; + wxStaticText *m_SetPort_text; + wxTextCtrl *m_SetPort; + wxButton *m_HostGame; + + // Connect tab : + wxTextCtrl *m_ConAddr; + wxStaticText *m_ConAddr_text; + wxButton *m_JoinGame; + wxCheckBox *m_UseRandomPort; + + // Connection window + wxButton *m_Game_str; + wxTextCtrl *m_Logging; + wxTextCtrl *m_Chat; + wxButton *m_Chat_ok; + // Right part + wxButton *m_wtfismyip; + wxButton *m_ChangeGame; + // Left Part + wxButton *m_Disconnect; + wxStaticText *m_ConInfo_text; + wxButton *m_GetPing; + wxCheckBox *m_Ready; + wxCheckBox *m_RecordGame; + + // wxWidgets event table + DECLARE_EVENT_TABLE() +}; + +class GameListPopup : public wxDialog +{ + public: + GameListPopup(NetPlay *net_ptr, wxArrayString GameNames); + ~GameListPopup() {} + protected: + void OnButtons(wxCommandEvent& event); + wxArrayString m_GameList_str; + NetPlay* m_netParent; + wxListBox *m_GameList; + wxButton *m_Accept; + wxButton *m_Cancel; + DECLARE_EVENT_TABLE() +}; + +enum +{ + ID_NOTEBOOK, + ID_TAB_HOST, + ID_TAB_CONN, + ID_BUTTON_HOST, + ID_BUTTON_JOIN, + ID_NETMODE, + ID_GAMELIST, + ID_GAMELIST_TXT, + ID_LOGGING_TXT, + ID_CHAT, + ID_SETNICK_TXT, + ID_SETNICK, + ID_SETPORT, + ID_SETPORT_TXT, + ID_CONNADDR, + ID_CONNADDR_TXT, + ID_CONNINFO_TXT, + ID_USE_RANDOMPORT, + ID_BUTTON_GETPING, + ID_BUTTON_GETIP, + ID_CHANGEGAME, + ID_BUTTON_QUIT, + ID_BUTTON_CHAT, + ID_READY, + ID_RECORD, + + ID_SOCKET, + ID_SERVER, + + HOST_FULL = 200, // ... + HOST_ERROR, // Sent on socket error + HOST_DISCONNECTED, + HOST_NEWPLAYER, + HOST_PLAYERLEFT, + CLIENTS_READY, + CLIENTS_NOTREADY, + GUI_UPDATE, // Refresh the shown selectedgame on GUI + ADD_TEXT, // Add text to m_Logging (string) + ADD_INFO, // Sent when updating net infos (string) + NET_EVENT +}; + +#endif // _NETWINDOW_H_ + diff --git a/Source/Dolphin.sln b/Source/Dolphin.sln index 2a4606a6b0..fa3dcda674 100644 --- a/Source/Dolphin.sln +++ b/Source/Dolphin.sln @@ -59,6 +59,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Dolphin", "Core\DolphinWX\D {33546D62-7F34-4EA6-A88E-D538B36E16BF} = {33546D62-7F34-4EA6-A88E-D538B36E16BF} {11F55366-12EC-4C44-A8CB-1D4E315D61ED} = {11F55366-12EC-4C44-A8CB-1D4E315D61ED} {3E03C179-8251-46E4-81F4-466F114BAC63} = {3E03C179-8251-46E4-81F4-466F114BAC63} + {823DDC98-42D5-4A38-88CF-9DC06C788AE4} = {823DDC98-42D5-4A38-88CF-9DC06C788AE4} {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} = {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} {521498BE-6089-4780-8223-E67C22F4E068} = {521498BE-6089-4780-8223-E67C22F4E068} {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA} = {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA}