NetPlay: completely redone - should be somewhat usable when using Single Core and DSP LLE Plugin.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@5425 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
Jordan Woyak 2010-05-01 19:10:35 +00:00
parent 1796cbc917
commit a6d6d27328
16 changed files with 1544 additions and 2497 deletions

View File

@ -86,6 +86,7 @@
#define HAVE_OPENAL 1 #define HAVE_OPENAL 1
#define HAVE_ALSA 0 #define HAVE_ALSA 0
#define HAVE_PORTAUDIO 0 #define HAVE_PORTAUDIO 0
#define HAVE_SFML 1
// it is VERY DANGEROUS to mix _SECURE_SCL=0 and _SECURE_SCL=1 compiled libraries. // it is VERY DANGEROUS to mix _SECURE_SCL=0 and _SECURE_SCL=1 compiled libraries.
// You will get bizarre crash bugs whenever you use STL. // You will get bizarre crash bugs whenever you use STL.

View File

@ -415,7 +415,12 @@ u32 CEXIIPL::GetGCTime()
u64 ltime = Common::Timer::GetTimeSinceJan1970(); u64 ltime = Common::Timer::GetTimeSinceJan1970();
return ((u32)ltime - cJanuary2000 - Bias); return ((u32)ltime - cJanuary2000 - Bias);
#else #else
u64 ltime = Common::Timer::GetLocalTimeSinceJan1970();
// hack in some netplay stuff
u64 ltime = GetNetGCTime();
if (0 == ltime)
ltime = Common::Timer::GetLocalTimeSinceJan1970();
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii) if (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii)
return ((u32)ltime - cJanuary2000 - cWiiBias/* + 32434790*/); return ((u32)ltime - cJanuary2000 - cWiiBias/* + 32434790*/);
else else

View File

@ -32,6 +32,8 @@ public:
void DoState(PointerWrap &p); void DoState(PointerWrap &p);
static u32 GetGCTime(); static u32 GetGCTime();
static u32 GetNetGCTime();
static void Descrambler(u8* data, u32 size); static void Descrambler(u8* data, u32 size);
private: private:

View File

@ -923,185 +923,15 @@
Name="NetPlay" Name="NetPlay"
> >
<File <File
RelativePath=".\Src\NetEvent.cpp" RelativePath=".\Src\LockingQueue.h"
>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
<FileConfiguration
Name="DebugFast|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
<FileConfiguration
Name="DebugFast|x64"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\Src\NetFunctions.cpp"
>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
<FileConfiguration
Name="DebugFast|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
<FileConfiguration
Name="DebugFast|x64"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\Src\NetSockets.cpp"
>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
<FileConfiguration
Name="DebugFast|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
<FileConfiguration
Name="DebugFast|x64"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
ForcedIncludeFiles="$(NOINHERIT)"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\Src\NetSockets.h"
> >
</File> </File>
<File <File
RelativePath=".\Src\NetStructs.h" RelativePath=".\Src\NetPlay.cpp"
>
</File>
<File
RelativePath=".\Src\NetPlay.h"
> >
</File> </File>
<File <File

View File

@ -187,8 +187,7 @@ void CFrame::CreateMenu()
toolsMenu->Append(IDM_CHEATS, _T("Action &Replay Manager")); toolsMenu->Append(IDM_CHEATS, _T("Action &Replay Manager"));
#if defined(HAVE_SFML) && HAVE_SFML #if defined(HAVE_SFML) && HAVE_SFML
// Disabled for now, netplay doesn't quite work currently toolsMenu->Append(IDM_NETPLAY, _T("Start &NetPlay"));
// toolsMenu->Append(IDM_NETPLAY, _T("Start &NetPlay"));
#endif #endif
if (DiscIO::CNANDContentManager::Access().GetNANDLoader(std::string (File::GetUserPath(D_WIIMENU_IDX))).IsValid()) if (DiscIO::CNANDContentManager::Access().GetNANDLoader(std::string (File::GetUserPath(D_WIIMENU_IDX))).IsValid())
@ -1000,7 +999,7 @@ void CFrame::StatusBarMessage(const char * Text, ...)
void CFrame::OnNetPlay(wxCommandEvent& WXUNUSED (event)) void CFrame::OnNetPlay(wxCommandEvent& WXUNUSED (event))
{ {
#if defined(HAVE_SFML) && HAVE_SFML #if defined(HAVE_SFML) && HAVE_SFML
new NetPlay(this, m_GameListCtrl->GetGamePaths(), m_GameListCtrl->GetGameNames()); new NetPlaySetupDiag(this, m_GameListCtrl);
#endif #endif
} }

View File

@ -0,0 +1,47 @@
#ifndef _LOCKINGQUEUE_H_
#define _LOCKINGQUEUE_H_
#include "Thread.h"
#include <queue>
// i should make one of those single reader/ single writer queues
template <typename T>
class LockingQueue
{
public:
size_t Size()
{
m_crit.Enter();
const size_t s = m_queue.size();
m_crit.Leave();
return s;
}
void Push(const T& t)
{
m_crit.Enter();
m_queue.push(t);
m_crit.Leave();
}
bool Pop(T& t)
{
m_crit.Enter();
if (m_queue.size())
{
t = m_queue.front();
m_queue.pop();
m_crit.Leave();
return true;
}
m_crit.Leave();
return false;
}
private:
std::queue<T> m_queue;
Common::CriticalSection m_crit;
};
#endif

View File

@ -1,289 +0,0 @@
// Copyright (C) 2003 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 "NetSockets.h"
#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::FromAscii(StringFromFormat("*Player : %s is now connected to Host...\n", buffer).c_str()));
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::FromAscii(StringFromFormat("*Player : %s left the game\n\n", buffer).c_str()));
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::FromAscii(StringFromFormat("*Host changed Game to : %s\n", buffer).c_str()));
// 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
m_netptr->LoadGame();
break;
}
case 0xA1: // Received pad data from host in versus mode
{
if (m_data_received)
wxThread::Sleep(10);
m_socket.Receive((char*)m_netvalues[0], 8, recv_size);
m_data_received = true;
#ifdef NET_DEBUG
char msgbuf[64];
sprintf(msgbuf, "Received Values: 0x%08x : 0x%08x \n", m_netvalues[0][0], m_netvalues[0][1]);
Event->AppendText(wxString::FromAscii(msgbuf));
#endif
break;
}
}
delete[] buffer;
}
void ServerSide::OnServerData(int 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::FromAscii(StringFromFormat("> Your IP is : %s\n", buffer).c_str()));
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 (int 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::FromAscii(
StringFromFormat("WARNING : Player %s does Not have this Game !\n", m_client[sock].nick.c_str()).c_str());
four_bytes = (int)error_str.size();
for (int i=0; i < 2; i++)
Event->AppendText(error_str);
// Send to all
for (int 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 (int 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
{
if (m_data_received)
wxThread::Sleep(10);
m_client[sock].socket.Receive((char*)m_netvalues[sock], 8, recv_size);
m_data_received = true;
#ifdef NET_DEBUG
char msgbuf[64];
sprintf(msgbuf, "Received Values: 0x%08x : 0x%08x \n", m_netvalues[sock][0], m_netvalues[sock][1]);
Event->AppendText(wxString::FromAscii(msgbuf));
#endif
break;
}
}
delete[] buffer;
}

View File

@ -1,588 +0,0 @@
// Copyright (C) 2003 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 "NetSockets.h"
#include "NetWindow.h"
#include "HW/SI_DeviceGCController.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")
wxT("*Player : %s has been dropped from the game.\n\n"),
(const char *)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;
m_numClients--;
}
break;
case HOST_PLAYERLEFT:
{
m_numClients--;
}
break;
case HOST_NEWPLAYER:
{
m_numClients++;
m_NetModel = event.GetInt();
}
break;
case CLIENTS_READY:
{
m_clients_ready = true;
// Tell clients everyone is ready...
if (m_ready)
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
if (m_isHosting == 1)
{
long ping[3] = {0};
unsigned char value = 0x50;
// Get ping
m_sock_server->Write(0, 0, 0, ping);
float fping = (ping[0]+ping[1]+ping[2])/(float)m_numClients;
// Tell client everyone is ready
for (int i=0; i < m_numClients ; i++)
m_sock_server->Write(i, (const char*)&value, 1);
// Sleep a bit to start the game at more or less the same time than the peer
wxMilliSleep(fping/2);
m_Logging->AppendText(wxString::Format(wxT("** Everyone is ready... Loading Game ! **\n")
wxT("** Ping to client(s) is : %f ms\n"), fping));
}
else
m_Logging->AppendText(_("** Everyone is ready... Loading Game ! **\n"));
// TODO : Throttle should be on by default, to avoid stuttering
//soundStream->GetMixer()->SetThrottle(true);
int line_p = 0;
int line_n = 0;
m_critical.Enter();
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();
m_data_received = false;
m_critical.Leave();
// 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)
{
if (m_numClients < 1)
{
m_Logging->AppendText(_("** WARNING : Ping too high (>2000ms) or connection lost ! \n** WARNING : Stopping Netplay... \n"));
NetClass_ptr = NULL;
return false;
}
// 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);
if (m_NetModel == 0) // Use 2 players Model
{
if (padnb == 0)
{
// Update the timer and increment total frame number
m_frame++;
if (m_frame == 1)
{
// We make sure everyone's pad is enabled
for (int i = 0; i < m_numClients+1; i++)
SerialInterface::ChangeDevice(SI_GC_CONTROLLER, i);
// Better disable unused ports
for (int i = m_numClients+1; i < 4; i++)
SerialInterface::ChangeDevice(SI_NONE, i);
}
if (m_timer.GetTimeDifference() > 1000)
m_timer.Update();
#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 player = 0;
#ifdef USE_TCP
unsigned char init_value = 0xA1;
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);
}
else {
// Send pads values
m_sock_client->Write((const char*)&init_value, 1);
m_sock_client->Write((const char*)netValues, 8);
player = 1;
}
#else // UDP
u32 padsValues[3];
padsValues[0] = m_frame;
padsValues[1] = netValues[0];
padsValues[2] = netValues[1];
if (m_isHosting == 1) {
// Send pads values
m_sock_server->WriteUDP(0, (const char*)padsValues, 12);
}
else {
// Send pads values
m_sock_client->WriteUDP((const char*)padsValues, 12);
player = 1;
}
#endif
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, false);
else
m_data_received = m_sock_client->isNewPadData(0, false);
if (m_data_received)
{
// Set our practical frame delay
m_frameDelay = m_loopframe;
m_loopframe = 0;
// First Data has been received !
m_Logging->AppendText(_("** Data received from Peer. Starting Sync !"));
m_Logging->AppendText(wxString::Format(wxT(" Frame Delay : %d **\n"), m_frameDelay));
}
else {
if (m_loopframe > 126)
{
m_Logging->AppendText(_("** WARNING : Ping too high (>2000ms) or connection lost ! \n** WARNING : Stopping Netplay... \n"));
NetClass_ptr = NULL;
}
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);
u32 recvedValues[2];
m_pads[player].nHi[saveslot] = netValues[0];
m_pads[player].nLow[saveslot] = netValues[1];
// Read the socket for pad values
if (m_isHosting == 1)
m_sock_server->isNewPadData(recvedValues, true);
else
m_sock_client->isNewPadData(recvedValues, true);
if (player == 0)
{
// Store received peer values
m_pads[1].nHi[m_loopframe] = recvedValues[0];
m_pads[1].nLow[m_loopframe] = recvedValues[1];
// Apply synced pad values
netValues[0] = m_pads[0].nHi[m_loopframe];
netValues[1] = m_pads[0].nLow[m_loopframe];
}
else
{
// Apply received pad values
netValues[0] = recvedValues[0];
netValues[1] = recvedValues[1];
}
}
#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 = (int)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, game.c_str(), game_size + 1);
}
m_selectedGame = game;
UpdateNetWindow(false);
m_Logging->AppendText(wxString::Format( wxT(" *Game has been changed to : %s \r\n "), wxString(game.c_str(), wxConvUTF8).c_str()));
NOTICE_LOG(NETPLAY,"Game has been changed to : %s \n",game.c_str());
}
}
void NetPlay::OnQuit(wxCloseEvent& WXUNUSED(event))
{
// Disable netplay
NetClass_ptr = NULL;
// Destroy the Window
Destroy();
// Then Kill the threads
if (m_isHosting == 0)
m_sock_client->Delete();
else if (m_isHosting == 1) {
m_sock_server->Delete();
}
}
void NetPlay::OnDisconnect(wxCommandEvent& WXUNUSED(event))
{
wxCloseEvent close;
OnQuit(close);
}
bool ClientSide::isNewPadData(u32 *netValues, bool current, bool isVersus)
{
#ifdef USE_TCP
if (current)
{
while (1)
{
m_CriticalSection.Enter();
if (m_data_received && isVersus)
{
netValues[0] = m_netvalues[0][0];
netValues[1] = m_netvalues[0][1];
m_data_received = false;
m_CriticalSection.Leave();
break;
}
m_CriticalSection.Leave();
if (TestDestroy())
break;
}
return true;
}
else
wxCriticalSectionLocker lock(m_CriticalSection);
return m_data_received;
#else
size_t recv_size;
if (current)
{
m_CriticalSection.Enter();
if (isVersus)
{
if (m_netvalues[0][1] != 0)
{
netValues[0] = m_netvalues[0][1];
netValues[1] = m_netvalues[0][2];
}
else
{
while (true)
{
u32 frame_saved = m_netvalues[0][0];
bool pass = RecvT(m_socketUDP, (char*)&m_netvalues[0], 12, recv_size, 5);
if (m_netvalues[0][0] < frame_saved+1)
continue;
if (m_netvalues[0][0] > frame_saved+1 || !pass)
PanicAlert("Network ERROR !");
netValues[0] = m_netvalues[0][1];
netValues[1] = m_netvalues[0][2];
break;
}
}
}
m_netvalues[0][1] = 0;
m_CriticalSection.Leave();
return true;
}
else
return RecvT(m_socketUDP, (char*)&m_netvalues[0], 12, recv_size, 1/1000);
#endif
}
bool ServerSide::isNewPadData(u32 *netValues, bool current, int client)
{
#ifdef USE_TCP
if (current)
{
while (1)
{
m_CriticalSection.Enter();
if (m_data_received)
{
netValues[0] = m_netvalues[client][0];
netValues[1] = m_netvalues[client][1];
m_data_received = false;
m_CriticalSection.Leave();
break;
}
m_CriticalSection.Leave();
if (TestDestroy())
break;
}
return true;
}
else
wxCriticalSectionLocker lock(m_CriticalSection);
return m_data_received;
#else
size_t recv_size;
if (current)
{
m_CriticalSection.Enter();
if (m_netvalues[0][1] != 0)
{
netValues[0] = m_netvalues[client][1];
netValues[1] = m_netvalues[client][2];
}
else
{
while (true)
{
u32 frame_saved = m_netvalues[client][0];
bool pass = RecvT(m_socketUDP, (char*)&m_netvalues[client], 12, recv_size, 5);
if (m_netvalues[client][0] < frame_saved+1)
continue;
if (m_netvalues[client][0] > frame_saved+1 || !pass)
PanicAlert("Network ERROR !");
netValues[0] = m_netvalues[client][1];
netValues[1] = m_netvalues[client][2];
break;
}
}
m_netvalues[client][1] = 0;
m_CriticalSection.Leave();
return true;
}
else
return RecvT(m_socketUDP, (char*)&m_netvalues[client], 12, recv_size, 1/1000);
#endif
}

View File

@ -0,0 +1,912 @@
// Copyright (C) 2003 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 "HW/SI_DeviceGCController.h"
#include "HW/EXI_DeviceIPL.h"
#include "Frame.h"
#include "NetPlay.h"
#include "NetWindow.h"
#include <sstream>
// this will be removed soon
#define TEMP_FIXED_BUFFER 20
enum
{
CON_ERR_SERVER_FULL = 1,
CON_ERR_GAME_RUNNING,
CON_ERR_VERSION_MISMATCH
};
NetPlay* NetClass_ptr = NULL;
extern CFrame* main_frame;
DEFINE_EVENT_TYPE(wxEVT_THREAD)
THREAD_RETURN NetPlayThreadFunc(void* arg)
{
((NetPlay*)arg)->Entry();
return 0;
}
NetPlay::~NetPlay()
{
::NetClass_ptr = NULL;
}
NetPlayServer::~NetPlayServer()
{
if (is_connected)
{
m_do_loop = false;
m_thread->WaitForDeath();
delete m_thread;
}
}
NetPlayClient::~NetPlayClient()
{
if (is_connected)
{
m_do_loop = false;
m_thread->WaitForDeath();
delete m_thread;
}
}
NetPlayServer::Player::Player()
{
memset(pad_map, -1, sizeof(pad_map));
}
NetPlayClient::Player::Player()
{
memset(pad_map, -1, sizeof(pad_map));
}
NetPlayServer::NetPlayServer(const u16 port, const std::string& name, NetPlayDiag* const npd, const std::string& game)
{
m_dialog = npd;
m_selected_game = game;
if (m_socket.Listen(port))
{
Player player;
player.pid = 0;
player.revision = NETPLAY_DOLPHIN_VER;
player.socket = m_socket;
player.name = name;
// map local pad 1 to game pad 1
player.pad_map[0] = 0;
// add self to player list
m_players[m_socket] = player;
//PanicAlert("Listening");
UpdateGUI();
is_connected = true;
m_selector.Add(m_socket);
m_thread = new Common::Thread(NetPlayThreadFunc, this);
}
else
is_connected = false;
}
NetPlayClient::NetPlayClient(const std::string& address, const u16 port, const std::string& name, NetPlayDiag* const npd)
{
m_dialog = npd;
is_connected = false;
// why is false successful? documentation says true is
if (0 == m_socket.Connect(port, address))
{
// send connect message
sf::Packet spac;
spac << NETPLAY_VERSION;
spac << NETPLAY_DOLPHIN_VER;
spac << name;
m_socket.Send(spac);
sf::Packet rpac;
// TODO: make this not hang
m_socket.Receive(rpac);
u8 error;
rpac >> error;
// got error message
if (error)
{
switch (error)
{
case CON_ERR_SERVER_FULL :
PanicAlert("The server is full!");
break;
case CON_ERR_VERSION_MISMATCH :
PanicAlert("The NetPlay versions are incompatible!");
break;
case CON_ERR_GAME_RUNNING :
PanicAlert("The game is currently running!");
break;
}
m_socket.Close();
}
else
{
rpac >> m_pid;
Player player;
player.name = "Player";
player.pid = m_pid;
player.revision = NETPLAY_DOLPHIN_VER;
// add self to player list
m_players[m_pid] = player;
UpdateGUI();
//PanicAlert("Connection successful: assigned player id: %d", m_pid);
is_connected = true;
m_selector.Add(m_socket);
m_thread = new Common::Thread(NetPlayThreadFunc, this);
}
}
else
PanicAlert("Failed to Connect!");
}
void NetPlayServer::Entry()
{
while (m_do_loop)
{
const unsigned int num = m_selector.Wait(0.01f);
for (unsigned int i=0; i<num; ++i)
{
sf::SocketTCP ready_socket = m_selector.GetSocketReady(i);
// listening socket
if (ready_socket == m_socket)
{
sf::SocketTCP accept_socket;
m_socket.Accept(accept_socket);
m_crit.other.Enter();
unsigned int error = OnConnect(accept_socket);
m_crit.other.Leave();
if (error)
{
sf::Packet spac;
spac << (MessageId)error;
// don't need to lock, this client isn't in the client map
accept_socket.Send(spac);
// TODO: not sure if client gets the message if i close right away
accept_socket.Close();
}
}
// client socket
else
{
sf::Packet rpac;
switch (ready_socket.Receive(rpac))
{
case sf::Socket::Done :
// if a bad packet is recieved, disconnect the client
if (0 == OnData(rpac, ready_socket))
break;
case sf::Socket::Disconnected :
m_crit.other.Enter();
OnDisconnect(ready_socket);
m_crit.other.Leave();
break;
}
}
}
}
// close listening socket and client sockets
{
std::map<sf::SocketTCP, Player>::reverse_iterator
i = m_players.rbegin(),
e = m_players.rend();
for ( ; i!=e; ++i)
i->second.socket.Close();
}
return;
}
unsigned int NetPlayServer::OnConnect(sf::SocketTCP& socket)
{
sf::Packet rpac;
// TODO: make this not hang / check if good packet
socket.Receive(rpac);
std::string npver;
rpac >> npver;
// dolphin netplay version
if (npver != NETPLAY_VERSION)
return CON_ERR_VERSION_MISMATCH;
// game is currently running
if (m_is_running)
return CON_ERR_GAME_RUNNING;
// too many players
if (m_players.size() >= 255)
return CON_ERR_SERVER_FULL;
Player player;
player.socket = socket;
rpac >> player.revision;
rpac >> player.name;
// give new client first available id
player.pid = 0;
std::map<sf::SocketTCP, Player>::const_iterator
i,
e = m_players.end();
for (u8 p = 1; 0 == player.pid; ++p)
{
for (i = m_players.begin(); ; ++i)
{
if (e == i)
{
player.pid = p;
break;
}
if (p == i->second.pid)
break;
}
}
// TODO: this is crappy
// try to automatically assign new user a pad
{
bool is_mapped[4] = {false,false,false,false};
for ( unsigned int m = 0; m<4; ++m)
{
for (i = m_players.begin(); i!=e; ++i)
{
if (i->second.pad_map[m] >= 0)
is_mapped[i->second.pad_map[m]] = true;
}
}
for ( unsigned int m = 0; m<4; ++m)
if (false == is_mapped[m])
{
player.pad_map[0] = m;
break;
}
}
// ENTER
m_crit.send.Enter();
// send join message to already connected clients
sf::Packet spac;
spac << (MessageId)NP_MSG_PLAYER_JOIN;
spac << player.pid << player.name << player.revision;
SendToClients(spac);
// send new client success message with their id
spac.Clear();
spac << (MessageId)0;
spac << player.pid;
socket.Send(spac);
// send user their pad mapping
spac.Clear();
spac << (MessageId)NP_MSG_PAD_MAPPING;
spac << player.pid;
for (unsigned int pm = 0; pm<4; ++pm)
spac << player.pad_map[pm];
socket.Send(spac);
// send new client the selected game
spac.Clear();
spac << (MessageId)NP_MSG_CHANGE_GAME;
spac << m_selected_game;
socket.Send(spac);
// sync values with new client
for (i = m_players.begin(); i!=e; ++i)
{
spac.Clear();
spac << (MessageId)NP_MSG_PLAYER_JOIN;
spac << i->second.pid << i->second.name << i->second.revision;
socket.Send(spac);
spac.Clear();
spac << (MessageId)NP_MSG_PAD_MAPPING;
spac << i->second.pid;
for (unsigned int pm = 0; pm<4; ++pm)
spac << i->second.pad_map[pm];
socket.Send(spac);
}
// LEAVE
m_crit.send.Leave();
// add client to the player list
m_crit.players.Enter();
m_players[socket] = player;
m_crit.players.Leave();
// add client to selector/ used for receiving
m_selector.Add(socket);
UpdateGUI();
return 0;
}
unsigned int NetPlayServer::OnDisconnect(sf::SocketTCP& socket)
{
if (m_is_running)
PanicAlert("Disconnect while game is running. Game will likely hang!");
sf::Packet spac;
spac << (MessageId)NP_MSG_PLAYER_LEAVE;
spac << m_players[socket].pid;
m_selector.Remove(socket);
m_crit.players.Enter();
m_players.erase(m_players.find(socket));
m_crit.players.Leave();
UpdateGUI();
m_crit.send.Enter();
SendToClients(spac);
m_crit.send.Leave();
return 0;
}
void NetPlay::UpdateGUI()
{
if (m_dialog)
{
wxCommandEvent evt(wxEVT_THREAD, 1);
m_dialog->AddPendingEvent(evt);
}
}
void NetPlayServer::SendToClients(sf::Packet& packet, const u8 skip_pid)
{
std::map<sf::SocketTCP, Player>::iterator
i = m_players.begin(),
e = m_players.end();
for ( ; i!=e; ++i)
if (i->second.pid && (i->second.pid != skip_pid))
i->second.socket.Send(packet);
}
unsigned int NetPlayServer::OnData(sf::Packet& packet, sf::SocketTCP& socket)
{
MessageId mid;
packet >> mid;
// don't need lock because this is the only thread that modifies the players
// only need locks for writes to m_players in this thread
Player& player = m_players[socket];
switch (mid)
{
case NP_MSG_CHAT_MESSAGE :
{
std::string msg;
packet >> msg;
// send msg to other clients
sf::Packet spac;
spac << (MessageId)NP_MSG_CHAT_MESSAGE;
spac << player.pid;
spac << msg;
m_crit.send.Enter();
SendToClients(spac, player.pid);
m_crit.send.Leave();
// add to gui
std::ostringstream ss;
ss << player.name << '[' << (char)(player.pid+'0') << "]: " << msg;
m_dialog->chat_msgs.Push(ss.str());
UpdateGUI();
}
break;
case NP_MSG_PAD_DATA :
{
PadMapping map;
NetPad np;
packet >> map >> np.nHi >> np.nLo;
// check if client's pad indeed maps in game
if (map >= 0 && map < 4)
map = player.pad_map[map];
else
map = -1;
// if not, they are hacking, so disconnect them
// this could happen right after a pad map change, but that isn't implimented yet
if (map < 0)
return 1;
// add to pad buffer
m_crit.buffer.Enter();
m_pad_buffer[map].push(np);
m_crit.buffer.Leave();
// relay to clients
sf::Packet spac;
spac << (MessageId)NP_MSG_PAD_DATA;
spac << map; // in game mapping
spac << np.nHi << np.nLo;
m_crit.send.Enter();
SendToClients(spac, player.pid);
m_crit.send.Leave();
}
break;
default :
//PanicAlert("Unknown message received with id : %d", mid);
// unknown message, kick the client
return 1;
break;
}
return 0;
}
unsigned int NetPlayClient::OnData(sf::Packet& packet)
{
MessageId mid;
packet >> mid;
switch (mid)
{
case NP_MSG_PLAYER_JOIN :
{
Player player;
packet >> player.pid;
packet >> player.name;
packet >> player.revision;
m_crit.players.Enter();
m_players[player.pid] = player;
m_crit.players.Leave();
UpdateGUI();
}
break;
case NP_MSG_PLAYER_LEAVE :
{
PlayerId pid;
packet >> pid;
m_crit.players.Enter();
m_players.erase(m_players.find(pid));
m_crit.players.Leave();
UpdateGUI();
}
break;
case NP_MSG_CHAT_MESSAGE :
{
PlayerId pid;
packet >> pid;
std::string msg;
packet >> msg;
// don't need lock to read in this thread
const Player& player = m_players[pid];
// add to gui
std::ostringstream ss;
ss << player.name << '[' << (char)(pid+'0') << "]: " << msg;
m_dialog->chat_msgs.Push(ss.str());
UpdateGUI();
}
break;
case NP_MSG_PAD_MAPPING :
{
PlayerId pid;
packet >> pid;
m_crit.players.Enter();
Player& player = m_players[pid];
for (unsigned int i=0; i<4; ++i)
packet >> player.pad_map[i];
m_crit.players.Leave();
UpdateGUI();
}
break;
case NP_MSG_PAD_DATA :
{
PadMapping map;
NetPad np;
packet >> map >> np.nHi >> np.nLo;
// add to pad buffer
m_crit.buffer.Enter();
m_pad_buffer[map].push(np);
m_crit.buffer.Leave();
}
break;
case NP_MSG_CHANGE_GAME :
{
m_crit.other.Enter();
packet >> m_selected_game;
m_crit.other.Leave();
// update gui
wxCommandEvent evt(wxEVT_THREAD, 45);
evt.SetString(wxString(m_selected_game.c_str(), *wxConvCurrent));
m_dialog->AddPendingEvent(evt);
}
break;
case NP_MSG_START_GAME :
{
// kinda silly
wxCommandEvent evt;
m_dialog->OnStart(evt);
}
break;
default :
PanicAlert("Unknown message received with id : %d", mid);
break;
}
return 0;
}
void NetPlayClient::Entry()
{
while (m_do_loop)
{
if (m_selector.Wait(0.01f))
{
sf::Packet rpac;
switch (m_socket.Receive(rpac))
{
case sf::Socket::Done :
OnData(rpac);
break;
case sf::Socket::Disconnected :
PanicAlert("Lost connection to server! If the game is running, it will likely hang!");
m_do_loop = false;
break;
}
}
}
m_socket.Close();
return;
}
template <typename P>
static inline std::string PlayerToString(const P& p)
{
std::ostringstream ss;
ss << p.name << '[' << (char)(p.pid+'0') << "] : " << p.revision << " |";
for (unsigned int i=0; i<4; ++i)
ss << (p.pad_map[i]>=0 ? (char)(p.pad_map[i]+'1') : '-');
ss << '|';
return ss.str();
}
void NetPlayClient::GetPlayerList(std::string &list)
{
m_crit.players.Enter();
std::ostringstream ss;
std::map<u8, Player>::const_iterator
i = m_players.begin(),
e = m_players.end();
for ( ; i!=e; ++i)
ss << PlayerToString(i->second) << '\n';
list = ss.str();
m_crit.players.Leave();
}
void NetPlayServer::GetPlayerList(std::string &list)
{
m_crit.players.Enter();
std::ostringstream ss;
std::map<sf::SocketTCP, Player>::const_iterator
i = m_players.begin(),
e = m_players.end();
for ( ; i!=e; ++i)
ss << PlayerToString(i->second) << '\n';
list = ss.str();
m_crit.players.Leave();
}
void NetPlayServer::SendChatMessage(const std::string& msg)
{
sf::Packet spac;
spac << (MessageId)NP_MSG_CHAT_MESSAGE;
spac << (PlayerId)0; // server id always 0
spac << msg;
m_crit.send.Enter();
SendToClients(spac);
m_crit.send.Leave();
}
void NetPlayClient::SendChatMessage(const std::string& msg)
{
sf::Packet spac;
spac << (MessageId)NP_MSG_CHAT_MESSAGE;
spac << msg;
m_crit.send.Enter();
m_socket.Send(spac);
m_crit.send.Leave();
}
NetPad::NetPad()
{
//SPADStatus sp;
//memset(&sp, 0, sizeof(sp));
//memset(&sp.stickX, 0x80, 4);
//*this = NetPad(&sp);
// i'd rather do something like this
nHi = 0x00808080;
nLo = 0x80800000;
}
NetPad::NetPad(const SPADStatus* const pad_status)
{
nHi = (u32)((u8)pad_status->stickY);
nHi |= (u32)((u8)pad_status->stickX << 8);
nHi |= (u32)((u16)pad_status->button << 16);
nHi |= 0x00800000;
nLo = (u8)pad_status->triggerRight;
nLo |= (u32)((u8)pad_status->triggerLeft << 8);
nLo |= (u32)((u8)pad_status->substickY << 16);
nLo |= (u32)((u8)pad_status->substickX << 24);
}
bool NetPlayServer::GetNetPads(const u8 pad_nb, const SPADStatus* const pad_status, NetPad* const netvalues)
{
m_crit.players.Enter();
// in game mapping for this local pad, kinda silly method
PadMapping in_game_num = m_players[m_socket].pad_map[pad_nb];
// check if this local pad maps in game
if (in_game_num >= 0)
{
NetPad np(pad_status);
// add to buffer
m_crit.buffer.Enter();
m_pad_buffer[in_game_num].push(np);
m_crit.buffer.Leave();
// send to clients
sf::Packet spac;
spac << (MessageId)NP_MSG_PAD_DATA;
spac << in_game_num; // in game pad num
spac << np.nHi << np.nLo;
m_crit.send.Enter();
SendToClients(spac);
m_crit.send.Leave();
}
// get padstate from buffer and send to game
m_crit.buffer.Enter();
while (0 == m_pad_buffer[pad_nb].size())
{
m_crit.buffer.Leave();
// wait for receiving thread to push some data
Common::SleepCurrentThread(10);
m_crit.buffer.Enter();
}
*netvalues = m_pad_buffer[pad_nb].front();
m_pad_buffer[pad_nb].pop();
m_crit.buffer.Leave();
m_crit.players.Leave();
return true;
}
bool NetPlayServer::SetSelectedGame(const std::string &game)
{
// warning removal
game.size();
return true;
}
bool NetPlayClient::GetNetPads(const u8 pad_nb, const SPADStatus* const pad_status, NetPad* const netvalues)
{
m_crit.players.Enter();
// in game mapping for this local pad
PadMapping in_game_num = m_players[m_pid].pad_map[pad_nb];
// does this local pad map in game?
if (in_game_num >= 0)
{
NetPad np(pad_status);
// add to buffer
m_crit.buffer.Enter();
m_pad_buffer[in_game_num].push(np);
m_crit.buffer.Leave();
// send to server
sf::Packet spac;
spac << (MessageId)NP_MSG_PAD_DATA;
spac << (PadMapping)pad_nb; // local pad num
spac << np.nHi << np.nLo;
m_crit.send.Enter();
m_socket.Send(spac);
m_crit.send.Leave();
}
// get padstate from buffer and send to game
m_crit.buffer.Enter();
while (0 == m_pad_buffer[pad_nb].size())
{
m_crit.buffer.Leave();
// wait for receiving thread to push some data
Common::SleepCurrentThread(10);
m_crit.buffer.Enter();
}
*netvalues = m_pad_buffer[pad_nb].front();
m_pad_buffer[pad_nb].pop();
m_crit.buffer.Leave();
m_crit.players.Leave();
return true;
}
bool NetPlayClient::SetSelectedGame(const std::string &game)
{
// warning removal
game.size();
return true;
}
bool NetPlayServer::StartGame(const std::string &path)
{
m_crit.other.Enter();
if (m_is_running)
{
PanicAlert("Game is already running!");
return false;
}
m_is_running = true;
::NetClass_ptr = this;
m_crit.buffer.Enter();
// testing
NetPad np;
for (unsigned int i=0; i<TEMP_FIXED_BUFFER; ++i)
for (unsigned int p=0; p<4; ++p)
m_pad_buffer[p].push(np);
m_crit.buffer.Leave();
// tell clients to start game
sf::Packet spac;
spac << (MessageId)NP_MSG_START_GAME;
m_crit.send.Enter();
SendToClients(spac);
m_crit.send.Leave();
// boot game
::main_frame->BootGame(path);
//BootManager::BootCore(path);
m_crit.other.Leave();
return true;
}
bool NetPlayClient::StartGame(const std::string &path)
{
m_crit.other.Enter();
m_is_running = true;
::NetClass_ptr = this;
m_crit.buffer.Enter();
// testing
NetPad np;
for (unsigned int i=0; i<TEMP_FIXED_BUFFER; ++i)
for (unsigned int p=0; p<4; ++p)
m_pad_buffer[p].push(np);
m_crit.buffer.Leave();
// boot game
::main_frame->BootGame(path);
//BootManager::BootCore(path);
m_crit.other.Leave();
return true;
}
// Actual Core function which is called on every frame
int CSIDevice_GCController::GetNetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus)
{
if (NetClass_ptr)
// TODO: NetClass_ptr could go null here, need a lock
return NetClass_ptr->GetNetPads(numPAD, &PadStatus, (NetPad*)PADStatus) ? 1 : 0;
else
return 2;
}
// so all players' games get the same time
u32 CEXIIPL::GetNetGCTime()
{
if (NetClass_ptr)
// TODO: NetClass_ptr could go null here, need a lock
return 1272737767; // watev
else
return 0;
}

View File

@ -0,0 +1,190 @@
#ifndef _NETPLAY_H
#define _NETPLAY_H
#include "Common.h"
#include "CommonTypes.h"
//#define WIN32_LEAN_AND_MEAN
#include "Thread.h"
// hax, i hope something like this isn't needed on non-windows
#define _WINSOCK2API_
#include <SFML/Network.hpp>
#include "pluginspecs_pad.h"
#include "svnrev.h"
//#include <wx/wx.h>
#include <map>
#include <queue>
class NetPlayDiag;
class NetPad
{
public:
NetPad();
NetPad(const SPADStatus* const);
u32 nHi;
u32 nLo;
};
#define NETPLAY_VERSION "Dolphin NetPlay 2.0"
#ifdef _M_X64
#define NP_ARCH "x64"
#else
#define NP_ARCH "x86"
#endif
#ifdef _WIN32
#define NETPLAY_DOLPHIN_VER SVN_REV_STR" win-"NP_ARCH
#elif __APPLE__
#define NETPLAY_DOLPHIN_VER SVN_REV_STR" osx-"NP_ARCH
#else
#define NETPLAY_DOLPHIN_VER SVN_REV_STR" nix-"NP_ARCH
#endif
// messages
#define NP_MSG_PLAYER_JOIN 0x10
#define NP_MSG_PLAYER_LEAVE 0x11
#define NP_MSG_CHAT_MESSAGE 0x30
#define NP_MSG_PAD_DATA 0x60
#define NP_MSG_PAD_MAPPING 0x61
#define NP_MSG_PAD_BUFFER 0x62
#define NP_MSG_START_GAME 0xA0
#define NP_MSG_CHANGE_GAME 0xA1
#define NP_MSG_READY 0xD0
#define NP_MSG_NOT_READY 0xD1
#define NP_MSG_PING 0xE0
#define NP_MSG_PONG 0xE1
// end messages
// gui messages
#define NP_GUI_UPDATE 0x10
// end messages
typedef u8 MessageId;
typedef u8 PlayerId;
typedef s8 PadMapping;
class NetPlay
{
public:
NetPlay() : m_is_running(false), m_do_loop(true) {}
virtual ~NetPlay();
virtual void Entry() = 0;
bool is_connected;
// Send and receive pads values
virtual bool GetNetPads(const u8 pad_nb, const SPADStatus* const, NetPad* const netvalues) = 0;
virtual bool SetSelectedGame(const std::string& game) = 0;
virtual void GetPlayerList(std::string& list) = 0;
virtual void SendChatMessage(const std::string& msg) = 0;
virtual bool StartGame(const std::string &path) = 0;
protected:
//NetPlay(Common::ThreadFunc entry, void* arg) : m_thread(entry, arg) {}
void UpdateGUI();
struct
{
Common::CriticalSection send, players, buffer, other;
} m_crit;
//LockingQueue<NetPad> m_pad_buffer[4];
std::queue<NetPad> m_pad_buffer[4];
NetPlayDiag* m_dialog;
sf::SocketTCP m_socket;
Common::Thread* m_thread;
sf::Selector<sf::SocketTCP> m_selector;
std::string m_selected_game;
bool m_is_running;
volatile bool m_do_loop;
private:
};
class NetPlayServer : public NetPlay
{
public:
void Entry();
NetPlayServer(const u16 port, const std::string& name, NetPlayDiag* const npd = NULL, const std::string& game = "");
~NetPlayServer();
void GetPlayerList(std::string& list);
// Send and receive pads values
bool GetNetPads(const u8 pad_nb, const SPADStatus* const, NetPad* const netvalues);
bool SetSelectedGame(const std::string& game);
void SendChatMessage(const std::string& msg);
bool StartGame(const std::string &path);
private:
class Player
{
public:
Player();
PlayerId pid;
sf::SocketTCP socket;
std::string name;
PadMapping pad_map[4];
std::string revision;
};
void SendToClients(sf::Packet& packet, const u8 skip_pid = 0);
unsigned int OnConnect(sf::SocketTCP& socket);
unsigned int OnDisconnect(sf::SocketTCP& socket);
unsigned int OnData(sf::Packet& packet, sf::SocketTCP& socket);
std::map<sf::SocketTCP, Player> m_players;
};
class NetPlayClient : public NetPlay
{
public:
void Entry();
NetPlayClient(const std::string& address, const u16 port, const std::string& name, NetPlayDiag* const npd = NULL);
~NetPlayClient();
void GetPlayerList(std::string& list);
// Send and receive pads values
bool GetNetPads(const u8 pad_nb, const SPADStatus* const, NetPad* const netvalues);
bool SetSelectedGame(const std::string& game);
void SendChatMessage(const std::string& msg);
bool StartGame(const std::string &path);
private:
class Player
{
public:
Player();
PlayerId pid;
std::string name;
PadMapping pad_map[4];
std::string revision;
};
unsigned int OnData(sf::Packet& packet);
PlayerId m_pid;
std::map<u8, Player> m_players;
};
#endif

View File

@ -1,607 +0,0 @@
// Copyright (C) 2003 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 "NetSockets.h"
#include "NetWindow.h"
//--------------------------------
// GUI EVENTS
//--------------------------------
void NetEvent::AppendText(const wxString text)
{
// I have the feeling SendEvent may be a bit safer/better...
#if 0
SendEvent(ADD_TEXT, std::string(text.mb_str())); //NETPLAY: FIXME 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()) );
#if ! wxCHECK_VERSION(2, 9, 0)
m_netptr->AddPendingEvent(event);
#endif
}
//--------------------------------
// SERVER SIDE THREAD
//--------------------------------
ServerSide::ServerSide(NetPlay* netptr, sf::SocketTCP socket, sf::SocketUDP socketUDP, int netmodel, std::string nick)
: wxThread()
{
m_numplayers = 0;
m_data_received = false;
m_netmodel = netmodel;
m_socket = socket;
m_socketUDP = socketUDP;
m_netptr = netptr;
m_nick = nick;
Event = new NetEvent(m_netptr);
}
char ServerSide::GetSocket(sf::SocketTCP Socket)
{
for (int i=0; i < m_numplayers; i++)
{
if(m_client[i].socket == Socket)
return i;
}
return 0xE;
}
bool ServerSide::RecvT(sf::SocketUDP Socket, char * Data, size_t Max, size_t& Recvd, float Time)
{
sf::SelectorUDP Selector;
sf::IPAddress Addr;
unsigned short Port;
Selector.Add(Socket);
if (Selector.Wait(Time) > 0)
{
Socket.Receive(Data, Max, Recvd, Addr, Port);
return true;
}
else
{
return false;
}
}
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;
m_client[m_numplayers].address = Address;
if (SyncValues(m_numplayers, Address))
{
// Add it to the selector
m_selector.Add(Incoming);
Event->SendEvent(HOST_NEWPLAYER);
m_numplayers++;
}
else
{
Event->AppendText(_("ERROR : Unable to establish UDP connection !\n"));
Incoming.Close();
}
}
}
else
{
unsigned char recv;
int socket_nb;
size_t recv_size;
sf::Socket::Status recv_status;
socket_nb = GetSocket(Socket);
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::FromAscii(StringFromFormat("*Player : %s left the game.\n\n",
player_left.c_str()).c_str()) );
// We need to adjust the struct...
for (int j = socket_nb; j < m_numplayers; j++)
{
m_client[j].socket = m_client[j+1].socket;
m_client[j].nick = m_client[j+1].nick;
m_client[j].ready = m_client[j+1].ready;
}
// Send disconnected message to all
unsigned char send = 0x11;
unsigned int str_size = (int)player_left.size();
for (int j=0; j < m_numplayers ; j++)
{
m_client[j].socket.Send((const char*)&send, 1);
m_client[j].socket.Send((const char*)&str_size, 4);
m_client[j].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())
{
// Stop listening
m_socket.Close();
// Delete the Thread and close clients sockets
for (int i=0; i < m_numplayers ; i++)
m_client[i].socket.Close();
break;
}
}
return NULL;
}
bool 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;
bool errorUDP = false;
// 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 client's UDP Port
Socket.Receive((char *)&m_client[m_numplayers].port, sizeof(short), received);
// 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;
// Test UDP Socket
if (m_socketUDP.Send((const char*)&m_numplayers, 1, Address, m_client[m_numplayers].port) == sf::Socket::Done)
{
// Test UDP Socket Receive, 2s timeout
if (!RecvT(m_socketUDP, (char*)&init_number, 1, received, 2))
{
ERROR_LOG(NETPLAY,"Connection to client timed out or closed");
errorUDP = true;
}
}
else
{
ERROR_LOG(NETPLAY,"Failed to send info! closing connection!");
errorUDP = true;
}
// Check if the client has the game
Socket.Receive((char *)&init_number, 1, received);
delete[] buffer;
if (!errorUDP)
{
// 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::FromAscii(StringFromFormat("*Connection established to %s (%s:%d)\n",
m_client[socketnb].nick.c_str(), Address.ToString().c_str(), m_client[m_numplayers].port).c_str()));
if (init_number != 0x1F) // Not Found
//for (int i = 0; i < 4; i++)
//note for sl1nk3 : what is that for doing there?
Event->AppendText(_("WARNING : Game Not Found on Client Side !\n"));
// UDP connecton successful
init_number = 0x16;
Socket.Send((const char *)&init_number, 1);
}
else // UDP Error, disconnect client
{
// UDP connecton failed
init_number = 0x17;
Socket.Send((const char *)&init_number, 1);
return false;
}
return true;
}
void ServerSide::Write(int 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;
u32 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);
}
void ServerSide::WriteUDP(int socknb, const char *data, size_t size)
{
wxCriticalSectionLocker lock(m_CriticalSection);
m_socketUDP.Send(data, size, m_client[socknb].address, m_client[socknb].port);
}
//--------------------------------
// CLIENT SIDE THREAD
//--------------------------------
ClientSide::ClientSide(NetPlay* netptr, sf::SocketTCP socket, sf::SocketUDP socketUDP, std::string addr, std::string nick)
: wxThread()
{
m_numplayers = 0;
m_data_received = false;
m_netmodel = 0;
m_socket = socket;
m_socketUDP = socketUDP;
m_port = m_socketUDP.GetPort();
m_netptr = netptr;
m_nick = nick;
m_addr = addr;
Event = new NetEvent(m_netptr);
}
bool ClientSide::RecvT(sf::SocketUDP Socket, char * Data, size_t Max, size_t& Recvd, float Time)
{
sf::SelectorUDP Selector;
sf::IPAddress Addr;
unsigned short Port;
Selector.Add(Socket);
if (Selector.Wait(Time) > 0)
{
Socket.Receive(Data, Max, Recvd, Addr, Port);
return true;
}
else
{
return false;
}
}
void *ClientSide::Entry()
{
Event->AppendText(_("*Connection Request... "));
// If we get here, the connection is already accepted, however the game may be full
if (SyncValues())
{
// Tell the server if the client has the game
CheckGameFound();
// Check UDP connection
unsigned char value;
size_t val_sz;
m_socket.Receive((char *)&value, 1, val_sz);
if (value == 0x16) // UDP connection successful
{
Event->AppendText(_("Connection successful !\n"));
Event->AppendText( wxString::FromAscii( StringFromFormat("*Connection established to %s (%s)\n*Game is : %s\n",
m_hostnick.c_str(), m_addr.c_str(), m_selectedgame.c_str()).c_str()));
}
else
{
Event->AppendText(_("UDP Connection FAILED !\nERROR : Unable to establish UDP Connection, please Check UDP Port forwarding !\n"));
m_socket.Close();
Event->SendEvent(HOST_ERROR);
return NULL;
}
}
else // Server is Full
{
m_socket.Close();
Event->SendEvent(HOST_FULL);
return NULL;
}
m_netptr->ChangeSelectedGame(m_selectedgame);
Event->SendEvent(HOST_NEWPLAYER, "NULL", m_netmodel);
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 = (int)m_nick.size();
unsigned char byterecv;
bool errorUDP;
char *buffer = NULL;
size_t recv_size;
unsigned short server_port;
std::string host = m_addr.substr(0, m_addr.find(':'));
TryParseInt(m_addr.substr(m_addr.find(':')+1).c_str(), (int *)&server_port);
// 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 UDP Port
// TODO : fix port sending. it sends the set port in the main window. not the actual using port
// when checked to use random this will , ofcourse , send wrong port
m_socket.Send((const char *)&m_port, sizeof(short));
// 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);
// Test UDP Socket
if (m_socketUDP.Send((const char*)&m_numplayers, 1, host.c_str(), server_port) == sf::Socket::Done)
{
// Test UDP Socket Receive, 2s timeout
if (!RecvT(m_socketUDP, (char*)&byterecv, 1, recv_size, 2))
{
errorUDP = true;
ERROR_LOG(NETPLAY,"Connection Timed Out or closed");
}
}
else
errorUDP = true;
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(wxT("WARNING : You do not have the Selected Game !\n"));
NOTICE_LOG(NETPLAY, "Game '%s' not found!", m_selectedgame.c_str());
}
}
}
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);
}
void ClientSide::WriteUDP(const char *data, size_t size)
{
wxCriticalSectionLocker lock(m_CriticalSection);
unsigned short server_port;
std::string host = m_addr.substr(0, m_addr.find(':'));
TryParseInt(m_addr.substr(m_addr.find(':')+1).c_str(), (int *)&server_port);
m_socketUDP.Send(data, size, host.c_str(), server_port);
}

View File

@ -1,128 +0,0 @@
// Copyright (C) 2003 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 _NETSOCKETS_H_
#define _NETSOCKETS_H_
#include <SFML/Network.hpp>
class NetPlay;
#include "Common.h"
#include "NetStructs.h"
#include <wx/wx.h>
struct Clients {
std::string nick;
sf::SocketTCP socket;
unsigned short port;
sf::IPAddress address;
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, sf::SocketUDP, int netmodel, std::string nick);
~ServerSide() {};
virtual void *Entry();
void Write(int socknb, const char *data, size_t size, long *ping=NULL);
void WriteUDP(int socknb, const char *data, size_t size);
bool isNewPadData(u32 *netValues, bool current, int client=0);
private:
bool SyncValues(unsigned char, sf::IPAddress);
bool RecvT(sf::SocketUDP Socket, char * Data, size_t Max, size_t& Recvd, float Time = 0);
char GetSocket(sf::SocketTCP Socket);
void OnServerData(int sock, unsigned char data);
void IsEveryoneReady();
NetPlay *m_netptr;
NetEvent *Event;
u32 m_netvalues[3][3];
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
sf::SocketUDP m_socketUDP;
wxCriticalSection m_CriticalSection;
};
class ClientSide : public wxThread
{
public:
ClientSide(NetPlay* netptr, sf::SocketTCP, sf::SocketUDP, std::string addr, std::string nick);
~ClientSide() {}
virtual void *Entry();
void Write(const char *data, size_t size, long *ping=NULL);
void WriteUDP(const char *data, size_t size);
bool isNewPadData(u32 *netValues, bool current, bool isVersus=true);
private:
bool SyncValues();
void CheckGameFound();
void OnClientData(unsigned char data);
bool RecvT(sf::SocketUDP Socket, char * Data, size_t Max, size_t& Recvd, float Time=0);
NetPlay *m_netptr;
NetEvent *Event;
u32 m_netvalues[3][3];
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
sf::SocketUDP m_socketUDP;
unsigned short m_port;
std::string m_addr; // Contains the server addr
wxCriticalSection m_CriticalSection;
};
#endif

View File

@ -1,26 +0,0 @@
// Copyright (C) 2003 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 _NETSTRUCTS_H
#define _NETSTRUCTS_H
struct Netpads {
int nHi[128];
int nLow[128];
};
#endif // _NETSTRUCTS_H

View File

@ -15,557 +15,373 @@
// Official SVN repository and contact information can be found at // Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/ // http://code.google.com/p/dolphin-emu/
#include "NetSockets.h" #include "NetPlay.h"
#include "NetWindow.h" #include "NetWindow.h"
#include "FileUtil.h"
// Main Frame window #include <sstream>
BEGIN_EVENT_TABLE(NetPlay, wxFrame) #define _connect_macro_( b, f, c, s ) (b)->Connect( wxID_ANY, (c), wxCommandEventHandler( f ), (wxObject*)0, (wxEvtHandler*)s )
EVT_BUTTON(ID_BUTTON_JOIN, NetPlay::OnJoin)
EVT_BUTTON(ID_BUTTON_HOST, NetPlay::OnHost)
EVT_BUTTON(ID_BUTTON_EXIT, NetPlay::OnDisconnect)
EVT_HOST_COMMAND(wxID_ANY, NetPlay::OnNetEvent) #define NETPLAY_TITLEBAR "Dolphin NetPlay"
EVT_CHECKBOX(ID_READY, NetPlay::OnGUIEvent) BEGIN_EVENT_TABLE(NetPlayDiag, wxFrame)
EVT_CHECKBOX(ID_RECORD, NetPlay::OnGUIEvent) EVT_COMMAND(wxID_ANY, wxEVT_THREAD, NetPlayDiag::OnThread)
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() END_EVENT_TABLE()
NetPlay::NetPlay(wxWindow* parent, std::string GamePaths, std::string GameNames) : NetPlay* netplay_ptr = NULL;
wxFrame(parent, wxID_ANY, _T("Net Play"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE & ~ wxMAXIMIZE_BOX & ~ wxCLOSE_BOX)
NetPlaySetupDiag::NetPlaySetupDiag(wxWindow* parent, const CGameListCtrl* const game_list)
: wxFrame(parent, wxID_ANY, wxT(NETPLAY_TITLEBAR), wxDefaultPosition, wxDefaultSize)
, m_game_list(game_list)
{ {
m_selectedGame = 'a'; m_hostaddr = 'a'; //PanicAlert("ALERT: NetPlay is not 100%% functional !!!!");
m_games = GameNames; m_paths = GamePaths;
m_isHosting = 2; m_ready = m_clients_ready = false;
m_loopframe = m_frame = m_NetModel = m_numClients = 0;
DrawGUI(); wxPanel* const panel = new wxPanel(this);
}
NetPlay::~NetPlay() // top row
{ wxStaticText* const nick_lbl = new wxStaticText(panel, wxID_ANY, wxT("Nickname :"), wxDefaultPosition, wxDefaultSize);
ConfigIni.Load(File::GetUserPath(F_DOLPHINCONFIG_IDX)); m_nickname_text = new wxTextCtrl(panel, wxID_ANY, wxT("Player"));
ConfigIni.Set("Netplay", "Nickname", m_nick); wxBoxSizer* const nick_szr = new wxBoxSizer(wxHORIZONTAL);
ConfigIni.Set("Netplay", "UsedPort", (int)m_port); nick_szr->Add(nick_lbl, 0, wxCENTER);
ConfigIni.Set("Netplay", "LastIP", m_address); nick_szr->Add(m_nickname_text, 0, wxALL, 5);
ConfigIni.Save(File::GetUserPath(F_DOLPHINCONFIG_IDX));
}
void NetPlay::OnJoin(wxCommandEvent& WXUNUSED(event)) // tabs
{ wxNotebook* const notebook = new wxNotebook(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
unsigned short server_port; wxPanel* const connect_tab = new wxPanel(notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize);
notebook->AddPage(connect_tab, wxT("Connect"));
wxPanel* const host_tab = new wxPanel(notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize);
notebook->AddPage(host_tab, wxT("Host"));
m_address = std::string(m_ConAddr->GetValue().mb_str());
m_nick = std::string(m_SetNick->GetValue().mb_str());
sf::IPAddress host = m_address.substr(0, m_address.find(':')); // connect tab
std::string port_str = m_address.substr(m_address.find(':') + 1);
TryParseUInt(port_str, (u32*)&server_port); // Server port
TryParseUInt((const char *)m_SetPort->GetValue().mb_str(), (u32*)&m_port); // User port
if (m_nick.size() > 255)
m_nick = m_nick.substr(0 , 255);
SetTitle(wxT("Net Play : Connecting to Host..."));
// Create the client socket
sf::SocketTCP sock_client;
sf::SocketUDP sock_client_UDP;
if (sock_client.Connect(server_port, host, 1.5) == sf::Socket::Done)
{ {
// Try to Bind the UDP Socket wxStaticText* const ip_lbl = new wxStaticText(connect_tab, wxID_ANY, wxT("Address :"), wxDefaultPosition, wxDefaultSize);
if (sock_client_UDP.Bind(m_port)) m_connect_ip_text = new wxTextCtrl(connect_tab, wxID_ANY, wxT("localhost"));
{ wxStaticText* const port_lbl = new wxStaticText(connect_tab, wxID_ANY, wxT("Port :"), wxDefaultPosition, wxDefaultSize);
m_sock_client = new ClientSide(this, sock_client, sock_client_UDP, m_address, m_nick); m_connect_port_text = new wxTextCtrl(connect_tab, wxID_ANY, wxT("2626"));
m_sock_client->Create();
m_sock_client->Run();
// Create the GUI wxButton* const connect_btn = new wxButton(connect_tab, wxID_ANY, wxT("Connect"));
m_isHosting = false; //connect_button->Disable();
DrawNetWindow(); _connect_macro_(connect_btn, NetPlaySetupDiag::OnJoin, wxEVT_COMMAND_BUTTON_CLICKED, this);
}
else wxStaticText* const alert_lbl = new wxStaticText(connect_tab, wxID_ANY
{ , wxT("ALERT:\n\nNetPlay will currently only work properly when using the following settings:\n")
SetTitle(wxT("Net Play")); wxT(" - Dual Core [OFF]\n - DSP LLE Plugin\n - DSPLLE on thread [OFF]\n - Manually set the exact number of controller that will be used to [Standard Controller]")
PanicAlert("Can't Bind UDP socket on the specified Port: %d ! \n" wxT("\n\nAll players should try to use the same Dolphin version and settings.")
"Make sure port is forwarded and not in use !", m_port); wxT("\nDisable all memory cards or send them to all players before starting.")
} wxT("\nWiimote support has not been implemented.\nWii games will likely desync for other reasons as well.")
wxT("\n\nYou must forward TCP port to host!!")
, wxDefaultPosition, wxDefaultSize);
wxBoxSizer* const top_szr = new wxBoxSizer(wxHORIZONTAL);
top_szr->Add(ip_lbl, 0, wxCENTER | wxRIGHT, 5);
top_szr->Add(m_connect_ip_text, 3);
top_szr->Add(port_lbl, 0, wxCENTER | wxRIGHT | wxLEFT, 5);
top_szr->Add(m_connect_port_text, 1);
wxBoxSizer* const con_szr = new wxBoxSizer(wxVERTICAL);
con_szr->Add(top_szr, 0, wxALL | wxEXPAND, 5);
con_szr->AddStretchSpacer(1);
con_szr->Add(alert_lbl, 0, wxLEFT | wxRIGHT | wxEXPAND, 5);
con_szr->AddStretchSpacer(1);
con_szr->Add(connect_btn, 0, wxALL | wxALIGN_RIGHT, 5);
connect_tab->SetSizerAndFit(con_szr);
} }
else
// host tab
{ {
SetTitle(wxT("Net Play")); wxStaticText* const port_lbl = new wxStaticText(host_tab, wxID_ANY, wxT("Port :"), wxDefaultPosition, wxDefaultSize);
PanicAlert("Can't connect to the specified IP Address ! \nMake sure Hosting port is forwarded !"); m_host_port_text = new wxTextCtrl(host_tab, wxID_ANY, wxT("2626"));
}
}
void NetPlay::OnHost(wxCommandEvent& WXUNUSED(event)) wxButton* const host_btn = new wxButton(host_tab, wxID_ANY, wxT("Host"));
{ //host_button->Disable();
TryParseInt(m_SetPort->GetValue().mb_str(), (int*)&m_port); _connect_macro_(host_btn, NetPlaySetupDiag::OnHost, wxEVT_COMMAND_BUTTON_CLICKED, this);
m_nick = std::string(m_SetNick->GetValue().mb_str()); m_game_lbox = new wxListBox(host_tab, wxID_ANY);
std::istringstream ss(game_list->GetGameNames());
std::string game;
while (std::getline(ss,game))
m_game_lbox->Append(wxString(game.c_str(), *wxConvCurrent));
if (m_GameList->GetSelection() == wxNOT_FOUND) { wxBoxSizer* const top_szr = new wxBoxSizer(wxHORIZONTAL);
PanicAlert("No Game Selected ! Please select a Game..."); top_szr->Add(port_lbl, 0, wxCENTER | wxRIGHT, 5);
return; top_szr->Add(m_host_port_text, 0);
}
if (!m_SetPort->GetValue().size() || m_port < 1000 || m_port > 65535) {
PanicAlert("Bad Port entered (%d) ! Please enter a working socket port...", m_port);
return;
}
if (m_nick.size() > 255)
m_nick = m_nick.substr(0 , 255);
m_NetModel = m_NetMode->GetSelection(); wxBoxSizer* const host_szr = new wxBoxSizer(wxVERTICAL);
m_selectedGame = std::string(m_GameList_str[m_GameList->GetSelection()].mb_str()); host_szr->Add(top_szr, 0, wxALL | wxEXPAND, 5);
NOTICE_LOG(NETPLAY,"Game has been set to : %s \n",m_selectedGame.c_str()); host_szr->Add(m_game_lbox, 1, wxLEFT | wxRIGHT | wxEXPAND, 5);
host_szr->Add(host_btn, 0, wxALL | wxALIGN_RIGHT, 5);
// Create the listening socket host_tab->SetSizerAndFit(host_szr);
sf::SocketTCP sock_server;
sf::SocketUDP sock_server_UDP;
// Start the listening socket and bind UDP socket port
if (sock_server.Listen(m_port) && sock_server_UDP.Bind(m_port))
{
m_sock_server = new ServerSide(this, sock_server, sock_server_UDP, 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")
wxT("*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(buffer.c_str(), *wxConvCurrent));
} }
netmodes_str.Add(wxT("P2P Versus (2 players, faster)")); // bottom row
// TODO : netmodes_str.Add(wxT("Server Mode (4 players, slower)")); wxButton* const quit_btn = new wxButton(panel, wxID_ANY, wxT("Quit"));
_connect_macro_(quit_btn, NetPlaySetupDiag::OnQuit, wxEVT_COMMAND_BUTTON_CLICKED, this);
wxPanel *panel = new wxPanel(this); // main sizer
wxBoxSizer* const main_szr = new wxBoxSizer(wxVERTICAL);
main_szr->Add(nick_szr, 0, wxALL | wxALIGN_RIGHT, 5);
main_szr->Add(notebook, 1, wxLEFT | wxRIGHT | wxEXPAND, 5);
main_szr->Add(quit_btn, 0, wxALL | wxALIGN_RIGHT, 5);
// Tabs panel->SetSizerAndFit(main_szr);
m_Notebook = new wxNotebook(panel, 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"));
// Tow window, Nickname & Port settings //wxBoxSizer* const diag_szr = new wxBoxSizer(wxVERTICAL);
m_SetNick_text = new wxStaticText(panel, wxID_ANY, wxT(" Nickname : "), wxDefaultPosition, wxDefaultSize); //diag_szr->Add(panel);
m_SetNick = new wxTextCtrl(panel, ID_SETNICK, wxT("LOLWUT!"), wxDefaultPosition, wxDefaultSize); //SetSizerAndFit(diag_szr);
m_SetPort_text = new wxStaticText(panel, wxID_ANY, wxT(" Port to Use : "), wxDefaultPosition, wxDefaultSize);
m_SetPort = new wxTextCtrl(panel, ID_SETPORT, wxT("12345"), wxDefaultPosition, wxDefaultSize);
// CONNECTION TAB
m_ConAddr_text = new wxStaticText(m_Tab_Connect, wxID_ANY, 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_JoinGame = new wxButton(m_Tab_Connect, ID_BUTTON_JOIN, wxT("Connect"), wxDefaultPosition, wxDefaultSize);
m_ExitWindowC = new wxButton(m_Tab_Connect, ID_BUTTON_EXIT, wxT("Quit"), wxDefaultPosition, wxDefaultSize);
// Sizers CONNECT main_szr->SetSizeHints(this);
wxBoxSizer* sConnectTop = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer* sConnectSizer = new wxBoxSizer(wxVERTICAL);
sConnectTop->Add(m_ConAddr_text, 0, wxALL|wxALIGN_CENTER, 5);
sConnectTop->Add(m_ConAddr, 1, 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);
sConnectSizer->Add(m_ExitWindowC, 0, wxALL|wxALIGN_CENTER, 5);
m_Tab_Connect->SetSizer(sConnectSizer);
// HOSTING TAB
m_GameList_text = new wxStaticText(m_Tab_Host, wxID_ANY,
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_NEEDED_SB);
m_HostGame = new wxButton(m_Tab_Host, ID_BUTTON_HOST, wxT("Host"), wxDefaultPosition, wxDefaultSize);
m_ExitWindowH = new wxButton(m_Tab_Host, ID_BUTTON_EXIT, wxT("Quit"), wxDefaultPosition, wxDefaultSize);
m_NetMode = new wxChoice(m_Tab_Host, ID_NETMODE, wxDefaultPosition, wxDefaultSize, netmodes_str, 0, wxDefaultValidator);
m_NetMode->SetSelection(0);
// Sizers HOST
wxBoxSizer *sHostBox = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *sHostBottom = new wxBoxSizer(wxHORIZONTAL);
sHostBottom->Add(m_NetMode, 0, wxALL|wxALIGN_CENTER, 5);
sHostBottom->AddStretchSpacer();
sHostBottom->Add(m_HostGame, 0, wxALL, 10);
sHostBottom->Add(m_ExitWindowH, 0, wxALL|wxALIGN_CENTER, 5);
sHostBox->Add(m_GameList_text, 0, wxALL|wxALIGN_CENTER, 5);
sHostBox->Add(m_GameList, 1, wxALL|wxEXPAND, 6);
sHostBox->Add(sHostBottom, 0, wxALL|wxEXPAND, 1);
m_Tab_Host->SetSizer(sHostBox);
// 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, 1, wxALL|wxALIGN_CENTER, 3);
sMain_top->AddStretchSpacer();
sMain_top->Add(m_SetPort_text, 0, wxALL|wxALIGN_CENTER, 3);
sMain_top->Add(m_SetPort, 1, wxALL|wxALIGN_CENTER, 3);
sMain->Add(sMain_top, 0, wxALL|wxEXPAND, 5);
sMain->Add(m_Notebook, 1, wxALL|wxEXPAND, 5);
// Adjust panel to window's size, and set resizing minimum boundaries
panel->SetSizerAndFit(sMain);
sMain->SetSizeHints((wxWindow*)this);
if (ConfigIni.Load(File::GetUserPath(F_DOLPHINCONFIG_IDX)))
{
ConfigIni.Get("Netplay", "Nickname", &m_nick, "Unnamed");
ConfigIni.Get("Netplay", "UsedPort", (int*)&m_port, 12345);
ConfigIni.Get("Netplay", "LastIP", &m_address, "127.0.0.1:54321");
m_SetNick->SetValue(wxString::FromAscii(m_nick.c_str()));
m_SetPort->SetValue(wxString::Format(wxT("%d"), m_port));
m_ConAddr->SetValue(wxString::FromAscii(m_address.c_str()));
}
Center(); Show();
}
void NetPlay::DrawNetWindow()
{
// Remove everything from the precedent GUI :D
DestroyChildren();
SetTitle(wxT("Net Play : Connection Window"));
wxPanel *panel = new wxPanel(this);
wxBoxSizer* sMain = new wxBoxSizer(wxVERTICAL);
wxBoxSizer* sTop = new wxBoxSizer(wxVERTICAL);
wxBoxSizer* sBottom = new wxBoxSizer(wxHORIZONTAL);
m_Game_str = new wxButton(panel, wxID_ANY, wxT(" Game : "), wxDefaultPosition, wxSize(400, 25), wxBU_LEFT);
m_Game_str->Disable();
m_Logging = new wxTextCtrl(panel, ID_LOGGING_TXT, wxEmptyString,
wxDefaultPosition, wxSize(400, 250),
wxTE_RICH2 | wxTE_MULTILINE | wxTE_READONLY);
m_Chat = new wxTextCtrl(panel, ID_CHAT, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
m_Chat_ok = new wxButton(panel, ID_BUTTON_CHAT, wxT("Send"));;
m_Ready = new wxCheckBox(panel, ID_READY, wxT("Click here when ready"), wxDefaultPosition, wxDefaultSize, 0);
m_RecordGame = new wxCheckBox(panel, ID_RECORD, wxT("Record Game Input"), wxDefaultPosition, wxDefaultSize, 0);
// TODO: Fix the recording ?
m_RecordGame->Disable();
m_ConInfo_text = new wxStaticText(panel, ID_CONNINFO_TXT, wxT(" Fps : 0 | Ping : 00 ms"));
m_GetPing = new wxButton(panel, ID_BUTTON_GETPING, wxT("Ping"), wxDefaultPosition, wxDefaultSize);
m_Disconnect = new wxButton(panel, 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(panel, ID_BUTTON_GETIP, wxT("What is my IP"));
m_ChangeGame = new wxButton(panel, ID_CHANGEGAME, wxT("Change Game"));
wxStaticBoxSizer* sBottom3 = new wxStaticBoxSizer(wxVERTICAL, panel, wxT("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);
panel->SetSizerAndFit(sMain);
sMain->SetSizeHints((wxWindow*)this);
Center();
Show(); Show();
} }
// String of the type : FPSxPINGxFRAME_DELAY void NetPlaySetupDiag::OnHost(wxCommandEvent& event)
void NetPlay::UpdateNetWindow(bool update_infos, wxString infos)
{ {
std::vector<std::string> str_arr; // warning removal
event.GetId();
if (update_infos) if (::netplay_ptr)
{ {
SplitString(std::string(infos.mb_str()), "x", str_arr); PanicAlert("A NetPlay window is already open!!");
return;
}
m_ConInfo_text->SetLabel if (-1 == m_game_lbox->GetSelection())
(wxString::FromAscii(StringFromFormat(" Fps : %s | Ping : %s | Frame Delay : %s", {
str_arr[0].c_str(), str_arr[1].c_str(), PanicAlert("You must choose a game!!");
str_arr[2].c_str()).c_str()) ); return;
}
std::string game(m_game_lbox->GetStringSelection().mb_str());
NetPlayDiag* const npd = new NetPlayDiag(m_parent, m_game_list, game, true);
unsigned long port = 0;
m_host_port_text->GetValue().ToULong(&port);
::netplay_ptr = new NetPlayServer(u16(port)
, std::string(m_nickname_text->GetValue().mb_str()), npd, game);
if (::netplay_ptr->is_connected)
{
//NetPlayServerDiag* const npsd =
npd->Show();
Destroy();
} }
else else
{ {
m_critical.Enter(); PanicAlert("Failed to Listen!!");
//m_Game_str->SetLabel(wxString::Format(wxT(" Game : %s"), m_selectedGame.c_str())); npd->Destroy();
m_Game_str->SetLabel(wxString::FromAscii(std::string("Game " + m_selectedGame).c_str())); // dialog will delete netplay
m_critical.Leave(); //delete ::netplay_ptr;
} }
} }
void NetPlay::OnGUIEvent(wxCommandEvent& event) void NetPlaySetupDiag::OnJoin(wxCommandEvent& event)
{ {
unsigned char value;; // warning removal
switch (event.GetId()) event.GetId();
if (::netplay_ptr)
{ {
case ID_READY: PanicAlert("A NetPlay window is already open!!");
{ return;
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;
m_Disconnect->Enable(!(m_ready));
if (m_isHosting == 1)
{
m_ChangeGame->Enable(!(m_ready));
if (m_numClients > 0)
{
int buffer_size = (int)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()));
// Everyone is ready
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;
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;
// TODO : there seems to be a random bug here that i can't reproduce... looked like a loop bug :/
wxString chat_str = wxString::Format(wxT("> %s : %s\n"), wxString(m_nick.c_str(), wxConvUTF8).c_str() , m_Chat->GetValue().c_str() );
int chat_size = (int)chat_str.size();
if(chat_size-m_nick.size()-6 > 0)
{
m_Chat->Clear();
// 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.mb_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.mb_str(), chat_size + 1);
}
// Do not wait for the server, just write as soon as sent
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;
}
} }
}
NetPlayDiag* const npd = new NetPlayDiag(m_parent, m_game_list, "");
// GameList popup window unsigned long port = 0;
m_connect_port_text->GetValue().ToULong(&port);
BEGIN_EVENT_TABLE(GameListPopup, wxDialog) ::netplay_ptr = new NetPlayClient(std::string(m_connect_ip_text->GetValue().mb_str())
EVT_BUTTON(wxID_OK, GameListPopup::OnButtons) , (u16)port, std::string(m_nickname_text->GetValue().mb_str()), npd);
EVT_BUTTON(wxID_CANCEL, GameListPopup::OnButtons) if (::netplay_ptr->is_connected)
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: //NetPlayServerDiag* const npsd =
if (m_GameList->GetSelection() != wxNOT_FOUND) npd->Show();
m_netParent->ChangeSelectedGame(std::string(m_GameList_str[m_GameList->GetSelection()].mb_str()));
Destroy(); Destroy();
break; }
case wxID_CANCEL: else
Destroy(); {
break; //PanicAlert("Failed to Connect!!");
npd->Destroy();
// dialog will delete netplay
//delete ::netplay_ptr;
} }
} }
void NetPlaySetupDiag::OnQuit(wxCommandEvent& event)
{
// warning removal
event.GetId();
Destroy();
}
NetPlayDiag::NetPlayDiag(wxWindow* parent, const CGameListCtrl* const game_list
, const std::string& game, const bool is_hosting)
: wxFrame(parent, wxID_ANY, wxT(NETPLAY_TITLEBAR), wxDefaultPosition, wxDefaultSize)
, m_game_list(game_list)
, m_selected_game(game)
{
wxPanel* const panel = new wxPanel(this);
// top crap
m_game_btn = new wxButton(panel, wxID_ANY
, wxString(m_selected_game.c_str(), *wxConvCurrent).Prepend(wxT(" Game : ")), wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
m_game_btn->Disable();
// middle crap
// chat
m_chat_text = new wxTextCtrl(panel, wxID_ANY, wxEmptyString
, wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxTE_MULTILINE);
m_chat_msg_text = new wxTextCtrl(panel, wxID_ANY, wxEmptyString
, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
_connect_macro_(m_chat_msg_text, NetPlayDiag::OnChat, wxEVT_COMMAND_TEXT_ENTER, this);
wxButton* const chat_msg_btn = new wxButton(panel, wxID_ANY, wxT("Send"));
_connect_macro_(chat_msg_btn, NetPlayDiag::OnChat, wxEVT_COMMAND_BUTTON_CLICKED, this);
wxBoxSizer* const chat_msg_szr = new wxBoxSizer(wxHORIZONTAL);
chat_msg_szr->Add(m_chat_msg_text, 1);
chat_msg_szr->Add(chat_msg_btn, 0);
wxStaticBoxSizer* const chat_szr = new wxStaticBoxSizer(wxVERTICAL, panel, wxT("Chat"));
chat_szr->Add(m_chat_text, 1, wxEXPAND);
chat_szr->Add(chat_msg_szr, 0, wxEXPAND | wxTOP, 5);
m_player_lbox = new wxListBox(panel, wxID_ANY, wxDefaultPosition, wxSize(192,-1));
wxStaticBoxSizer* const player_szr = new wxStaticBoxSizer(wxVERTICAL, panel, wxT("Players"));
player_szr->Add(m_player_lbox, 1, wxEXPAND);
// player list
if (is_hosting)
{
wxButton* const player_config_btn = new wxButton(panel, wxID_ANY, wxT("Configure Pads [not implemented]"));
player_config_btn->Disable();
player_szr->Add(player_config_btn, 0, wxEXPAND | wxTOP, 5);
}
wxBoxSizer* const mid_szr = new wxBoxSizer(wxHORIZONTAL);
mid_szr->Add(chat_szr, 1, wxEXPAND | wxRIGHT, 5);
mid_szr->Add(player_szr, 0, wxEXPAND);
// bottom crap
wxButton* const quit_btn = new wxButton(panel, wxID_ANY, wxT("Quit"));
_connect_macro_(quit_btn, NetPlayDiag::OnQuit, wxEVT_COMMAND_BUTTON_CLICKED, this);
wxBoxSizer* const bottom_szr = new wxBoxSizer(wxHORIZONTAL);
if (is_hosting)
{
wxButton* const start_btn = new wxButton(panel, wxID_ANY, wxT("Start"));
_connect_macro_(start_btn, NetPlayDiag::OnStart, wxEVT_COMMAND_BUTTON_CLICKED, this);
bottom_szr->Add(start_btn);
}
bottom_szr->AddStretchSpacer(1);
bottom_szr->Add(quit_btn);
// main sizer
wxBoxSizer* const main_szr = new wxBoxSizer(wxVERTICAL);
main_szr->Add(m_game_btn, 0, wxEXPAND | wxALL, 5);
main_szr->Add(mid_szr, 1, wxEXPAND | wxLEFT | wxRIGHT, 5);
main_szr->Add(bottom_szr, 0, wxEXPAND | wxALL, 5);
panel->SetSizerAndFit(main_szr);
main_szr->SetSizeHints(this);
SetSize(512, 512-128);
Center();
}
NetPlayDiag::~NetPlayDiag()
{
if (::netplay_ptr)
{
delete netplay_ptr;
::netplay_ptr = NULL;
}
}
void NetPlayDiag::OnChat(wxCommandEvent& event)
{
// warning removal
event.GetId();
wxString s = m_chat_msg_text->GetValue();
if (s.Length())
{
::netplay_ptr->SendChatMessage(std::string(s.mb_str()));
m_chat_text->AppendText(s.Prepend(wxT(" >> ")).Append(wxT('\n')));
m_chat_msg_text->Clear();
}
}
void NetPlayDiag::OnStart(wxCommandEvent& event)
{
// warning removal
event.GetId();
// find path for selected game
std::string ntmp, ptmp, path;
std::istringstream nss(m_game_list->GetGameNames()), pss(m_game_list->GetGamePaths());
while(std::getline(nss,ntmp))
{
std::getline(pss,ptmp);
if (m_selected_game == ntmp)
{
path = ptmp;
break;
}
}
if (path.length())
::netplay_ptr->StartGame(path);
else
PanicAlert("Game not found!!");
}
void NetPlayDiag::OnQuit(wxCommandEvent& event)
{
// warning removal
event.GetId();
Destroy();
}
// update gui
void NetPlayDiag::OnThread(wxCommandEvent& event)
{
// warning removal
event.GetId();
// player list
std::string tmps;
::netplay_ptr->GetPlayerList(tmps);
m_player_lbox->Clear();
std::istringstream ss(tmps);
while (std::getline(ss, tmps))
m_player_lbox->Append(wxString(tmps.c_str(), *wxConvCurrent));
// update selected game :/
if (45 == event.GetId())
{
m_selected_game.assign(event.GetString().mb_str());
m_game_btn->SetLabel(event.GetString().Prepend(wxT(" Game : ")));
}
// chat messages
while (chat_msgs.Size())
{
std::string s;
chat_msgs.Pop(s);
//PanicAlert("message: %s", s.c_str());
m_chat_text->AppendText(wxString(s.c_str(), *wxConvCurrent).Append(wxT('\n')));
}
}

View File

@ -18,202 +18,87 @@
#ifndef _NETWINDOW_H_ #ifndef _NETWINDOW_H_
#define _NETWINDOW_H_ #define _NETWINDOW_H_
#include "CommonTypes.h"
#include <queue>
#include <string> #include <string>
#include <wx/wx.h> #include <wx/wx.h>
#include <wx/sizer.h> #include <wx/sizer.h>
#include <wx/dialog.h> #include <wx/dialog.h>
#include <wx/notebook.h> #include <wx/notebook.h>
#include <wx/gbsizer.h> #include <wx/gbsizer.h>
#include <wx/listbox.h> #include <wx/listbox.h>
//#include <wx/thread.h>
#include <wx/thread.h> #include "GameListCtrl.h"
#include "Globals.h" // just leaving these here so i can find something later if i need it
#include "BootManager.h" //#include "Frame.h"
#include "Common.h" //#include "Globals.h"
#include "NetStructs.h" //#include "BootManager.h"
#include "Core.h" //#include "Common.h"
#include "pluginspecs_pad.h" //#include "NetStructs.h"
#include "HW/SI.h" //#include "Core.h"
#include "HW/SI_Device.h" //#include "pluginspecs_pad.h"
#include "HW/SI_DeviceGCController.h" //#include "HW/SI.h"
#include "Timer.h" //#include "HW/SI_Device.h"
//#include "HW/SI_DeviceGCController.h"
//#include "Timer.h"
#ifdef _DEBUG #include "LockingQueue.h"
#define NET_DEBUG
#endif
// Use TCP instead of UDP to send pad data @ 60fps. Suitable and better for LAN netplay, class NetPlaySetupDiag : public wxFrame
// Unrealistic for Internet netplay, unless you have an uberfast connexion (<10ms ping)
// #define USE_TCP
class ServerSide;
class ClientSide;
class NetPlay : public wxFrame
{ {
public: public:
NetPlay(wxWindow* parent, std::string GamePath = "", std::string GameName = ""); NetPlaySetupDiag(wxWindow* parent, const CGameListCtrl* const game_list);
~NetPlay();
void UpdateNetWindow(bool update_infos, wxString=wxT("NULL")); private:
void AppendText(const wxString text) { m_Logging->AppendText(text); } void OnJoin(wxCommandEvent& event);
void OnHost(wxCommandEvent& event);
void OnQuit(wxCommandEvent& event);
// Send and receive pads values wxTextCtrl *m_nickname_text,
bool GetNetPads(u8 pad_nb, SPADStatus, u32 *netvalues); *m_host_port_text,
void ChangeSelectedGame(std::string game); *m_connect_port_text,
void IsGameFound(unsigned char*, std::string); *m_connect_ip_text;
std::string GetSelectedGame() { wxCriticalSectionLocker lock(m_critical); return m_selectedGame; }
void LoadGame(); wxListBox* m_game_lbox;
protected: const CGameListCtrl* const m_game_list;
// 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);
// Net play vars (used ingame)
int m_frame;
int m_lastframe;
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
std::string m_address; // The address entered into connection box
unsigned short m_port;
Netpads m_pads[4]; // this struct is used to save synced pad values
IniFile ConfigIni;
// Sockets objects
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;
wxButton *m_ExitWindowH;
// Connect tab :
wxTextCtrl *m_ConAddr;
wxStaticText *m_ConAddr_text;
wxButton *m_JoinGame;
wxButton *m_ExitWindowC;
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 class NetPlayDiag : public wxFrame
{ {
public: public:
GameListPopup(NetPlay *net_ptr, wxArrayString GameNames); NetPlayDiag(wxWindow* parent, const CGameListCtrl* const game_list
~GameListPopup() {} , const std::string& game, const bool is_hosting = false);
protected: ~NetPlayDiag();
void OnButtons(wxCommandEvent& event);
wxArrayString m_GameList_str; LockingQueue<std::string> chat_msgs;
NetPlay* m_netParent; //std::string chat_msg;
wxListBox *m_GameList;
wxButton *m_Accept; void OnStart(wxCommandEvent& event);
wxButton *m_Cancel;
DECLARE_EVENT_TABLE() private:
DECLARE_EVENT_TABLE()
void OnChat(wxCommandEvent& event);
void OnQuit(wxCommandEvent& event);
void OnThread(wxCommandEvent& event);
wxListBox* m_player_lbox;
wxTextCtrl* m_chat_text;
wxTextCtrl* m_chat_msg_text;
std::string m_selected_game;
wxButton* m_game_btn;
const CGameListCtrl* const m_game_list;
//NetPlay* const m_netplay;
}; };
enum DECLARE_EVENT_TYPE(wxEVT_THREAD, -1)
{
ID_NOTEBOOK,
ID_TAB_HOST,
ID_TAB_CONN,
ID_BUTTON_HOST,
ID_BUTTON_JOIN,
ID_BUTTON_EXIT,
ID_NETMODE,
ID_GAMELIST,
ID_LOGGING_TXT,
ID_CHAT,
ID_SETNICK,
ID_SETPORT,
ID_CONNADDR,
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_ #endif // _NETWINDOW_H_

View File

@ -37,9 +37,7 @@ if wxenv['HAVE_WX']:
'stdafx.cpp', 'stdafx.cpp',
'WxUtils.cpp', 'WxUtils.cpp',
'MemoryCards/WiiSaveCrypted.cpp', 'MemoryCards/WiiSaveCrypted.cpp',
'NetEvent.cpp', 'NetPlay.cpp',
'NetFunctions.cpp',
'NetSockets.cpp',
'NetWindow.cpp', 'NetWindow.cpp',
] ]