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}